1 /*-------------------------------------------------------------------------
2  *
3  * json.c
4  *		JSON data type support.
5  *
6  * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *	  src/backend/utils/adt/json.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "catalog/pg_type.h"
17 #include "funcapi.h"
18 #include "libpq/pqformat.h"
19 #include "miscadmin.h"
20 #include "parser/parse_coerce.h"
21 #include "utils/array.h"
22 #include "utils/builtins.h"
23 #include "utils/date.h"
24 #include "utils/datetime.h"
25 #include "utils/json.h"
26 #include "utils/jsonfuncs.h"
27 #include "utils/lsyscache.h"
28 #include "utils/typcache.h"
29 
30 typedef enum					/* type categories for datum_to_json */
31 {
32 	JSONTYPE_NULL,				/* null, so we didn't bother to identify */
33 	JSONTYPE_BOOL,				/* boolean (built-in types only) */
34 	JSONTYPE_NUMERIC,			/* numeric (ditto) */
35 	JSONTYPE_DATE,				/* we use special formatting for datetimes */
36 	JSONTYPE_TIMESTAMP,
37 	JSONTYPE_TIMESTAMPTZ,
38 	JSONTYPE_JSON,				/* JSON itself (and JSONB) */
39 	JSONTYPE_ARRAY,				/* array */
40 	JSONTYPE_COMPOSITE,			/* composite */
41 	JSONTYPE_CAST,				/* something with an explicit cast to JSON */
42 	JSONTYPE_OTHER				/* all else */
43 } JsonTypeCategory;
44 
45 typedef struct JsonAggState
46 {
47 	StringInfo	str;
48 	JsonTypeCategory key_category;
49 	Oid			key_output_func;
50 	JsonTypeCategory val_category;
51 	Oid			val_output_func;
52 } JsonAggState;
53 
54 static void composite_to_json(Datum composite, StringInfo result,
55 							  bool use_line_feeds);
56 static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
57 							  Datum *vals, bool *nulls, int *valcount,
58 							  JsonTypeCategory tcategory, Oid outfuncoid,
59 							  bool use_line_feeds);
60 static void array_to_json_internal(Datum array, StringInfo result,
61 								   bool use_line_feeds);
62 static void json_categorize_type(Oid typoid,
63 								 JsonTypeCategory *tcategory,
64 								 Oid *outfuncoid);
65 static void datum_to_json(Datum val, bool is_null, StringInfo result,
66 						  JsonTypeCategory tcategory, Oid outfuncoid,
67 						  bool key_scalar);
68 static void add_json(Datum val, bool is_null, StringInfo result,
69 					 Oid val_type, bool key_scalar);
70 static text *catenate_stringinfo_string(StringInfo buffer, const char *addon);
71 
72 /*
73  * Input.
74  */
75 Datum
json_in(PG_FUNCTION_ARGS)76 json_in(PG_FUNCTION_ARGS)
77 {
78 	char	   *json = PG_GETARG_CSTRING(0);
79 	text	   *result = cstring_to_text(json);
80 	JsonLexContext *lex;
81 
82 	/* validate it */
83 	lex = makeJsonLexContext(result, false);
84 	pg_parse_json_or_ereport(lex, &nullSemAction);
85 
86 	/* Internal representation is the same as text, for now */
87 	PG_RETURN_TEXT_P(result);
88 }
89 
90 /*
91  * Output.
92  */
93 Datum
json_out(PG_FUNCTION_ARGS)94 json_out(PG_FUNCTION_ARGS)
95 {
96 	/* we needn't detoast because text_to_cstring will handle that */
97 	Datum		txt = PG_GETARG_DATUM(0);
98 
99 	PG_RETURN_CSTRING(TextDatumGetCString(txt));
100 }
101 
102 /*
103  * Binary send.
104  */
105 Datum
json_send(PG_FUNCTION_ARGS)106 json_send(PG_FUNCTION_ARGS)
107 {
108 	text	   *t = PG_GETARG_TEXT_PP(0);
109 	StringInfoData buf;
110 
111 	pq_begintypsend(&buf);
112 	pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
113 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
114 }
115 
116 /*
117  * Binary receive.
118  */
119 Datum
json_recv(PG_FUNCTION_ARGS)120 json_recv(PG_FUNCTION_ARGS)
121 {
122 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
123 	char	   *str;
124 	int			nbytes;
125 	JsonLexContext *lex;
126 
127 	str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
128 
129 	/* Validate it. */
130 	lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
131 	pg_parse_json_or_ereport(lex, &nullSemAction);
132 
133 	PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
134 }
135 
136 /*
137  * Determine how we want to print values of a given type in datum_to_json.
138  *
139  * Given the datatype OID, return its JsonTypeCategory, as well as the type's
140  * output function OID.  If the returned category is JSONTYPE_CAST, we
141  * return the OID of the type->JSON cast function instead.
142  */
143 static void
json_categorize_type(Oid typoid,JsonTypeCategory * tcategory,Oid * outfuncoid)144 json_categorize_type(Oid typoid,
145 					 JsonTypeCategory *tcategory,
146 					 Oid *outfuncoid)
147 {
148 	bool		typisvarlena;
149 
150 	/* Look through any domain */
151 	typoid = getBaseType(typoid);
152 
153 	*outfuncoid = InvalidOid;
154 
155 	/*
156 	 * We need to get the output function for everything except date and
157 	 * timestamp types, array and composite types, booleans, and non-builtin
158 	 * types where there's a cast to json.
159 	 */
160 
161 	switch (typoid)
162 	{
163 		case BOOLOID:
164 			*tcategory = JSONTYPE_BOOL;
165 			break;
166 
167 		case INT2OID:
168 		case INT4OID:
169 		case INT8OID:
170 		case FLOAT4OID:
171 		case FLOAT8OID:
172 		case NUMERICOID:
173 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
174 			*tcategory = JSONTYPE_NUMERIC;
175 			break;
176 
177 		case DATEOID:
178 			*tcategory = JSONTYPE_DATE;
179 			break;
180 
181 		case TIMESTAMPOID:
182 			*tcategory = JSONTYPE_TIMESTAMP;
183 			break;
184 
185 		case TIMESTAMPTZOID:
186 			*tcategory = JSONTYPE_TIMESTAMPTZ;
187 			break;
188 
189 		case JSONOID:
190 		case JSONBOID:
191 			getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
192 			*tcategory = JSONTYPE_JSON;
193 			break;
194 
195 		default:
196 			/* Check for arrays and composites */
197 			if (OidIsValid(get_element_type(typoid)) || typoid == ANYARRAYOID
198 				|| typoid == ANYCOMPATIBLEARRAYOID || typoid == RECORDARRAYOID)
199 				*tcategory = JSONTYPE_ARRAY;
200 			else if (type_is_rowtype(typoid))	/* includes RECORDOID */
201 				*tcategory = JSONTYPE_COMPOSITE;
202 			else
203 			{
204 				/* It's probably the general case ... */
205 				*tcategory = JSONTYPE_OTHER;
206 				/* but let's look for a cast to json, if it's not built-in */
207 				if (typoid >= FirstNormalObjectId)
208 				{
209 					Oid			castfunc;
210 					CoercionPathType ctype;
211 
212 					ctype = find_coercion_pathway(JSONOID, typoid,
213 												  COERCION_EXPLICIT,
214 												  &castfunc);
215 					if (ctype == COERCION_PATH_FUNC && OidIsValid(castfunc))
216 					{
217 						*tcategory = JSONTYPE_CAST;
218 						*outfuncoid = castfunc;
219 					}
220 					else
221 					{
222 						/* non builtin type with no cast */
223 						getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
224 					}
225 				}
226 				else
227 				{
228 					/* any other builtin type */
229 					getTypeOutputInfo(typoid, outfuncoid, &typisvarlena);
230 				}
231 			}
232 			break;
233 	}
234 }
235 
236 /*
237  * Turn a Datum into JSON text, appending the string to "result".
238  *
239  * tcategory and outfuncoid are from a previous call to json_categorize_type,
240  * except that if is_null is true then they can be invalid.
241  *
242  * If key_scalar is true, the value is being printed as a key, so insist
243  * it's of an acceptable type, and force it to be quoted.
244  */
245 static void
datum_to_json(Datum val,bool is_null,StringInfo result,JsonTypeCategory tcategory,Oid outfuncoid,bool key_scalar)246 datum_to_json(Datum val, bool is_null, StringInfo result,
247 			  JsonTypeCategory tcategory, Oid outfuncoid,
248 			  bool key_scalar)
249 {
250 	char	   *outputstr;
251 	text	   *jsontext;
252 
253 	check_stack_depth();
254 
255 	/* callers are expected to ensure that null keys are not passed in */
256 	Assert(!(key_scalar && is_null));
257 
258 	if (is_null)
259 	{
260 		appendStringInfoString(result, "null");
261 		return;
262 	}
263 
264 	if (key_scalar &&
265 		(tcategory == JSONTYPE_ARRAY ||
266 		 tcategory == JSONTYPE_COMPOSITE ||
267 		 tcategory == JSONTYPE_JSON ||
268 		 tcategory == JSONTYPE_CAST))
269 		ereport(ERROR,
270 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
271 				 errmsg("key value must be scalar, not array, composite, or json")));
272 
273 	switch (tcategory)
274 	{
275 		case JSONTYPE_ARRAY:
276 			array_to_json_internal(val, result, false);
277 			break;
278 		case JSONTYPE_COMPOSITE:
279 			composite_to_json(val, result, false);
280 			break;
281 		case JSONTYPE_BOOL:
282 			outputstr = DatumGetBool(val) ? "true" : "false";
283 			if (key_scalar)
284 				escape_json(result, outputstr);
285 			else
286 				appendStringInfoString(result, outputstr);
287 			break;
288 		case JSONTYPE_NUMERIC:
289 			outputstr = OidOutputFunctionCall(outfuncoid, val);
290 
291 			/*
292 			 * Don't call escape_json for a non-key if it's a valid JSON
293 			 * number.
294 			 */
295 			if (!key_scalar && IsValidJsonNumber(outputstr, strlen(outputstr)))
296 				appendStringInfoString(result, outputstr);
297 			else
298 				escape_json(result, outputstr);
299 			pfree(outputstr);
300 			break;
301 		case JSONTYPE_DATE:
302 			{
303 				char		buf[MAXDATELEN + 1];
304 
305 				JsonEncodeDateTime(buf, val, DATEOID, NULL);
306 				appendStringInfo(result, "\"%s\"", buf);
307 			}
308 			break;
309 		case JSONTYPE_TIMESTAMP:
310 			{
311 				char		buf[MAXDATELEN + 1];
312 
313 				JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
314 				appendStringInfo(result, "\"%s\"", buf);
315 			}
316 			break;
317 		case JSONTYPE_TIMESTAMPTZ:
318 			{
319 				char		buf[MAXDATELEN + 1];
320 
321 				JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
322 				appendStringInfo(result, "\"%s\"", buf);
323 			}
324 			break;
325 		case JSONTYPE_JSON:
326 			/* JSON and JSONB output will already be escaped */
327 			outputstr = OidOutputFunctionCall(outfuncoid, val);
328 			appendStringInfoString(result, outputstr);
329 			pfree(outputstr);
330 			break;
331 		case JSONTYPE_CAST:
332 			/* outfuncoid refers to a cast function, not an output function */
333 			jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
334 			outputstr = text_to_cstring(jsontext);
335 			appendStringInfoString(result, outputstr);
336 			pfree(outputstr);
337 			pfree(jsontext);
338 			break;
339 		default:
340 			outputstr = OidOutputFunctionCall(outfuncoid, val);
341 			escape_json(result, outputstr);
342 			pfree(outputstr);
343 			break;
344 	}
345 }
346 
347 /*
348  * Encode 'value' of datetime type 'typid' into JSON string in ISO format using
349  * optionally preallocated buffer 'buf'.  Optional 'tzp' determines time-zone
350  * offset (in seconds) in which we want to show timestamptz.
351  */
352 char *
JsonEncodeDateTime(char * buf,Datum value,Oid typid,const int * tzp)353 JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
354 {
355 	if (!buf)
356 		buf = palloc(MAXDATELEN + 1);
357 
358 	switch (typid)
359 	{
360 		case DATEOID:
361 			{
362 				DateADT		date;
363 				struct pg_tm tm;
364 
365 				date = DatumGetDateADT(value);
366 
367 				/* Same as date_out(), but forcing DateStyle */
368 				if (DATE_NOT_FINITE(date))
369 					EncodeSpecialDate(date, buf);
370 				else
371 				{
372 					j2date(date + POSTGRES_EPOCH_JDATE,
373 						   &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
374 					EncodeDateOnly(&tm, USE_XSD_DATES, buf);
375 				}
376 			}
377 			break;
378 		case TIMEOID:
379 			{
380 				TimeADT		time = DatumGetTimeADT(value);
381 				struct pg_tm tt,
382 						   *tm = &tt;
383 				fsec_t		fsec;
384 
385 				/* Same as time_out(), but forcing DateStyle */
386 				time2tm(time, tm, &fsec);
387 				EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
388 			}
389 			break;
390 		case TIMETZOID:
391 			{
392 				TimeTzADT  *time = DatumGetTimeTzADTP(value);
393 				struct pg_tm tt,
394 						   *tm = &tt;
395 				fsec_t		fsec;
396 				int			tz;
397 
398 				/* Same as timetz_out(), but forcing DateStyle */
399 				timetz2tm(time, tm, &fsec, &tz);
400 				EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
401 			}
402 			break;
403 		case TIMESTAMPOID:
404 			{
405 				Timestamp	timestamp;
406 				struct pg_tm tm;
407 				fsec_t		fsec;
408 
409 				timestamp = DatumGetTimestamp(value);
410 				/* Same as timestamp_out(), but forcing DateStyle */
411 				if (TIMESTAMP_NOT_FINITE(timestamp))
412 					EncodeSpecialTimestamp(timestamp, buf);
413 				else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
414 					EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
415 				else
416 					ereport(ERROR,
417 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
418 							 errmsg("timestamp out of range")));
419 			}
420 			break;
421 		case TIMESTAMPTZOID:
422 			{
423 				TimestampTz timestamp;
424 				struct pg_tm tm;
425 				int			tz;
426 				fsec_t		fsec;
427 				const char *tzn = NULL;
428 
429 				timestamp = DatumGetTimestampTz(value);
430 
431 				/*
432 				 * If a time zone is specified, we apply the time-zone shift,
433 				 * convert timestamptz to pg_tm as if it were without a time
434 				 * zone, and then use the specified time zone for converting
435 				 * the timestamp into a string.
436 				 */
437 				if (tzp)
438 				{
439 					tz = *tzp;
440 					timestamp -= (TimestampTz) tz * USECS_PER_SEC;
441 				}
442 
443 				/* Same as timestamptz_out(), but forcing DateStyle */
444 				if (TIMESTAMP_NOT_FINITE(timestamp))
445 					EncodeSpecialTimestamp(timestamp, buf);
446 				else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
447 									  tzp ? NULL : &tzn, NULL) == 0)
448 				{
449 					if (tzp)
450 						tm.tm_isdst = 1;	/* set time-zone presence flag */
451 
452 					EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
453 				}
454 				else
455 					ereport(ERROR,
456 							(errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
457 							 errmsg("timestamp out of range")));
458 			}
459 			break;
460 		default:
461 			elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
462 			return NULL;
463 	}
464 
465 	return buf;
466 }
467 
468 /*
469  * Process a single dimension of an array.
470  * If it's the innermost dimension, output the values, otherwise call
471  * ourselves recursively to process the next dimension.
472  */
473 static void
array_dim_to_json(StringInfo result,int dim,int ndims,int * dims,Datum * vals,bool * nulls,int * valcount,JsonTypeCategory tcategory,Oid outfuncoid,bool use_line_feeds)474 array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
475 				  bool *nulls, int *valcount, JsonTypeCategory tcategory,
476 				  Oid outfuncoid, bool use_line_feeds)
477 {
478 	int			i;
479 	const char *sep;
480 
481 	Assert(dim < ndims);
482 
483 	sep = use_line_feeds ? ",\n " : ",";
484 
485 	appendStringInfoChar(result, '[');
486 
487 	for (i = 1; i <= dims[dim]; i++)
488 	{
489 		if (i > 1)
490 			appendStringInfoString(result, sep);
491 
492 		if (dim + 1 == ndims)
493 		{
494 			datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
495 						  outfuncoid, false);
496 			(*valcount)++;
497 		}
498 		else
499 		{
500 			/*
501 			 * Do we want line feeds on inner dimensions of arrays? For now
502 			 * we'll say no.
503 			 */
504 			array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
505 							  valcount, tcategory, outfuncoid, false);
506 		}
507 	}
508 
509 	appendStringInfoChar(result, ']');
510 }
511 
512 /*
513  * Turn an array into JSON.
514  */
515 static void
array_to_json_internal(Datum array,StringInfo result,bool use_line_feeds)516 array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
517 {
518 	ArrayType  *v = DatumGetArrayTypeP(array);
519 	Oid			element_type = ARR_ELEMTYPE(v);
520 	int		   *dim;
521 	int			ndim;
522 	int			nitems;
523 	int			count = 0;
524 	Datum	   *elements;
525 	bool	   *nulls;
526 	int16		typlen;
527 	bool		typbyval;
528 	char		typalign;
529 	JsonTypeCategory tcategory;
530 	Oid			outfuncoid;
531 
532 	ndim = ARR_NDIM(v);
533 	dim = ARR_DIMS(v);
534 	nitems = ArrayGetNItems(ndim, dim);
535 
536 	if (nitems <= 0)
537 	{
538 		appendStringInfoString(result, "[]");
539 		return;
540 	}
541 
542 	get_typlenbyvalalign(element_type,
543 						 &typlen, &typbyval, &typalign);
544 
545 	json_categorize_type(element_type,
546 						 &tcategory, &outfuncoid);
547 
548 	deconstruct_array(v, element_type, typlen, typbyval,
549 					  typalign, &elements, &nulls,
550 					  &nitems);
551 
552 	array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
553 					  outfuncoid, use_line_feeds);
554 
555 	pfree(elements);
556 	pfree(nulls);
557 }
558 
559 /*
560  * Turn a composite / record into JSON.
561  */
562 static void
composite_to_json(Datum composite,StringInfo result,bool use_line_feeds)563 composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
564 {
565 	HeapTupleHeader td;
566 	Oid			tupType;
567 	int32		tupTypmod;
568 	TupleDesc	tupdesc;
569 	HeapTupleData tmptup,
570 			   *tuple;
571 	int			i;
572 	bool		needsep = false;
573 	const char *sep;
574 
575 	sep = use_line_feeds ? ",\n " : ",";
576 
577 	td = DatumGetHeapTupleHeader(composite);
578 
579 	/* Extract rowtype info and find a tupdesc */
580 	tupType = HeapTupleHeaderGetTypeId(td);
581 	tupTypmod = HeapTupleHeaderGetTypMod(td);
582 	tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
583 
584 	/* Build a temporary HeapTuple control structure */
585 	tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
586 	tmptup.t_data = td;
587 	tuple = &tmptup;
588 
589 	appendStringInfoChar(result, '{');
590 
591 	for (i = 0; i < tupdesc->natts; i++)
592 	{
593 		Datum		val;
594 		bool		isnull;
595 		char	   *attname;
596 		JsonTypeCategory tcategory;
597 		Oid			outfuncoid;
598 		Form_pg_attribute att = TupleDescAttr(tupdesc, i);
599 
600 		if (att->attisdropped)
601 			continue;
602 
603 		if (needsep)
604 			appendStringInfoString(result, sep);
605 		needsep = true;
606 
607 		attname = NameStr(att->attname);
608 		escape_json(result, attname);
609 		appendStringInfoChar(result, ':');
610 
611 		val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
612 
613 		if (isnull)
614 		{
615 			tcategory = JSONTYPE_NULL;
616 			outfuncoid = InvalidOid;
617 		}
618 		else
619 			json_categorize_type(att->atttypid, &tcategory, &outfuncoid);
620 
621 		datum_to_json(val, isnull, result, tcategory, outfuncoid, false);
622 	}
623 
624 	appendStringInfoChar(result, '}');
625 	ReleaseTupleDesc(tupdesc);
626 }
627 
628 /*
629  * Append JSON text for "val" to "result".
630  *
631  * This is just a thin wrapper around datum_to_json.  If the same type will be
632  * printed many times, avoid using this; better to do the json_categorize_type
633  * lookups only once.
634  */
635 static void
add_json(Datum val,bool is_null,StringInfo result,Oid val_type,bool key_scalar)636 add_json(Datum val, bool is_null, StringInfo result,
637 		 Oid val_type, bool key_scalar)
638 {
639 	JsonTypeCategory tcategory;
640 	Oid			outfuncoid;
641 
642 	if (val_type == InvalidOid)
643 		ereport(ERROR,
644 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
645 				 errmsg("could not determine input data type")));
646 
647 	if (is_null)
648 	{
649 		tcategory = JSONTYPE_NULL;
650 		outfuncoid = InvalidOid;
651 	}
652 	else
653 		json_categorize_type(val_type,
654 							 &tcategory, &outfuncoid);
655 
656 	datum_to_json(val, is_null, result, tcategory, outfuncoid, key_scalar);
657 }
658 
659 /*
660  * SQL function array_to_json(row)
661  */
662 Datum
array_to_json(PG_FUNCTION_ARGS)663 array_to_json(PG_FUNCTION_ARGS)
664 {
665 	Datum		array = PG_GETARG_DATUM(0);
666 	StringInfo	result;
667 
668 	result = makeStringInfo();
669 
670 	array_to_json_internal(array, result, false);
671 
672 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
673 }
674 
675 /*
676  * SQL function array_to_json(row, prettybool)
677  */
678 Datum
array_to_json_pretty(PG_FUNCTION_ARGS)679 array_to_json_pretty(PG_FUNCTION_ARGS)
680 {
681 	Datum		array = PG_GETARG_DATUM(0);
682 	bool		use_line_feeds = PG_GETARG_BOOL(1);
683 	StringInfo	result;
684 
685 	result = makeStringInfo();
686 
687 	array_to_json_internal(array, result, use_line_feeds);
688 
689 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
690 }
691 
692 /*
693  * SQL function row_to_json(row)
694  */
695 Datum
row_to_json(PG_FUNCTION_ARGS)696 row_to_json(PG_FUNCTION_ARGS)
697 {
698 	Datum		array = PG_GETARG_DATUM(0);
699 	StringInfo	result;
700 
701 	result = makeStringInfo();
702 
703 	composite_to_json(array, result, false);
704 
705 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
706 }
707 
708 /*
709  * SQL function row_to_json(row, prettybool)
710  */
711 Datum
row_to_json_pretty(PG_FUNCTION_ARGS)712 row_to_json_pretty(PG_FUNCTION_ARGS)
713 {
714 	Datum		array = PG_GETARG_DATUM(0);
715 	bool		use_line_feeds = PG_GETARG_BOOL(1);
716 	StringInfo	result;
717 
718 	result = makeStringInfo();
719 
720 	composite_to_json(array, result, use_line_feeds);
721 
722 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
723 }
724 
725 /*
726  * SQL function to_json(anyvalue)
727  */
728 Datum
to_json(PG_FUNCTION_ARGS)729 to_json(PG_FUNCTION_ARGS)
730 {
731 	Datum		val = PG_GETARG_DATUM(0);
732 	Oid			val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
733 	StringInfo	result;
734 	JsonTypeCategory tcategory;
735 	Oid			outfuncoid;
736 
737 	if (val_type == InvalidOid)
738 		ereport(ERROR,
739 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
740 				 errmsg("could not determine input data type")));
741 
742 	json_categorize_type(val_type,
743 						 &tcategory, &outfuncoid);
744 
745 	result = makeStringInfo();
746 
747 	datum_to_json(val, false, result, tcategory, outfuncoid, false);
748 
749 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
750 }
751 
752 /*
753  * json_agg transition function
754  *
755  * aggregate input column as a json array value.
756  */
757 Datum
json_agg_transfn(PG_FUNCTION_ARGS)758 json_agg_transfn(PG_FUNCTION_ARGS)
759 {
760 	MemoryContext aggcontext,
761 				oldcontext;
762 	JsonAggState *state;
763 	Datum		val;
764 
765 	if (!AggCheckCallContext(fcinfo, &aggcontext))
766 	{
767 		/* cannot be called directly because of internal-type argument */
768 		elog(ERROR, "json_agg_transfn called in non-aggregate context");
769 	}
770 
771 	if (PG_ARGISNULL(0))
772 	{
773 		Oid			arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
774 
775 		if (arg_type == InvalidOid)
776 			ereport(ERROR,
777 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
778 					 errmsg("could not determine input data type")));
779 
780 		/*
781 		 * Make this state object in a context where it will persist for the
782 		 * duration of the aggregate call.  MemoryContextSwitchTo is only
783 		 * needed the first time, as the StringInfo routines make sure they
784 		 * use the right context to enlarge the object if necessary.
785 		 */
786 		oldcontext = MemoryContextSwitchTo(aggcontext);
787 		state = (JsonAggState *) palloc(sizeof(JsonAggState));
788 		state->str = makeStringInfo();
789 		MemoryContextSwitchTo(oldcontext);
790 
791 		appendStringInfoChar(state->str, '[');
792 		json_categorize_type(arg_type, &state->val_category,
793 							 &state->val_output_func);
794 	}
795 	else
796 	{
797 		state = (JsonAggState *) PG_GETARG_POINTER(0);
798 		appendStringInfoString(state->str, ", ");
799 	}
800 
801 	/* fast path for NULLs */
802 	if (PG_ARGISNULL(1))
803 	{
804 		datum_to_json((Datum) 0, true, state->str, JSONTYPE_NULL,
805 					  InvalidOid, false);
806 		PG_RETURN_POINTER(state);
807 	}
808 
809 	val = PG_GETARG_DATUM(1);
810 
811 	/* add some whitespace if structured type and not first item */
812 	if (!PG_ARGISNULL(0) &&
813 		(state->val_category == JSONTYPE_ARRAY ||
814 		 state->val_category == JSONTYPE_COMPOSITE))
815 	{
816 		appendStringInfoString(state->str, "\n ");
817 	}
818 
819 	datum_to_json(val, false, state->str, state->val_category,
820 				  state->val_output_func, false);
821 
822 	/*
823 	 * The transition type for json_agg() is declared to be "internal", which
824 	 * is a pass-by-value type the same size as a pointer.  So we can safely
825 	 * pass the JsonAggState pointer through nodeAgg.c's machinations.
826 	 */
827 	PG_RETURN_POINTER(state);
828 }
829 
830 /*
831  * json_agg final function
832  */
833 Datum
json_agg_finalfn(PG_FUNCTION_ARGS)834 json_agg_finalfn(PG_FUNCTION_ARGS)
835 {
836 	JsonAggState *state;
837 
838 	/* cannot be called directly because of internal-type argument */
839 	Assert(AggCheckCallContext(fcinfo, NULL));
840 
841 	state = PG_ARGISNULL(0) ?
842 		NULL :
843 		(JsonAggState *) PG_GETARG_POINTER(0);
844 
845 	/* NULL result for no rows in, as is standard with aggregates */
846 	if (state == NULL)
847 		PG_RETURN_NULL();
848 
849 	/* Else return state with appropriate array terminator added */
850 	PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
851 }
852 
853 /*
854  * json_object_agg transition function.
855  *
856  * aggregate two input columns as a single json object value.
857  */
858 Datum
json_object_agg_transfn(PG_FUNCTION_ARGS)859 json_object_agg_transfn(PG_FUNCTION_ARGS)
860 {
861 	MemoryContext aggcontext,
862 				oldcontext;
863 	JsonAggState *state;
864 	Datum		arg;
865 
866 	if (!AggCheckCallContext(fcinfo, &aggcontext))
867 	{
868 		/* cannot be called directly because of internal-type argument */
869 		elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
870 	}
871 
872 	if (PG_ARGISNULL(0))
873 	{
874 		Oid			arg_type;
875 
876 		/*
877 		 * Make the StringInfo in a context where it will persist for the
878 		 * duration of the aggregate call. Switching context is only needed
879 		 * for this initial step, as the StringInfo routines make sure they
880 		 * use the right context to enlarge the object if necessary.
881 		 */
882 		oldcontext = MemoryContextSwitchTo(aggcontext);
883 		state = (JsonAggState *) palloc(sizeof(JsonAggState));
884 		state->str = makeStringInfo();
885 		MemoryContextSwitchTo(oldcontext);
886 
887 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
888 
889 		if (arg_type == InvalidOid)
890 			ereport(ERROR,
891 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
892 					 errmsg("could not determine data type for argument %d", 1)));
893 
894 		json_categorize_type(arg_type, &state->key_category,
895 							 &state->key_output_func);
896 
897 		arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
898 
899 		if (arg_type == InvalidOid)
900 			ereport(ERROR,
901 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
902 					 errmsg("could not determine data type for argument %d", 2)));
903 
904 		json_categorize_type(arg_type, &state->val_category,
905 							 &state->val_output_func);
906 
907 		appendStringInfoString(state->str, "{ ");
908 	}
909 	else
910 	{
911 		state = (JsonAggState *) PG_GETARG_POINTER(0);
912 		appendStringInfoString(state->str, ", ");
913 	}
914 
915 	/*
916 	 * Note: since json_object_agg() is declared as taking type "any", the
917 	 * parser will not do any type conversion on unknown-type literals (that
918 	 * is, undecorated strings or NULLs).  Such values will arrive here as
919 	 * type UNKNOWN, which fortunately does not matter to us, since
920 	 * unknownout() works fine.
921 	 */
922 
923 	if (PG_ARGISNULL(1))
924 		ereport(ERROR,
925 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
926 				 errmsg("field name must not be null")));
927 
928 	arg = PG_GETARG_DATUM(1);
929 
930 	datum_to_json(arg, false, state->str, state->key_category,
931 				  state->key_output_func, true);
932 
933 	appendStringInfoString(state->str, " : ");
934 
935 	if (PG_ARGISNULL(2))
936 		arg = (Datum) 0;
937 	else
938 		arg = PG_GETARG_DATUM(2);
939 
940 	datum_to_json(arg, PG_ARGISNULL(2), state->str, state->val_category,
941 				  state->val_output_func, false);
942 
943 	PG_RETURN_POINTER(state);
944 }
945 
946 /*
947  * json_object_agg final function.
948  */
949 Datum
json_object_agg_finalfn(PG_FUNCTION_ARGS)950 json_object_agg_finalfn(PG_FUNCTION_ARGS)
951 {
952 	JsonAggState *state;
953 
954 	/* cannot be called directly because of internal-type argument */
955 	Assert(AggCheckCallContext(fcinfo, NULL));
956 
957 	state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
958 
959 	/* NULL result for no rows in, as is standard with aggregates */
960 	if (state == NULL)
961 		PG_RETURN_NULL();
962 
963 	/* Else return state with appropriate object terminator added */
964 	PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
965 }
966 
967 /*
968  * Helper function for aggregates: return given StringInfo's contents plus
969  * specified trailing string, as a text datum.  We need this because aggregate
970  * final functions are not allowed to modify the aggregate state.
971  */
972 static text *
catenate_stringinfo_string(StringInfo buffer,const char * addon)973 catenate_stringinfo_string(StringInfo buffer, const char *addon)
974 {
975 	/* custom version of cstring_to_text_with_len */
976 	int			buflen = buffer->len;
977 	int			addlen = strlen(addon);
978 	text	   *result = (text *) palloc(buflen + addlen + VARHDRSZ);
979 
980 	SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
981 	memcpy(VARDATA(result), buffer->data, buflen);
982 	memcpy(VARDATA(result) + buflen, addon, addlen);
983 
984 	return result;
985 }
986 
987 /*
988  * SQL function json_build_object(variadic "any")
989  */
990 Datum
json_build_object(PG_FUNCTION_ARGS)991 json_build_object(PG_FUNCTION_ARGS)
992 {
993 	int			nargs;
994 	int			i;
995 	const char *sep = "";
996 	StringInfo	result;
997 	Datum	   *args;
998 	bool	   *nulls;
999 	Oid		   *types;
1000 
1001 	/* fetch argument values to build the object */
1002 	nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
1003 
1004 	if (nargs < 0)
1005 		PG_RETURN_NULL();
1006 
1007 	if (nargs % 2 != 0)
1008 		ereport(ERROR,
1009 				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1010 				 errmsg("argument list must have even number of elements"),
1011 		/* translator: %s is a SQL function name */
1012 				 errhint("The arguments of %s must consist of alternating keys and values.",
1013 						 "json_build_object()")));
1014 
1015 	result = makeStringInfo();
1016 
1017 	appendStringInfoChar(result, '{');
1018 
1019 	for (i = 0; i < nargs; i += 2)
1020 	{
1021 		appendStringInfoString(result, sep);
1022 		sep = ", ";
1023 
1024 		/* process key */
1025 		if (nulls[i])
1026 			ereport(ERROR,
1027 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1028 					 errmsg("argument %d cannot be null", i + 1),
1029 					 errhint("Object keys should be text.")));
1030 
1031 		add_json(args[i], false, result, types[i], true);
1032 
1033 		appendStringInfoString(result, " : ");
1034 
1035 		/* process value */
1036 		add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
1037 	}
1038 
1039 	appendStringInfoChar(result, '}');
1040 
1041 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
1042 }
1043 
1044 /*
1045  * degenerate case of json_build_object where it gets 0 arguments.
1046  */
1047 Datum
json_build_object_noargs(PG_FUNCTION_ARGS)1048 json_build_object_noargs(PG_FUNCTION_ARGS)
1049 {
1050 	PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1051 }
1052 
1053 /*
1054  * SQL function json_build_array(variadic "any")
1055  */
1056 Datum
json_build_array(PG_FUNCTION_ARGS)1057 json_build_array(PG_FUNCTION_ARGS)
1058 {
1059 	int			nargs;
1060 	int			i;
1061 	const char *sep = "";
1062 	StringInfo	result;
1063 	Datum	   *args;
1064 	bool	   *nulls;
1065 	Oid		   *types;
1066 
1067 	/* fetch argument values to build the array */
1068 	nargs = extract_variadic_args(fcinfo, 0, false, &args, &types, &nulls);
1069 
1070 	if (nargs < 0)
1071 		PG_RETURN_NULL();
1072 
1073 	result = makeStringInfo();
1074 
1075 	appendStringInfoChar(result, '[');
1076 
1077 	for (i = 0; i < nargs; i++)
1078 	{
1079 		appendStringInfoString(result, sep);
1080 		sep = ", ";
1081 		add_json(args[i], nulls[i], result, types[i], false);
1082 	}
1083 
1084 	appendStringInfoChar(result, ']');
1085 
1086 	PG_RETURN_TEXT_P(cstring_to_text_with_len(result->data, result->len));
1087 }
1088 
1089 /*
1090  * degenerate case of json_build_array where it gets 0 arguments.
1091  */
1092 Datum
json_build_array_noargs(PG_FUNCTION_ARGS)1093 json_build_array_noargs(PG_FUNCTION_ARGS)
1094 {
1095 	PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
1096 }
1097 
1098 /*
1099  * SQL function json_object(text[])
1100  *
1101  * take a one or two dimensional array of text as key/value pairs
1102  * for a json object.
1103  */
1104 Datum
json_object(PG_FUNCTION_ARGS)1105 json_object(PG_FUNCTION_ARGS)
1106 {
1107 	ArrayType  *in_array = PG_GETARG_ARRAYTYPE_P(0);
1108 	int			ndims = ARR_NDIM(in_array);
1109 	StringInfoData result;
1110 	Datum	   *in_datums;
1111 	bool	   *in_nulls;
1112 	int			in_count,
1113 				count,
1114 				i;
1115 	text	   *rval;
1116 	char	   *v;
1117 
1118 	switch (ndims)
1119 	{
1120 		case 0:
1121 			PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1122 			break;
1123 
1124 		case 1:
1125 			if ((ARR_DIMS(in_array)[0]) % 2)
1126 				ereport(ERROR,
1127 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1128 						 errmsg("array must have even number of elements")));
1129 			break;
1130 
1131 		case 2:
1132 			if ((ARR_DIMS(in_array)[1]) != 2)
1133 				ereport(ERROR,
1134 						(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1135 						 errmsg("array must have two columns")));
1136 			break;
1137 
1138 		default:
1139 			ereport(ERROR,
1140 					(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1141 					 errmsg("wrong number of array subscripts")));
1142 	}
1143 
1144 	deconstruct_array(in_array,
1145 					  TEXTOID, -1, false, TYPALIGN_INT,
1146 					  &in_datums, &in_nulls, &in_count);
1147 
1148 	count = in_count / 2;
1149 
1150 	initStringInfo(&result);
1151 
1152 	appendStringInfoChar(&result, '{');
1153 
1154 	for (i = 0; i < count; ++i)
1155 	{
1156 		if (in_nulls[i * 2])
1157 			ereport(ERROR,
1158 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1159 					 errmsg("null value not allowed for object key")));
1160 
1161 		v = TextDatumGetCString(in_datums[i * 2]);
1162 		if (i > 0)
1163 			appendStringInfoString(&result, ", ");
1164 		escape_json(&result, v);
1165 		appendStringInfoString(&result, " : ");
1166 		pfree(v);
1167 		if (in_nulls[i * 2 + 1])
1168 			appendStringInfoString(&result, "null");
1169 		else
1170 		{
1171 			v = TextDatumGetCString(in_datums[i * 2 + 1]);
1172 			escape_json(&result, v);
1173 			pfree(v);
1174 		}
1175 	}
1176 
1177 	appendStringInfoChar(&result, '}');
1178 
1179 	pfree(in_datums);
1180 	pfree(in_nulls);
1181 
1182 	rval = cstring_to_text_with_len(result.data, result.len);
1183 	pfree(result.data);
1184 
1185 	PG_RETURN_TEXT_P(rval);
1186 
1187 }
1188 
1189 /*
1190  * SQL function json_object(text[], text[])
1191  *
1192  * take separate key and value arrays of text to construct a json object
1193  * pairwise.
1194  */
1195 Datum
json_object_two_arg(PG_FUNCTION_ARGS)1196 json_object_two_arg(PG_FUNCTION_ARGS)
1197 {
1198 	ArrayType  *key_array = PG_GETARG_ARRAYTYPE_P(0);
1199 	ArrayType  *val_array = PG_GETARG_ARRAYTYPE_P(1);
1200 	int			nkdims = ARR_NDIM(key_array);
1201 	int			nvdims = ARR_NDIM(val_array);
1202 	StringInfoData result;
1203 	Datum	   *key_datums,
1204 			   *val_datums;
1205 	bool	   *key_nulls,
1206 			   *val_nulls;
1207 	int			key_count,
1208 				val_count,
1209 				i;
1210 	text	   *rval;
1211 	char	   *v;
1212 
1213 	if (nkdims > 1 || nkdims != nvdims)
1214 		ereport(ERROR,
1215 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1216 				 errmsg("wrong number of array subscripts")));
1217 
1218 	if (nkdims == 0)
1219 		PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1220 
1221 	deconstruct_array(key_array,
1222 					  TEXTOID, -1, false, TYPALIGN_INT,
1223 					  &key_datums, &key_nulls, &key_count);
1224 
1225 	deconstruct_array(val_array,
1226 					  TEXTOID, -1, false, TYPALIGN_INT,
1227 					  &val_datums, &val_nulls, &val_count);
1228 
1229 	if (key_count != val_count)
1230 		ereport(ERROR,
1231 				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1232 				 errmsg("mismatched array dimensions")));
1233 
1234 	initStringInfo(&result);
1235 
1236 	appendStringInfoChar(&result, '{');
1237 
1238 	for (i = 0; i < key_count; ++i)
1239 	{
1240 		if (key_nulls[i])
1241 			ereport(ERROR,
1242 					(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1243 					 errmsg("null value not allowed for object key")));
1244 
1245 		v = TextDatumGetCString(key_datums[i]);
1246 		if (i > 0)
1247 			appendStringInfoString(&result, ", ");
1248 		escape_json(&result, v);
1249 		appendStringInfoString(&result, " : ");
1250 		pfree(v);
1251 		if (val_nulls[i])
1252 			appendStringInfoString(&result, "null");
1253 		else
1254 		{
1255 			v = TextDatumGetCString(val_datums[i]);
1256 			escape_json(&result, v);
1257 			pfree(v);
1258 		}
1259 	}
1260 
1261 	appendStringInfoChar(&result, '}');
1262 
1263 	pfree(key_datums);
1264 	pfree(key_nulls);
1265 	pfree(val_datums);
1266 	pfree(val_nulls);
1267 
1268 	rval = cstring_to_text_with_len(result.data, result.len);
1269 	pfree(result.data);
1270 
1271 	PG_RETURN_TEXT_P(rval);
1272 }
1273 
1274 
1275 /*
1276  * Produce a JSON string literal, properly escaping characters in the text.
1277  */
1278 void
escape_json(StringInfo buf,const char * str)1279 escape_json(StringInfo buf, const char *str)
1280 {
1281 	const char *p;
1282 
1283 	appendStringInfoCharMacro(buf, '"');
1284 	for (p = str; *p; p++)
1285 	{
1286 		switch (*p)
1287 		{
1288 			case '\b':
1289 				appendStringInfoString(buf, "\\b");
1290 				break;
1291 			case '\f':
1292 				appendStringInfoString(buf, "\\f");
1293 				break;
1294 			case '\n':
1295 				appendStringInfoString(buf, "\\n");
1296 				break;
1297 			case '\r':
1298 				appendStringInfoString(buf, "\\r");
1299 				break;
1300 			case '\t':
1301 				appendStringInfoString(buf, "\\t");
1302 				break;
1303 			case '"':
1304 				appendStringInfoString(buf, "\\\"");
1305 				break;
1306 			case '\\':
1307 				appendStringInfoString(buf, "\\\\");
1308 				break;
1309 			default:
1310 				if ((unsigned char) *p < ' ')
1311 					appendStringInfo(buf, "\\u%04x", (int) *p);
1312 				else
1313 					appendStringInfoCharMacro(buf, *p);
1314 				break;
1315 		}
1316 	}
1317 	appendStringInfoCharMacro(buf, '"');
1318 }
1319 
1320 /*
1321  * SQL function json_typeof(json) -> text
1322  *
1323  * Returns the type of the outermost JSON value as TEXT.  Possible types are
1324  * "object", "array", "string", "number", "boolean", and "null".
1325  *
1326  * Performs a single call to json_lex() to get the first token of the supplied
1327  * value.  This initial token uniquely determines the value's type.  As our
1328  * input must already have been validated by json_in() or json_recv(), the
1329  * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
1330  * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
1331  */
1332 Datum
json_typeof(PG_FUNCTION_ARGS)1333 json_typeof(PG_FUNCTION_ARGS)
1334 {
1335 	text	   *json;
1336 
1337 	JsonLexContext *lex;
1338 	JsonTokenType tok;
1339 	char	   *type;
1340 	JsonParseErrorType result;
1341 
1342 	json = PG_GETARG_TEXT_PP(0);
1343 	lex = makeJsonLexContext(json, false);
1344 
1345 	/* Lex exactly one token from the input and check its type. */
1346 	result = json_lex(lex);
1347 	if (result != JSON_SUCCESS)
1348 		json_ereport_error(result, lex);
1349 	tok = lex->token_type;
1350 	switch (tok)
1351 	{
1352 		case JSON_TOKEN_OBJECT_START:
1353 			type = "object";
1354 			break;
1355 		case JSON_TOKEN_ARRAY_START:
1356 			type = "array";
1357 			break;
1358 		case JSON_TOKEN_STRING:
1359 			type = "string";
1360 			break;
1361 		case JSON_TOKEN_NUMBER:
1362 			type = "number";
1363 			break;
1364 		case JSON_TOKEN_TRUE:
1365 		case JSON_TOKEN_FALSE:
1366 			type = "boolean";
1367 			break;
1368 		case JSON_TOKEN_NULL:
1369 			type = "null";
1370 			break;
1371 		default:
1372 			elog(ERROR, "unexpected json token: %d", tok);
1373 	}
1374 
1375 	PG_RETURN_TEXT_P(cstring_to_text(type));
1376 }
1377