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 : Datum *vals, 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, Datum *vals,
433 : 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 : StringInfo result;
635 :
636 18 : result = makeStringInfo();
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 : StringInfo result;
652 :
653 24 : result = makeStringInfo();
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 : StringInfo result;
668 :
669 708 : result = makeStringInfo();
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 : StringInfo result;
685 :
686 48 : result = makeStringInfo();
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 260 : StringInfo result = makeStringInfo();
767 :
768 260 : datum_to_json_internal(val, false, result, tcategory, outfuncoid,
769 : false);
770 :
771 260 : return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
772 : }
773 :
774 : /*
775 : * json_agg transition function
776 : *
777 : * aggregate input column as a json array value.
778 : */
779 : static Datum
780 554 : json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
781 : {
782 : MemoryContext aggcontext,
783 : oldcontext;
784 : JsonAggState *state;
785 : Datum val;
786 :
787 554 : if (!AggCheckCallContext(fcinfo, &aggcontext))
788 : {
789 : /* cannot be called directly because of internal-type argument */
790 0 : elog(ERROR, "json_agg_transfn called in non-aggregate context");
791 : }
792 :
793 554 : if (PG_ARGISNULL(0))
794 : {
795 124 : Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
796 :
797 124 : if (arg_type == InvalidOid)
798 0 : ereport(ERROR,
799 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
800 : errmsg("could not determine input data type")));
801 :
802 : /*
803 : * Make this state object in a context where it will persist for the
804 : * duration of the aggregate call. MemoryContextSwitchTo is only
805 : * needed the first time, as the StringInfo routines make sure they
806 : * use the right context to enlarge the object if necessary.
807 : */
808 124 : oldcontext = MemoryContextSwitchTo(aggcontext);
809 124 : state = (JsonAggState *) palloc(sizeof(JsonAggState));
810 124 : state->str = makeStringInfo();
811 124 : MemoryContextSwitchTo(oldcontext);
812 :
813 124 : appendStringInfoChar(state->str, '[');
814 124 : json_categorize_type(arg_type, false, &state->val_category,
815 : &state->val_output_func);
816 : }
817 : else
818 : {
819 430 : state = (JsonAggState *) PG_GETARG_POINTER(0);
820 : }
821 :
822 554 : if (absent_on_null && PG_ARGISNULL(1))
823 90 : PG_RETURN_POINTER(state);
824 :
825 464 : if (state->str->len > 1)
826 346 : appendStringInfoString(state->str, ", ");
827 :
828 : /* fast path for NULLs */
829 464 : if (PG_ARGISNULL(1))
830 : {
831 54 : datum_to_json_internal((Datum) 0, true, state->str, JSONTYPE_NULL,
832 : InvalidOid, false);
833 54 : PG_RETURN_POINTER(state);
834 : }
835 :
836 410 : val = PG_GETARG_DATUM(1);
837 :
838 : /* add some whitespace if structured type and not first item */
839 410 : if (!PG_ARGISNULL(0) && state->str->len > 1 &&
840 304 : (state->val_category == JSONTYPE_ARRAY ||
841 298 : state->val_category == JSONTYPE_COMPOSITE))
842 : {
843 112 : appendStringInfoString(state->str, "\n ");
844 : }
845 :
846 410 : datum_to_json_internal(val, false, state->str, state->val_category,
847 : state->val_output_func, false);
848 :
849 : /*
850 : * The transition type for json_agg() is declared to be "internal", which
851 : * is a pass-by-value type the same size as a pointer. So we can safely
852 : * pass the JsonAggState pointer through nodeAgg.c's machinations.
853 : */
854 410 : PG_RETURN_POINTER(state);
855 : }
856 :
857 :
858 : /*
859 : * json_agg aggregate function
860 : */
861 : Datum
862 158 : json_agg_transfn(PG_FUNCTION_ARGS)
863 : {
864 158 : return json_agg_transfn_worker(fcinfo, false);
865 : }
866 :
867 : /*
868 : * json_agg_strict aggregate function
869 : */
870 : Datum
871 396 : json_agg_strict_transfn(PG_FUNCTION_ARGS)
872 : {
873 396 : return json_agg_transfn_worker(fcinfo, true);
874 : }
875 :
876 : /*
877 : * json_agg final function
878 : */
879 : Datum
880 136 : json_agg_finalfn(PG_FUNCTION_ARGS)
881 : {
882 : JsonAggState *state;
883 :
884 : /* cannot be called directly because of internal-type argument */
885 : Assert(AggCheckCallContext(fcinfo, NULL));
886 :
887 272 : state = PG_ARGISNULL(0) ?
888 136 : NULL :
889 124 : (JsonAggState *) PG_GETARG_POINTER(0);
890 :
891 : /* NULL result for no rows in, as is standard with aggregates */
892 136 : if (state == NULL)
893 12 : PG_RETURN_NULL();
894 :
895 : /* Else return state with appropriate array terminator added */
896 124 : PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
897 : }
898 :
899 : /* Functions implementing hash table for key uniqueness check */
900 : static uint32
901 1680 : json_unique_hash(const void *key, Size keysize)
902 : {
903 1680 : const JsonUniqueHashEntry *entry = (JsonUniqueHashEntry *) key;
904 1680 : uint32 hash = hash_bytes_uint32(entry->object_id);
905 :
906 1680 : hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
907 :
908 1680 : return hash;
909 : }
910 :
911 : static int
912 120 : json_unique_hash_match(const void *key1, const void *key2, Size keysize)
913 : {
914 120 : const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
915 120 : const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
916 :
917 120 : if (entry1->object_id != entry2->object_id)
918 0 : return entry1->object_id > entry2->object_id ? 1 : -1;
919 :
920 120 : if (entry1->key_len != entry2->key_len)
921 0 : return entry1->key_len > entry2->key_len ? 1 : -1;
922 :
923 120 : return strncmp(entry1->key, entry2->key, entry1->key_len);
924 : }
925 :
926 : /*
927 : * Uniqueness detection support.
928 : *
929 : * In order to detect uniqueness during building or parsing of a JSON
930 : * object, we maintain a hash table of key names already seen.
931 : */
932 : static void
933 324 : json_unique_check_init(JsonUniqueCheckState *cxt)
934 : {
935 : HASHCTL ctl;
936 :
937 324 : memset(&ctl, 0, sizeof(ctl));
938 324 : ctl.keysize = sizeof(JsonUniqueHashEntry);
939 324 : ctl.entrysize = sizeof(JsonUniqueHashEntry);
940 324 : ctl.hcxt = CurrentMemoryContext;
941 324 : ctl.hash = json_unique_hash;
942 324 : ctl.match = json_unique_hash_match;
943 :
944 324 : *cxt = hash_create("json object hashtable",
945 : 32,
946 : &ctl,
947 : HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
948 324 : }
949 :
950 : static void
951 88 : json_unique_builder_init(JsonUniqueBuilderState *cxt)
952 : {
953 88 : json_unique_check_init(&cxt->check);
954 88 : cxt->mcxt = CurrentMemoryContext;
955 88 : cxt->skipped_keys.data = NULL;
956 88 : }
957 :
958 : static bool
959 1680 : json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
960 : {
961 : JsonUniqueHashEntry entry;
962 : bool found;
963 :
964 1680 : entry.key = key;
965 1680 : entry.key_len = strlen(key);
966 1680 : entry.object_id = object_id;
967 :
968 1680 : (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
969 :
970 1680 : return !found;
971 : }
972 :
973 : /*
974 : * On-demand initialization of a throwaway StringInfo. This is used to
975 : * read a key name that we don't need to store in the output object, for
976 : * duplicate key detection when the value is NULL.
977 : */
978 : static StringInfo
979 42 : json_unique_builder_get_throwawaybuf(JsonUniqueBuilderState *cxt)
980 : {
981 42 : StringInfo out = &cxt->skipped_keys;
982 :
983 42 : if (!out->data)
984 : {
985 30 : MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
986 :
987 30 : initStringInfo(out);
988 30 : MemoryContextSwitchTo(oldcxt);
989 : }
990 : else
991 : /* Just reset the string to empty */
992 12 : out->len = 0;
993 :
994 42 : return out;
995 : }
996 :
997 : /*
998 : * json_object_agg transition function.
999 : *
1000 : * aggregate two input columns as a single json object value.
1001 : */
1002 : static Datum
1003 1518 : json_object_agg_transfn_worker(FunctionCallInfo fcinfo,
1004 : bool absent_on_null, bool unique_keys)
1005 : {
1006 : MemoryContext aggcontext,
1007 : oldcontext;
1008 : JsonAggState *state;
1009 : StringInfo out;
1010 : Datum arg;
1011 : bool skip;
1012 : int key_offset;
1013 :
1014 1518 : if (!AggCheckCallContext(fcinfo, &aggcontext))
1015 : {
1016 : /* cannot be called directly because of internal-type argument */
1017 0 : elog(ERROR, "json_object_agg_transfn called in non-aggregate context");
1018 : }
1019 :
1020 1518 : if (PG_ARGISNULL(0))
1021 : {
1022 : Oid arg_type;
1023 :
1024 : /*
1025 : * Make the StringInfo in a context where it will persist for the
1026 : * duration of the aggregate call. Switching context is only needed
1027 : * for this initial step, as the StringInfo and dynahash routines make
1028 : * sure they use the right context to enlarge the object if necessary.
1029 : */
1030 134 : oldcontext = MemoryContextSwitchTo(aggcontext);
1031 134 : state = (JsonAggState *) palloc(sizeof(JsonAggState));
1032 134 : state->str = makeStringInfo();
1033 134 : if (unique_keys)
1034 54 : json_unique_builder_init(&state->unique_check);
1035 : else
1036 80 : memset(&state->unique_check, 0, sizeof(state->unique_check));
1037 134 : MemoryContextSwitchTo(oldcontext);
1038 :
1039 134 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1040 :
1041 134 : if (arg_type == InvalidOid)
1042 0 : ereport(ERROR,
1043 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1044 : errmsg("could not determine data type for argument %d", 1)));
1045 :
1046 134 : json_categorize_type(arg_type, false, &state->key_category,
1047 : &state->key_output_func);
1048 :
1049 134 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
1050 :
1051 134 : if (arg_type == InvalidOid)
1052 0 : ereport(ERROR,
1053 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1054 : errmsg("could not determine data type for argument %d", 2)));
1055 :
1056 134 : json_categorize_type(arg_type, false, &state->val_category,
1057 : &state->val_output_func);
1058 :
1059 134 : appendStringInfoString(state->str, "{ ");
1060 : }
1061 : else
1062 : {
1063 1384 : state = (JsonAggState *) PG_GETARG_POINTER(0);
1064 : }
1065 :
1066 : /*
1067 : * Note: since json_object_agg() is declared as taking type "any", the
1068 : * parser will not do any type conversion on unknown-type literals (that
1069 : * is, undecorated strings or NULLs). Such values will arrive here as
1070 : * type UNKNOWN, which fortunately does not matter to us, since
1071 : * unknownout() works fine.
1072 : */
1073 :
1074 1518 : if (PG_ARGISNULL(1))
1075 12 : ereport(ERROR,
1076 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1077 : errmsg("null value not allowed for object key")));
1078 :
1079 : /* Skip null values if absent_on_null */
1080 1506 : skip = absent_on_null && PG_ARGISNULL(2);
1081 :
1082 1506 : if (skip)
1083 : {
1084 : /*
1085 : * We got a NULL value and we're not storing those; if we're not
1086 : * testing key uniqueness, we're done. If we are, use the throwaway
1087 : * buffer to store the key name so that we can check it.
1088 : */
1089 54 : if (!unique_keys)
1090 24 : PG_RETURN_POINTER(state);
1091 :
1092 30 : out = json_unique_builder_get_throwawaybuf(&state->unique_check);
1093 : }
1094 : else
1095 : {
1096 1452 : out = state->str;
1097 :
1098 : /*
1099 : * Append comma delimiter only if we have already output some fields
1100 : * after the initial string "{ ".
1101 : */
1102 1452 : if (out->len > 2)
1103 1324 : appendStringInfoString(out, ", ");
1104 : }
1105 :
1106 1482 : arg = PG_GETARG_DATUM(1);
1107 :
1108 1482 : key_offset = out->len;
1109 :
1110 1482 : datum_to_json_internal(arg, false, out, state->key_category,
1111 : state->key_output_func, true);
1112 :
1113 1482 : if (unique_keys)
1114 : {
1115 : /*
1116 : * Copy the key first, instead of pointing into the buffer. It will be
1117 : * added to the hash table, but the buffer may get reallocated as
1118 : * we're appending more data to it. That would invalidate pointers to
1119 : * keys in the current buffer.
1120 : */
1121 1308 : const char *key = MemoryContextStrdup(aggcontext,
1122 1308 : &out->data[key_offset]);
1123 :
1124 1308 : if (!json_unique_check_key(&state->unique_check.check, key, 0))
1125 36 : ereport(ERROR,
1126 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1127 : errmsg("duplicate JSON object key value: %s", key));
1128 :
1129 1272 : if (skip)
1130 18 : PG_RETURN_POINTER(state);
1131 : }
1132 :
1133 1428 : appendStringInfoString(state->str, " : ");
1134 :
1135 1428 : if (PG_ARGISNULL(2))
1136 12 : arg = (Datum) 0;
1137 : else
1138 1416 : arg = PG_GETARG_DATUM(2);
1139 :
1140 1428 : datum_to_json_internal(arg, PG_ARGISNULL(2), state->str,
1141 : state->val_category,
1142 : state->val_output_func, false);
1143 :
1144 1428 : PG_RETURN_POINTER(state);
1145 : }
1146 :
1147 : /*
1148 : * json_object_agg aggregate function
1149 : */
1150 : Datum
1151 150 : json_object_agg_transfn(PG_FUNCTION_ARGS)
1152 : {
1153 150 : return json_object_agg_transfn_worker(fcinfo, false, false);
1154 : }
1155 :
1156 : /*
1157 : * json_object_agg_strict aggregate function
1158 : */
1159 : Datum
1160 60 : json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
1161 : {
1162 60 : return json_object_agg_transfn_worker(fcinfo, true, false);
1163 : }
1164 :
1165 : /*
1166 : * json_object_agg_unique aggregate function
1167 : */
1168 : Datum
1169 1254 : json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
1170 : {
1171 1254 : return json_object_agg_transfn_worker(fcinfo, false, true);
1172 : }
1173 :
1174 : /*
1175 : * json_object_agg_unique_strict aggregate function
1176 : */
1177 : Datum
1178 54 : json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
1179 : {
1180 54 : return json_object_agg_transfn_worker(fcinfo, true, true);
1181 : }
1182 :
1183 : /*
1184 : * json_object_agg final function.
1185 : */
1186 : Datum
1187 104 : json_object_agg_finalfn(PG_FUNCTION_ARGS)
1188 : {
1189 : JsonAggState *state;
1190 :
1191 : /* cannot be called directly because of internal-type argument */
1192 : Assert(AggCheckCallContext(fcinfo, NULL));
1193 :
1194 104 : state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
1195 :
1196 : /* NULL result for no rows in, as is standard with aggregates */
1197 104 : if (state == NULL)
1198 6 : PG_RETURN_NULL();
1199 :
1200 : /* Else return state with appropriate object terminator added */
1201 98 : PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, " }"));
1202 : }
1203 :
1204 : /*
1205 : * Helper function for aggregates: return given StringInfo's contents plus
1206 : * specified trailing string, as a text datum. We need this because aggregate
1207 : * final functions are not allowed to modify the aggregate state.
1208 : */
1209 : static text *
1210 222 : catenate_stringinfo_string(StringInfo buffer, const char *addon)
1211 : {
1212 : /* custom version of cstring_to_text_with_len */
1213 222 : int buflen = buffer->len;
1214 222 : int addlen = strlen(addon);
1215 222 : text *result = (text *) palloc(buflen + addlen + VARHDRSZ);
1216 :
1217 222 : SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
1218 222 : memcpy(VARDATA(result), buffer->data, buflen);
1219 222 : memcpy(VARDATA(result) + buflen, addon, addlen);
1220 :
1221 222 : return result;
1222 : }
1223 :
1224 : Datum
1225 442 : json_build_object_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1226 : bool absent_on_null, bool unique_keys)
1227 : {
1228 : int i;
1229 442 : const char *sep = "";
1230 : StringInfo result;
1231 : JsonUniqueBuilderState unique_check;
1232 :
1233 442 : if (nargs % 2 != 0)
1234 18 : ereport(ERROR,
1235 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1236 : errmsg("argument list must have even number of elements"),
1237 : /* translator: %s is a SQL function name */
1238 : errhint("The arguments of %s must consist of alternating keys and values.",
1239 : "json_build_object()")));
1240 :
1241 424 : result = makeStringInfo();
1242 :
1243 424 : appendStringInfoChar(result, '{');
1244 :
1245 424 : if (unique_keys)
1246 34 : json_unique_builder_init(&unique_check);
1247 :
1248 902 : for (i = 0; i < nargs; i += 2)
1249 : {
1250 : StringInfo out;
1251 : bool skip;
1252 : int key_offset;
1253 :
1254 : /* Skip null values if absent_on_null */
1255 572 : skip = absent_on_null && nulls[i + 1];
1256 :
1257 572 : if (skip)
1258 : {
1259 : /* If key uniqueness check is needed we must save skipped keys */
1260 28 : if (!unique_keys)
1261 16 : continue;
1262 :
1263 12 : out = json_unique_builder_get_throwawaybuf(&unique_check);
1264 : }
1265 : else
1266 : {
1267 544 : appendStringInfoString(result, sep);
1268 544 : sep = ", ";
1269 544 : out = result;
1270 : }
1271 :
1272 : /* process key */
1273 556 : if (nulls[i])
1274 24 : ereport(ERROR,
1275 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1276 : errmsg("null value not allowed for object key")));
1277 :
1278 : /* save key offset before appending it */
1279 532 : key_offset = out->len;
1280 :
1281 532 : add_json(args[i], false, out, types[i], true);
1282 :
1283 496 : if (unique_keys)
1284 : {
1285 : /*
1286 : * check key uniqueness after key appending
1287 : *
1288 : * Copy the key first, instead of pointing into the buffer. It
1289 : * will be added to the hash table, but the buffer may get
1290 : * reallocated as we're appending more data to it. That would
1291 : * invalidate pointers to keys in the current buffer.
1292 : */
1293 98 : const char *key = pstrdup(&out->data[key_offset]);
1294 :
1295 98 : if (!json_unique_check_key(&unique_check.check, key, 0))
1296 34 : ereport(ERROR,
1297 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1298 : errmsg("duplicate JSON object key value: %s", key));
1299 :
1300 64 : if (skip)
1301 6 : continue;
1302 : }
1303 :
1304 456 : appendStringInfoString(result, " : ");
1305 :
1306 : /* process value */
1307 456 : add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
1308 : }
1309 :
1310 330 : appendStringInfoChar(result, '}');
1311 :
1312 330 : return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
1313 : }
1314 :
1315 : /*
1316 : * SQL function json_build_object(variadic "any")
1317 : */
1318 : Datum
1319 156 : json_build_object(PG_FUNCTION_ARGS)
1320 : {
1321 : Datum *args;
1322 : bool *nulls;
1323 : Oid *types;
1324 :
1325 : /* build argument values to build the object */
1326 156 : int nargs = extract_variadic_args(fcinfo, 0, true,
1327 : &args, &types, &nulls);
1328 :
1329 156 : if (nargs < 0)
1330 6 : PG_RETURN_NULL();
1331 :
1332 150 : PG_RETURN_DATUM(json_build_object_worker(nargs, args, nulls, types, false, false));
1333 : }
1334 :
1335 : /*
1336 : * degenerate case of json_build_object where it gets 0 arguments.
1337 : */
1338 : Datum
1339 6 : json_build_object_noargs(PG_FUNCTION_ARGS)
1340 : {
1341 6 : PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1342 : }
1343 :
1344 : Datum
1345 232 : json_build_array_worker(int nargs, const Datum *args, const bool *nulls, const Oid *types,
1346 : bool absent_on_null)
1347 : {
1348 : int i;
1349 232 : const char *sep = "";
1350 : StringInfo result;
1351 :
1352 232 : result = makeStringInfo();
1353 :
1354 232 : appendStringInfoChar(result, '[');
1355 :
1356 640 : for (i = 0; i < nargs; i++)
1357 : {
1358 408 : if (absent_on_null && nulls[i])
1359 18 : continue;
1360 :
1361 390 : appendStringInfoString(result, sep);
1362 390 : sep = ", ";
1363 390 : add_json(args[i], nulls[i], result, types[i], false);
1364 : }
1365 :
1366 232 : appendStringInfoChar(result, ']');
1367 :
1368 232 : return PointerGetDatum(cstring_to_text_with_len(result->data, result->len));
1369 : }
1370 :
1371 : /*
1372 : * SQL function json_build_array(variadic "any")
1373 : */
1374 : Datum
1375 54 : json_build_array(PG_FUNCTION_ARGS)
1376 : {
1377 : Datum *args;
1378 : bool *nulls;
1379 : Oid *types;
1380 :
1381 : /* build argument values to build the object */
1382 54 : int nargs = extract_variadic_args(fcinfo, 0, true,
1383 : &args, &types, &nulls);
1384 :
1385 54 : if (nargs < 0)
1386 6 : PG_RETURN_NULL();
1387 :
1388 48 : PG_RETURN_DATUM(json_build_array_worker(nargs, args, nulls, types, false));
1389 : }
1390 :
1391 : /*
1392 : * degenerate case of json_build_array where it gets 0 arguments.
1393 : */
1394 : Datum
1395 6 : json_build_array_noargs(PG_FUNCTION_ARGS)
1396 : {
1397 6 : PG_RETURN_TEXT_P(cstring_to_text_with_len("[]", 2));
1398 : }
1399 :
1400 : /*
1401 : * SQL function json_object(text[])
1402 : *
1403 : * take a one or two dimensional array of text as key/value pairs
1404 : * for a json object.
1405 : */
1406 : Datum
1407 48 : json_object(PG_FUNCTION_ARGS)
1408 : {
1409 48 : ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0);
1410 48 : int ndims = ARR_NDIM(in_array);
1411 : StringInfoData result;
1412 : Datum *in_datums;
1413 : bool *in_nulls;
1414 : int in_count,
1415 : count,
1416 : i;
1417 : text *rval;
1418 :
1419 48 : switch (ndims)
1420 : {
1421 6 : case 0:
1422 6 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1423 : break;
1424 :
1425 18 : case 1:
1426 18 : if ((ARR_DIMS(in_array)[0]) % 2)
1427 6 : ereport(ERROR,
1428 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1429 : errmsg("array must have even number of elements")));
1430 12 : break;
1431 :
1432 18 : case 2:
1433 18 : if ((ARR_DIMS(in_array)[1]) != 2)
1434 12 : ereport(ERROR,
1435 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1436 : errmsg("array must have two columns")));
1437 6 : break;
1438 :
1439 6 : default:
1440 6 : ereport(ERROR,
1441 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1442 : errmsg("wrong number of array subscripts")));
1443 : }
1444 :
1445 18 : deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
1446 :
1447 18 : count = in_count / 2;
1448 :
1449 18 : initStringInfo(&result);
1450 :
1451 18 : appendStringInfoChar(&result, '{');
1452 :
1453 1866 : for (i = 0; i < count; ++i)
1454 : {
1455 1848 : if (in_nulls[i * 2])
1456 0 : ereport(ERROR,
1457 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1458 : errmsg("null value not allowed for object key")));
1459 :
1460 1848 : if (i > 0)
1461 1830 : appendStringInfoString(&result, ", ");
1462 1848 : escape_json_text(&result, (text *) DatumGetPointer(in_datums[i * 2]));
1463 1848 : appendStringInfoString(&result, " : ");
1464 1848 : if (in_nulls[i * 2 + 1])
1465 12 : appendStringInfoString(&result, "null");
1466 : else
1467 : {
1468 1836 : escape_json_text(&result,
1469 1836 : (text *) DatumGetPointer(in_datums[i * 2 + 1]));
1470 : }
1471 : }
1472 :
1473 18 : appendStringInfoChar(&result, '}');
1474 :
1475 18 : pfree(in_datums);
1476 18 : pfree(in_nulls);
1477 :
1478 18 : rval = cstring_to_text_with_len(result.data, result.len);
1479 18 : pfree(result.data);
1480 :
1481 18 : PG_RETURN_TEXT_P(rval);
1482 : }
1483 :
1484 : /*
1485 : * SQL function json_object(text[], text[])
1486 : *
1487 : * take separate key and value arrays of text to construct a json object
1488 : * pairwise.
1489 : */
1490 : Datum
1491 42 : json_object_two_arg(PG_FUNCTION_ARGS)
1492 : {
1493 42 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(0);
1494 42 : ArrayType *val_array = PG_GETARG_ARRAYTYPE_P(1);
1495 42 : int nkdims = ARR_NDIM(key_array);
1496 42 : int nvdims = ARR_NDIM(val_array);
1497 : StringInfoData result;
1498 : Datum *key_datums,
1499 : *val_datums;
1500 : bool *key_nulls,
1501 : *val_nulls;
1502 : int key_count,
1503 : val_count,
1504 : i;
1505 : text *rval;
1506 :
1507 42 : if (nkdims > 1 || nkdims != nvdims)
1508 6 : ereport(ERROR,
1509 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1510 : errmsg("wrong number of array subscripts")));
1511 :
1512 36 : if (nkdims == 0)
1513 6 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1514 :
1515 30 : deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
1516 30 : deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
1517 :
1518 30 : if (key_count != val_count)
1519 12 : ereport(ERROR,
1520 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1521 : errmsg("mismatched array dimensions")));
1522 :
1523 18 : initStringInfo(&result);
1524 :
1525 18 : appendStringInfoChar(&result, '{');
1526 :
1527 78 : for (i = 0; i < key_count; ++i)
1528 : {
1529 66 : if (key_nulls[i])
1530 6 : ereport(ERROR,
1531 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1532 : errmsg("null value not allowed for object key")));
1533 :
1534 60 : if (i > 0)
1535 42 : appendStringInfoString(&result, ", ");
1536 60 : escape_json_text(&result, (text *) DatumGetPointer(key_datums[i]));
1537 60 : appendStringInfoString(&result, " : ");
1538 60 : if (val_nulls[i])
1539 0 : appendStringInfoString(&result, "null");
1540 : else
1541 60 : escape_json_text(&result,
1542 60 : (text *) DatumGetPointer(val_datums[i]));
1543 : }
1544 :
1545 12 : appendStringInfoChar(&result, '}');
1546 :
1547 12 : pfree(key_datums);
1548 12 : pfree(key_nulls);
1549 12 : pfree(val_datums);
1550 12 : pfree(val_nulls);
1551 :
1552 12 : rval = cstring_to_text_with_len(result.data, result.len);
1553 12 : pfree(result.data);
1554 :
1555 12 : PG_RETURN_TEXT_P(rval);
1556 : }
1557 :
1558 : /*
1559 : * escape_json_char
1560 : * Inline helper function for escape_json* functions
1561 : */
1562 : static pg_attribute_always_inline void
1563 4325480 : escape_json_char(StringInfo buf, char c)
1564 : {
1565 4325480 : switch (c)
1566 : {
1567 18 : case '\b':
1568 18 : appendStringInfoString(buf, "\\b");
1569 18 : break;
1570 6 : case '\f':
1571 6 : appendStringInfoString(buf, "\\f");
1572 6 : break;
1573 24 : case '\n':
1574 24 : appendStringInfoString(buf, "\\n");
1575 24 : break;
1576 6 : case '\r':
1577 6 : appendStringInfoString(buf, "\\r");
1578 6 : break;
1579 180 : case '\t':
1580 180 : appendStringInfoString(buf, "\\t");
1581 180 : break;
1582 630 : case '"':
1583 630 : appendStringInfoString(buf, "\\\"");
1584 630 : break;
1585 90 : case '\\':
1586 90 : appendStringInfoString(buf, "\\\\");
1587 90 : break;
1588 4324526 : default:
1589 4324526 : if ((unsigned char) c < ' ')
1590 6 : appendStringInfo(buf, "\\u%04x", (int) c);
1591 : else
1592 4324520 : appendStringInfoCharMacro(buf, c);
1593 4324526 : break;
1594 : }
1595 4325480 : }
1596 :
1597 : /*
1598 : * escape_json
1599 : * Produce a JSON string literal, properly escaping the NUL-terminated
1600 : * cstring.
1601 : */
1602 : void
1603 39598 : escape_json(StringInfo buf, const char *str)
1604 : {
1605 39598 : appendStringInfoCharMacro(buf, '"');
1606 :
1607 543444 : for (; *str != '\0'; str++)
1608 503846 : escape_json_char(buf, *str);
1609 :
1610 39598 : appendStringInfoCharMacro(buf, '"');
1611 39598 : }
1612 :
1613 : /*
1614 : * Define the number of bytes that escape_json_with_len will look ahead in the
1615 : * input string before flushing the input string to the destination buffer.
1616 : * Looking ahead too far could result in cachelines being evicted that will
1617 : * need to be reloaded in order to perform the appendBinaryStringInfo call.
1618 : * Smaller values will result in a larger number of calls to
1619 : * appendBinaryStringInfo and introduce additional function call overhead.
1620 : * Values larger than the size of L1d cache will likely result in worse
1621 : * performance.
1622 : */
1623 : #define ESCAPE_JSON_FLUSH_AFTER 512
1624 :
1625 : /*
1626 : * escape_json_with_len
1627 : * Produce a JSON string literal, properly escaping the possibly not
1628 : * NUL-terminated characters in 'str'. 'len' defines the number of bytes
1629 : * from 'str' to process.
1630 : */
1631 : void
1632 386870 : escape_json_with_len(StringInfo buf, const char *str, int len)
1633 : {
1634 : int vlen;
1635 :
1636 : Assert(len >= 0);
1637 :
1638 : /*
1639 : * Since we know the minimum length we'll need to append, let's just
1640 : * enlarge the buffer now rather than incrementally making more space when
1641 : * we run out. Add two extra bytes for the enclosing quotes.
1642 : */
1643 386870 : enlargeStringInfo(buf, len + 2);
1644 :
1645 : /*
1646 : * Figure out how many bytes to process using SIMD. Round 'len' down to
1647 : * the previous multiple of sizeof(Vector8), assuming that's a power-of-2.
1648 : */
1649 386870 : vlen = len & (int) (~(sizeof(Vector8) - 1));
1650 :
1651 386870 : appendStringInfoCharMacro(buf, '"');
1652 :
1653 386870 : for (int i = 0, copypos = 0;;)
1654 : {
1655 : /*
1656 : * To speed this up, try searching sizeof(Vector8) bytes at once for
1657 : * special characters that we need to escape. When we find one, we
1658 : * fall out of the Vector8 loop and copy the portion we've vector
1659 : * searched and then we process sizeof(Vector8) bytes one byte at a
1660 : * time. Once done, come back and try doing vector searching again.
1661 : * We'll also process any remaining bytes at the tail end of the
1662 : * string byte-by-byte. This optimization assumes that most chunks of
1663 : * sizeof(Vector8) bytes won't contain any special characters.
1664 : */
1665 416282 : for (; i < vlen; i += sizeof(Vector8))
1666 : {
1667 : Vector8 chunk;
1668 :
1669 29412 : vector8_load(&chunk, (const uint8 *) &str[i]);
1670 :
1671 : /*
1672 : * Break on anything less than ' ' or if we find a '"' or '\\'.
1673 : * Those need special handling. That's done in the per-byte loop.
1674 : */
1675 58752 : if (vector8_has_le(chunk, (unsigned char) 0x1F) ||
1676 58674 : vector8_has(chunk, (unsigned char) '"') ||
1677 29334 : vector8_has(chunk, (unsigned char) '\\'))
1678 : break;
1679 :
1680 : #ifdef ESCAPE_JSON_FLUSH_AFTER
1681 :
1682 : /*
1683 : * Flush what's been checked so far out to the destination buffer
1684 : * every so often to avoid having to re-read cachelines when
1685 : * escaping large strings.
1686 : */
1687 29298 : if (i - copypos >= ESCAPE_JSON_FLUSH_AFTER)
1688 : {
1689 30 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1690 30 : copypos = i;
1691 : }
1692 : #endif
1693 : }
1694 :
1695 : /*
1696 : * Write to the destination up to the point that we've vector searched
1697 : * so far. Do this only when switching into per-byte mode rather than
1698 : * once every sizeof(Vector8) bytes.
1699 : */
1700 386984 : if (copypos < i)
1701 : {
1702 26888 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1703 26888 : copypos = i;
1704 : }
1705 :
1706 : /*
1707 : * Per-byte loop for Vector8s containing special chars and for
1708 : * processing the tail of the string.
1709 : */
1710 4208618 : for (int b = 0; b < sizeof(Vector8); b++)
1711 : {
1712 : /* check if we've finished */
1713 4208504 : if (i == len)
1714 386870 : goto done;
1715 :
1716 : Assert(i < len);
1717 :
1718 3821634 : escape_json_char(buf, str[i++]);
1719 : }
1720 :
1721 114 : copypos = i;
1722 : /* We're not done yet. Try the vector search again. */
1723 : }
1724 :
1725 386870 : done:
1726 386870 : appendStringInfoCharMacro(buf, '"');
1727 386870 : }
1728 :
1729 : /*
1730 : * escape_json_text
1731 : * Append 'txt' onto 'buf' and escape using escape_json_with_len.
1732 : *
1733 : * This is more efficient than calling text_to_cstring and appending the
1734 : * result as that could require an additional palloc and memcpy.
1735 : */
1736 : void
1737 4834 : escape_json_text(StringInfo buf, const text *txt)
1738 : {
1739 : /* must cast away the const, unfortunately */
1740 4834 : text *tunpacked = pg_detoast_datum_packed(unconstify(text *, txt));
1741 4834 : int len = VARSIZE_ANY_EXHDR(tunpacked);
1742 : char *str;
1743 :
1744 4834 : str = VARDATA_ANY(tunpacked);
1745 :
1746 4834 : escape_json_with_len(buf, str, len);
1747 :
1748 : /* pfree any detoasted values */
1749 4834 : if (tunpacked != txt)
1750 0 : pfree(tunpacked);
1751 4834 : }
1752 :
1753 : /* Semantic actions for key uniqueness check */
1754 : static JsonParseErrorType
1755 186 : json_unique_object_start(void *_state)
1756 : {
1757 186 : JsonUniqueParsingState *state = _state;
1758 : JsonUniqueStackEntry *entry;
1759 :
1760 186 : if (!state->unique)
1761 0 : return JSON_SUCCESS;
1762 :
1763 : /* push object entry to stack */
1764 186 : entry = palloc(sizeof(*entry));
1765 186 : entry->object_id = state->id_counter++;
1766 186 : entry->parent = state->stack;
1767 186 : state->stack = entry;
1768 :
1769 186 : return JSON_SUCCESS;
1770 : }
1771 :
1772 : static JsonParseErrorType
1773 180 : json_unique_object_end(void *_state)
1774 : {
1775 180 : JsonUniqueParsingState *state = _state;
1776 : JsonUniqueStackEntry *entry;
1777 :
1778 180 : if (!state->unique)
1779 72 : return JSON_SUCCESS;
1780 :
1781 108 : entry = state->stack;
1782 108 : state->stack = entry->parent; /* pop object from stack */
1783 108 : pfree(entry);
1784 108 : return JSON_SUCCESS;
1785 : }
1786 :
1787 : static JsonParseErrorType
1788 274 : json_unique_object_field_start(void *_state, char *field, bool isnull)
1789 : {
1790 274 : JsonUniqueParsingState *state = _state;
1791 : JsonUniqueStackEntry *entry;
1792 :
1793 274 : if (!state->unique)
1794 0 : return JSON_SUCCESS;
1795 :
1796 : /* find key collision in the current object */
1797 274 : if (json_unique_check_key(&state->check, field, state->stack->object_id))
1798 224 : return JSON_SUCCESS;
1799 :
1800 50 : state->unique = false;
1801 :
1802 : /* pop all objects entries */
1803 122 : while ((entry = state->stack))
1804 : {
1805 72 : state->stack = entry->parent;
1806 72 : pfree(entry);
1807 : }
1808 50 : return JSON_SUCCESS;
1809 : }
1810 :
1811 : /* Validate JSON text and additionally check key uniqueness */
1812 : bool
1813 1336 : json_validate(text *json, bool check_unique_keys, bool throw_error)
1814 : {
1815 : JsonLexContext lex;
1816 1336 : JsonSemAction uniqueSemAction = {0};
1817 : JsonUniqueParsingState state;
1818 : JsonParseErrorType result;
1819 :
1820 1336 : makeJsonLexContext(&lex, json, check_unique_keys);
1821 :
1822 1336 : if (check_unique_keys)
1823 : {
1824 236 : state.lex = &lex;
1825 236 : state.stack = NULL;
1826 236 : state.id_counter = 0;
1827 236 : state.unique = true;
1828 236 : json_unique_check_init(&state.check);
1829 :
1830 236 : uniqueSemAction.semstate = &state;
1831 236 : uniqueSemAction.object_start = json_unique_object_start;
1832 236 : uniqueSemAction.object_field_start = json_unique_object_field_start;
1833 236 : uniqueSemAction.object_end = json_unique_object_end;
1834 : }
1835 :
1836 1336 : result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
1837 :
1838 1336 : if (result != JSON_SUCCESS)
1839 : {
1840 216 : if (throw_error)
1841 0 : json_errsave_error(result, &lex, NULL);
1842 :
1843 216 : return false; /* invalid json */
1844 : }
1845 :
1846 1120 : if (check_unique_keys && !state.unique)
1847 : {
1848 50 : if (throw_error)
1849 10 : ereport(ERROR,
1850 : (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1851 : errmsg("duplicate JSON object key value")));
1852 :
1853 40 : return false; /* not unique keys */
1854 : }
1855 :
1856 1070 : if (check_unique_keys)
1857 162 : freeJsonLexContext(&lex);
1858 :
1859 1070 : return true; /* ok */
1860 : }
1861 :
1862 : /*
1863 : * SQL function json_typeof(json) -> text
1864 : *
1865 : * Returns the type of the outermost JSON value as TEXT. Possible types are
1866 : * "object", "array", "string", "number", "boolean", and "null".
1867 : *
1868 : * Performs a single call to json_lex() to get the first token of the supplied
1869 : * value. This initial token uniquely determines the value's type. As our
1870 : * input must already have been validated by json_in() or json_recv(), the
1871 : * initial token should never be JSON_TOKEN_OBJECT_END, JSON_TOKEN_ARRAY_END,
1872 : * JSON_TOKEN_COLON, JSON_TOKEN_COMMA, or JSON_TOKEN_END.
1873 : */
1874 : Datum
1875 60 : json_typeof(PG_FUNCTION_ARGS)
1876 : {
1877 60 : text *json = PG_GETARG_TEXT_PP(0);
1878 : JsonLexContext lex;
1879 : char *type;
1880 : JsonParseErrorType result;
1881 :
1882 : /* Lex exactly one token from the input and check its type. */
1883 60 : makeJsonLexContext(&lex, json, false);
1884 60 : result = json_lex(&lex);
1885 60 : if (result != JSON_SUCCESS)
1886 0 : json_errsave_error(result, &lex, NULL);
1887 :
1888 60 : switch (lex.token_type)
1889 : {
1890 12 : case JSON_TOKEN_OBJECT_START:
1891 12 : type = "object";
1892 12 : break;
1893 12 : case JSON_TOKEN_ARRAY_START:
1894 12 : type = "array";
1895 12 : break;
1896 6 : case JSON_TOKEN_STRING:
1897 6 : type = "string";
1898 6 : break;
1899 12 : case JSON_TOKEN_NUMBER:
1900 12 : type = "number";
1901 12 : break;
1902 12 : case JSON_TOKEN_TRUE:
1903 : case JSON_TOKEN_FALSE:
1904 12 : type = "boolean";
1905 12 : break;
1906 6 : case JSON_TOKEN_NULL:
1907 6 : type = "null";
1908 6 : break;
1909 0 : default:
1910 0 : elog(ERROR, "unexpected json token: %d", lex.token_type);
1911 : }
1912 :
1913 60 : PG_RETURN_TEXT_P(cstring_to_text(type));
1914 : }
|