Commits

Andrew Dunstan committed ec935ee

backport upstream changes

  • Participants
  • Parent commits 5d6e7a0

Comments (0)

Files changed (7)

 exists, when rendering a datum of a non-builtin type to json. This means, 
 for example, that a record containing an hstore will have the hstore 
 rendered sanely as a json object rather than as an opaque piece of text.
-* `json_get(json,text)` gives back the value of the named field from a 
+* `json_get(json,text, ...)` gives back the value of the named field from a 
 json object
 * `json_get(json, int)` gives back the value of the indicated element from a 
 json array. Elements are counted from 0, unlike SQL arrays.
-* `json_get_as_text(json, text)` and `json_get_as_text(json, int)` do the same 
+* `json_get_text(json, text, ...)` and `json_get_text(json, int)` do the same 
 thing as as the `json_get()` functions, but if the item to be returned is a 
 string it is returned de-escaped and de-quoted.
-* `json_get_path(json, variadic text[])` and 
-`json_get_path_as_text(json, variadic text[])` return the element at the 
+* `json_get_path(json, text[])` and 
+`json_get_path_text(json, text[])` return the element at the 
 denoted path. Elements in the path can be field names if the element at this 
 nesting level is a json object or textified integers if the item is an array.
 * operators `->` for `json_get()`, `#>` for  `json_get_path()`, `->>` for 
-`json_get_as_text(`) and `#>>` for `json_get_path_as_text()`
+`json_get_text(`) and `#>>` for `json_get_path_text()`
 * `json_object_keys(json)` returns the set of keys in the json object
 * `json_array_length(json)` returns the length of the json array
 * `json_unnest(json)` returns the elements in the json array. Unlike SQL's 

File sql/json_enhancements.sql

 
 CREATE FUNCTION json_get(the_json json, element integer)
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_aelem'
+AS 'MODULE_PATHNAME' , 'json_get_int'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_as_text(the_json json, element integer)
+CREATE FUNCTION json_get_text(the_json json, element integer)
 RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_aelem_as_text'
+AS 'MODULE_PATHNAME' , 'json_get_text_int'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get(the_json json, field_name text)
+CREATE FUNCTION json_get(the_json json, variadic path_elements text[])
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_ofield'
+AS 'MODULE_PATHNAME' , 'json_get'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_as_text(the_json json, field_name text)
+CREATE FUNCTION json_get_text(the_json json, variadic path_elements text[])
 RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_ofield_as_text'
+AS 'MODULE_PATHNAME' , 'json_get_text'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION json_get_op(json, text)
+RETURNS json
+AS 'MODULE_PATHNAME' , 'json_get_op'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION json_get_text_op(json, text)
+RETURNS text
+AS 'MODULE_PATHNAME' , 'json_get_text_op'
 LANGUAGE C STRICT IMMUTABLE;
 
 CREATE FUNCTION json_object_keys(the_json json)
 AS 'MODULE_PATHNAME' 
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_path(the_json json, VARIADIC path_element text[])
+CREATE FUNCTION json_get_path(the_json json, path_element text[])
 RETURNS json
-AS 'MODULE_PATHNAME' 
+AS 'MODULE_PATHNAME'
 LANGUAGE C STRICT IMMUTABLE;
 
