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