2014-10-13 15:45 GMT+02:00 Andrew Dunstan <and...@dunslane.net>:

>
> On 10/13/2014 06:41 AM, Pavel Stehule wrote:
>
>> Hi
>>
>> I am working on review of this patch.
>>
>
>
> The patch attached to the message you are replying to was never intended
> to be reviewed. It was only given by way of illustration of a technique.
>
> The original patch to be reviewed is on the message <
> http://www.postgresql.org/message-id/5425d277.4030...@dunslane.net> as
> shown on the commitfest app. I have just submitted a revised patch to fix
> the compiler warnings you complained of, at <http://www.postgresql.org/
> message-id/543bd598.4020...@dunslane.net> I have not found any segfaults
> in the regression tests.
>
> And please don't top-post on the PostgreSQL lists.
>

Attached small fix of uninitialized local variable

Regards

Pavel


>
> cheers
>
> andrew
>
>
>
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
new file mode 100644
index 9beebb3..c9b84f8
*** a/src/backend/utils/adt/jsonb.c
--- b/src/backend/utils/adt/jsonb.c
***************
*** 12,22 ****
--- 12,31 ----
   */
  #include "postgres.h"
  
+ #include "miscadmin.h"
+ #include "access/htup_details.h"
+ #include "access/transam.h"
+ #include "catalog/pg_cast.h"
+ #include "catalog/pg_type.h"
  #include "libpq/pqformat.h"
  #include "utils/builtins.h"
+ #include "utils/datetime.h"
+ #include "utils/lsyscache.h"
  #include "utils/json.h"
  #include "utils/jsonapi.h"
  #include "utils/jsonb.h"
+ #include "utils/syscache.h"
+ #include "utils/typcache.h"
  
  typedef struct JsonbInState
  {
*************** typedef struct JsonbInState
*** 24,29 ****
--- 33,55 ----
  	JsonbValue *res;
  } JsonbInState;
  
+ /* unlike with json categories, we need to treat json and jsonb differently */
+ typedef enum					/* type categories for datum_to_jsonb */
+ {
+ 	JSONBTYPE_NULL,				/* null, so we didn't bother to identify */
+ 	JSONBTYPE_BOOL,				/* boolean (built-in types only) */
+ 	JSONBTYPE_NUMERIC,			/* numeric (ditto) */
+ 	JSONBTYPE_TIMESTAMP,		/* we use special formatting for timestamp */
+ 	JSONBTYPE_TIMESTAMPTZ,		/* ... and timestamptz */
+ 	JSONBTYPE_JSON,				/* JSON */
+ 	JSONBTYPE_JSONB,			/* JSONB */
+ 	JSONBTYPE_ARRAY,			/* array */
+ 	JSONBTYPE_COMPOSITE,		/* composite */
+ 	JSONBTYPE_JSONCAST,			/* something with an explicit cast to JSON */
+ 	JSONBTYPE_JSONBCAST,		/* something with an explicit cast to JSONB */
+ 	JSONBTYPE_OTHER				/* all else */
+ }	JsonbTypeCategory;
+ 
  static inline Datum jsonb_from_cstring(char *json, int len);
  static size_t checkStringLen(size_t len);
  static void jsonb_in_object_start(void *pstate);