-/* non-variadic variant for use as operator */
-CREATE FUNCTION json_get_path_op(the_json json, path_element text[])
+CREATE FUNCTION json_get_path_text(the_json json, path_element text[])
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION json_get_path_as_text(the_json json, VARIADIC path_element text[])
-RETURNS text
-AS 'MODULE_PATHNAME' 
-LANGUAGE C STRICT IMMUTABLE;
-
-/* non-variadic variant for use as operator */
-CREATE FUNCTION json_get_path_as_text_op(the_json json, path_element text[])
-RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_path_as_text'
+AS 'MODULE_PATHNAME'
 LANGUAGE C STRICT IMMUTABLE;
 
 CREATE FUNCTION json_unnest(the_json json)
 LANGUAGE C IMMUTABLE;
 
 CREATE OPERATOR -> (
-       PROCEDURE = json_get,
+       PROCEDURE = json_get_op,
        LEFTARG = json, RIGHTARG = text
 );
 
 );
 
 CREATE OPERATOR ->> (
-       PROCEDURE = json_get_as_text,
+       PROCEDURE = json_get_text_op,
        LEFTARG = json, RIGHTARG = text
 );
 
 CREATE OPERATOR ->> (
-       PROCEDURE = json_get_as_text,
+       PROCEDURE = json_get_text,
        LEFTARG = json, RIGHTARG = int
 );
 
 CREATE OPERATOR #> (
-       PROCEDURE = json_get_path_op,
+       PROCEDURE = json_get_path,
        LEFTARG = json, RIGHTARG = text[]
 );
 
 CREATE OPERATOR #>> (
-       PROCEDURE = json_get_path_as_text_op,
+       PROCEDURE = json_get_path_text,
        LEFTARG = json, RIGHTARG = text[]
 );
 

File sql/json_enhancements_no_hstore.sql

 
 CREATE FUNCTION json_get(the_json json, element integer)
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_aelem'
+AS 'MODULE_PATHNAME' , 'json_get_int'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_as_text(the_json json, element integer)
+CREATE FUNCTION json_get_text(the_json json, element integer)
 RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_aelem_as_text'
+AS 'MODULE_PATHNAME' , 'json_get_text_int'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get(the_json json, field_name text)
+CREATE FUNCTION json_get(the_json json, variadic path_elements text[])
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_ofield'
+AS 'MODULE_PATHNAME' , 'json_get'
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_as_text(the_json json, field_name text)
+CREATE FUNCTION json_get_text(the_json json, variadic path_elements text[])
 RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_ofield_as_text'
+AS 'MODULE_PATHNAME' , 'json_get_text'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION json_get_op(json, text)
+RETURNS json
+AS 'MODULE_PATHNAME' , 'json_get_op'
+LANGUAGE C STRICT IMMUTABLE;
+
+CREATE FUNCTION json_get_text_op(json, text)
+RETURNS text
+AS 'MODULE_PATHNAME' , 'json_get_text_op'
 LANGUAGE C STRICT IMMUTABLE;
 
 CREATE FUNCTION json_object_keys(the_json json)
 AS 'MODULE_PATHNAME' 
 LANGUAGE C STRICT IMMUTABLE;
 
-CREATE FUNCTION json_get_path(the_json json, VARIADIC path_element text[])
+CREATE FUNCTION json_get_path(the_json json, path_element text[])
 RETURNS json
-AS 'MODULE_PATHNAME' 
+AS 'MODULE_PATHNAME'
 LANGUAGE C STRICT IMMUTABLE;
 
-/* non-variadic variant for use as operator */
-CREATE FUNCTION json_get_path_op(the_json json, path_element text[])
+CREATE FUNCTION json_get_path_text(the_json json, path_element text[])
 RETURNS json
-AS 'MODULE_PATHNAME' , 'json_get_path'
-LANGUAGE C STRICT IMMUTABLE;
-
-CREATE FUNCTION json_get_path_as_text(the_json json, VARIADIC path_element text[])
-RETURNS text
-AS 'MODULE_PATHNAME' 
-LANGUAGE C STRICT IMMUTABLE;
-
-/* non-variadic variant for use as operator */
-CREATE FUNCTION json_get_path_as_text_op(the_json json, path_element text[])
-RETURNS text
-AS 'MODULE_PATHNAME' , 'json_get_path_as_text'
+AS 'MODULE_PATHNAME'
 LANGUAGE C STRICT IMMUTABLE;
 
 CREATE FUNCTION json_unnest(the_json json)
 LANGUAGE C IMMUTABLE;
 
 CREATE OPERATOR -> (
-       PROCEDURE = json_get,
+       PROCEDURE = json_get_op,
        LEFTARG = json, RIGHTARG = text
 );
 
 );
 
 CREATE OPERATOR ->> (
-       PROCEDURE = json_get_as_text,
+       PROCEDURE = json_get_text_op,
        LEFTARG = json, RIGHTARG = text
 );
 
 CREATE OPERATOR ->> (
-       PROCEDURE = json_get_as_text,
+       PROCEDURE = json_get_text,
        LEFTARG = json, RIGHTARG = int
 );
 
