Andrew Dunstan avatar Andrew Dunstan committed 0e59621

json_array_length

Comments (0)

Files changed (5)

src/backend/utils/adt/jsonfuncs.c

 #include "utils/json.h"
 #include "utils/jsonapi.h"
 
-
+/* semantic action functions for json_object_keys */
 static void okeys_object_start(void *state);
 static void okeys_object_end(void *state);
 static void okeys_object_field_start(void *state, char *fname, bool isnull);
 static void okeys_array_end(void *state);
 static void okeys_scalar(void *state, char *token, JsonTokenType tokentype);
 
+/* semantic action functions for json_get* functions */
 static void get_object_start(void *state);
 static void get_object_end(void *state);
 static void get_object_field_start(void *state, char *fname, bool isnull);
 static void get_array_element_start(void *state, bool isnull);
 static void get_array_element_end(void *state, bool isnull);
 static void get_scalar(void *state, char *token, JsonTokenType tokentype);
+/* common workeer function for json_get* functions */
 static text *get_worker(char *json, char *field, int elem_index, bool normalize_results);
 
+/* semantic action functions for json_array_length */
+static void alen_object_start(void *state);
+static void alen_array_start(void *state);
+static void alen_array_end(void *state);
+static void alen_scalar(void *state, char *token, JsonTokenType tokentype);
+static void alen_array_element_start(void *state, bool isnull);
+
 typedef enum
 {
 	JSON_SEARCH_OBJECT = 1,
 	JSON_SEARCH_ARRAY
 }	JsonSearch;
 
+/* stats for json_object_keys */
 typedef struct
 {
 	int			lex_level;
 	int			sent_count;
 }	okeysState, *OkeysState;
 
-
+/* state for json_get* functions */
 typedef struct
 {
 	JsonLexContext *lex;
 	bool		next_scalar;
 }	getState, *GetState;
 
+/* state for json_array_length */
+typedef struct
+{
+	int			lex_level;
+	int         count;
+}	alenState, *AlenState;
+
 
 
 PG_FUNCTION_INFO_V1(json_object_keys);
 	}
 
 }
+
+
+PG_FUNCTION_INFO_V1(json_array_length);
+
+Datum
+json_array_length(PG_FUNCTION_ARGS)
+{
+	text	   *json = PG_GETARG_TEXT_P(0);
+	char	   *jsonstr = text_to_cstring(json);
+
+	AlenState	   state;
+	JsonLexContext lex;
+	JsonSemAction  sem;
+
+	state = palloc0(sizeof(alenState));
+	sem = palloc0(sizeof(jsonSemAction));
+
+	/* palloc0 does this for us */
+#if 0	 
+	state->lex_level = 0;
+	state->count = 0;
+#endif
+
+	sem->semstate = (void *) state;
+	sem->object_start = alen_object_start;
+	sem->array_start = alen_array_start;
+	sem->array_end = alen_array_end;
+	sem->scalar = alen_scalar;
+	sem->array_element_start = alen_array_element_start;
+
+	/* Set up lexing context. */
+	lex.input = jsonstr;
+	lex.token_terminator = lex.input;
+	lex.line_number = 1;
+	lex.line_start = jsonstr;
+	lex.strval = NULL ; /* just counting, so this is faster */
+
+	pg_parse_json(&lex, sem);
+
+	PG_RETURN_INT32(state->count);
+}
+
+
+static void
+alen_array_start(void *state)
+{
+	AlenState	_state = (AlenState) state;
+
+	_state->lex_level++;
+}
+
+static void
+alen_array_end(void *state)
+{
+	AlenState	_state = (AlenState) state;
+
+	_state->lex_level--;
+}
+
+static void
+alen_object_start(void *state)
+{
+	AlenState	_state = (AlenState) state;
+
+	if (_state->lex_level == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_array_length on an object")));
+}
+
+static void
+alen_scalar(void *state, char *token, JsonTokenType tokentype)
+{
+	AlenState	_state = (AlenState) state;
+
+	if (_state->lex_level == 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+				 errmsg("cannot call json_array_length on a scalar")));
+}
+
+static void
+alen_array_element_start(void *state, bool isnull)
+{
+	AlenState	_state = (AlenState) state;
+
+	if (_state->lex_level == 1)
+		_state->count++;
+}
+

src/include/catalog/pg_proc.h

 DESCR("get json array element as text");
 DATA(insert OID = 5005 (  json_object_keys PGNSP PGUID 12 1 100 0 0 f f f f t t s 1 0 25 "114" _null_ _null_ _null_ _null_ json_object_keys _null_ _null_ _null_ ));
 DESCR("get json object keys");
+DATA(insert OID = 5006 (  json_array_length PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 23 "114" _null_ _null_ _null_ _null_ json_array_length _null_ _null_ _null_ ));
+DESCR("length of json array");
 
 
 /* uuid */

src/include/utils/json.h

 extern Datum json_get_ofield_as_text(PG_FUNCTION_ARGS);
 extern Datum json_get_ofield(PG_FUNCTION_ARGS);
 extern Datum json_object_keys(PG_FUNCTION_ARGS);
+extern Datum json_array_length(PG_FUNCTION_ARGS);
 
 #endif   /* JSON_H */

src/test/regress/expected/json.out

  field2
 (2 rows)
 
+-- array length
+SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+ json_array_length 
+-------------------
+                 5
+(1 row)
+
+SELECT json_array_length('[]');
+ json_array_length 
+-------------------
+                 0
+(1 row)
+
+SELECT json_array_length('{"f1":1,"f2":[5,6]}');
+ERROR:  cannot call json_array_length on an object
+SELECT json_array_length('4');
+ERROR:  cannot call json_array_length on a scalar

src/test/regress/sql/json.sql

 FROM test_json
 WHERE json_type = 'object';
 
+-- array length
+
+SELECT json_array_length('[1,2,3,{"f1":1,"f2":[5,6]},4]');
+
+SELECT json_array_length('[]');
+
+SELECT json_array_length('{"f1":1,"f2":[5,6]}');
+
+SELECT json_array_length('4');
+
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.