errmsg("set-valued function called in context that "
"cannot accept a set")));
-
rsi->returnMode = SFRM_Materialize;
- /*
- * get the tupdesc from the result set info - it must be a record type
- * because we already checked that arg1 is a record type.
- */
- (void) get_call_result_type(fcinfo, NULL, &tupdesc);
+ /* if the json is null send back an empty set */
+ if (PG_ARGISNULL(1))
+ PG_RETURN_NULL();
+
+ json = PG_GETARG_TEXT_P(1);
+
+ if (PG_ARGISNULL(0))
+ {
+ rec = NULL;
+
+ /*
+ * get the tupdesc from the result set info - it must be a record type
+ * because we already checked that arg1 is a record type
+ */
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("function returning record called in context "
+ "that cannot accept type record")));
+ }
+ else
+ {
+ rec = PG_GETARG_HEAPTUPLEHEADER(0);
+
+ /*
+ * use the input record's own type marking to find a tupdesc for it.
+ */
+ tupType = HeapTupleHeaderGetTypeId(rec);
+ tupTypmod = HeapTupleHeaderGetTypMod(rec);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ }
+
+ tupType = tupdesc->tdtypeid;
+ tupTypmod = tupdesc->tdtypmod;
+ ncolumns = tupdesc->natts;
state = palloc0(sizeof(PopulateRecordsetState));
sem = palloc0(sizeof(JsonSemAction));
-
/* make these in a sufficiently long-lived memory context */
old_cxt = MemoryContextSwitchTo(rsi->econtext->ecxt_per_query_memory);
MemoryContextSwitchTo(old_cxt);
- /* if the json is null send back an empty set */
- if (PG_ARGISNULL(1))
- PG_RETURN_NULL();
-
- json = PG_GETARG_TEXT_P(1);
-
- if (PG_ARGISNULL(0))
- rec = NULL;
- else
- rec = PG_GETARG_HEAPTUPLEHEADER(0);
-
- tupType = tupdesc->tdtypeid;
- tupTypmod = tupdesc->tdtypmod;
- ncolumns = tupdesc->natts;
+ /* unnecessary, but harmless, if tupdesc came from get_call_result_type: */
+ ReleaseTupleDesc(tupdesc);
lex = makeJsonLexContext(json, true);
ERROR: cannot call json_populate_recordset on a nested object
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
ERROR: cannot call json_populate_recordset on a nested object
+-- negative cases where the wrong record type is supplied
+select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+ERROR: function return row and query-specified return row do not match
+DETAIL: Returned row contains 1 attribute, but query expects 2.
+select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+ERROR: function return row and query-specified return row do not match
+DETAIL: Returned type integer at ordinal position 1, but query expects text.
+select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+ERROR: function return row and query-specified return row do not match
+DETAIL: Returned row contains 3 attributes, but query expects 2.
+select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);
+ERROR: function return row and query-specified return row do not match
+DETAIL: Returned type integer at ordinal position 1, but query expects text.
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"a":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
select * from json_populate_recordset(row('def',99,null)::jpop,'[{"c":[100,200,300],"x":43.2},{"a":{"z":true},"b":3,"c":"2012-01-20 10:42:53"}]') q;
+-- negative cases where the wrong record type is supplied
+select * from json_populate_recordset(row(0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+select * from json_populate_recordset(row(0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+select * from json_populate_recordset(row(0::int,0::int,0::int),'[{"a":"1","b":"2"},{"a":"3"}]') q (a text, b text);
+select * from json_populate_recordset(row(1000000000::int,50::int),'[{"b":"2"},{"a":"3"}]') q (a text, b text);