-CREATE OPERATOR -> (
-       PROCEDURE = json_get_path_op,
+CREATE OPERATOR #> (
+       PROCEDURE = json_get_path,
        LEFTARG = json, RIGHTARG = text[]
 );
 
-CREATE OPERATOR ->> (
-       PROCEDURE = json_get_path_as_text_op,
+CREATE OPERATOR #>> (
+       PROCEDURE = json_get_path_text,
        LEFTARG = json, RIGHTARG = text[]
 );
 
-
 CREATE FUNCTION to_json(anyelement)
 RETURNS json
 AS 'MODULE_PATHNAME'

File src/json_extra.h

 extern Datum json_agg_finalfn(PG_FUNCTION_ARGS);
 
 /* functions in jsonfuncs.c */
-extern Datum json_get_aelem_as_text(PG_FUNCTION_ARGS);
-extern Datum json_get_aelem(PG_FUNCTION_ARGS);
-extern Datum json_get_ofield_as_text(PG_FUNCTION_ARGS);
-extern Datum json_get_ofield(PG_FUNCTION_ARGS);
+extern Datum json_get(PG_FUNCTION_ARGS);
+extern Datum json_get_text(PG_FUNCTION_ARGS);
+extern Datum json_get_int(PG_FUNCTION_ARGS);
+extern Datum json_get_text_int(PG_FUNCTION_ARGS);
+extern Datum json_get_op(PG_FUNCTION_ARGS);
+extern Datum json_get_text_op(PG_FUNCTION_ARGS);
+extern Datum json_get_path(PG_FUNCTION_ARGS);
+extern Datum json_get_path_text(PG_FUNCTION_ARGS);
 extern Datum json_object_keys(PG_FUNCTION_ARGS);
 extern Datum json_array_length(PG_FUNCTION_ARGS);
 extern Datum json_each(PG_FUNCTION_ARGS);
 extern Datum json_each_as_text(PG_FUNCTION_ARGS);
-extern Datum json_get_path(PG_FUNCTION_ARGS);
-extern Datum json_get_path_as_text(PG_FUNCTION_ARGS);
 extern Datum json_unnest(PG_FUNCTION_ARGS);
 extern Datum json_populate_record(PG_FUNCTION_ARGS);
 extern Datum json_populate_recordset(PG_FUNCTION_ARGS);

File src/jsonfuncs.c

 static void get_scalar(void *state, char *token, JsonTokenType tokentype);
 
 /* common worker function for json_get* functions */
-static text *get_worker(text *json, char *field, int elem_index, char **path,
-		   int npath, bool normalize_results);
+static inline Datum get_all(PG_FUNCTION_ARGS, bool as_text);
+static inline Datum get_path_all(PG_FUNCTION_ARGS, bool as_text);
+static inline text *get_worker(text *json, char *field, int elem_index, 
+							   char **tpath, int *ipath, int npath, 
+							   bool normalize_results);
 
 /* semantic action functions for json_array_length */
 static void alen_object_start(void *state);
 
 /*
  * json_get* functions
- * these all use a common worker, just with some slightly
- * different setup options.
+ * json_get and json_get_text are variadic forms
+ * json_get_path and json_get_path_text take an array of text for the path
+ * Note: we have separate _int and _op versions of these function to keep the
+ * opr_sanity checks happy.
  */
 
 
-/*
- * SQL function json_get(json text) -> json
- *
- * return json for named field
- *
- * also used for json -> text operator
- */
-PG_FUNCTION_INFO_V1(json_get_ofield);
+PG_FUNCTION_INFO_V1(json_get);
 
 Datum
