From 8daec6003ae1f651565b54e5774b71f1b75092c7 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Mon, 23 Jun 2014 20:58:57 +0900
Subject: [PATCH] Fix incorrect hash table management in json_to_recordset

The hash table used to store items parsed by json_to_recordset was being
created each time a new json item was found in the string being parsed,
having as consequence to remove in the hash table items that were already
parsed. This commit makes sure that the hash table is not created twice
when the code path starting json_to_recordset is reaching at least the
second nested level in JSON string parsed.

Per bug #10728 by Matti Hameister.
---
 src/backend/utils/adt/jsonfuncs.c    | 6 ++++++
 src/test/regress/expected/json.out   | 8 ++++++++
 src/test/regress/expected/json_1.out | 8 ++++++++
 src/test/regress/sql/json.sql        | 3 +++
 4 files changed, 25 insertions(+)

diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 71179f6..26d978c 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -2735,6 +2735,10 @@ populate_recordset_object_start(void *state)
 	int			lex_level = _state->lex->lex_level;
 	HASHCTL		ctl;
 
+	/*
+	 * Sanity checks. If nested objects are found in this code path
+	 * use the existing hash table instead of creating a new one.
+	 */
 	if (lex_level == 0)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -2743,6 +2747,8 @@ populate_recordset_object_start(void *state)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
 		 errmsg("cannot call json_populate_recordset with nested objects")));
+	else if (lex_level > 1)
+		return;
 
 	/* set up a new hash for this entry */
 	memset(&ctl, 0, sizeof(ctl));
diff --git a/src/test/regress/expected/json.out b/src/test/regress/expected/json.out
index 8b8556b..8415474 100644
--- a/src/test/regress/expected/json.out
+++ b/src/test/regress/expected/json.out
@@ -1223,3 +1223,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
  2 | bar | t
 (2 rows)
 
+SELECT * FROM json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
+    as x(a int, b json, c boolean);
+ a |      b      | c 
+---+-------------+---
+ 1 | {"d":"foo"} | t
+ 2 | {"d":"bar"} | f
+(2 rows)
+
diff --git a/src/test/regress/expected/json_1.out b/src/test/regress/expected/json_1.out
index b32c3ed..2e44164 100644
--- a/src/test/regress/expected/json_1.out
+++ b/src/test/regress/expected/json_1.out
@@ -1219,3 +1219,11 @@ select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","
  2 | bar | t
 (2 rows)
 
+SELECT * FROM json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
+    as x(a int, b json, c boolean);
+ a |      b      | c 
+---+-------------+---
+ 1 | {"d":"foo"} | t
+ 2 | {"d":"bar"} | f
+(2 rows)
+
diff --git a/src/test/regress/sql/json.sql b/src/test/regress/sql/json.sql
index 3d5ed50..a671185 100644
--- a/src/test/regress/sql/json.sql
+++ b/src/test/regress/sql/json.sql
@@ -447,3 +447,6 @@ select * from json_to_record('{"a":1,"b":"foo","c":"bar"}',true)
 
 select * from json_to_recordset('[{"a":1,"b":"foo","d":false},{"a":2,"b":"bar","c":true}]',false)
     as x(a int, b text, c boolean);
+
+SELECT * FROM json_to_recordset('[{"a":1,"b":{"d":"foo"},"c":true},{"a":2,"c":false,"b":{"d":"bar"}}]', true)
+    as x(a int, b json, c boolean);
-- 
2.0.0