*************** static void jsonb_in_array_end(void *pst
*** 33,38 ****
--- 59,80 ----
  static void jsonb_in_object_field_start(void *pstate, char *fname, bool isnull);
  static void jsonb_put_escaped_value(StringInfo out, JsonbValue *scalarVal);
  static void jsonb_in_scalar(void *pstate, char *token, JsonTokenType tokentype);
+ static void jsonb_categorize_type(Oid typoid,
+ 					  JsonbTypeCategory * tcategory,
+ 					  Oid *outfuncoid);
+ static void composite_to_jsonb(Datum composite, JsonbInState *result);
+ static void array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims,
+ 				   Datum *vals, bool *nulls, int *valcount,
+ 				   JsonbTypeCategory tcategory, Oid outfuncoid);
+ static void array_to_jsonb_internal(Datum array, JsonbInState *result);
+ static void jsonb_categorize_type(Oid typoid,
+ 					  JsonbTypeCategory * tcategory,
+ 					  Oid *outfuncoid);
+ static void datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 			   JsonbTypeCategory tcategory, Oid outfuncoid,
+ 			   bool key_scalar);
+ static void add_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 		  Oid val_type, bool key_scalar);
  
  /*
   * jsonb type input function
*************** JsonbToCString(StringInfo out, JsonbCont
*** 462,464 ****
--- 504,1786 ----
  
  	return out->data;
  }
+ 
+ 
+ /*
+  * Determine how we want to render values of a given type in datum_to_jsonb.
+  *
+  * Given the datatype OID, return its JsonbTypeCategory, as well as the type's
+  * output function OID.  If the returned category is JSONBTYPE_CAST, we
+  * return the OID of the type->JSON cast function instead.
+  */
+ static void
+ jsonb_categorize_type(Oid typoid,
+ 					  JsonbTypeCategory * tcategory,
+ 					  Oid *outfuncoid)
+ {
+ 	bool		typisvarlena;
+ 
+ 	/* Look through any domain */
+ 	typoid = getBaseType(typoid);
+ 
+ 	/* We'll usually need to return the type output function */
+ 	getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
+ 
+ 	/* Check for known types */
+ 	switch (typoid)
+ 	{
+ 		case BOOLOID:
+ 			*tcategory = JSONBTYPE_BOOL;
+ 			break;
+ 
+ 		case INT2OID:
+ 		case INT4OID:
+ 		case INT8OID:
+ 		case FLOAT4OID:
+ 		case FLOAT8OID:
+ 		case NUMERICOID:
+ 			*tcategory = JSONBTYPE_NUMERIC;
+ 			break;
+ 
+ 		case TIMESTAMPOID:
+ 			*tcategory = JSONBTYPE_TIMESTAMP;
+ 			break;
+ 
+ 		case TIMESTAMPTZOID:
+ 			*tcategory = JSONBTYPE_TIMESTAMPTZ;
+ 			break;
+ 
+ 		case JSONBOID:
+ 			*tcategory = JSONBTYPE_JSONB;
+ 			break;
+ 
+ 		case JSONOID:
+ 			*tcategory = JSONBTYPE_JSON;
+ 			break;
+ 
+ 		default:
+ 			/* Check for arrays and composites */
+ 			if (OidIsValid(get_element_type(typoid)))
+ 				*tcategory = JSONBTYPE_ARRAY;
+ 			else if (type_is_rowtype(typoid))
+ 				*tcategory = JSONBTYPE_COMPOSITE;
+ 			else
+ 			{
+ 				/* It's probably the general case ... */
+ 				*tcategory = JSONBTYPE_OTHER;
+ 
+ 				/*
+ 				 * but let's look for a cast to json or jsonb, if it's not
+ 				 * built-in
+ 				 */
+ 				if (typoid >= FirstNormalObjectId)
+ 				{
+ 					HeapTuple	tuple;
+ 
+ 					tuple = SearchSysCache2(CASTSOURCETARGET,
+ 											ObjectIdGetDatum(typoid),
+ 											ObjectIdGetDatum(JSONBOID));
+ 					if (HeapTupleIsValid(tuple))
+ 					{
+ 						Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
+ 
+ 						if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+ 						{
+ 							*tcategory = JSONBTYPE_JSONBCAST;
+ 							*outfuncoid = castForm->castfunc;
+ 						}
+ 
+ 						ReleaseSysCache(tuple);
+ 					}
+ 					else
+ 					{
+ 						tuple = SearchSysCache2(CASTSOURCETARGET,
+ 												ObjectIdGetDatum(typoid),
+ 												ObjectIdGetDatum(JSONOID));
+ 						if (HeapTupleIsValid(tuple))
+ 						{
+ 							Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
+ 
+ 							if (castForm->castmethod == COERCION_METHOD_FUNCTION)
+ 							{
+ 								*tcategory = JSONBTYPE_JSONCAST;
+ 								*outfuncoid = castForm->castfunc;
+ 							}
+ 
+ 							ReleaseSysCache(tuple);
+ 						}
+ 					}
+ 				}
+ 				break;
+ 			}
+ 	}
+ }
+ 
+ /*
+  * Turn a Datum into jsonb, adding it to the result JsonbInState.
+  *
+  * tcategory and outfuncoid are from a previous call to json_categorize_type,
+  * except that if is_null is true then they can be invalid.
+  *
+  * If key_scalar is true, the value is stores as a key, so insist
+  * it's of an acceptable type, and force it to be a jbvString.
+  */
+ static void
+ datum_to_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 			   JsonbTypeCategory tcategory, Oid outfuncoid,
+ 			   bool key_scalar)
+ {
+ 	char	   *outputstr;
+ 	bool		numeric_error;
+ 	JsonbValue	jb;
+ 	bool		scalar_jsonb = false;
+ 
+ 	if (is_null)
+ 	{
+ 		jb.type = jbvNull;
+ 	}
+ 	else if (key_scalar &&
+ 			 (tcategory == JSONBTYPE_ARRAY ||
+ 			  tcategory == JSONBTYPE_COMPOSITE ||
+ 			  tcategory == JSONBTYPE_JSON ||
+ 			  tcategory == JSONBTYPE_JSONB ||
+ 			  tcategory == JSONBTYPE_JSONCAST ||
+ 			  tcategory == JSONBTYPE_JSONBCAST))
+ 	{
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 		  errmsg("key value must be scalar, not array, composite or json")));
+ 	}
+ 	else
+ 	{
+ 		if (tcategory == JSONBTYPE_JSONCAST || tcategory == JSONBTYPE_JSONBCAST)
+ 			val = OidFunctionCall1(outfuncoid, val);
+ 
+ 		switch (tcategory)
+ 		{
+ 			case JSONBTYPE_ARRAY:
+ 				array_to_jsonb_internal(val, result);
+ 				break;
+ 			case JSONBTYPE_COMPOSITE:
+ 				composite_to_jsonb(val, result);
+ 				break;
+ 			case JSONBTYPE_BOOL:
+ 				if (key_scalar)
+ 				{
+ 					outputstr = DatumGetBool(val) ? "true" : "false";
+ 					jb.type = jbvString;
+ 					jb.val.string.len = strlen(outputstr);
+ 					jb.val.string.val = outputstr;
+ 				}
+ 				else
+ 				{
+ 					jb.type = jbvBool;
+ 					jb.val.boolean = DatumGetBool(val);
+ 				}
+ 				break;
+ 			case JSONBTYPE_NUMERIC:
+ 				outputstr = OidOutputFunctionCall(outfuncoid, val);
+ 				if (key_scalar)
+ 				{
+ 					/* always quote keys */
+ 					jb.type = jbvString;
+ 					jb.val.string.len = strlen(outputstr);
+ 					jb.val.string.val = outputstr;
+ 				}
+ 				else
+ 				{
+ 					/*
+ 					 * Make it numeric if it's a valid JSON number, otherwise
+ 					 * a string. Invalid numeric output will always have an
+ 					 * 'N' or 'n' in it (I think).
+ 					 */
+ 					numeric_error = (strchr(outputstr, 'N') != NULL ||
+ 									 strchr(outputstr, 'n') != NULL);
+ 					if (!numeric_error)
+ 					{
+ 						jb.type = jbvNumeric;
+ 						jb.val.numeric = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(outputstr), 0, -1));
+ 
+ 						pfree(outputstr);
+ 					}
+ 					else
+ 					{
+ 						jb.type = jbvString;
+ 						jb.val.string.len = strlen(outputstr);
+ 						jb.val.string.val = outputstr;
+ 					}
+ 				}
+ 				break;
+ 			case JSONBTYPE_TIMESTAMP:
+ 				{
+ 					Timestamp	timestamp;
+ 					struct pg_tm tm;
+ 					fsec_t		fsec;
+ 					char		buf[MAXDATELEN + 1];
+ 
+ 					timestamp = DatumGetTimestamp(val);
+ 
+ 					/* XSD doesn't support infinite values */
+ 					if (TIMESTAMP_NOT_FINITE(timestamp))
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ 								 errmsg("timestamp out of range"),
+ 								 errdetail("JSON does not support infinite timestamp values.")));
+ 					else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
+ 						EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ 								 errmsg("timestamp out of range")));
+ 
+ 					jb.type = jbvString;
+ 					jb.val.string.len = strlen(buf);
+ 					jb.val.string.val = pstrdup(buf);
+ 				}
+ 				break;
+ 			case JSONBTYPE_TIMESTAMPTZ:
+ 				{
+ 					TimestampTz timestamp;
+ 					struct pg_tm tm;
+ 					int			tz;
+ 					fsec_t		fsec;
+ 					const char *tzn = NULL;
+ 					char		buf[MAXDATELEN + 1];
+ 
+ 					timestamp = DatumGetTimestamp(val);
+ 
+ 					/* XSD doesn't support infinite values */
+ 					if (TIMESTAMP_NOT_FINITE(timestamp))
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ 								 errmsg("timestamp out of range"),
+ 								 errdetail("JSON does not support infinite timestamp values.")));
+ 					else if (timestamp2tm(timestamp, &tz, &tm, &fsec, &tzn, NULL) == 0)
+ 						EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
+ 					else
+ 						ereport(ERROR,
+ 								(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
+ 								 errmsg("timestamp out of range")));
+ 
+ 					jb.type = jbvString;
+ 					jb.val.string.len = strlen(buf);
+ 					jb.val.string.val = pstrdup(buf);
+ 				}
+ 				break;
+ 			case JSONBTYPE_JSONCAST:
+ 			case JSONBTYPE_JSON:
+ 				{
+ 					/* parse the json right into the existing result object */
+ 					JsonLexContext *lex;
+ 					JsonSemAction sem;
+ 					text	   *json = DatumGetTextP(val);
+ 
+ 					lex = makeJsonLexContext(json, true);
+ 
+ 					memset(&sem, 0, sizeof(sem));
+ 
+ 					sem.semstate = (void *) result;
+ 
+ 					sem.object_start = jsonb_in_object_start;
+ 					sem.array_start = jsonb_in_array_start;
+ 					sem.object_end = jsonb_in_object_end;
+ 					sem.array_end = jsonb_in_array_end;
+ 					sem.scalar = jsonb_in_scalar;
+ 					sem.object_field_start = jsonb_in_object_field_start;
+ 
+ 					pg_parse_json(lex, &sem);
+ 				}
+ 				break;
+ 			case JSONBTYPE_JSONBCAST:
+ 			case JSONBTYPE_JSONB:
+ 				{
+ 					Jsonb	   *jsonb = DatumGetJsonb(val);
+ 					int			type;
+ 					JsonbIterator *it;
+ 
+ 					it = JsonbIteratorInit(&jsonb->root);
+ 
+ 					if (JB_ROOT_IS_SCALAR(jsonb))
+ 					{
+ 						(void) JsonbIteratorNext(&it, &jb, true);
+ 						Assert(jb.type == jbvArray);
+ 						(void) JsonbIteratorNext(&it, &jb, true);
+ 						scalar_jsonb = true;
+ 					}
+ 					else
+ 					{
+ 						while ((type = JsonbIteratorNext(&it, &jb, false))
+ 							   != WJB_DONE)
+ 						{
+ 							if (type == WJB_END_ARRAY || type == WJB_END_OBJECT ||
+ 								type == WJB_BEGIN_ARRAY || type == WJB_BEGIN_OBJECT)
+ 								result->res = pushJsonbValue(&result->parseState,
+ 															 type, NULL);
+ 							else
+ 								result->res = pushJsonbValue(&result->parseState,
+ 															 type, &jb);
+ 						}
+ 					}
+ 				}
+ 				break;
+ 			default:
+ 				outputstr = OidOutputFunctionCall(outfuncoid, val);
+ 				jb.type = jbvString;
+ 				jb.val.string.len = checkStringLen(strlen(outputstr));
+ 				jb.val.string.val = outputstr;
+ 				break;
+ 		}
+ 	}
+ 	if (tcategory >= JSONBTYPE_JSON && tcategory <= JSONBTYPE_JSONBCAST &&
+ 		!scalar_jsonb)
+ 	{
+ 		/* work has been done recursively */
+ 		return;
+ 	}
+ 	else if (result->parseState == NULL)
+ 	{
+ 		/* single root scalar */
+ 		JsonbValue	va;
+ 
+ 		va.type = jbvArray;
+ 		va.val.array.rawScalar = true;
+ 		va.val.array.nElems = 1;
+ 
+ 		result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, &va);
+ 		result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
+ 		result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ 	}
+ 	else
+ 	{
+ 		JsonbValue *o = &result->parseState->contVal;
+ 
+ 		switch (o->type)
+ 		{
+ 			case jbvArray:
+ 				result->res = pushJsonbValue(&result->parseState, WJB_ELEM, &jb);
+ 				break;
+ 			case jbvObject:
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 key_scalar ? WJB_KEY : WJB_VALUE,
+ 											 &jb);
+ 				break;
+ 			default:
+ 				elog(ERROR, "unexpected parent of nested structure");
+ 		}
+ 	}
+ }
+ 
+ /*
+  * Process a single dimension of an array.
+  * If it's the innermost dimension, output the values, otherwise call
+  * ourselves recursively to process the next dimension.
+  */
+ static void
+ array_dim_to_jsonb(JsonbInState *result, int dim, int ndims, int *dims, Datum *vals,
+ 				   bool *nulls, int *valcount, JsonbTypeCategory tcategory,
+ 				   Oid outfuncoid)
+ {
+ 	int			i;
+ 
+ 	Assert(dim < ndims);
+ 
+ 	result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	for (i = 1; i <= dims[dim]; i++)
+ 	{
+ 		if (dim + 1 == ndims)
+ 		{
+ 			datum_to_jsonb(vals[*valcount], nulls[*valcount], result, tcategory,
+ 						   outfuncoid, false);
+ 			(*valcount)++;
+ 		}
+ 		else
+ 		{
+ 			array_dim_to_jsonb(result, dim + 1, ndims, dims, vals, nulls,
+ 							   valcount, tcategory, outfuncoid);
+ 		}
+ 	}
+ 
+ 	result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ }
+ 
+ /*
+  * Turn an array into JSON.
+  */
+ static void
+ array_to_jsonb_internal(Datum array, JsonbInState *result)
+ {
+ 	ArrayType  *v = DatumGetArrayTypeP(array);
+ 	Oid			element_type = ARR_ELEMTYPE(v);
+ 	int		   *dim;
+ 	int			ndim;
+ 	int			nitems;
+ 	int			count = 0;
+ 	Datum	   *elements;
+ 	bool	   *nulls;
+ 	int16		typlen;
+ 	bool		typbyval;
+ 	char		typalign;
+ 	JsonbTypeCategory tcategory;
+ 	Oid			outfuncoid;
+ 
+ 	ndim = ARR_NDIM(v);
+ 	dim = ARR_DIMS(v);
+ 	nitems = ArrayGetNItems(ndim, dim);
+ 
+ 	if (nitems <= 0)
+ 	{
+ 		result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_ARRAY, NULL);
+ 		result->res = pushJsonbValue(&result->parseState, WJB_END_ARRAY, NULL);
+ 		return;
+ 	}
+ 
+ 	get_typlenbyvalalign(element_type,
+ 						 &typlen, &typbyval, &typalign);
+ 
+ 	jsonb_categorize_type(element_type,
+ 						  &tcategory, &outfuncoid);
+ 
+ 	deconstruct_array(v, element_type, typlen, typbyval,
+ 					  typalign, &elements, &nulls,
+ 					  &nitems);
+ 
+ 	array_dim_to_jsonb(result, 0, ndim, dim, elements, nulls, &count, tcategory,
+ 					   outfuncoid);
+ 
+ 	pfree(elements);
+ 	pfree(nulls);
+ }
+ 
+ /*
+  * Turn a composite / record into JSON.
+  */
+ static void
+ composite_to_jsonb(Datum composite, JsonbInState *result)
+ {
+ 	HeapTupleHeader td;
+ 	Oid			tupType;
+ 	int32		tupTypmod;
+ 	TupleDesc	tupdesc;
+ 	HeapTupleData tmptup,
+ 			   *tuple;
+ 	int			i;
+ 
+ 	td = DatumGetHeapTupleHeader(composite);
+ 
+ 	/* Extract rowtype info and find a tupdesc */
+ 	tupType = HeapTupleHeaderGetTypeId(td);
+ 	tupTypmod = HeapTupleHeaderGetTypMod(td);
+ 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ 
+ 	/* Build a temporary HeapTuple control structure */
+ 	tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ 	tmptup.t_data = td;
+ 	tuple = &tmptup;
+ 
+ 	result->res = pushJsonbValue(&result->parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	for (i = 0; i < tupdesc->natts; i++)
+ 	{
+ 		Datum		val;
+ 		bool		isnull;
+ 		char	   *attname;
+ 		JsonbTypeCategory tcategory;
+ 		Oid			outfuncoid;
+ 		JsonbValue	v;
+ 
+ 		if (tupdesc->attrs[i]->attisdropped)
+ 			continue;
+ 
+ 		attname = NameStr(tupdesc->attrs[i]->attname);
+ 
+ 		v.type = jbvString;
+ 		/* don't need checkStringLen here - can't exceed maximum name length */
+ 		v.val.string.len = strlen(attname);
+ 		v.val.string.val = attname;
+ 
+ 		result->res = pushJsonbValue(&result->parseState, WJB_KEY, &v);
+ 
+ 		val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
+ 
+ 		if (isnull)
+ 		{
+ 			tcategory = JSONBTYPE_NULL;
+ 			outfuncoid = InvalidOid;
+ 		}
+ 		else
+ 			jsonb_categorize_type(tupdesc->attrs[i]->atttypid,
+ 								  &tcategory, &outfuncoid);
+ 
+ 		datum_to_jsonb(val, isnull, result, tcategory, outfuncoid, false);
+ 	}
+ 
+ 	result->res = pushJsonbValue(&result->parseState, WJB_END_OBJECT, NULL);
+ 	ReleaseTupleDesc(tupdesc);
+ }
+ 
+ /*
+  * Append JSON text for "val" to "result".
+  *
+  * This is just a thin wrapper around datum_to_json.  If the same type will be
+  * printed many times, avoid using this; better to do the json_categorize_type
+  * lookups only once.
+  */
+ 
+ static void
+ add_jsonb(Datum val, bool is_null, JsonbInState *result,
+ 		  Oid val_type, bool key_scalar)
+ {
+ 	JsonbTypeCategory tcategory;
+ 	Oid			outfuncoid;
+ 
+ 	if (val_type == InvalidOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("could not determine input data type")));
+ 
+ 	if (is_null)
+ 	{
+ 		tcategory = JSONBTYPE_NULL;
+ 		outfuncoid = InvalidOid;
+ 	}
+ 	else
+ 		jsonb_categorize_type(val_type,
+ 							  &tcategory, &outfuncoid);
+ 
+ 	datum_to_jsonb(val, is_null, result, tcategory, outfuncoid, key_scalar);
+ }
+ 
+ /*
+  * SQL function to_jsonb(anyvalue)
+  */
+ Datum
+ to_jsonb(PG_FUNCTION_ARGS)
+ {
+ 	Datum		val = PG_GETARG_DATUM(0);
+ 	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
+ 	JsonbInState result;
+ 	JsonbTypeCategory tcategory;
+ 	Oid			outfuncoid;
+ 
+ 	if (val_type == InvalidOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("could not determine input data type")));
+ 
+ 	jsonb_categorize_type(val_type,
+ 						  &tcategory, &outfuncoid);
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ /*
+  * SQL function jsonb_build_object(variadic "any")
+  */
+ Datum
+ jsonb_build_object(PG_FUNCTION_ARGS)
+ {
+ 	int			nargs = PG_NARGS();
+ 	int			i;
+ 	Datum		arg;
+ 	Oid			val_type;
+ 	JsonbInState result;
+ 
+ 	if (nargs % 2 != 0)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("invalid number or arguments: object must be matched key value pairs")));
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	for (i = 0; i < nargs; i += 2)
+ 	{
+ 
+ 		/* process key */
+ 
+ 		if (PG_ARGISNULL(i))
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("arg %d: key cannot be null", i + 1)));
+ 		val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
+ 
+ 		/*
+ 		 * turn a constant (more or less literal) value that's of unknown type
+ 		 * into text. Unknowns come in as a cstring pointer.
+ 		 */
+ 		if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
+ 		{
+ 			val_type = TEXTOID;
+ 			if (PG_ARGISNULL(i))
+ 				arg = (Datum) 0;
+ 			else
+ 				arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
+ 		}
+ 		else
+ 		{
+ 			arg = PG_GETARG_DATUM(i);
+ 		}
+ 		if (val_type == InvalidOid || val_type == UNKNOWNOID)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("arg %d: could not determine data type", i + 1)));
+ 
+ 		add_jsonb(arg, false, &result, val_type, true);
+ 
+ 		/* process value */
+ 
+ 		val_type = get_fn_expr_argtype(fcinfo->flinfo, i + 1);
+ 		/* see comments above */
+ 		if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i + 1))
+ 		{
+ 			val_type = TEXTOID;
+ 			if (PG_ARGISNULL(i + 1))
+ 				arg = (Datum) 0;
+ 			else
+ 				arg = CStringGetTextDatum(PG_GETARG_POINTER(i + 1));
+ 		}
+ 		else
+ 		{
+ 			arg = PG_GETARG_DATUM(i + 1);
+ 		}
+ 		if (val_type == InvalidOid || val_type == UNKNOWNOID)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("arg %d: could not determine data type", i + 2)));
+ 		add_jsonb(arg, PG_ARGISNULL(i + 1), &result, val_type, false);
+ 
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ /*
+  * degenerate case of jsonb_build_object where it gets 0 arguments.
+  */
+ Datum
+ jsonb_build_object_noargs(PG_FUNCTION_ARGS)
+ {
+ 	JsonbInState result;
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ /*
+  * SQL function jsonb_build_array(variadic "any")
+  */
+ Datum
+ jsonb_build_array(PG_FUNCTION_ARGS)
+ {
+ 	int			nargs = PG_NARGS();
+ 	int			i;
+ 	Datum		arg;
+ 	Oid			val_type;
+ 	JsonbInState result;
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 
+ 	for (i = 0; i < nargs; i++)
+ 	{
+ 		val_type = get_fn_expr_argtype(fcinfo->flinfo, i);
+ 		arg = PG_GETARG_DATUM(i + 1);
+ 		/* see comments in jsonb_build_object above */
+ 		if (val_type == UNKNOWNOID && get_fn_expr_arg_stable(fcinfo->flinfo, i))
+ 		{
+ 			val_type = TEXTOID;
+ 			if (PG_ARGISNULL(i))
+ 				arg = (Datum) 0;
+ 			else
+ 				arg = CStringGetTextDatum(PG_GETARG_POINTER(i));
+ 		}
+ 		else
+ 		{
+ 			arg = PG_GETARG_DATUM(i);
+ 		}
+ 		if (val_type == InvalidOid || val_type == UNKNOWNOID)
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 					 errmsg("arg %d: could not determine data type", i + 1)));
+ 		add_jsonb(arg, PG_ARGISNULL(i), &result, val_type, false);
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ /*
+  * degenerate case of jsonb_build_array where it gets 0 arguments.
+  */
+ Datum
+ jsonb_build_array_noargs(PG_FUNCTION_ARGS)
+ {
+ 	JsonbInState result;
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_ARRAY, NULL);
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_ARRAY, NULL);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ 
+ /*
+  * SQL function jsonb_object(text[])
+  *
+  * take a one or two dimensional array of text as name value pairs
+  * for a json object.
+  *
+  */
+ Datum
+ jsonb_object(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
+ 	int			ndims = ARR_NDIM(in_array);
+ 	Datum	   *in_datums;
+ 	bool	   *in_nulls;
+ 	int			in_count,
+ 				count,
+ 				i;
+ 	JsonbInState result;
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	switch (ndims)
+ 	{
+ 		case 0:
+ 			goto close_object;
+ 			break;
+ 
+ 		case 1:
+ 			if ((ARR_DIMS(in_array)[0]) % 2)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ 						 errmsg("array must have even number of elements")));
+ 			break;
+ 
+ 		case 2:
+ 			if ((ARR_DIMS(in_array)[1]) != 2)
+ 				ereport(ERROR,
+ 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ 						 errmsg("array must have two columns")));
+ 			break;
+ 
+ 		default:
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ 					 errmsg("wrong number of array subscripts")));
+ 	}
+ 
+ 	deconstruct_array(in_array,
+ 					  TEXTOID, -1, false, 'i',
+ 					  &in_datums, &in_nulls, &in_count);
+ 
+ 	count = in_count / 2;
+ 
+ 	for (i = 0; i < count; ++i)
+ 	{
+ 		JsonbValue	v;
+ 		char	   *str;
+ 		int			len;
+ 
+ 		if (in_nulls[i * 2])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 					 errmsg("null value not allowed for object key")));
+ 
+ 		str = TextDatumGetCString(in_datums[i * 2]);
+ 		len = strlen(str);
+ 
+ 		v.type = jbvString;
+ 
+ 		v.val.string.len = len;
+ 		v.val.string.val = str;
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 
+ 		if (in_nulls[i * 2 + 1])
+ 		{
+ 			v.type = jbvNull;
+ 		}
+ 		else
+ 		{
+ 			str = TextDatumGetCString(in_datums[i * 2 + 1]);
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 		}
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_VALUE, &v);
+ 	}
+ 
+ 	pfree(in_datums);
+ 	pfree(in_nulls);
+ 
+ close_object:
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ /*
+  * SQL function jsonb_object(text[], text[])
+  *
+  * take separate name and value arrays of text to construct a json object
+  * pairwise.
+  */
+ Datum
+ jsonb_object_two_arg(PG_FUNCTION_ARGS)
+ {
+ 	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
+ 	ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
+ 	int			nkdims = ARR_NDIM(key_array);
+ 	int			nvdims = ARR_NDIM(val_array);
+ 	Datum	   *key_datums,
+ 			   *val_datums;
+ 	bool	   *key_nulls,
+ 			   *val_nulls;
+ 	int			key_count,
+ 				val_count,
+ 				i;
+ 	JsonbInState result;
+ 
+ 	memset(&result, 0, sizeof(JsonbInState));
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_BEGIN_OBJECT, NULL);
+ 
+ 	if (nkdims > 1 || nkdims != nvdims)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ 				 errmsg("wrong number of array subscripts")));
+ 
+ 	if (nkdims == 0)
+ 		PG_RETURN_DATUM(CStringGetTextDatum("{}"));
+ 
+ 	deconstruct_array(key_array,
+ 					  TEXTOID, -1, false, 'i',
+ 					  &key_datums, &key_nulls, &key_count);
+ 
+ 	deconstruct_array(val_array,
+ 					  TEXTOID, -1, false, 'i',
+ 					  &val_datums, &val_nulls, &val_count);
+ 
+ 	if (key_count != val_count)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+ 				 errmsg("mismatched array dimensions")));
+ 
+ 	for (i = 0; i < key_count; ++i)
+ 	{
+ 		JsonbValue	v;
+ 		char	   *str;
+ 		int			len;
+ 
+ 		if (key_nulls[i])
+ 			ereport(ERROR,
+ 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ 					 errmsg("null value not allowed for object key")));
+ 
+ 		str = TextDatumGetCString(key_datums[i]);
+ 		len = strlen(str);
+ 
+ 		v.type = jbvString;
+ 
+ 		v.val.string.len = len;
+ 		v.val.string.val = str;
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_KEY, &v);
+ 
+ 		if (val_nulls[i])
+ 		{
+ 			v.type = jbvNull;
+ 		}
+ 		else
+ 		{
+ 			str = TextDatumGetCString(val_datums[i]);
+ 			len = strlen(str);
+ 
+ 			v.type = jbvString;
+ 
+ 			v.val.string.len = len;
+ 			v.val.string.val = str;
+ 		}
+ 
+ 		result.res = pushJsonbValue(&result.parseState, WJB_VALUE, &v);
+ 	}
+ 
+ 	result.res = pushJsonbValue(&result.parseState, WJB_END_OBJECT, NULL);
+ 
+ 	pfree(key_datums);
+ 	pfree(key_nulls);
+ 	pfree(val_datums);
+ 	pfree(val_nulls);
+ 
+ 	PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ }
+ 
+ 
+ /*
+  * jsonb_agg aggregate function
+  */
+ Datum
+ jsonb_agg_transfn(PG_FUNCTION_ARGS)
+ {
+ 	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ 	MemoryContext oldcontext,
+ 				aggcontext;
+ 	JsonbInState elem;
+ 	JsonbTypeCategory tcategory;
+ 	Oid			outfuncoid;
+ 	Datum		val;
+ 	JsonbInState *result;
+ 	bool		single_scalar = false;
+ 	JsonbIterator *it;
+ 	Jsonb	   *jbelem;
+ 	JsonbValue	v;
+ 	int			type;
+ 
+ 	if (val_type == InvalidOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("could not determine input data type")));
+ 
+ 	if (!AggCheckCallContext(fcinfo, &aggcontext))
+ 	{
+ 		/* cannot be called directly because of internal-type argument */
+ 		elog(ERROR, "jsonb_agg_transfn called in non-aggregate context");
+ 	}
+ 
+ 	/* turn the argument into jsonb in the normal function context */
+ 
+ 	val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
+ 
+ 	jsonb_categorize_type(val_type,
+ 						  &tcategory, &outfuncoid);
+ 
+ 	memset(&elem, 0, sizeof(JsonbInState));
+ 
+ 	datum_to_jsonb(val, false, &elem, tcategory, outfuncoid, false);
+ 
+ 	jbelem = JsonbValueToJsonb(elem.res);
+ 
+ 	/* switch to the aggregate context for accumulation operations */
+ 
+ 	oldcontext = MemoryContextSwitchTo(aggcontext);
+ 
+ 	/* set up the accumulator on the first go round */
+ 
+ 	if (PG_ARGISNULL(0))
+ 	{
+ 		result = palloc0(sizeof(JsonbInState));
+ 		result->res = pushJsonbValue(&result->parseState,
+ 									 WJB_BEGIN_ARRAY, NULL);
+ 
+ 	}
+ 	else
+ 	{
+ 		result = (JsonbInState *) PG_GETARG_POINTER(0);
+ 	}
+ 
+ 	it = JsonbIteratorInit(&jbelem->root);
+ 
+ 	while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+ 	{
+ 		switch (type)
+ 		{
+ 			case WJB_BEGIN_ARRAY:
+ 				if (v.val.array.rawScalar)
+ 					single_scalar = true;
+ 				else
+ 					result->res = pushJsonbValue(&result->parseState,
+ 												 type, NULL);
+ 				break;
+ 			case WJB_END_ARRAY:
+ 				if (!single_scalar)
+ 					result->res = pushJsonbValue(&result->parseState,
+ 												 type, NULL);
+ 				break;
+ 			case WJB_BEGIN_OBJECT:
+ 			case WJB_END_OBJECT:
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 type, NULL);
+ 				break;
+ 			case WJB_ELEM:
+ 			case WJB_KEY:
+ 			case WJB_VALUE:
+ 				if (v.type == jbvString)
+ 				{
+ 					/* copy string values in the aggreagate context */
+ 					char	   *buf = palloc(v.val.string.len + 1);;
+ 					snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
+ 					v.val.string.val = buf;
+ 				}
+ 				else if (v.type == jbvNumeric)
+ 				{
+ 					/* same for numeric */
+ 				  v.val.numeric = 
+ 					DatumGetNumeric(DirectFunctionCall1(numeric_uplus, 
+ 														NumericGetDatum(v.val.numeric)));
+ 
+ 				}
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 type, &v);
+ 				break;
+ 		}
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	PG_RETURN_POINTER(result);
+ }
+ 
+ Datum
+ jsonb_agg_finalfn(PG_FUNCTION_ARGS)
+ {
+ 	JsonbInState *result;
+ 	Jsonb	   *out;
+ 
+ 	/* cannot be called directly because of internal-type argument */
+ 	Assert(AggCheckCallContext(fcinfo, NULL));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();		/* returns null iff no input values */
+ 
+ 	result = (JsonbInState *) PG_GETARG_POINTER(0);
+ 
+ 	result->res = pushJsonbValue(&result->parseState,
+ 								 WJB_END_ARRAY, NULL);
+ 
+ 
+ 	out = JsonbValueToJsonb(result->res);
+ 
+ 	PG_RETURN_POINTER(out);
+ }
+ 
+ /*
+  * jsonb_object_agg aggregate function
+  */
+ Datum
+ jsonb_object_agg_transfn(PG_FUNCTION_ARGS)
+ {
+ 	Oid			val_type;
+ 	MemoryContext oldcontext,
+ 				aggcontext;
+ 	JsonbInState elem;
+ 	JsonbTypeCategory tcategory;
+ 	Oid			outfuncoid;
+ 	Datum		val;
+ 	JsonbInState *result;
+ 	bool		single_scalar;
+ 	JsonbIterator *it;
+ 	Jsonb	   *jbkey,
+ 			   *jbval;
+ 	JsonbValue	v;
+ 	int			type;
+ 
+ 	if (!AggCheckCallContext(fcinfo, &aggcontext))
+ 	{
+ 		/* cannot be called directly because of internal-type argument */
+ 		elog(ERROR, "jsonb_object_agg_transfn called in non-aggregate context");
+ 	}
+ 
+ 	/* turn the argument into jsonb in the normal function context */
+ 
+ 	val_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
+ 
+ 	if (val_type == InvalidOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("could not determine input data type")));
+ 
+ 	val = PG_ARGISNULL(1) ? (Datum) 0 : PG_GETARG_DATUM(1);
+ 
+ 	jsonb_categorize_type(val_type,
+ 						  &tcategory, &outfuncoid);
+ 
+ 	memset(&elem, 0, sizeof(JsonbInState));
+ 
+ 	datum_to_jsonb(val, false, &elem, tcategory, outfuncoid, true);
+ 
+ 	jbkey = JsonbValueToJsonb(elem.res);
+ 
+ 	val_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
+ 
+ 	if (val_type == InvalidOid)
+ 		ereport(ERROR,
+ 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 				 errmsg("could not determine input data type")));
+ 
+ 	val = PG_ARGISNULL(2) ? (Datum) 0 : PG_GETARG_DATUM(2);
+ 
+ 	jsonb_categorize_type(val_type,
+ 						  &tcategory, &outfuncoid);
+ 
+ 	memset(&elem, 0, sizeof(JsonbInState));
+ 
+ 	datum_to_jsonb(val, false, &elem, tcategory, outfuncoid, false);
+ 
+ 	jbval = JsonbValueToJsonb(elem.res);
+ 
+ 	/* switch to the aggregate context for accumulation operations */
+ 
+ 	oldcontext = MemoryContextSwitchTo(aggcontext);
+ 
+ 	/* set up the accumulator on the first go round */
+ 
+ 	if (PG_ARGISNULL(0))
+ 	{
+ 		result = palloc0(sizeof(JsonbInState));
+ 		result->res = pushJsonbValue(&result->parseState,
+ 									 WJB_BEGIN_OBJECT, NULL);
+ 
+ 	}
+ 	else
+ 	{
+ 		result = (JsonbInState *) PG_GETARG_POINTER(0);
+ 	}
+ 
+ 	it = JsonbIteratorInit(&jbkey->root);
+ 
+ 	/*
+ 	 * keys should be scalar, and we should have already checked for that
+ 	 * above when calling datum_to_jsonb, so we only need to look for these
+ 	 * things.
+ 	 */
+ 
+ 	while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+ 	{
+ 		switch (type)
+ 		{
+ 			case WJB_BEGIN_ARRAY:
+ 				if (!v.val.array.rawScalar)
+ 					elog(ERROR, "unexpected structure for key");
+ 				break;
+ 			case WJB_ELEM:
+ 				if (v.type == jbvString)
+ 				{
+ 					/* copy string values in the aggreagate context */
+ 					char	   *buf = palloc(v.val.string.len + 1);;
+ 					snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
+ 					v.val.string.val = buf;
+ 				}
+ 				else
+ 				{
+ 					ereport(ERROR,
+ 							(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ 							 errmsg("object keys must be strings")));
+ 				}
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 WJB_KEY, &v);
+ 				break;
+ 			case WJB_END_ARRAY:
+ 				break;
+ 			default:
+ 				elog(ERROR, "unexpected structure for key");
+ 				break;
+ 		}
+ 	}
+ 
+ 	it = JsonbIteratorInit(&jbval->root);
+ 
+ 	single_scalar = false;
+ 
+ 	/*
+ 	 * values can be anything, including structured and null, so we treate
+ 	 * them as in json_agg_transfn, except that single scalars are always
+ 	 * pushed as WJB_VALUE items.
+ 	 */
+ 
+ 	while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
+ 	{
+ 		switch (type)
+ 		{
+ 			case WJB_BEGIN_ARRAY:
+ 				if (v.val.array.rawScalar)
+ 					single_scalar = true;
+ 				else
+ 					result->res = pushJsonbValue(&result->parseState,
+ 												 type, NULL);
+ 				break;
+ 			case WJB_END_ARRAY:
+ 				if (!single_scalar)
+ 					result->res = pushJsonbValue(&result->parseState,
+ 												 type, NULL);
+ 				break;
+ 			case WJB_BEGIN_OBJECT:
+ 			case WJB_END_OBJECT:
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 type, NULL);
+ 				break;
+ 			case WJB_ELEM:
+ 			case WJB_KEY:
+ 			case WJB_VALUE:
+ 				if (v.type == jbvString)
+ 				{
+ 					/* copy string values in the aggreagate context */
+ 					char	   *buf = palloc(v.val.string.len + 1);;
+ 					snprintf(buf, v.val.string.len + 1, "%s", v.val.string.val);
+ 					v.val.string.val = buf;
+ 				}
+ 				else if (v.type == jbvNumeric)
+ 				{
+ 					/* same for numeric */
+ 					v.val.numeric = 
+ 					  DatumGetNumeric(DirectFunctionCall1(numeric_uplus, 
+ 														  NumericGetDatum(v.val.numeric)));
+ 
+ 				}
+ 				result->res = pushJsonbValue(&result->parseState,
+ 											 single_scalar ? WJB_VALUE : type,
+ 											 &v);
+ 				break;
+ 		}
+ 	}
+ 
+ 	MemoryContextSwitchTo(oldcontext);
+ 
+ 	PG_RETURN_POINTER(result);
+ }
+ 
+ Datum
+ jsonb_object_agg_finalfn(PG_FUNCTION_ARGS)
+ {
+ 	JsonbInState *result;
+ 	Jsonb	   *out;
+ 
+ 	/* cannot be called directly because of internal-type argument */
+ 	Assert(AggCheckCallContext(fcinfo, NULL));
+ 
+ 	if (PG_ARGISNULL(0))
+ 		PG_RETURN_NULL();		/* returns null iff no input values */
+ 
+ 	result = (JsonbInState *) PG_GETARG_POINTER(0);
+ 
+ 	result->res = pushJsonbValue(&result->parseState,
+ 								 WJB_END_OBJECT, NULL);
+ 
+ 
+ 	out = JsonbValueToJsonb(result->res);
+ 
+ 	PG_RETURN_POINTER(out);
+ }
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
new file mode 100644
index 2ff8539..4cce30d
*** a/src/backend/utils/adt/jsonb_util.c
--- b/src/backend/utils/adt/jsonb_util.c
*************** convertJsonbValue(StringInfo buffer, JEn
*** 1427,1433 ****
  	else if (val->type == jbvObject)
  		convertJsonbObject(buffer, header, val, level);
  	else
! 		elog(ERROR, "unknown type of jsonb container");
  }
  
  static void