-json_get_ofield(PG_FUNCTION_ARGS)
+json_get(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	text	   *fname = PG_GETARG_TEXT_P(1);
-	char	   *fnamestr = text_to_cstring(fname);
-	text	   *result;
-
-	result = get_worker(json, fnamestr, -1, NULL, -1, false);
-
-	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
-	else
-		PG_RETURN_NULL();
+	/* not the text variant, so set as_text to false. */
+	return get_all(fcinfo, false);
 }
 
-/*
- * SQL function json_get_as_text(json text) -> text
- *
- * return text for named field. If the field is a
- * string the de-escaped value of the string is delivered.
- *
- * also used for json ->> text operator
- */
-
-PG_FUNCTION_INFO_V1(json_get_ofield_as_text);
+PG_FUNCTION_INFO_V1(json_get_text);
 
 Datum
-json_get_ofield_as_text(PG_FUNCTION_ARGS)
+json_get_text(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	text	   *fname = PG_GETARG_TEXT_P(1);
-	char	   *fnamestr = text_to_cstring(fname);
-	text	   *result;
-
-	result = get_worker(json, fnamestr, -1, NULL, -1, true);
-	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
-	else
-		PG_RETURN_NULL();
+	/* the text variant, so set as_text to true. */
+	return get_all(fcinfo, true);
 }
 
-/*
- * SQL function json_get(json, int) -> json
- *
- * return json for numbered field
- *
- * also used for json -> int operator
- */
-
-PG_FUNCTION_INFO_V1(json_get_aelem);
+PG_FUNCTION_INFO_V1(json_get_int);
 
 Datum
-json_get_aelem(PG_FUNCTION_ARGS)
+json_get_int(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	int			element = PG_GETARG_INT32(1);
-	text	   *result;
-
-	result = get_worker(json, NULL, element, NULL, -1, false);
-
-	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
-	else
-		PG_RETURN_NULL();
+	/* not the text variant, so set as_text to false. */
+	return get_all(fcinfo, false);
 }
 
+PG_FUNCTION_INFO_V1(json_get_text_int);
 
-/*
- * SQL function json_get_as_text(json, int) -> text
- *
- * return text for numbered field . If the field is a
- * string the de-escaped value of the string is delivered.
- *
- * also used for json ->> int operator
- */
+Datum
+json_get_text_int(PG_FUNCTION_ARGS)
+{
+	/* the text variant, so set as_text to true. */
+	return get_all(fcinfo, true);
+}
 
-PG_FUNCTION_INFO_V1(json_get_aelem_as_text);
+PG_FUNCTION_INFO_V1(json_get_op);
 
 Datum
-json_get_aelem_as_text(PG_FUNCTION_ARGS)
+json_get_op(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
-	int			element = PG_GETARG_INT32(1);
-	text	   *result;
+	/* not the text variant, so set as_text to false. */
+	return get_all(fcinfo, false);
+}
 
-	result = get_worker(json, NULL, element, NULL, -1, true);
+PG_FUNCTION_INFO_V1(json_get_text_op);
 
-	if (result != NULL)
-		PG_RETURN_TEXT_P(result);
-	else
-		PG_RETURN_NULL();
+Datum
+json_get_text_op(PG_FUNCTION_ARGS)
+{
+	/* the text variant, so set as_text to true. */
+	return get_all(fcinfo, true);
 }
 
