Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * json.c
4 : * JSON data type support.
5 : *
6 : * Portions Copyright (c) 1996-2025, 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 "access/htup_details.h"
17 : #include "catalog/pg_proc.h"
18 : #include "catalog/pg_type.h"
19 : #include "common/hashfn.h"
20 : #include "funcapi.h"
21 : #include "libpq/pqformat.h"
22 : #include "miscadmin.h"
23 : #include "port/simd.h"
24 : #include "utils/array.h"
25 : #include "utils/builtins.h"
26 : #include "utils/date.h"
27 : #include "utils/datetime.h"
28 : #include "utils/fmgroids.h"
29 : #include "utils/json.h"
30 : #include "utils/jsonfuncs.h"
31 : #include "utils/lsyscache.h"
32 : #include "utils/typcache.h"
33 :
34 :
35 : /*
36 : * Support for fast key uniqueness checking.
37 : *
38 : * We maintain a hash table of used keys in JSON objects for fast detection
39 : * of duplicates.
40 : */
41 : /* Common context for key uniqueness check */
42 : typedef struct HTAB *JsonUniqueCheckState; /* hash table for key names */
43 :
44 : /* Hash entry for JsonUniqueCheckState */
45 : typedef struct JsonUniqueHashEntry
46 : {
47 : const char *key;
48 : int key_len;
49 : int object_id;
50 : } JsonUniqueHashEntry;
51 :
52 : /* Stack element for key uniqueness check during JSON parsing */
53 : typedef struct JsonUniqueStackEntry
54 : {
55 : struct JsonUniqueStackEntry *parent;
56 : int object_id;
57 : } JsonUniqueStackEntry;
58 :
59 : /* Context struct for key uniqueness check during JSON parsing */
60 : typedef struct JsonUniqueParsingState
61 : {
62 : JsonLexContext *lex;
63 : JsonUniqueCheckState check;
64 : JsonUniqueStackEntry *stack;
65 : int id_counter;
66 : bool unique;
67 : } JsonUniqueParsingState;
68 :
69 : /* Context struct for key uniqueness check during JSON building */
70 : typedef struct JsonUniqueBuilderState
71 : {
72 : JsonUniqueCheckState check; /* unique check */
73 : StringInfoData skipped_keys; /* skipped keys with NULL values */
74 : MemoryContext mcxt; /* context for saving skipped keys */
75 : } JsonUniqueBuilderState;
76 :
77 :
78 : /* State struct for JSON aggregation */
79 : typedef struct JsonAggState
80 : {
81 : StringInfo str;
82 : JsonTypeCategory key_category;
83 : Oid key_output_func;
84 : JsonTypeCategory val_category;
85 : Oid val_output_func;
86 : JsonUniqueBuilderState unique_check;
87 : } JsonAggState;
88 :
89 : static void composite_to_json(Datum composite, StringInfo result,
90 : bool use_line_feeds);
91 : static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
92 : const Datum *vals, const bool *nulls, int *valcount,
93 : JsonTypeCategory tcategory, Oid outfuncoid,
94 : bool use_line_feeds);
95 : static void array_to_json_internal(Datum array, StringInfo result,
96 : bool use_line_feeds);
97 : static void datum_to_json_internal(Datum val, bool is_null, StringInfo result,
98 : JsonTypeCategory tcategory, Oid outfuncoid,
99 : bool key_scalar);
100 : static void add_json(Datum val, bool is_null, StringInfo result,
101 : Oid val_type, bool key_scalar);
102 : static text *catenate_stringinfo_string(StringInfo buffer, const char *addon);
103 :
104 : /*
105 : * Input.
106 : */
107 : Datum
108 6332 : json_in(PG_FUNCTION_ARGS)
109 : {
110 6332 : char *json = PG_GETARG_CSTRING(0);
111 6332 : text *result = cstring_to_text(json);
112 : JsonLexContext lex;
113 :
114 : /* validate it */
115 6332 : makeJsonLexContext(&lex, result, false);
116 6332 : if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
117 12 : PG_RETURN_NULL();
118 :
119 : /* Internal representation is the same as text */
120 6128 : PG_RETURN_TEXT_P(result);
121 : }
122 :
123 : /*
124 : * Output.
125 : */
126 : Datum
127 5692 : json_out(PG_FUNCTION_ARGS)
128 : {
129 : /* we needn't detoast because text_to_cstring will handle that */
130 5692 : Datum txt = PG_GETARG_DATUM(0);
131 :
132 5692 : PG_RETURN_CSTRING(TextDatumGetCString(txt));
133 : }
134 :
135 : /*
136 : * Binary send.
137 : */
138 : Datum
139 0 : json_send(PG_FUNCTION_ARGS)
140 : {
141 0 : text *t = PG_GETARG_TEXT_PP(0);
142 : StringInfoData buf;
143 :
144 0 : pq_begintypsend(&buf);
145 0 : pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
146 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
147 : }
148 :
149 : /*
150 : * Binary receive.
151 : */
152 : Datum
153 0 : json_recv(PG_FUNCTION_ARGS)
154 : {
155 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
156 : char *str;
157 : int nbytes;
158 : JsonLexContext lex;
159 :
160 0 : str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
161 :
162 : /* Validate it. */
163 0 : makeJsonLexContextCstringLen(&lex, str, nbytes, GetDatabaseEncoding(),
164 : false);
165 0 : pg_parse_json_or_ereport(&lex, &nullSemAction);
166 :
167 0 : PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
168 : }
169 :
170 : /*
171 : * Turn a Datum into JSON text, appending the string to "result".
172 : *
173 : * tcategory and outfuncoid are from a previous call to json_categorize_type,
174 : * except that if is_null is true then they can be invalid.
175 : *
176 : * If key_scalar is true, the value is being printed as a key, so insist
177 : * it's of an acceptable type, and force it to be quoted.
178 : */
179 : static void
180 8484 : datum_to_json_internal(Datum val, bool is_null, StringInfo result,
181 : JsonTypeCategory tcategory, Oid outfuncoid,
182 : bool key_scalar)
183 : {
184 : char *outputstr;
185 : text *jsontext;
186 :
187 8484 : check_stack_depth();
188 :
189 : /* callers are expected to ensure that null keys are not passed in */
190 : Assert(!(key_scalar && is_null));
191 :
192 8484 : if (is_null)
193 : {
194 318 : appendBinaryStringInfo(result, "null", strlen("null"));
195 318 : return;
196 : }
197 :
198 8166 : if (key_scalar &&
199 1996 : (tcategory == JSONTYPE_ARRAY ||
200 1990 : tcategory == JSONTYPE_COMPOSITE ||
201 1978 : tcategory == JSONTYPE_JSON ||
202 : tcategory == JSONTYPE_CAST))
203 36 : ereport(ERROR,
204 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
205 : errmsg("key value must be scalar, not array, composite, or json")));
206 :
207 8130 : switch (tcategory)
208 : {
209 324 : case JSONTYPE_ARRAY:
210 324 : array_to_json_internal(val, result, false);
211 324 : break;
212 500 : case JSONTYPE_COMPOSITE:
213 500 : composite_to_json(val, result, false);
214 500 : break;
215 40 : case JSONTYPE_BOOL:
216 40 : if (key_scalar)
217 0 : appendStringInfoChar(result, '"');
218 40 : if (DatumGetBool(val))
219 28 : appendBinaryStringInfo(result, "true", strlen("true"));
220 : else
221 12 : appendBinaryStringInfo(result, "false", strlen("false"));
222 40 : if (key_scalar)
223 0 : appendStringInfoChar(result, '"');
224 40 : break;
225 5142 : case JSONTYPE_NUMERIC:
226 5142 : outputstr = OidOutputFunctionCall(outfuncoid, val);
227 :
228 : /*
229 : * Don't quote a non-key if it's a valid JSON number (i.e., not
230 : * "Infinity", "-Infinity", or "NaN"). Since we know this is a
231 : * numeric data type's output, we simplify and open-code the
232 : * validation for better performance.
233 : */
234 5142 : if (!key_scalar &&
235 3636 : ((*outputstr >= '0' && *outputstr <= '9') ||
236 66 : (*outputstr == '-' &&
237 54 : (outputstr[1] >= '0' && outputstr[1] <= '9'))))
238 3618 : appendStringInfoString(result, outputstr);
239 : else
240 : {
241 1524 : appendStringInfoChar(result, '"');
242 1524 : appendStringInfoString(result, outputstr);
243 1524 : appendStringInfoChar(result, '"');
244 : }
245 5142 : pfree(outputstr);
246 5142 : break;
247 24 : case JSONTYPE_DATE:
248 : {
249 : char buf[MAXDATELEN + 1];
250 :
251 24 : JsonEncodeDateTime(buf, val, DATEOID, NULL);
252 24 : appendStringInfoChar(result, '"');
253 24 : appendStringInfoString(result, buf);
254 24 : appendStringInfoChar(result, '"');
255 : }
256 24 : break;
257 28 : case JSONTYPE_TIMESTAMP:
258 : {
259 : char buf[MAXDATELEN + 1];
260 :
261 28 : JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
262 28 : appendStringInfoChar(result, '"');
263 28 : appendStringInfoString(result, buf);
264 28 : appendStringInfoChar(result, '"');
265 : }
266 28 : break;
267 24 : case JSONTYPE_TIMESTAMPTZ:
268 : {
269 : char buf[MAXDATELEN + 1];
270 :
271 24 : JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
272 24 : appendStringInfoChar(result, '"');
273 24 : appendStringInfoString(result, buf);
274 24 : appendStringInfoChar(result, '"');
275 : }
276 24 : break;
277 824 : case JSONTYPE_JSON:
278 : /* JSON and JSONB output will already be escaped */
279 824 : outputstr = OidOutputFunctionCall(outfuncoid, val);
280 824 : appendStringInfoString(result, outputstr);
281 824 : pfree(outputstr);
282 824 : break;
283 4 : case JSONTYPE_CAST:
284 : /* outfuncoid refers to a cast function, not an output function */
285 4 : jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
286 4 : appendBinaryStringInfo(result, VARDATA_ANY(jsontext),
287 4 : VARSIZE_ANY_EXHDR(jsontext));
288 4 : pfree(jsontext);
289 4 : break;
290 1220 : default:
291 : /* special-case text types to save useless palloc/memcpy cycles */
292 1220 : if (outfuncoid == F_TEXTOUT || outfuncoid == F_VARCHAROUT ||
293 : outfuncoid == F_BPCHAROUT)
294 916 : escape_json_text(result, (text *) DatumGetPointer(val));
295 : else
296 : {
297 304 : outputstr = OidOutputFunctionCall(outfuncoid, val);
298 304 : escape_json(result, outputstr);
299 304 : pfree(outputstr);
300 : }
301 1220 : break;
302 : }
303 : }
304 :
305 : /*
306 : * Encode 'value' of datetime type 'typid' into JSON string in ISO format using
307 : * optionally preallocated buffer 'buf'. Optional 'tzp' determines time-zone
308 : * offset (in seconds) in which we want to show timestamptz.
309 : */
310 : char *
311 1732 : JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
312 : {
313 1732 : if (!buf)
314 60 : buf = palloc(MAXDATELEN + 1);
315 :
316 1732 : switch (typid)
317 : {
318 240 : case DATEOID:
319 : {
320 : DateADT date;
321 : struct pg_tm tm;
322 :
323 240 : date = DatumGetDateADT(value);
324 :
325 : /* Same as date_out(), but forcing DateStyle */
326 240 : if (DATE_NOT_FINITE(date))
327 24 : EncodeSpecialDate(date, buf);
328 : else
329 : {
330 216 : j2date(date + POSTGRES_EPOCH_JDATE,
331 : &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
332 216 : EncodeDateOnly(&tm, USE_XSD_DATES, buf);
333 : }
334 : }
335 240 : break;
336 270 : case TIMEOID:
337 : {
338 270 : TimeADT time = DatumGetTimeADT(value);
339 : struct pg_tm tt,
340 270 : *tm = &tt;
341 : fsec_t fsec;
342 :
343 : /* Same as time_out(), but forcing DateStyle */
344 270 : time2tm(time, tm, &fsec);
345 270 : EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
346 : }
347 270 : break;
348 342 : case TIMETZOID:
349 : {
350 342 : TimeTzADT *time = DatumGetTimeTzADTP(value);
351 : struct pg_tm tt,
352 342 : *tm = &tt;
353 : fsec_t fsec;
354 : int tz;
355 :
356 : /* Same as timetz_out(), but forcing DateStyle */
357 342 : timetz2tm(time, tm, &fsec, &tz);
358 342 : EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
359 : }
360 342 : break;
361 370 : case TIMESTAMPOID:
362 : {
363 : Timestamp timestamp;
364 : struct pg_tm tm;
365 : fsec_t fsec;
366 :
367 370 : timestamp = DatumGetTimestamp(value);
368 : /* Same as timestamp_out(), but forcing DateStyle */
369 370 : if (TIMESTAMP_NOT_FINITE(timestamp))
370 24 : EncodeSpecialTimestamp(timestamp, buf);
371 346 : else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
372 346 : EncodeDateTime(&tm, fsec, false, 0, NULL, USE_XSD_DATES, buf);
373 : else
374 0 : ereport(ERROR,
375 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
376 : errmsg("timestamp out of range")));
377 : }
378 370 : break;
379 510 : case TIMESTAMPTZOID:
380 : {
381 : TimestampTz timestamp;
382 : struct pg_tm tm;
383 : int tz;
384 : fsec_t fsec;
385 510 : const char *tzn = NULL;
386 :
387 510 : timestamp = DatumGetTimestampTz(value);
388 :
389 : /*
390 : * If a time zone is specified, we apply the time-zone shift,
391 : * convert timestamptz to pg_tm as if it were without a time
392 : * zone, and then use the specified time zone for converting
393 : * the timestamp into a string.
394 : */
395 510 : if (tzp)
396 : {
397 462 : tz = *tzp;
398 462 : timestamp -= (TimestampTz) tz * USECS_PER_SEC;
399 : }
400 :
401 : /* Same as timestamptz_out(), but forcing DateStyle */
402 510 : if (TIMESTAMP_NOT_FINITE(timestamp))
403 24 : EncodeSpecialTimestamp(timestamp, buf);
404 486 : else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
405 : tzp ? NULL : &tzn, NULL) == 0)
406 : {
407 486 : if (tzp)
408 462 : tm.tm_isdst = 1; /* set time-zone presence flag */
409 :
410 486 : EncodeDateTime(&tm, fsec, true, tz, tzn, USE_XSD_DATES, buf);
411 : }
412 : else
413 0 : ereport(ERROR,
414 : (errcode(ERRCODE_DATETIME_VALUE_OUT_OF_RANGE),
415 : errmsg("timestamp out of range")));
416 : }
417 510 : break;
418 0 : default:
419 0 : elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
420 : return NULL;
421 : }
422 :
423 1732 : return buf;
424 : }
425 :
426 : /*
427 : * Process a single dimension of an array.
428 : * If it's the innermost dimension, output the values, otherwise call
429 : * ourselves recursively to process the next dimension.
430 : */
431 : static void
432 378 : array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, const Datum *vals,
433 : const bool *nulls, int *valcount, JsonTypeCategory tcategory,
434 : Oid outfuncoid, bool use_line_feeds)
435 : {
436 : int i;
437 : const char *sep;
438 :
439 : Assert(dim < ndims);
440 :
441 378 : sep = use_line_feeds ? ",\n " : ",";
442 :
443 378 : appendStringInfoChar(result, '[');
444 :
445 1410 : for (i = 1; i <= dims[dim]; i++)
446 : {
447 1032 : if (i > 1)
448 654 : appendStringInfoString(result, sep);
449 :
450 1032 : if (dim + 1 == ndims)
451 : {
452 1020 : datum_to_json_internal(vals[*valcount], nulls[*valcount],
453 : result, tcategory,
454 : outfuncoid, false);
455 1020 : (*valcount)++;
456 : }
457 : else
458 : {
459 : /*
460 : * Do we want line feeds on inner dimensions of arrays? For now
461 : * we'll say no.
462 : */
463 12 : array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
464 : valcount, tcategory, outfuncoid, false);
465 : }
466 : }
467 :
468 378 : appendStringInfoChar(result, ']');
469 378 : }
470 :
471 : /*
472 : * Turn an array into JSON.
473 : */
474 : static void
475 366 : array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
476 : {
477 366 : ArrayType *v = DatumGetArrayTypeP(array);
478 366 : Oid element_type = ARR_ELEMTYPE(v);
479 : int *dim;
480 : int ndim;
481 : int nitems;
482 366 : int count = 0;
483 : Datum *elements;
484 : bool *nulls;
485 : int16 typlen;
486 : bool typbyval;
487 : char typalign;
488 : JsonTypeCategory tcategory;
489 : Oid outfuncoid;
490 :
491 366 : ndim = ARR_NDIM(v);
492 366 : dim = ARR_DIMS(v);
493 366 : nitems = ArrayGetNItems(ndim, dim);
494 :
495 366 : if (nitems <= 0)
496 : {
497 0 : appendStringInfoString(result, "[]");
498 0 : return;
499 : }
500 :
501 366 : get_typlenbyvalalign(element_type,
502 : &typlen, &typbyval, &typalign);
503 :
504 366 : json_categorize_type(element_type, false,
505 : &tcategory, &outfuncoid);
506 :
507 366 : deconstruct_array(v, element_type, typlen, typbyval,
508 : typalign, &elements, &nulls,
509 : &nitems);
510 :
511 366 : array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
512 : outfuncoid, use_line_feeds);
513 :
514 366 : pfree(elements);
515 366 : pfree(nulls);
516 : }
517 :
518 : /*
519 : * Turn a composite / record into JSON.
520 : */
521 : static void
522 1256 : composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
523 : {
524 : HeapTupleHeader td;
525 : Oid tupType;
526 : int32 tupTypmod;
527 : TupleDesc tupdesc;
528 : HeapTupleData tmptup,
529 : *tuple;
530 : int i;
531 1256 : bool needsep = false;
532 : const char *sep;
533 : int seplen;
534 :
535 : /*
536 : * We can avoid expensive strlen() calls by precalculating the separator
537 : * length.
538 : */
539 1256 : sep = use_line_feeds ? ",\n " : ",";
540 1256 : seplen = use_line_feeds ? strlen(",\n ") : strlen(",");
541 :
542 1256 : td = DatumGetHeapTupleHeader(composite);
543 :
544 : /* Extract rowtype info and find a tupdesc */
545 1256 : tupType = HeapTupleHeaderGetTypeId(td);
546 1256 : tupTypmod = HeapTupleHeaderGetTypMod(td);
547 1256 : tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
548 :
549 : /* Build a temporary HeapTuple control structure */
550 1256 : tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
551 1256 : tmptup.t_data = td;
552 1256 : tuple = &tmptup;
553 :
554 1256 : appendStringInfoChar(result, '{');
555 :
556 3708 : for (i = 0; i < tupdesc->natts; i++)
557 : {
558 : Datum val;
559 : bool isnull;
560 : char *attname;
561 : JsonTypeCategory tcategory;
562 : Oid outfuncoid;
563 2452 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
564 :
565 2452 : if (att->attisdropped)
566 0 : continue;
567 :
568 2452 : if (needsep)
569 1196 : appendBinaryStringInfo(result, sep, seplen);
570 2452 : needsep = true;
571 :
572 2452 : attname = NameStr(att->attname);
573 2452 : escape_json(result, attname);
574 2452 : appendStringInfoChar(result, ':');
575 :
576 2452 : val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
577 :
578 2452 : if (isnull)
579 : {
580 180 : tcategory = JSONTYPE_NULL;
581 180 : outfuncoid = InvalidOid;
582 : }
583 : else
584 2272 : json_categorize_type(att->atttypid, false, &tcategory,
585 : &outfuncoid);
586 :
587 2452 : datum_to_json_internal(val, isnull, result, tcategory, outfuncoid,
588 : false);
589 : }
590 :
591 1256 : appendStringInfoChar(result, '}');
592 1256 : ReleaseTupleDesc(tupdesc);
593 1256 : }
594 :
595 : /*
596 : * Append JSON text for "val" to "result".
597 : *
598 : * This is just a thin wrapper around datum_to_json. If the same type will be
599 : * printed many times, avoid using this; better to do the json_categorize_type
600 : * lookups only once.
601 : */
602 : static void
603 1378 : add_json(Datum val, bool is_null, StringInfo result,
604 : Oid val_type, bool key_scalar)
605 : {
606 : JsonTypeCategory tcategory;
607 : Oid outfuncoid;
608 :
609 1378 : if (val_type == InvalidOid)
610 0 : ereport(ERROR,
611 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
612 : errmsg("could not determine input data type")));
613 :
614 1378 : if (is_null)
615 : {
616 72 : tcategory = JSONTYPE_NULL;
617 72 : outfuncoid = InvalidOid;
618 : }
619 : else
620 1306 : json_categorize_type(val_type, false,
621 : &tcategory, &outfuncoid);
622 :
623 1378 : datum_to_json_internal(val, is_null, result, tcategory, outfuncoid,
624 : key_scalar);
625 1342 : }
626 :
627 : /*
628 : * SQL function array_to_json(row)
629 : */
630 : Datum
631 18 : array_to_json(PG_FUNCTION_ARGS)
632 : {
633 18 : Datum array = PG_GETARG_DATUM(0);
634 : StringInfoData result;
635 :
636 18 : initStringInfo(&result);
637 :
638 18 : array_to_json_internal(array, &result, false);
639 :
640 18 : PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
641 : }
642 :
643 : /*
644 : * SQL function array_to_json(row, prettybool)
645 : */
646 : Datum
647 24 : array_to_json_pretty(PG_FUNCTION_ARGS)
648 : {
649 24 : Datum array = PG_GETARG_DATUM(0);
650 24 : bool use_line_feeds = PG_GETARG_BOOL(1);
651 : StringInfoData result;
652 :
653 24 : initStringInfo(&result);
654 :
655 24 : array_to_json_internal(array, &result, use_line_feeds);
656 :
657 24 : PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
658 : }
659 :
660 : /*
661 : * SQL function row_to_json(row)
662 : */
663 : Datum
664 708 : row_to_json(PG_FUNCTION_ARGS)
665 : {
666 708 : Datum array = PG_GETARG_DATUM(0);
667 : StringInfoData result;
668 :
669 708 : initStringInfo(&result);
670 :
671 708 : composite_to_json(array, &result, false);
672 :
673 708 : PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
674 : }
675 :
676 : /*
677 : * SQL function row_to_json(row, prettybool)
678 : */
679 : Datum
680 48 : row_to_json_pretty(PG_FUNCTION_ARGS)
681 : {
682 48 : Datum array = PG_GETARG_DATUM(0);
683 48 : bool use_line_feeds = PG_GETARG_BOOL(1);
684 : StringInfoData result;
685 :
686 48 : initStringInfo(&result);
687 :
688 48 : composite_to_json(array, &result, use_line_feeds);
689 :
690 48 : PG_RETURN_TEXT_P(cstring_to_text_with_len(result.data, result.len));
691 : }
692 :
693 : /*
694 : * Is the given type immutable when coming out of a JSON context?
695 : *
696 : * At present, datetimes are all considered mutable, because they
697 : * depend on timezone. XXX we should also drill down into objects
698 : * and arrays, but do not.
699 : */
700 : bool
701 0 : to_json_is_immutable(Oid typoid)
702 : {
703 : JsonTypeCategory tcategory;
704 : Oid outfuncoid;
705 :
706 0 : json_categorize_type(typoid, false, &tcategory, &outfuncoid);
707 :
708 0 : switch (tcategory)
709 : {
710 0 : case JSONTYPE_BOOL:
711 : case JSONTYPE_JSON:
712 : case JSONTYPE_JSONB:
713 : case JSONTYPE_NULL:
714 0 : return true;
715 :
716 0 : case JSONTYPE_DATE:
717 : case JSONTYPE_TIMESTAMP:
718 : case JSONTYPE_TIMESTAMPTZ:
719 0 : return false;
720 :
721 0 : case JSONTYPE_ARRAY:
722 0 : return false; /* TODO recurse into elements */
723 :
724 0 : case JSONTYPE_COMPOSITE:
725 0 : return false; /* TODO recurse into fields */
726 :
727 0 : case JSONTYPE_NUMERIC:
728 : case JSONTYPE_CAST:
729 : case JSONTYPE_OTHER:
730 0 : return func_volatile(outfuncoid) == PROVOLATILE_IMMUTABLE;
731 : }
732 :
733 0 : return false; /* not reached */
734 : }
735 :
736 : /*
737 : * SQL function to_json(anyvalue)
738 : */
739 : Datum
740 180 : to_json(PG_FUNCTION_ARGS)
741 : {
742 180 : Datum val = PG_GETARG_DATUM(0);
743 180 : Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
744 : JsonTypeCategory tcategory;
745 : Oid outfuncoid;
746 :
747 180 : if (val_type == InvalidOid)
748 0 : ereport(ERROR,
749 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
750 : errmsg("could not determine input data type")));
751 :
752 180 : json_categorize_type(val_type, false,
753 : &tcategory, &outfuncoid);
754 :
755 180 : PG_RETURN_DATUM(datum_to_json(val, tcategory, outfuncoid));
756 : }
757 :
758 : /*
759 : * Turn a Datum into JSON text.
760 : *
761 : * tcategory and outfuncoid are from a previous call to json_categorize_type.
762 : */
763 : Datum
764 260 : datum_to_json(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
765 : {
766 : StringInfoData result;
767 :
768 260 : initStringInfo(&result);
769 260 : datum_to_json_internal(val, false, &result, tcategory, outfuncoid,
770 : false);
771 :
772 260 : return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
773 : }
774 :
775 : /*
776 : * json_agg transition function
777 : *
778 : * aggregate input column as a json array value.
779 : */
780 : static Datum
781 554 : json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
782 : {
783 : MemoryContext aggcontext,
784 : oldcontext;
785 : JsonAggState *state;
786 : Datum val;
787 :
788 554 : if (!AggCheckCallContext(fcinfo, &aggcontext))
789 : {
790 : /* cannot be called directly because of internal-type argument */
791 0 : elog(ERROR, "json_agg_transfn called in non-aggregate context");
792 : }
793 :
794 554 : if (PG_ARGISNULL(0))
795 : {
796 124 : Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
797 :
798 124 : if (arg_type == InvalidOid)
799 0 : ereport(ERROR,
800 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
801 : errmsg("could not determine input data type")));
802 :
803 : /*
804 : * Make this state object in a context where it will persist for the
805 : * duration of the aggregate call. MemoryContextSwitchTo is only
806 : * needed the first time, as the StringInfo routines make sure they
807 : * use the right context to enlarge the object if necessary.
808 : */
809 124 : oldcontext = MemoryContextSwitchTo(aggcontext);
810 124 : state = (JsonAggState *) palloc(sizeof(JsonAggState));
811 124 : state->str = makeStringInfo();
812 124 : MemoryContextSwitchTo(oldcontext);
813 :
814 124 : appendStringInfoChar(state->str, '[');
815 124 : json_categorize_type(arg_type, false, &state->val_category,
816 : &state->val_output_func);
817 : }
818 : else
819 : {
820 430 : state = (JsonAggState *) PG_GETARG_POINTER(0);
821 : }
822 :
823 554 : if (absent_on_null && PG_ARGISNULL(1))
824 90 : PG_RETURN_POINTER(state);
825 :
826 464 : if (state->str->len > 1)
827 346 : appendStringInfoString(state->str, ", ");
828 :
829 : /* fast path for NULLs */
830 464 : if (PG_ARGISNULL(1))
831 : {
832 54 : datum_to_json_internal((Datum) 0, true, state->str, JSONTYPE_NULL,
833 : InvalidOid, false);
834 54 : PG_RETURN_POINTER(state);
835 : }
836 :
837 410 : val = PG_GETARG_DATUM(1);
838 :
839 : /* add some whitespace if structured type and not first item */
840 410 : if (!PG_ARGISNULL(0) && state->str->len > 1 &&
841 304 : (state->val_category == JSONTYPE_ARRAY ||
842 298 : state->val_category == JSONTYPE_COMPOSITE))
843 : {
844 112 : appendStringInfoString(state->str, "\n ");
845 : }
846 :
847 410 : datum_to_json_internal(val, false, state->str, state->val_category,
848 : state->val_output_func, false);
849 :
850 : /*
851 : * The transition type for json_agg() is declared to be "internal", which
852 : * is a pass-by-value type the same size as a pointer. So we can safely
853 : * pass the JsonAggState pointer through nodeAgg.c's machinations.
854 : */
855 410 : PG_RETURN_POINTER(state);
856 : }
857 :
858 :
859 : /*
860 : * json_agg aggregate function
861 : */
862 : Datum
863 158 : json_agg_transfn(PG_FUNCTION_ARGS)
864 : {
865 158 : return json_agg_transfn_worker(fcinfo, false);
866 : }
867 :
868 : /*
869 : * json_agg_strict aggregate function
870 : */
871 : Datum
872 396 : json_agg_strict_transfn(PG_FUNCTION_ARGS)
873 : {
874 396 : return json_agg_transfn_worker(fcinfo, true);
875 : }
876 :
877 : /*
878 : * json_agg final function
879 : */
880 : Datum
881 136 : json_agg_finalfn(PG_FUNCTION_ARGS)
882 : {
883 : JsonAggState *state;
884 :
885 : /* cannot be called directly because of internal-type argument */
886 : Assert(AggCheckCallContext(fcinfo, NULL));
887 :
888 272 : state = PG_ARGISNULL(0) ?
889 136 : NULL :
890 124 : (JsonAggState *) PG_GETARG_POINTER(0);
891 :
892 : /* NULL result for no rows in, as is standard with aggregates */
893 136 : if (state == NULL)
894 12 : PG_RETURN_NULL();
895 :
896 : /* Else return state with appropriate array terminator added */
897 124 : PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
898 : }
899 :
900 : /* Functions implementing hash table for key uniqueness check */
901 : static uint32
902 1680 : json_unique_hash(const void *key, Size keysize)
903 : {
904 1680 : const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
905 1680 : uint32 hash = hash_bytes_uint32(entry->object_id);
906 :
907 1680 : hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
908 :
909 1680 : return hash;
910 : }
911 :
912 : static int
913 120 : json_unique_hash_match(const void *key1, const void *key2, Size keysize)
914 : {
915 120 : const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
916 120 : const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
917 :
918 120 : if (entry1->object_id != entry2->object_id)
919 0 : return entry1->object_id > entry2->object_id ? 1 : -1;
920 :
921 120 : if (entry1->key_len != entry2->key_len)
922 0 : return entry1->key_len > entry2->key_len ? 1 : -1;
923 :
924 120 : return strncmp(entry1->key, entry2->key, entry1->key_len);
925 : }
926 :
927 : /*
928 : * Uniqueness detection support.
929 : *
930 : * In order to detect uniqueness during building or parsing of a JSON
931 : * object, we maintain a hash table of key names already seen.
932 : */
933 : static void
934 324 : json_unique_check_init(JsonUniqueCheckState *cxt)
935 : {
936 : HASHCTL ctl;
937 :
938 324 : memset(&ctl, 0, sizeof(ctl));
939 324 : ctl.keysize = sizeof(JsonUniqueHashEntry);
940 324 : ctl.entrysize = sizeof(JsonUniqueHashEntry);
941 324 : ctl.hcxt = CurrentMemoryContext;
942 324 : ctl.hash = json_unique_hash;
943 324 : ctl.match = json_unique_hash_match;
944 :
945 324 : *cxt = hash_create("json object hashtable",
946 : 32,
947 : &ctl,
948 : HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
949 324 : }
950 :
951 : static void
952 88 : json_unique_builder_init(JsonUniqueBuilderState *cxt)
953 : {
954 88 : json_unique_check_init(&cxt->check);
955 88 : cxt->mcxt = CurrentMemoryContext;
956 88 : cxt->skipped_keys.data = NULL;
957 88 : }
958 :
959 : static bool
960 1680 : json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
961 : {
962 : JsonUniqueHashEntry entry;
963 : bool found;
964 :
965 1680 : entry.key = key;
966 1680 : entry.key_len = strlen(key);
967 1680 : entry.object_id = object_id;
968 :
969 1680 : (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
970 :
971 1680 : return !found;
972 : }
973 :
974 : /*
975 : * On-demand initialization of a throwaway StringInfo. This is used to
976 : * read a key name that we don't need to store in the output object, for
977 : * duplicate key detection when the value is NULL.
978 : */
979 : static StringInfo
980 42 : json_unique_builder_get_throwawaybuf(JsonUniqueBuilderState *cxt)
981 : {
982 42 : StringInfo out = &cxt->skipped_keys;
983 :
984 42 : if (!out->data)
985 : {
986 30 : MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
987 :
988 30 : initStringInfo(out);
989 30 : MemoryContextSwitchTo(oldcxt);
990 : }
991 : else
992 : /* Just reset the string to empty */
993 12 : out->len = 0;
994 :
995 42 : return out;
996 : }
997 :
998 : /*
999 : * json_object_agg transition function.
1000 : *
1001 : * aggregate two input columns as a single json object value.
1002 : */
1003 : static Datum
1004 1518 : json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
1005 : bool absent_on_null, bool unique_keys)
1006 : {
1007 : MemoryContext aggcontext,
1008 : oldcontext;
1009 : JsonAggState *state;
1010 : StringInfo out;
1011 : Datum arg;
1012 : bool skip;
1013 : int key_offset;
1014 :
1015 1518 : if (!AggCheckCallContext(fcinfo, &aggcontext))
1016 : {
1017 : /* cannot be called directly because of internal-type argument */
1018 0 : elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
1019 : }
1020 :
1021 1518 : if (PG_ARGISNULL(0))
1022 : {
1023 : Oid arg_type;
1024 :
1025 : /*
1026 : * Make the StringInfo in a context where it will persist for the
1027 : * duration of the aggregate call. Switching context is only needed
1028 : * for this initial step, as the StringInfo and dynahash routines make
1029 : * sure they use the right context to enlarge the object if necessary.
1030 : */
1031 134 : oldcontext = MemoryContextSwitchTo(aggcontext);
1032 134 : state = (JsonAggState *) palloc(sizeof(JsonAggState));
1033 134 : state->str = makeStringInfo();
1034 134 : if (unique_keys)
1035 54 : json_unique_builder_init(&state->unique_check);
1036 : else
1037 80 : memset(&state->unique_check, 0, sizeof(state->unique_check));
1038 134 : MemoryContextSwitchTo(oldcontext);
1039 :
1040 134 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1041 :
1042 134 : if (arg_type == InvalidOid)
1043 0 : ereport(ERROR,
1044 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1045 : errmsg("could not determine data type for argument %d", 1)));
1046 :
1047 134 : json_categorize_type(arg_type, false, &state->key_category,
1048 : &state->key_output_func);
1049 :
1050 134 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
1051 :
1052 134 : if (arg_type == InvalidOid)
1053 0 : ereport(ERROR,
1054 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1055 : errmsg("could not determine data type for argument %d", 2)));
1056 :
1057 134 : json_categorize_type(arg_type, false, &state->val_category,
1058 : &state->val_output_func);
1059 :
1060 134 : appendStringInfoString(state->str, "{ ");
1061 : }
1062 : else
1063 : {
1064 1384 : state = (JsonAggState *) PG_GETARG_POINTER(0);
1065 : }
1066 :
1067 : /*
1068 : * Note: since json_object_agg() is declared as taking type "any", the
1069 : * parser will not do any type conversion on unknown-type literals (that
1070 : * is, undecorated strings or NULLs). Such values will arrive here as
1071 : * type UNKNOWN, which fortunately does not matter to us, since
1072 : * unknownout() works fine.
1073 : */
1074 :
1075 1518 : if (PG_ARGISNULL(1))
1076 12 : ereport(ERROR,
1077 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1078 : errmsg("null value not allowed for object key")));
1079 :
1080 : /* Skip null values if absent_on_null */
1081 1506 : skip = absent_on_null && PG_ARGISNULL(2);
1082 :
1083 1506 : if (skip)
1084 : {
1085 : /*
1086 : * We got a NULL value and we're not storing those; if we're not
1087 : * testing key uniqueness, we're done. If we are, use the throwaway
1088 : * buffer to store the key name so that we can check it.
1089 : */
1090 54 : if (!unique_keys)
1091 24 : PG_RETURN_POINTER(state);
1092 :
1093 30 : out = json_unique_builder_get_throwawaybuf(&state->unique_check);
1094 : }
1095 : else
1096 : {
1097 1452 : out = state->str;
1098 :
1099 : /*
1100 : * Append comma delimiter only if we have already output some fields
1101 : * after the initial string "{ ".
1102 : */
1103 1452 : if (out->len > 2)
1104 1324 : appendStringInfoString(out, ", ");
1105 : }
1106 :
1107 1482 : arg = PG_GETARG_DATUM(1);
1108 :
1109 1482 : key_offset = out->len;
1110 :
1111 1482 : datum_to_json_internal(arg, false, out, state->key_category,
1112 : state->key_output_func, true);
1113 :
1114 1482 : if (unique_keys)
1115 : {
1116 : /*
1117 : * Copy the key first, instead of pointing into the buffer. It will be
1118 : * added to the hash table, but the buffer may get reallocated as
1119 : * we're appending more data to it. That would invalidate pointers to
1120 : * keys in the current buffer.
1121 : */
1122 1308 : const char *key = MemoryContextStrdup(aggcontext,
1123 1308 : &out->data[key_offset]);
1124 :
1125 1308 : if (!json_unique_check_key(&state->unique_check.check, key, 0))
1126 36 : ereport(ERROR,
1127 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1128 : errmsg("duplicate JSON object key value: %s", key));
1129 :
1130 1272 : if (skip)
1131 18 : PG_RETURN_POINTER(state);
1132 : }
1133 :
1134 1428 : appendStringInfoString(state->str, " : ");
1135 :
1136 1428 : if (PG_ARGISNULL(2))
1137 12 : arg = (Datum) 0;
1138 : else
1139 1416 : arg = PG_GETARG_DATUM(2);
1140 :
1141 1428 : datum_to_json_internal(arg, PG_ARGISNULL(2), state->str,
1142 : state->val_category,
1143 : state->val_output_func, false);
1144 :
1145 1428 : PG_RETURN_POINTER(state);
1146 : }
1147 :
1148 : /*
1149 : * json_object_agg aggregate function
1150 : */
1151 : Datum
1152 150 : json_object_agg_transfn(PG_FUNCTION_ARGS)
1153 : {
1154 150 : return json_object_agg_transfn_worker(fcinfo, false, false);
1155 : }
1156 :
1157 : /*
1158 : * json_object_agg_strict aggregate function
1159 : */
1160 : Datum
1161 60 : json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
1162 : {
1163 60 : return json_object_agg_transfn_worker(fcinfo, true, false);
1164 : }
1165 :
1166 : /*
1167 : * json_object_agg_unique aggregate function
1168 : */
1169 : Datum
1170 1254 : json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
1171 : {
1172 1254 : return json_object_agg_transfn_worker(fcinfo, false, true);
1173 : }
1174 :
1175 : /*
1176 : * json_object_agg_unique_strict aggregate function
1177 : */
1178 : Datum
1179 54 : json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
1180 : {
1181 54 : return json_object_agg_transfn_worker(fcinfo, true, true);
1182 : }
1183 :
1184 : /*
1185 : * json_object_agg final function.
1186 : */
1187 : Datum
1188 104 : json_object_agg_finalfn(PG_FUNCTION_ARGS)
1189 : {
1190 : JsonAggState *state;
1191 :
1192 : /* cannot be called directly because of internal-type argument */
1193 : Assert(AggCheckCallContext(fcinfo, NULL));
1194 :
1195 104 : state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
1196 :
1197 : /* NULL result for no rows in, as is standard with aggregates */
1198 104 : if (state == NULL)
1199 6 : PG_RETURN_NULL();
1200 :
1201 : /* Else return state with appropriate object terminator added */
1202 98 : PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
1203 : }
1204 :
1205 : /*
1206 : * Helper function for aggregates: return given StringInfo's contents plus
1207 : * specified trailing string, as a text datum. We need this because aggregate
1208 : * final functions are not allowed to modify the aggregate state.
1209 : */
1210 : static text *
1211 222 : catenate_stringinfo_string(StringInfo buffer, const char *addon)
1212 : {
1213 : /* custom version of cstring_to_text_with_len */
1214 222 : int buflen = buffer->len;
1215 222 : int addlen = strlen(addon);
1216 222 : text *result = (text *) palloc(buflen + addlen + VARHDRSZ);
1217 :
1218 222 : SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
1219 222 : memcpy(VARDATA(result), buffer->data, buflen);
1220 222 : memcpy(VARDATA(result) + buflen, addon, addlen);
1221 :
1222 222 : return result;
1223 : }
1224 :
1225 : Datum
1226 442 : json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1227 : bool absent_on_null, bool unique_keys)
1228 : {
1229 : int i;
1230 442 : const char *sep = "";
1231 : StringInfo result;
1232 : JsonUniqueBuilderState unique_check;
1233 :
1234 442 : if (nargs % 2 != 0)
1235 18 : ereport(ERROR,
1236 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1237 : errmsg("argument list must have even number of elements"),
1238 : /* translator: %s is a SQL function name */
1239 : errhint("The arguments of %s must consist of alternating keys and values.",
1240 : "json_build_object()")));
1241 :
1242 424 : result = makeStringInfo();
1243 :
1244 424 : appendStringInfoChar(result, '{');
1245 :
1246 424 : if (unique_keys)
1247 34 : json_unique_builder_init(&unique_check);
1248 :
1249 902 : for (i = 0; i < nargs; i += 2)
1250 : {
1251 : StringInfo out;
1252 : bool skip;
1253 : int key_offset;
1254 :
1255 : /* Skip null values if absent_on_null */
1256 572 : skip = absent_on_null && nulls[i + 1];
1257 :
1258 572 : if (skip)
1259 : {
1260 : /* If key uniqueness check is needed we must save skipped keys */
1261 28 : if (!unique_keys)
1262 16 : continue;
1263 :
1264 12 : out = json_unique_builder_get_throwawaybuf(&unique_check);
1265 : }
1266 : else
1267 : {
1268 544 : appendStringInfoString(result, sep);
1269 544 : sep = ", ";
1270 544 : out = result;
1271 : }
1272 :
1273 : /* process key */
1274 556 : if (nulls[i])
1275 24 : ereport(ERROR,
1276 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1277 : errmsg("null value not allowed for object key")));
1278 :
1279 : /* save key offset before appending it */
1280 532 : key_offset = out->len;
1281 :
1282 532 : add_json(args[i], false, out, types[i], true);
1283 :
1284 496 : if (unique_keys)
1285 : {
1286 : /*
1287 : * check key uniqueness after key appending
1288 : *
1289 : * Copy the key first, instead of pointing into the buffer. It
1290 : * will be added to the hash table, but the buffer may get
1291 : * reallocated as we're appending more data to it. That would
1292 : * invalidate pointers to keys in the current buffer.
1293 : */
1294 98 : const char *key = pstrdup(&out->data[key_offset]);
1295 :
1296 98 : if (!json_unique_check_key(&unique_check.check, key, 0))
1297 34 : ereport(ERROR,
1298 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1299 : errmsg("duplicate JSON object key value: %s", key));
1300 :
1301 64 : if (skip)
1302 6 : continue;
1303 : }
1304 :
1305 456 : appendStringInfoString(result, " : ");
1306 :
1307 : /* process value */
1308 456 : add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
1309 : }
1310 :
1311 330 : appendStringInfoChar(result, '}');
1312 :
1313 330 : return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
1314 : }
1315 :
1316 : /*
1317 : * SQL function json_build_object(variadic "any")
1318 : */
1319 : Datum
1320 156 : json_build_object(PG_FUNCTION_ARGS)
1321 : {
1322 : Datum *args;
1323 : bool *nulls;
1324 : Oid *types;
1325 :
1326 : /* build argument values to build the object */
1327 156 : int nargs = extract_variadic_args(fcinfo, 0, true,
1328 : &args, &types, &nulls);
1329 :
1330 156 : if (nargs < 0)
1331 6 : PG_RETURN_NULL();
1332 :
1333 150 : PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
1334 : }
1335 :
1336 : /*
1337 : * degenerate case of json_build_object where it gets 0 arguments.
1338 : */
1339 : Datum
1340 6 : json_build_object_noargs(PG_FUNCTION_ARGS)
1341 : {
1342 6 : PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1343 : }
1344 :
1345 : Datum
1346 232 : json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1347 : bool absent_on_null)
1348 : {
1349 : int i;
1350 232 : const char *sep = "";
1351 : StringInfoData result;
1352 :
1353 232 : initStringInfo(&result);
1354 :
1355 232 : appendStringInfoChar(&result, '[');
1356 :
1357 640 : for (i = 0; i < nargs; i++)
1358 : {
1359 408 : if (absent_on_null && nulls[i])
1360 18 : continue;
1361 :
1362 390 : appendStringInfoString(&result, sep);
1363 390 : sep = ", ";
1364 390 : add_json(args[i], nulls[i], &result, types[i], false);
1365 : }
1366 :
1367 232 : appendStringInfoChar(&result, ']');
1368 :
1369 232 : return PointerGetDatum(cstring_to_text_with_len(result.data, result.len));
1370 : }
1371 :
1372 : /*
1373 : * SQL function json_build_array(variadic "any")
1374 : */
1375 : Datum
1376 54 : json_build_array(PG_FUNCTION_ARGS)
1377 : {
1378 : Datum *args;
1379 : bool *nulls;
1380 : Oid *types;
1381 :
1382 : /* build argument values to build the object */
1383 54 : int nargs = extract_variadic_args(fcinfo, 0, true,
1384 : &args, &types, &nulls);
1385 :
1386 54 : if (nargs < 0)
1387 6 : PG_RETURN_NULL();
1388 :
1389 48 : PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
1390 : }
1391 :
1392 : /*
1393 : * degenerate case of json_build_array where it gets 0 arguments.
1394 : */
1395 : Datum
1396 6 : json_build_array_noargs(PG_FUNCTION_ARGS)
1397 : {
1398 6 : PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
1399 : }
1400 :
1401 : /*
1402 : * SQL function json_object(text[])
1403 : *
1404 : * take a one or two dimensional array of text as key/value pairs
1405 : * for a json object.
1406 : */
1407 : Datum
1408 48 : json_object(PG_FUNCTION_ARGS)
1409 : {
1410 48 : ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0);
1411 48 : int ndims = ARR_NDIM(in_array);
1412 : StringInfoData result;
1413 : Datum *in_datums;
1414 : bool *in_nulls;
1415 : int in_count,
1416 : count,
1417 : i;
1418 : text *rval;
1419 :
1420 48 : switch (ndims)
1421 : {
1422 6 : case 0:
1423 6 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1424 : break;
1425 :
1426 18 : case 1:
1427 18 : if ((ARR_DIMS(in_array)[0]) % 2)
1428 6 : ereport(ERROR,
1429 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1430 : errmsg("array must have even number of elements")));
1431 12 : break;
1432 :
1433 18 : case 2:
1434 18 : if ((ARR_DIMS(in_array)[1]) != 2)
1435 12 : ereport(ERROR,
1436 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1437 : errmsg("array must have two columns")));
1438 6 : break;
1439 :
1440 6 : default:
1441 6 : ereport(ERROR,
1442 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1443 : errmsg("wrong number of array subscripts")));
1444 : }
1445 :
1446 18 : deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
1447 :
1448 18 : count = in_count / 2;
1449 :
1450 18 : initStringInfo(&result);
1451 :
1452 18 : appendStringInfoChar(&result, '{');
1453 :
1454 1866 : for (i = 0; i < count; ++i)
1455 : {
1456 1848 : if (in_nulls[i * 2])
1457 0 : ereport(ERROR,
1458 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1459 : errmsg("null value not allowed for object key")));
1460 :
1461 1848 : if (i > 0)
1462 1830 : appendStringInfoString(&result, ", ");
1463 1848 : escape_json_text(&result, (text *) DatumGetPointer(in_datums[i * 2]));
1464 1848 : appendStringInfoString(&result, " : ");
1465 1848 : if (in_nulls[i * 2 + 1])
1466 12 : appendStringInfoString(&result, "null");
1467 : else
1468 : {
1469 1836 : escape_json_text(&result,
1470 1836 : (text *) DatumGetPointer(in_datums[i * 2 + 1]));
1471 : }
1472 : }
1473 :
1474 18 : appendStringInfoChar(&result, '}');
1475 :
1476 18 : pfree(in_datums);
1477 18 : pfree(in_nulls);
1478 :
1479 18 : rval = cstring_to_text_with_len(result.data, result.len);
1480 18 : pfree(result.data);
1481 :
1482 18 : PG_RETURN_TEXT_P(rval);
1483 : }
1484 :
1485 : /*
1486 : * SQL function json_object(text[], text[])
1487 : *
1488 : * take separate key and value arrays of text to construct a json object
1489 : * pairwise.
1490 : */
1491 : Datum
1492 42 : json_object_two_arg(PG_FUNCTION_ARGS)
1493 : {
1494 42 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(0);
1495 42 : ArrayType *val_array = PG_GETARG_ARRAYTYPE_P(1);
1496 42 : int nkdims = ARR_NDIM(key_array);
1497 42 : int nvdims = ARR_NDIM(val_array);
1498 : StringInfoData result;
1499 : Datum *key_datums,
1500 : *val_datums;
1501 : bool *key_nulls,
1502 : *val_nulls;
1503 : int key_count,
1504 : val_count,
1505 : i;
1506 : text *rval;
1507 :
1508 42 : if (nkdims > 1 || nkdims != nvdims)
1509 6 : ereport(ERROR,
1510 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1511 : errmsg("wrong number of array subscripts")));
1512 :
1513 36 : if (nkdims == 0)
1514 6 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1515 :
1516 30 : deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
1517 30 : deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
1518 :
1519 30 : if (key_count != val_count)
1520 12 : ereport(ERROR,
1521 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1522 : errmsg("mismatched array dimensions")));
1523 :
1524 18 : initStringInfo(&result);
1525 :
1526 18 : appendStringInfoChar(&result, '{');
1527 :
1528 78 : for (i = 0; i < key_count; ++i)
1529 : {
1530 66 : if (key_nulls[i])
1531 6 : ereport(ERROR,
1532 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1533 : errmsg("null value not allowed for object key")));
1534 :
1535 60 : if (i > 0)
1536 42 : appendStringInfoString(&result, ", ");
1537 60 : escape_json_text(&result, (text *) DatumGetPointer(key_datums[i]));
1538 60 : appendStringInfoString(&result, " : ");
1539 60 : if (val_nulls[i])
1540 0 : appendStringInfoString(&result, "null");
1541 : else
1542 60 : escape_json_text(&result,
1543 60 : (text *) DatumGetPointer(val_datums[i]));
1544 : }
1545 :
1546 12 : appendStringInfoChar(&result, '}');
1547 :
1548 12 : pfree(key_datums);
1549 12 : pfree(key_nulls);
1550 12 : pfree(val_datums);
1551 12 : pfree(val_nulls);
1552 :
1553 12 : rval = cstring_to_text_with_len(result.data, result.len);
1554 12 : pfree(result.data);
1555 :
1556 12 : PG_RETURN_TEXT_P(rval);
1557 : }
1558 :
1559 : /*
1560 : * escape_json_char
1561 : * Inline helper function for escape_json* functions
1562 : */
1563 : static pg_attribute_always_inline void
1564 4375524 : escape_json_char(StringInfo buf, char c)
1565 : {
1566 4375524 : switch (c)
1567 : {
1568 18 : case '\b':
1569 18 : appendStringInfoString(buf, "\\b");
1570 18 : break;
1571 6 : case '\f':
1572 6 : appendStringInfoString(buf, "\\f");
1573 6 : break;
1574 24 : case '\n':
1575 24 : appendStringInfoString(buf, "\\n");
1576 24 : break;
1577 6 : case '\r':
1578 6 : appendStringInfoString(buf, "\\r");
1579 6 : break;
1580 180 : case '\t':
1581 180 : appendStringInfoString(buf, "\\t");
1582 180 : break;
1583 630 : case '"':
1584 630 : appendStringInfoString(buf, "\\\"");
1585 630 : break;
1586 90 : case '\\':
1587 90 : appendStringInfoString(buf, "\\\\");
1588 90 : break;
1589 4374570 : default:
1590 4374570 : if ((unsigned char) c < ' ')
1591 6 : appendStringInfo(buf, "\\u%04x", (int) c);
1592 : else
1593 4374564 : appendStringInfoCharMacro(buf, c);
1594 4374570 : break;
1595 : }
1596 4375524 : }
1597 :
1598 : /*
1599 : * escape_json
1600 : * Produce a JSON string literal, properly escaping the NUL-terminated
1601 : * cstring.
1602 : */
1603 : void
1604 39598 : escape_json(StringInfo buf, const char *str)
1605 : {
1606 39598 : appendStringInfoCharMacro(buf, '"');
1607 :
1608 543444 : for (; *str != '\0'; str++)
1609 503846 : escape_json_char(buf, *str);
1610 :
1611 39598 : appendStringInfoCharMacro(buf, '"');
1612 39598 : }
1613 :
1614 : /*
1615 : * Define the number of bytes that escape_json_with_len will look ahead in the
1616 : * input string before flushing the input string to the destination buffer.
1617 : * Looking ahead too far could result in cachelines being evicted that will
1618 : * need to be reloaded in order to perform the appendBinaryStringInfo call.
1619 : * Smaller values will result in a larger number of calls to
1620 : * appendBinaryStringInfo and introduce additional function call overhead.
1621 : * Values larger than the size of L1d cache will likely result in worse
1622 : * performance.
1623 : */
1624 : #define ESCAPE_JSON_FLUSH_AFTER 512
1625 :
1626 : /*
1627 : * escape_json_with_len
1628 : * Produce a JSON string literal, properly escaping the possibly not
1629 : * NUL-terminated characters in 'str'. 'len' defines the number of bytes
1630 : * from 'str' to process.
1631 : */
1632 : void
1633 391262 : escape_json_with_len(StringInfo buf, const char *str, int len)
1634 : {
1635 : int vlen;
1636 :
1637 : Assert(len >= 0);
1638 :
1639 : /*
1640 : * Since we know the minimum length we'll need to append, let's just
1641 : * enlarge the buffer now rather than incrementally making more space when
1642 : * we run out. Add two extra bytes for the enclosing quotes.
1643 : */
1644 391262 : enlargeStringInfo(buf, len + 2);
1645 :
1646 : /*
1647 : * Figure out how many bytes to process using SIMD. Round 'len' down to
1648 : * the previous multiple of sizeof(Vector8), assuming that's a power-of-2.
1649 : */
1650 391262 : vlen = len & (int) (~(sizeof(Vector8) - 1));
1651 :
1652 391262 : appendStringInfoCharMacro(buf, '"');
1653 :
1654 391262 : for (int i = 0, copypos = 0;;)
1655 : {
1656 : /*
1657 : * To speed this up, try searching sizeof(Vector8) bytes at once for
1658 : * special characters that we need to escape. When we find one, we
1659 : * fall out of the Vector8 loop and copy the portion we've vector
1660 : * searched and then we process sizeof(Vector8) bytes one byte at a
1661 : * time. Once done, come back and try doing vector searching again.
1662 : * We'll also process any remaining bytes at the tail end of the
1663 : * string byte-by-byte. This optimization assumes that most chunks of
1664 : * sizeof(Vector8) bytes won't contain any special characters.
1665 : */
1666 420772 : for (; i < vlen; i += sizeof(Vector8))
1667 : {
1668 : Vector8 chunk;
1669 :
1670 29510 : vector8_load(&chunk, (const uint8 *) &str[i]);
1671 :
1672 : /*
1673 : * Break on anything less than ' ' or if we find a '"' or '\\'.
1674 : * Those need special handling. That's done in the per-byte loop.
1675 : */
1676 58948 : if (vector8_has_le(chunk, (unsigned char) 0x1F) ||
1677 58870 : vector8_has(chunk, (unsigned char) '"') ||
1678 29432 : vector8_has(chunk, (unsigned char) '\\'))
1679 : break;
1680 :
1681 : #ifdef ESCAPE_JSON_FLUSH_AFTER
1682 :
1683 : /*
1684 : * Flush what's been checked so far out to the destination buffer
1685 : * every so often to avoid having to re-read cachelines when
1686 : * escaping large strings.
1687 : */
1688 29396 : if (i - copypos >= ESCAPE_JSON_FLUSH_AFTER)
1689 : {
1690 30 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1691 30 : copypos = i;
1692 : }
1693 : #endif
1694 : }
1695 :
1696 : /*
1697 : * Write to the destination up to the point that we've vector searched
1698 : * so far. Do this only when switching into per-byte mode rather than
1699 : * once every sizeof(Vector8) bytes.
1700 : */
1701 391376 : if (copypos < i)
1702 : {
1703 26984 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1704 26984 : copypos = i;
1705 : }
1706 :
1707 : /*
1708 : * Per-byte loop for Vector8s containing special chars and for
1709 : * processing the tail of the string.
1710 : */
1711 4263054 : for (int b = 0; b < sizeof(Vector8); b++)
1712 : {
1713 : /* check if we've finished */
1714 4262940 : if (i == len)
1715 391262 : goto done;
1716 :
1717 : Assert(i < len);
1718 :
1719 3871678 : escape_json_char(buf, str[i++]);
1720 : }
1721 :
1722 114 : copypos = i;
1723 : /* We're not done yet. Try the vector search again. */
1724 : }
1725 :
1726 391262 : done:
1727 391262 : appendStringInfoCharMacro(buf, '"');
1728 391262 : }
1729 :
1730 : /*
1731 : * escape_json_text
1732 : * Append 'txt' onto 'buf' and escape using escape_json_with_len.
1733 : *
1734 : * This is more efficient than calling text_to_cstring and appending the
1735 : * result as that could require an additional palloc and memcpy.
1736 : */
1737 : void
1738 4834 : escape_json_text(StringInfo buf, const text *txt)
1739 : {
1740 : /* must cast away the const, unfortunately */
1741 4834 : text *tunpacked = pg_detoast_datum_packed(unconstify(text *, txt));
1742 4834 : int len = VARSIZE_ANY_EXHDR(tunpacked);
1743 : char *str;
1744 :
1745 4834 : str = VARDATA_ANY(tunpacked);
1746 :
1747 4834 : escape_json_with_len(buf, str, len);
1748 :
1749 : /* pfree any detoasted values */
1750 4834 : if (tunpacked != txt)
1751 0 : pfree(tunpacked);
1752 4834 : }
1753 :
1754 : /* Semantic actions for key uniqueness check */
1755 : static JsonParseErrorType
1756 186 : json_unique_object_start(void *_state)
1757 : {
1758 186 : JsonUniqueParsingState *state = _state;
1759 : JsonUniqueStackEntry *entry;
1760 :
1761 186 : if (!state->unique)
1762 0 : return JSON_SUCCESS;
1763 :
1764 : /* push object entry to stack */
1765 186 : entry = palloc(sizeof(*entry));
1766 186 : entry->object_id = state->id_counter++;
1767 186 : entry->parent = state->stack;
1768 186 : state->stack = entry;
1769 :
1770 186 : return JSON_SUCCESS;
1771 : }
1772 :
1773 : static JsonParseErrorType
1774 180 : json_unique_object_end(void *_state)
1775 : {
1776 180 : JsonUniqueParsingState *state = _state;
1777 : JsonUniqueStackEntry *entry;
1778 :
1779 180 : if (!state->unique)
1780 72 : return JSON_SUCCESS;
1781 :
1782 108 : entry = state->stack;
1783 108 : state->stack = entry->parent; /* pop object from stack */
1784 108 : pfree(entry);
1785 108 : return JSON_SUCCESS;
1786 : }
1787 :
1788 : static JsonParseErrorType
1789 274 : json_unique_object_field_start(void *_state, char *field, bool isnull)
1790 : {
1791 274 : JsonUniqueParsingState *state = _state;
1792 : JsonUniqueStackEntry *entry;
1793 :
1794 274 : if (!state->unique)
1795 0 : return JSON_SUCCESS;
1796 :
1797 : /* find key collision in the current object */
1798 274 : if (json_unique_check_key(&state->check, field, state->stack->object_id))
1799 224 : return JSON_SUCCESS;
1800 :
1801 50 : state->unique = false;
1802 :
1803 : /* pop all objects entries */
1804 122 : while ((entry = state->stack))
1805 : {
1806 72 : state->stack = entry->parent;
1807 72 : pfree(entry);
1808 : }
1809 50 : return JSON_SUCCESS;
1810 : }
1811 :
1812 : /* Validate JSON text and additionally check key uniqueness */
1813 : bool
1814 1336 : json_validate(text *json, bool check_unique_keys, bool throw_error)
1815 : {
1816 : JsonLexContext lex;
1817 1336 : JsonSemAction uniqueSemAction = {0};
1818 : JsonUniqueParsingState state;
1819 : JsonParseErrorType result;
1820 :
1821 1336 : makeJsonLexContext(&lex, json, check_unique_keys);
1822 :
1823 1336 : if (check_unique_keys)
1824 : {
1825 236 : state.lex = &lex;
1826 236 : state.stack = NULL;
1827 236 : state.id_counter = 0;
1828 236 : state.unique = true;
1829 236 : json_unique_check_init(&state.check);
1830 :
1831 236 : uniqueSemAction.semstate = &state;
1832 236 : uniqueSemAction.object_start = json_unique_object_start;
1833 236 : uniqueSemAction.object_field_start = json_unique_object_field_start;
1834 236 : uniqueSemAction.object_end = json_unique_object_end;
1835 : }
1836 :
1837 1336 : result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
1838 :
1839 1336 : if (result != JSON_SUCCESS)
1840 : {
1841 216 : if (throw_error)
1842 0 : json_errsave_error(result, &lex, NULL);
1843 :
1844 216 : return false; /* invalid json */
1845 : }
1846 :
1847 1120 : if (check_unique_keys && !state.unique)
1848 : {
1849 50 : if (throw_error)
1850 10 : ereport(ERROR,
1851 : (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1852 : errmsg("duplicate JSON object key value")));
1853 :
1854 40 : return false; /* not unique keys */
1855 : }
1856 :
1857 1070 : if (check_unique_keys)
1858 162 : freeJsonLexContext(&lex);
1859 :
1860 1070 : return true; /* ok */
1861 : }
1862 :
1863 : /*
1864 : * SQL function json_typeof(json) -> text
1865 : *
1866 : * Returns the type of the outermost JSON value as TEXT. Possible types are
1867 : * "object", "array", "string", "number", "boolean", and "null".
1868 : *
1869 : * Performs a single call to json_lex() to get the first token of the supplied
1870 : * value. This initial token uniquely determines the value's type. As our
1871 : * input must already have been validated by json_in() or json_recv(), the
1872 : * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
1873 : * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
1874 : */
1875 : Datum
1876 60 : json_typeof(PG_FUNCTION_ARGS)
1877 : {
1878 60 : text *json = PG_GETARG_TEXT_PP(0);
1879 : JsonLexContext lex;
1880 : char *type;
1881 : JsonParseErrorType result;
1882 :
1883 : /* Lex exactly one token from the input and check its type. */
1884 60 : makeJsonLexContext(&lex, json, false);
1885 60 : result = json_lex(&lex);
1886 60 : if (result != JSON_SUCCESS)
1887 0 : json_errsave_error(result, &lex, NULL);
1888 :
1889 60 : switch (lex.token_type)
1890 : {
1891 12 : case JSON_TOKEN_OBJECT_START:
1892 12 : type = "object";
1893 12 : break;
1894 12 : case JSON_TOKEN_ARRAY_START:
1895 12 : type = "array";
1896 12 : break;
1897 6 : case JSON_TOKEN_STRING:
1898 6 : type = "string";
1899 6 : break;
1900 12 : case JSON_TOKEN_NUMBER:
1901 12 : type = "number";
1902 12 : break;
1903 12 : case JSON_TOKEN_TRUE:
1904 : case JSON_TOKEN_FALSE:
1905 12 : type = "boolean";
1906 12 : break;
1907 6 : case JSON_TOKEN_NULL:
1908 6 : type = "null";
1909 6 : break;
1910 0 : default:
1911 0 : elog(ERROR, "unexpected json token: %d", lex.token_type);
1912 : }
1913 :
1914 60 : PG_RETURN_TEXT_P(cstring_to_text(type));
1915 : }
|