--- 1427,1433 ----
  	else if (val->type == jbvObject)
  		convertJsonbObject(buffer, header, val, level);
  	else
! 		elog(ERROR, "unknown type of jsonb container to convert");
  }
  
  static void
diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h
new file mode 100644
index 3ba9e5e..8e0735b
*** a/src/include/catalog/pg_aggregate.h
--- b/src/include/catalog/pg_aggregate.h
*************** DATA(insert ( 3545	n 0 bytea_string_agg_
*** 286,291 ****
--- 286,295 ----
  DATA(insert ( 3175	n 0 json_agg_transfn	json_agg_finalfn			-				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
  DATA(insert ( 3197	n 0 json_object_agg_transfn json_object_agg_finalfn -				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
  
+ /* jsonb */
+ DATA(insert ( 3267	n 0 jsonb_agg_transfn	jsonb_agg_finalfn			-				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
+ DATA(insert ( 3270	n 0 jsonb_object_agg_transfn jsonb_object_agg_finalfn -				-				-				f f 0	2281	0	0		0	_null_ _null_ ));
+ 
  /* ordered-set and hypothetical-set aggregates */
  DATA(insert ( 3972	o 1 ordered_set_transition			percentile_disc_final					-		-		-		t f 0	2281	0	0		0	_null_ _null_ ));
  DATA(insert ( 3974	o 1 ordered_set_transition			percentile_cont_float8_final			-		-		-		f f 0	2281	0	0		0	_null_ _null_ ));
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
new file mode 100644
index 4736532..4609947
*** a/src/include/catalog/pg_proc.h
--- b/src/include/catalog/pg_proc.h
*************** DESCR("I/O");
*** 4599,4604 ****
--- 4599,4631 ----
  DATA(insert OID =  3803 (  jsonb_send		PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 17 "3802" _null_ _null_ _null_ _null_	jsonb_send _null_ _null_ _null_ ));
  DESCR("I/O");
  
+ DATA(insert OID = 3263 (  jsonb_object	 PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 3802 "1009" _null_ _null_ _null_ _null_ jsonb_object _null_ _null_ _null_ ));
+ DESCR("map text array of key value pairs to jsonb object");
+ DATA(insert OID = 3264 (  jsonb_object	 PGNSP PGUID 12 1 0 0 0 f f f f t f s 2 0 3802 "1009 1009" _null_ _null_ _null_ _null_ jsonb_object_two_arg _null_ _null_ _null_ ));
+ DESCR("map text array of key value pairs to jsonb object");
+ DATA(insert OID = 3787 (  to_jsonb	   PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 3802 "2283" _null_ _null_ _null_ _null_ to_jsonb _null_ _null_ _null_ ));
+ DESCR("map input to jsonb");
+ DATA(insert OID = 3265 (  jsonb_agg_transfn	 PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2281 "2281 2283" _null_ _null_ _null_ _null_ jsonb_agg_transfn _null_ _null_ _null_ ));
+ DESCR("jsonb aggregate transition function");
+ DATA(insert OID = 3266 (  jsonb_agg_finalfn	 PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_agg_finalfn _null_ _null_ _null_ ));
+ DESCR("jsonb aggregate final function");
+ DATA(insert OID = 3267 (  jsonb_agg		   PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 3802 "2283" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("aggregate input into jsonb");
+ DATA(insert OID = 3268 (  jsonb_object_agg_transfn	 PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2281 "2281 2276 2276" _null_ _null_ _null_ _null_ jsonb_object_agg_transfn _null_ _null_ _null_ ));
+ DESCR("jsonb object aggregate transition function");
+ DATA(insert OID = 3269 (  jsonb_object_agg_finalfn	 PGNSP PGUID 12 1 0 0 0 f f f f f f i 1 0 3802 "2281" _null_ _null_ _null_ _null_ jsonb_object_agg_finalfn _null_ _null_ _null_ ));
+ DESCR("jsonb object aggregate final function");
+ DATA(insert OID = 3270 (  jsonb_object_agg		   PGNSP PGUID 12 1 0 0 0 t f f f f f i 2 0 3802 "2276 2276" _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ ));
+ DESCR("aggregate inputs into jsonb object");
+ DATA(insert OID = 3259 (  jsonb_build_array	   PGNSP PGUID 12 1 0 2276 0 f f f f f f s 1 0 3802 "2276" "{2276}" "{v}" _null_ _null_ jsonb_build_array _null_ _null_ _null_ ));
+ DESCR("build a jsonb array from any inputs");
+ DATA(insert OID = 3260 (  jsonb_build_array	   PGNSP PGUID 12 1 0 0 0 f f f f f f s 0 0 3802  "" _null_ _null_ _null_ _null_ jsonb_build_array_noargs _null_ _null_ _null_ ));
+ DESCR("build an empty jsonb array");
+ DATA(insert OID = 3261 (  jsonb_build_object    PGNSP PGUID 12 1 0 2276 0 f f f f f f s 1 0 3802 "2276" "{2276}" "{v}" _null_ _null_ jsonb_build_object _null_ _null_ _null_ ));
+ DESCR("build a jsonb object from pairwise key/value inputs");
+ DATA(insert OID = 3262 (  jsonb_build_object    PGNSP PGUID 12 1 0 0 0 f f f f f f s 0 0 3802  "" _null_ _null_ _null_ _null_ jsonb_build_object_noargs _null_ _null_ _null_ ));
+ DESCR("build an empty jsonb object");
+ 
  DATA(insert OID = 3478 (  jsonb_object_field			PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field _null_ _null_ _null_ ));
  DATA(insert OID = 3214 (  jsonb_object_field_text	PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 25  "3802 25" _null_ _null_ "{from_json, field_name}" _null_ jsonb_object_field_text _null_ _null_ _null_ ));
  DATA(insert OID = 3215 (  jsonb_array_element		PGNSP PGUID 12 1 0 0 0 f f f f t f i 2 0 3802 "3802 23" _null_ _null_ "{from_json, element_index}" _null_ jsonb_array_element _null_ _null_ _null_ ));
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
new file mode 100644
index b89e4cb..d261aaa
*** a/src/include/utils/jsonb.h
--- b/src/include/utils/jsonb.h
*************** extern Datum jsonb_recv(PG_FUNCTION_ARGS
*** 350,355 ****
--- 350,371 ----
  extern Datum jsonb_send(PG_FUNCTION_ARGS);
  extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
  
+ /* generator routines */
+ extern Datum to_jsonb(PG_FUNCTION_ARGS);
+ 
+ extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
+ extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
+ extern Datum jsonb_build_array(PG_FUNCTION_ARGS);
+ extern Datum jsonb_build_array_noargs(PG_FUNCTION_ARGS);
+ extern Datum jsonb_object(PG_FUNCTION_ARGS);
+ extern Datum jsonb_object_two_arg(PG_FUNCTION_ARGS);
+ 
+ /* jsonb_agg, json_object_agg functions */
+ extern Datum jsonb_agg_transfn(PG_FUNCTION_ARGS);
+ extern Datum jsonb_agg_finalfn(PG_FUNCTION_ARGS);
+ extern Datum jsonb_object_agg_transfn(PG_FUNCTION_ARGS);
+ extern Datum jsonb_object_agg_finalfn(PG_FUNCTION_ARGS);
+ 
  /* Indexing-related ops */
  extern Datum jsonb_exists(PG_FUNCTION_ARGS);
  extern Datum jsonb_exists_any(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
new file mode 100644
index 9146f59..df9302d
*** a/src/test/regress/expected/jsonb.out
--- b/src/test/regress/expected/jsonb.out
*************** SELECT array_to_json(ARRAY [jsonb '{"a":
*** 301,306 ****
--- 301,328 ----
   [{"a": 1},{"b": [2, 3]}]
  (1 row)
  
+ --jsonb_agg
+ CREATE TEMP TABLE rows AS
+ SELECT x, 'txt' || x as y
+ FROM generate_series(1,3) AS x;
+ SELECT jsonb_agg(q)
+   FROM ( SELECT $$a$$ || x AS b, y AS c,
+                ARRAY[ROW(x.*,ARRAY[1,2,3]),
+                ROW(y.*,ARRAY[4,5,6])] AS z
+          FROM generate_series(1,2) x,
+               generate_series(4,5) y) q;
+                                                                                                                                                                     jsonb_agg                                                                                                                                                                     
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  [{"b": "a1", "c": 4, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a1", "c": 5, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 4, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 5, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}]
+ (1 row)
+ 
+ SELECT jsonb_agg(q)
+   FROM rows q;
+                                jsonb_agg                               
+ -----------------------------------------------------------------------
+  [{"x": 1, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
+ (1 row)
+ 
  -- jsonb extraction functions
  CREATE TEMP TABLE test_jsonb (
         json_type text,
*************** SELECT jsonb_typeof('"1.0"') AS string;
*** 1256,1261 ****
--- 1278,1397 ----
   string
  (1 row)
  
+ -- jsonb_build_array, jsonb_build_object, jsonb_object_agg
+ SELECT jsonb_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+                             jsonb_build_array                            
+ -------------------------------------------------------------------------
+  ["a", 1, "b", 1.2, "c", true, "d", null, "e", {"x": 3, "y": [1, 2, 3]}]
+ (1 row)
+ 
+ SELECT jsonb_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+                            jsonb_build_object                            
+ -------------------------------------------------------------------------
+  {"a": 1, "b": 1.2, "c": true, "d": null, "e": {"x": 3, "y": [1, 2, 3]}}
+ (1 row)
+ 
+ SELECT jsonb_build_object(
+        'a', jsonb_build_object('b',false,'c',99),
+        'd', jsonb_build_object('e',array[9,8,7]::int[],
+            'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r)));
+                                        jsonb_build_object                                       
+ ------------------------------------------------------------------------------------------------
+  {"a": {"b": false, "c": 99}, "d": {"e": [9, 8, 7], "f": {"name": "pg_class", "relkind": "r"}}}
+ (1 row)
+ 
+ -- empty objects/arrays
+ SELECT jsonb_build_array();
+  jsonb_build_array 
+ -------------------
+  []
+ (1 row)
+ 
+ SELECT jsonb_build_object();
+  jsonb_build_object 
+ --------------------
+  {}
+ (1 row)
+ 
+ -- make sure keys are quoted
+ SELECT jsonb_build_object(1,2);
+  jsonb_build_object 
+ --------------------
+  {"1": 2}
+ (1 row)
+ 
+ -- keys must be scalar and not null
+ SELECT jsonb_build_object(null,2);
+ ERROR:  arg 1: key cannot be null
+ SELECT jsonb_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r;
+ ERROR:  key value must be scalar, not array, composite or json
+ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3);
+ ERROR:  key value must be scalar, not array, composite or json
+ SELECT jsonb_build_object('{1,2,3}'::int[], 3);
+ ERROR:  key value must be scalar, not array, composite or json
+ CREATE TEMP TABLE foo (serial_num int, name text, type text);
+ INSERT INTO foo VALUES (847001,'t15','GE1043');
+ INSERT INTO foo VALUES (847002,'t16','GE1043');
+ INSERT INTO foo VALUES (847003,'sub-alpha','GESS90');
+ SELECT jsonb_build_object('turbines',jsonb_object_agg(serial_num,jsonb_build_object('name',name,'type',type)))
+ FROM foo;
+                                                                      jsonb_build_object                                                                      
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------
+  {"turbines": {"847001": {"name": "t15", "type": "GE1043"}, "847002": {"name": "t16", "type": "GE1043"}, "847003": {"name": "sub-alpha", "type": "GESS90"}}}
+ (1 row)
+ 
+ -- jsonb_object
+ -- one dimension
+ SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}');
+                    jsonb_object                    
+ ---------------------------------------------------
+  {"3": null, "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
+ -- same but with two dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+                    jsonb_object                    
+ ---------------------------------------------------
+  {"3": null, "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
+ -- odd number error
+ SELECT jsonb_object('{a,b,c}');
+ ERROR:  array must have even number of elements
+ -- one column error
+ SELECT jsonb_object('{{a},{b}}');
+ ERROR:  array must have two columns
+ -- too many columns error
+ SELECT jsonb_object('{{a,b,c},{b,c,d}}');
+ ERROR:  array must have two columns
+ -- too many dimensions error
+ SELECT jsonb_object('{{{a,b},{c,d}},{{b,c},{d,e}}}');
+ ERROR:  wrong number of array subscripts
+ --two argument form of jsonb_object
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c"}');
+                    jsonb_object                   
+ --------------------------------------------------
+  {"a": "1", "b": "2", "c": "3", "d e f": "a b c"}
+ (1 row)
+ 
+ -- too many dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+ ERROR:  wrong number of array subscripts
+ -- mismatched dimensions
+ select jsonb_object('{a,b,c,"d e f",g}','{1,2,3,"a b c"}');
+ ERROR:  mismatched array dimensions
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c",g}');
+ ERROR:  mismatched array dimensions
+ -- null key error
+ select jsonb_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}');
+ ERROR:  null value not allowed for object key
+ -- empty key is allowed
+ select jsonb_object('{a,b,"","d e f"}','{1,2,3,"a b c"}');
+                   jsonb_object                   
+ -------------------------------------------------
+  {"": "3", "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
  -- extract_path, extract_path_as_text
  SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
   jsonb_extract_path 
diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out
new file mode 100644
index 83d61f8..a4af230
*** a/src/test/regress/expected/jsonb_1.out
--- b/src/test/regress/expected/jsonb_1.out
*************** SELECT array_to_json(ARRAY [jsonb '{"a":
*** 301,306 ****
--- 301,328 ----
   [{"a": 1},{"b": [2, 3]}]
  (1 row)
  
+ --jsonb_agg
+ CREATE TEMP TABLE rows AS
+ SELECT x, 'txt' || x as y
+ FROM generate_series(1,3) AS x;
+ SELECT jsonb_agg(q)
+   FROM ( SELECT $$a$$ || x AS b, y AS c,
+                ARRAY[ROW(x.*,ARRAY[1,2,3]),
+                ROW(y.*,ARRAY[4,5,6])] AS z
+          FROM generate_series(1,2) x,
+               generate_series(4,5) y) q;
+                                                                                                                                                                     jsonb_agg                                                                                                                                                                     
+ --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+  [{"b": "a1", "c": 4, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a1", "c": 5, "z": [{"f1": 1, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 4, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 4, "f2": [4, 5, 6]}]}, {"b": "a2", "c": 5, "z": [{"f1": 2, "f2": [1, 2, 3]}, {"f1": 5, "f2": [4, 5, 6]}]}]
+ (1 row)
+ 
+ SELECT jsonb_agg(q)
+   FROM rows q;
+                                jsonb_agg                               
+ -----------------------------------------------------------------------
+  [{"x": 1, "y": "txt1"}, {"x": 2, "y": "txt2"}, {"x": 3, "y": "txt3"}]
+ (1 row)
+ 
  -- jsonb extraction functions
  CREATE TEMP TABLE test_jsonb (
         json_type text,
*************** SELECT jsonb_typeof('"1.0"') AS string;
*** 1256,1261 ****
--- 1278,1397 ----
   string
  (1 row)
  
+ -- jsonb_build_array, jsonb_build_object, jsonb_object_agg
+ SELECT jsonb_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+                             jsonb_build_array                            
+ -------------------------------------------------------------------------
+  ["a", 1, "b", 1.2, "c", true, "d", null, "e", {"x": 3, "y": [1, 2, 3]}]
+ (1 row)
+ 
+ SELECT jsonb_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+                            jsonb_build_object                            
+ -------------------------------------------------------------------------
+  {"a": 1, "b": 1.2, "c": true, "d": null, "e": {"x": 3, "y": [1, 2, 3]}}
+ (1 row)
+ 
+ SELECT jsonb_build_object(
+        'a', jsonb_build_object('b',false,'c',99),
+        'd', jsonb_build_object('e',array[9,8,7]::int[],
+            'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r)));
+                                        jsonb_build_object                                       
+ ------------------------------------------------------------------------------------------------
+  {"a": {"b": false, "c": 99}, "d": {"e": [9, 8, 7], "f": {"name": "pg_class", "relkind": "r"}}}
+ (1 row)
+ 
+ -- empty objects/arrays
+ SELECT jsonb_build_array();
+  jsonb_build_array 
+ -------------------
+  []
+ (1 row)
+ 
+ SELECT jsonb_build_object();
+  jsonb_build_object 
+ --------------------
+  {}
+ (1 row)
+ 
+ -- make sure keys are quoted
+ SELECT jsonb_build_object(1,2);
+  jsonb_build_object 
+ --------------------
+  {"1": 2}
+ (1 row)
+ 
+ -- keys must be scalar and not null
+ SELECT jsonb_build_object(null,2);
+ ERROR:  arg 1: key cannot be null
+ SELECT jsonb_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r;
+ ERROR:  key value must be scalar, not array, composite or json
+ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3);
+ ERROR:  key value must be scalar, not array, composite or json
+ SELECT jsonb_build_object('{1,2,3}'::int[], 3);
+ ERROR:  key value must be scalar, not array, composite or json
+ CREATE TEMP TABLE foo (serial_num int, name text, type text);
+ INSERT INTO foo VALUES (847001,'t15','GE1043');
+ INSERT INTO foo VALUES (847002,'t16','GE1043');
+ INSERT INTO foo VALUES (847003,'sub-alpha','GESS90');
+ SELECT jsonb_build_object('turbines',jsonb_object_agg(serial_num,jsonb_build_object('name',name,'type',type)))
+ FROM foo;
+                                                                      jsonb_build_object                                                                      
+ -------------------------------------------------------------------------------------------------------------------------------------------------------------
+  {"turbines": {"847001": {"name": "t15", "type": "GE1043"}, "847002": {"name": "t16", "type": "GE1043"}, "847003": {"name": "sub-alpha", "type": "GESS90"}}}
+ (1 row)
+ 
+ -- jsonb_object
+ -- one dimension
+ SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}');
+                    jsonb_object                    
+ ---------------------------------------------------
+  {"3": null, "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
+ -- same but with two dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+                    jsonb_object                    
+ ---------------------------------------------------
+  {"3": null, "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
+ -- odd number error
+ SELECT jsonb_object('{a,b,c}');
+ ERROR:  array must have even number of elements
+ -- one column error
+ SELECT jsonb_object('{{a},{b}}');
+ ERROR:  array must have two columns
+ -- too many columns error
+ SELECT jsonb_object('{{a,b,c},{b,c,d}}');
+ ERROR:  array must have two columns
+ -- too many dimensions error
+ SELECT jsonb_object('{{{a,b},{c,d}},{{b,c},{d,e}}}');
+ ERROR:  wrong number of array subscripts
+ --two argument form of jsonb_object
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c"}');
+                    jsonb_object                   
+ --------------------------------------------------
+  {"a": "1", "b": "2", "c": "3", "d e f": "a b c"}
+ (1 row)
+ 
+ -- too many dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+ ERROR:  wrong number of array subscripts
+ -- mismatched dimensions
+ select jsonb_object('{a,b,c,"d e f",g}','{1,2,3,"a b c"}');
+ ERROR:  mismatched array dimensions
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c",g}');
+ ERROR:  mismatched array dimensions
+ -- null key error
+ select jsonb_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}');
+ ERROR:  null value not allowed for object key
+ -- empty key is allowed
+ select jsonb_object('{a,b,"","d e f"}','{1,2,3,"a b c"}');
+                   jsonb_object                   
+ -------------------------------------------------
+  {"": "3", "a": "1", "b": "2", "d e f": "a b c"}
+ (1 row)
+ 
  -- extract_path, extract_path_as_text
  SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
   jsonb_extract_path 
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
new file mode 100644
index f1ed021..a808ed4
*** a/src/test/regress/sql/jsonb.sql
--- b/src/test/regress/sql/jsonb.sql
*************** SELECT '    '::jsonb;			-- ERROR, no val
*** 62,67 ****
--- 62,83 ----
  -- make sure jsonb is passed through json generators without being escaped
  SELECT array_to_json(ARRAY [jsonb '{"a":1}', jsonb '{"b":[2,3]}']);
  
+ --jsonb_agg
+ 
+ CREATE TEMP TABLE rows AS
+ SELECT x, 'txt' || x as y
+ FROM generate_series(1,3) AS x;
+ 
+ SELECT jsonb_agg(q)
+   FROM ( SELECT $$a$$ || x AS b, y AS c,
+                ARRAY[ROW(x.*,ARRAY[1,2,3]),
+                ROW(y.*,ARRAY[4,5,6])] AS z
+          FROM generate_series(1,2) x,
+               generate_series(4,5) y) q;
+ 
+ SELECT jsonb_agg(q)
+   FROM rows q;
+ 
  -- jsonb extraction functions
  CREATE TEMP TABLE test_jsonb (
         json_type text,
*************** SELECT jsonb_typeof('"hello"') AS string
*** 263,268 ****
--- 279,364 ----
  SELECT jsonb_typeof('"true"') AS string;
  SELECT jsonb_typeof('"1.0"') AS string;
  
+ -- jsonb_build_array, jsonb_build_object, jsonb_object_agg
+ 
+ SELECT jsonb_build_array('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+ 
+ SELECT jsonb_build_object('a',1,'b',1.2,'c',true,'d',null,'e',json '{"x": 3, "y": [1,2,3]}');
+ 
+ SELECT jsonb_build_object(
+        'a', jsonb_build_object('b',false,'c',99),
+        'd', jsonb_build_object('e',array[9,8,7]::int[],
+            'f', (select row_to_json(r) from ( select relkind, oid::regclass as name from pg_class where relname = 'pg_class') r)));
+ 
+ 
+ -- empty objects/arrays
+ SELECT jsonb_build_array();
+ 
+ SELECT jsonb_build_object();
+ 
+ -- make sure keys are quoted
+ SELECT jsonb_build_object(1,2);
+ 
+ -- keys must be scalar and not null
+ SELECT jsonb_build_object(null,2);
+ 
+ SELECT jsonb_build_object(r,2) FROM (SELECT 1 AS a, 2 AS b) r;
+ 
+ SELECT jsonb_build_object(json '{"a":1,"b":2}', 3);
+ 
+ SELECT jsonb_build_object('{1,2,3}'::int[], 3);
+ 
+ CREATE TEMP TABLE foo (serial_num int, name text, type text);
+ INSERT INTO foo VALUES (847001,'t15','GE1043');
+ INSERT INTO foo VALUES (847002,'t16','GE1043');
+ INSERT INTO foo VALUES (847003,'sub-alpha','GESS90');
+ 
+ SELECT jsonb_build_object('turbines',jsonb_object_agg(serial_num,jsonb_build_object('name',name,'type',type)))
+ FROM foo;
+ 
+ -- jsonb_object
+ 
+ -- one dimension
+ SELECT jsonb_object('{a,1,b,2,3,NULL,"d e f","a b c"}');
+ 
+ -- same but with two dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+ 
+ -- odd number error
+ SELECT jsonb_object('{a,b,c}');
+ 
+ -- one column error
+ SELECT jsonb_object('{{a},{b}}');
+ 
+ -- too many columns error
+ SELECT jsonb_object('{{a,b,c},{b,c,d}}');
+ 
+ -- too many dimensions error
+ SELECT jsonb_object('{{{a,b},{c,d}},{{b,c},{d,e}}}');
+ 
+ --two argument form of jsonb_object
+ 
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c"}');
+ 
+ -- too many dimensions
+ SELECT jsonb_object('{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}', '{{a,1},{b,2},{3,NULL},{"d e f","a b c"}}');
+ 
+ -- mismatched dimensions
+ 
+ select jsonb_object('{a,b,c,"d e f",g}','{1,2,3,"a b c"}');
+ 
+ select jsonb_object('{a,b,c,"d e f"}','{1,2,3,"a b c",g}');
+ 
+ -- null key error
+ 
+ select jsonb_object('{a,b,NULL,"d e f"}','{1,2,3,"a b c"}');
+ 
+ -- empty key is allowed
+ 
+ select jsonb_object('{a,b,"","d e f"}','{1,2,3,"a b c"}');
+ 
+ 
+ 
  -- extract_path, extract_path_as_text
  SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f4','f6');
  SELECT jsonb_extract_path('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}','f2');
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to