+static inline Datum
+get_all(PG_FUNCTION_ARGS, bool as_text)
+{
+	text *json = PG_GETARG_TEXT_P(0);
+	text *result = NULL;
 
-/*
- * SQL function json_get_path(json, variadic text[]) -> json
- *
- * return json for object pointed to by path contained in second
- * parameter. If the json structure refered to by a path element is
- * an array, the path element is treated as a (zero based) index. If
- * it's an object it is treated as a field name. Since SQL arrays are
- * homogeneous, integer arguments for array indexes must be passed as text.
- *
- * There is also a non-variadic function json_get_path_op
- * that maps to this function and is used in the construction of the
- * json -> text[] operator.
- */
+	Oid         element_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
 
-PG_FUNCTION_INFO_V1(json_get_path);
+	if (element_type == INT4OID)
+	{
+		int element =  PG_GETARG_INT32(1);
+		
+		result = get_worker(json, NULL, element, NULL, NULL, -1, as_text);
+	}
+	else if (element_type == TEXTOID)
+	{
+		text	   *fname = PG_GETARG_TEXT_P(1);
+		char	   *fnamestr = text_to_cstring(fname);
 
-Datum
-json_get_path(PG_FUNCTION_ARGS)
-{
-	text	   *json = PG_GETARG_TEXT_P(0);
-	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
-	text	   *result;
-	Datum	   *pathtext;
-	bool	   *pathnulls;
-	int			npath;
-	char	  **pathstr;
-	int			i;
+		result = get_worker(json, fnamestr, -1, NULL, NULL, -1, as_text);
+	}
+	else
+	{
+		/* must be a text array */
+		
+		ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
+		Datum	   *pathtext;
+		bool	   *pathnulls;
+		int			npath;
+		char	  **tpath;
+		int        *ipath;
+		int			i;
+		long        ind;
+		char       *endptr;
 
-	if (array_contains_nulls(path))
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot call %s with null path elements",
-						"json_get_path_as_text")));
+		if (array_contains_nulls(path))
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("cannot call function with null path elements")));
 
 
-	deconstruct_array(path, TEXTOID, -1, false, 'i',
-					  &pathtext, &pathnulls, &npath);
+		deconstruct_array(path, TEXTOID, -1, false, 'i',
+						  &pathtext, &pathnulls, &npath);
 
-	pathstr = palloc(npath * sizeof(char *));
+		/* 
+		 * the search is a bit optimized for a single element path.
+		 */
+		if (npath == 1)
+		{
+			char	   *fname = TextDatumGetCString(pathtext[0]);
+			
+			/* 
+			 * XXX is it worth checking for an int string here?
+			 * For the moment probably not. We'd have top fall back on the
+			 * multi-element path call if we found one, and it would be messy.
+			 * The alternative would be to abandon this use of the optimized
+			 * single element call.
+			 */
+			result = get_worker(json, fname, -1, NULL, NULL, -1, as_text);
+		}
+		else
+		{
 
-	for (i = 0; i < npath; i++)
-	{
-		pathstr[i] = TextDatumGetCString(pathtext[i]);
-		if (*pathstr[i] == '\0')
-			ereport(
-					ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("cannot call %s with empty path elements",
-							"json_get_path_as_text")));
+			tpath = palloc(npath * sizeof(char *));
+			ipath = palloc(npath * sizeof(int));
+		
+			for (i = 0; i < npath; i++)
+			{
+				tpath[i] = TextDatumGetCString(pathtext[i]);
+				if (*tpath[i] == '\0')
+					ereport(
+						ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("cannot call function with empty path elements")));
+				ind = strtol(tpath[i], &endptr, 10);
+				if (*endptr == '\0' && ind <= INT_MAX && ind >= 0)
+					ipath[i] = (int) ind;
+				else
+					ipath[i] = -1;
+			}
+			
+			result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
+		}
+		
 	}
 
-	result = get_worker(json, NULL, -1, pathstr, npath, false);
-
 	if (result != NULL)
 		PG_RETURN_TEXT_P(result);
 	else
 		PG_RETURN_NULL();
 }
 
-/*
- * SQL function json_get_path_as_text(json, variadic text[]) -> json
- *
- * return text for object pointed to by path contained in second
- * parameter. If the json structure refered to by a path element is
- * an array, the path element is treated as a (zero based) index. If
- * it's an object it is treated as a field name. Since SQL arrays are
- * homogeneous, integer arguments for array indexes must be passed as text.
- *
- * If the field is a string the de-escaped value of the string is delivered.
- *
- * There is also a non-variadic function json_get_path_as_text_op
- * that maps to this function and is used in the construction of the
- * json ->> text[] operator.
- */
+PG_FUNCTION_INFO_V1(json_get_path);
 
