Commits

Andrew Dunstan committed 7673379

some code tidying

Comments (0)

Files changed (2)

src/backend/utils/adt/jsonfuncs.c

 static void hash_array_start(void *state);
 static void hash_scalar(void *state, char *token, JsonTokenType tokentype);
 
-
+/* semantic action functions for populate_recordset */
+static void populate_recordset_object_field_start(void *state, char *fname, bool isnull);
+static void populate_recordset_object_field_end(void *state, char *fname, bool isnull);
+static void populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
+static void populate_recordset_object_start(void *state);
+static void populate_recordset_object_end(void *state);
+static void populate_recordset_array_start(void *state);
+static void populate_recordset_array_element_start(void *state, bool isnull);
 
 /* search type classification for json_get* functions */
 typedef enum
 }	JsonSearch;
 
 /* state for json_object_keys */
-typedef struct
+typedef struct okeysState
 {
 	JsonLexContext *lex;
 	char	  **result;
 }	okeysState, *OkeysState;
 
 /* state for json_get* functions */
-typedef struct
+typedef struct getState
 {
 	JsonLexContext *lex;
 	JsonSearch	search_type;
 }	getState, *GetState;
 
 /* state for json_array_length */
-typedef struct
+typedef struct alenState
 {
 	JsonLexContext *lex;
 	int			count;
 }	alenState, *AlenState;
 
 /* state for json_each */
-typedef struct
+typedef struct eachState
 {
 	JsonLexContext *lex;
 	Tuplestorestate *tuple_store;
 }	eachState, *EachState;
 
 /* state for json_unnest */
-typedef struct
+typedef struct unnestState
 {
 	JsonLexContext *lex;
 	Tuplestorestate *tuple_store;
 }	unnestState, *UnnestState;
 
 /* state for get_json_object_as_hash */
-typedef struct
+typedef struct jhashState
 {
 	JsonLexContext *lex;
 	HTAB	   *hash;
 }	jhashState, *JHashState;
 
 /* used to build the hashtable */
-typedef struct
+typedef struct jsonHashEntry
 {
 	char		fname[NAMEDATALEN];
 	char	   *val;
 	bool		isnull;
 }	jsonHashEntry, *JsonHashEntry;
 
-/* these two are stolen from hstore / record_out, used in populate_record */
+/* these two are stolen from hstore / record_out, used in populate_record* */
 typedef struct ColumnIOData
 {
 	Oid			column_type;
 	ColumnIOData columns[1];	/* VARIABLE LENGTH ARRAY */
 }	RecordIOData;
 
+/* state for populate_recordset */
+typedef struct populateRecordsetState
+{
+	JsonLexContext *lex;
+	HTAB *json_hash;
+	char * saved_scalar;
+	Tuplestorestate *tuple_store;
+	TupleDesc ret_tdesc;
+	HeapTupleHeader rec;
+	RecordIOData *my_extra;
+	MemoryContext fn_mcxt; /* used to stash IO funcs */
+} populateRecordsetState, *PopulateRecordsetState;
+
 /*
  * SQL function json_object-keys
  *
 	json_hash = get_json_object_as_hash(jsonstr, "json_populate_record");
 
 	/*
-	 * if the input hstore is empty, we can only skip the rest if we were
+	 * if the input json is empty, we can only skip the rest if we were
 	 * passed in a non-null record, since otherwise there may be issues with
 	 * domain nulls.
 	 */
 }
 
 
-typedef struct populateRecordsetState
-{
-	JsonLexContext *lex;
-	HTAB *json_hash;
-	char * saved_scalar;
-	Tuplestorestate *tuple_store;
-	TupleDesc ret_tdesc;
-	HeapTupleHeader rec;
-	RecordIOData *my_extra;
-	MemoryContext fn_mcxt;
-} populateRecordsetState, *PopulateRecordsetState;
-
-static void populate_one_record(PopulateRecordsetState state);
-static void populate_recordset_object_field_start(void *state, char *fname, bool isnull);
-static void populate_recordset_object_field_end(void *state, char *fname, bool isnull);
-static void populate_recordset_scalar(void *state, char *token, JsonTokenType tokentype);
-static void populate_recordset_object_start(void *state);
-static void populate_recordset_object_end(void *state);
-static void populate_recordset_array_start(void *state);
-
 /*
  * SQL function json_populate_recordset
  *
- * set fields in a set of records from the argument json
+ * set fields in a set of records from the argument json,
+ * which must be an array of objects.
  *
- * Code adapted shamelessly from hstore's populate_record
- * which is in turn partly adapted from record_out.
- *
- * The json is decomposed into a hash table, in which each
- * field in the record is then looked up by name.
+ * similar to jsonb_populate_record, but the tuple-building code
+ * is pushed down into the semantic action handlers so it's done
+ * per object in the array.
  */
 
 PG_FUNCTION_INFO_V1(json_populate_recordset);
 
 	sem->semstate = (void *) state;
 	sem->array_start = populate_recordset_array_start;