-PG_FUNCTION_INFO_V1(json_get_path_as_text);
+Datum
+json_get_path(PG_FUNCTION_ARGS)
+{
+	/* not the text variant, so set as_text to false. */
+	return get_path_all(fcinfo, false);
+}
+
+PG_FUNCTION_INFO_V1(json_get_path_text);
 
 Datum
-json_get_path_as_text(PG_FUNCTION_ARGS)
+json_get_path_text(PG_FUNCTION_ARGS)
 {
-	text	   *json = PG_GETARG_TEXT_P(0);
+	/* the text variant, so set as_text to true. */
+	return get_path_all(fcinfo, true);
+}
+
+
+static inline Datum
+get_path_all(PG_FUNCTION_ARGS, bool as_text)
+{
+	text *json = PG_GETARG_TEXT_P(0);
 	ArrayType  *path = PG_GETARG_ARRAYTYPE_P(1);
 	text	   *result;
 	Datum	   *pathtext;
 	bool	   *pathnulls;
 	int			npath;
-	char	  **pathstr;
+	char	  **tpath;
+	int        *ipath;
 	int			i;
+	long        ind;
+	char       *endptr;
 
 	if (array_contains_nulls(path))
 		ereport(ERROR,
 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-				 errmsg("cannot call %s with null path elements",
-						"json_get_path_as_text")));
+				 errmsg("cannot call function with null path elements")));
 
 
 	deconstruct_array(path, TEXTOID, -1, false, 'i',
 					  &pathtext, &pathnulls, &npath);
 
-	pathstr = palloc(npath * sizeof(char *));
+	tpath = palloc(npath * sizeof(char *));
+	ipath = palloc(npath * sizeof(int));
+	
 
 	for (i = 0; i < npath; i++)
 	{
-		pathstr[i] = TextDatumGetCString(pathtext[i]);
-		if (*pathstr[i] == '\0')
+		tpath[i] = TextDatumGetCString(pathtext[i]);
+		if (*tpath[i] == '\0')
 			ereport(
 					ERROR,
 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("cannot call %s with empty path elements",
-							"json_get_path_as_text")));
+					 errmsg("cannot call function with empty path elements")));
+		/*
+		 * we have no idea at this stage what structure the document is so
+		 * just convert anything in the path that we can to an integer and set
+		 * all the other integers to -1 which will never match.
+		 */
+		ind = strtol(tpath[i], &endptr, 10);
+		if (*endptr == '\0' && ind <= INT_MAX && ind >= 0)
+			ipath[i] = (int) ind;
+		else
+			ipath[i] = -1;
 	}
 
-	result = get_worker(json, NULL, -1, pathstr, npath, true);
+
+	result = get_worker(json, NULL, -1, tpath, ipath, npath, as_text);
 
 	if (result != NULL)
 		PG_RETURN_TEXT_P(result);
 		PG_RETURN_NULL();
 }
 
+
 /*
  * get_worker
  *
 get_worker(text *json,
 		   char *field,
 		   int elem_index,
-		   char **path,
+		   char **tpath,
+		   int  *ipath,
 		   int npath,
 		   bool normalize_results)
 {
 	JsonSemAction sem;
 
 	/* only allowed to use one of these */
-	Assert(elem_index < 0 || (path == NULL && field == NULL));
-	Assert(path == NULL || field == NULL);
+	Assert(elem_index < 0 || (tpath == NULL && ipath == NULL && field == NULL));
+	Assert(tpath == NULL || field == NULL);
 
 	state = palloc0(sizeof(getState));
 	sem = palloc0(sizeof(jsonSemAction));
 
 	state->lex = lex;
-	/* is is "_as_text" variant? */
+	/* is it "_as_text" variant? */
 	state->normalize_results = normalize_results;
 	if (field != NULL)
 	{
 		state->search_type = JSON_SEARCH_OBJECT;
 		state->search_term = field;
 	}
-	else if (path != NULL)
+	else if (tpath != NULL)
 	{
 		/* path array argument */
-		int			i;
-		long int	ind;
-		char	   *endptr;
-
 		state->search_type = JSON_SEARCH_PATH;
-		state->path = path;
+		state->path = tpath;
 		state->npath = npath;
 		state->current_path = palloc(sizeof(char *) * npath);
 		state->pathok = palloc(sizeof(bool) * npath);
 		state->pathok[0] = true;
 		state->array_level_index = palloc(sizeof(int) * npath);
-		state->path_level_index = palloc(sizeof(int) * npath);
+		state->path_level_index = ipath;
 
-		/*
-		 * we have no idea at this stage what structure the document is so
-		 * just convert anything in the path that we can to an integer and set
-		 * all the other integers to -1 which will never match.
-		 */
-		for (i = 0; i < npath; i++)
-		{
-			ind = strtol(path[i], &endptr, 10);
-			if (*endptr == '\0' && ind <= INT_MAX && ind >= 0)
-				state->path_level_index[i] = (int) ind;
-			else
-				state->path_level_index[i] = -1;
-		}
 	}
 	else
 	{
 
 	/*
 	 * Not all	variants need all the semantic routines. only set the ones
-	 * that ar actually needed for maximum efficiency.
+	 * that are actually needed for maximum efficiency.
 	 */
 	sem->object_start = get_object_start;
 	sem->array_start = get_array_start;
 	sem->scalar = get_scalar;
-	if (field != NULL || path != NULL)
+	if (field != NULL || tpath != NULL)
 	{
 		sem->object_field_start = get_object_field_start;
 		sem->object_field_end = get_object_field_end;
 	if (lex_level == 1 && _state->search_type == JSON_SEARCH_OBJECT &&
 		strcmp(fname, _state->search_term) == 0)
 	{
-		/* single field search and we have a match at the right nesting level */
+		
+		_state->tresult = NULL;
+		_state->result_start = NULL;
 		get_next = true;
 	}
 	else if (_state->search_type == JSON_SEARCH_PATH &&
 	{
 		/* path search, path so far is ok,	and we have a match */
 
+		/* this object overrides any previous matching object */
+
+		_state->tresult = NULL;
+		_state->result_start = NULL;
+
 		/* if not at end of path just mark path ok */
 		if (lex_level < _state->npath)
 			_state->pathok[lex_level] = true;
 
 	if (get_next)
 	{
-		/*
-		 * If tresult is already set it means we've already made this match.
-		 * So complain about it.
-		 */
-		if (_state->tresult != NULL || _state->result_start != NULL)
-			ereport(ERROR,
-					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-					 errmsg("field name is not unique in json object")));
-
 		if (_state->normalize_results &&
 			_state->lex->token_type == JSON_TOKEN_STRING)
 		{
 
 	hashentry = hash_search(_state->hash, name, HASH_ENTER, &found);
 
-	if (found)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("duplicate object field name: \"%s\"", fname)));
+	/* 
+	 * found being true indicates a duplicate. We don't do anything about that,
+	 * a later field with the same name overrides the earlier field.
+	 */
 
 	hashentry->isnull = isnull;
 	if (_state->save_json_start != NULL)
 
 	hashentry = hash_search(_state->json_hash, name, HASH_ENTER, &found);
 
-	if (found)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
-				 errmsg("duplicate object field name: \"%s\"", fname)));
+	/* 
+	 * found being true indicates a duplicate. We don't do anything about that,
+	 * a later field with the same name overrides the earlier field.
+	 */
 
 	hashentry->isnull = isnull;
 	if (_state->save_json_start != NULL)

File test/expected/json.out

  f6  | stringy
 (5 rows)
 
--- get_path, get_path_as_text
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
- json_get_path 
----------------
+-- variadic get, get_text
+select json_get('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ json_get  
+-----------
  "stringy"
 (1 row)
 
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
- json_get_path 
----------------
+select json_get('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ json_get 
+----------
  {"f3":1}
 (1 row)
 
-select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
- json_get_path 
----------------
+select json_get('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ json_get 
+----------
  "f3"
 (1 row)
 
-select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
- json_get_path 
----------------
+select json_get('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ json_get 
+----------
  1
 (1 row)
 
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
- json_get_path_as_text 
------------------------
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+ json_get_text 
+---------------
  stringy
 (1 row)
 
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
- json_get_path_as_text 
------------------------
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+ json_get_text 
+---------------
  {"f3":1}
 (1 row)
 
-select json_get_path_as_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
- json_get_path_as_text 
------------------------
+select json_get_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+ json_get_text 
+---------------
  f3
 (1 row)
 
-select json_get_path_as_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
- json_get_path_as_text 
------------------------
+select json_get_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+ json_get_text 
+---------------
  1
 (1 row)
 
--- get_path nulls
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+-- get nulls
+select json_get('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
  expect_false 
 --------------
  f
 (1 row)
 
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
  expect_true 
 -------------
  t
 (1 row)
 
-select json_get_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select json_get('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
  expect_false 
 --------------
  f
 (1 row)
 
-select json_get_path_as_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+select json_get_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
  expect_true 
 -------------
  t
 (1 row)
 
+-- get_path
+select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f4','f6']);
+ json_get_path 
+---------------
+ "stringy"
+(1 row)
+
+select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f2']);
+ json_get_path 
+---------------
+ {"f3":1}
+(1 row)
+
+select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','0']);
+ json_get_path 
+---------------
+ "f3"
+(1 row)
+
+select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','1']);
+ json_get_path 
+---------------
+ 1
+(1 row)
+
+select json_get_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f4','f6']);
+ json_get_path_text 
+--------------------
+ stringy
+(1 row)
+
+select json_get_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f2']);
+ json_get_path_text 
+--------------------
+ {"f3":1}
+(1 row)
+
+select json_get_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','0']);
+ json_get_path_text 
+--------------------
+ f3
+(1 row)
+
+select json_get_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','1']);
+ json_get_path_text 
+--------------------
+ 1
+(1 row)
+
 -- get_path operators
 select '{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json#>array['f4','f6'];
  ?column?  

File test/sql/json.sql

 select json_each_as_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":"null"}');
 select * from json_each_as_text('{"f1":[1,2,3],"f2":{"f3":1},"f4":null,"f5":99,"f6":"stringy"}') q;
 
--- get_path, get_path_as_text
-
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
-select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
-select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
-select json_get_path_as_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
-select json_get_path_as_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
-
--- get_path nulls
-
-select json_get_path('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
-select json_get_path_as_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
-select json_get_path('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
-select json_get_path_as_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+-- variadic get, get_text
+
+select json_get('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select json_get('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select json_get('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select json_get('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
+select json_get_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',0::text);
+select json_get_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}','f2',1::text);
+
+-- get nulls
+
+select json_get('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_false;
+select json_get_text('{"f2":{"f3":1},"f4":{"f5":null,"f6":"stringy"}}','f4','f5') is null as expect_true;
+select json_get('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_false;
+select json_get_text('{"f2":{"f3":1},"f4":[0,1,2,null]}','f4','3') is null as expect_true;
+
+-- get_path
+
+select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f4','f6']);
+select json_get_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f2']);
+select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','0']);
+select json_get_path('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','1']);
+select json_get_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f4','f6']);
+select json_get_path_text('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}', array['f2']);
+select json_get_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','0']);
+select json_get_path_text('{"f2":["f3",1],"f4":{"f5":99,"f6":"stringy"}}', array['f2','1']);
 
 -- get_path operators