+	sem->array_element_start = populate_recordset_array_element_start;
 	sem->scalar = populate_recordset_scalar;
 	sem->object_field_start = populate_recordset_object_field_start;
 	sem->object_field_end = populate_recordset_object_field_end;
 
 }
 
-static void populate_one_record(PopulateRecordsetState state)
+static void populate_recordset_object_start(void *state)
+{
+	PopulateRecordsetState _state = (PopulateRecordsetState) state;
+	int lex_level = _state->lex->lex_level;
+	HASHCTL		ctl;
+
+	if (lex_level == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			   errmsg("cannot call populate_recordset on an object")));
+	else if (lex_level > 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+			   errmsg("cannot call populate_recordset with nested objects")));
+
+	/* set up a new hash for this entry */
+	memset(&ctl, 0, sizeof(ctl));
+	ctl.keysize = NAMEDATALEN;
+	ctl.entrysize = sizeof(jsonHashEntry);
+	ctl.hcxt = CurrentMemoryContext;
+	_state->json_hash = hash_create("json object hashtable",
+									100,
+									&ctl,
+									HASH_ELEM | HASH_CONTEXT);
+}
+
+static void populate_recordset_object_end(void *state)
 {
 	PopulateRecordsetState _state = (PopulateRecordsetState) state;
-	HTAB *json_hash = state->json_hash;
+	HTAB *json_hash = _state->json_hash;
 	Datum *values;
 	bool *nulls;
 	char fname[NAMEDATALEN];
 	
 	tuplestore_puttuple(_state->tuple_store, rettuple);
 
+	hash_destroy(json_hash);
 }
 
-static void populate_recordset_object_start(void *state)
+static void populate_recordset_array_element_start(void *state, bool isnull)
 {
 	PopulateRecordsetState _state = (PopulateRecordsetState) state;
-	int lex_level = _state->lex->lex_level;
-	HASHCTL		ctl;
-
-	if (lex_level == 0)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("cannot call populate_recordset on an object")));
-	else if (lex_level > 1)
+	if (_state->lex->lex_level == 1 && 
+		_state->lex->token_type != JSON_TOKEN_OBJECT_START)
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-			   errmsg("cannot call populate_recordset with nested objects")));
-
-	/* set up a new hash for this entry */
-	memset(&ctl, 0, sizeof(ctl));
-	ctl.keysize = NAMEDATALEN;
-	ctl.entrysize = sizeof(jsonHashEntry);
-	ctl.hcxt = CurrentMemoryContext;
-	_state->json_hash = hash_create("json object hashtable",
-									100,
-									&ctl,
-									HASH_ELEM | HASH_CONTEXT);
-}
-
-static void populate_recordset_object_end(void *state)
-{
-	PopulateRecordsetState _state = (PopulateRecordsetState) state;
-
-	populate_one_record(_state);
-	hash_destroy(_state->json_hash);
+			   errmsg("must call populate_recordset on an array of objects")));
 }
 
 static void populate_recordset_array_start(void *state)

src/include/utils/jsonapi.h

 	JSON_TOKEN_END,
 }	JsonTokenType;
 
-typedef struct
+typedef struct JsonLexContext
 {
 	char	   *input;
 	char	   *token_start;
 
 
 /*
- * any of these actions can be NULL, in whi9ch case nothig is done.
+ * any of these actions can be NULL, in which case nothig is done.
  */
-typedef struct
+typedef struct jsonSemAction
 {
 	void	   *semstate;
 	json_struct_action object_start;