Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * json.c
4 : * JSON data type support.
5 : *
6 : * Portions Copyright (c) 1996-2026, 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 3169 : json_in(PG_FUNCTION_ARGS)
109 : {
110 3169 : char *json = PG_GETARG_CSTRING(0);
111 3169 : text *result = cstring_to_text(json);
112 : JsonLexContext lex;
113 :
114 : /* validate it */
115 3169 : makeJsonLexContext(&lex, result, false);
116 3169 : if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
117 6 : PG_RETURN_NULL();
118 :
119 : /* Internal representation is the same as text */
120 3064 : PG_RETURN_TEXT_P(result);
121 : }
122 :
123 : /*
124 : * Output.
125 : */
126 : Datum
127 2846 : json_out(PG_FUNCTION_ARGS)
128 : {
129 : /* we needn't detoast because text_to_cstring will handle that */
130 2846 : Datum txt = PG_GETARG_DATUM(0);
131 :
132 2846 : 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 4242 : 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 4242 : 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 4242 : if (is_null)
193 : {
194 159 : appendBinaryStringInfo(result, "null", strlen("null"));
195 159 : return;
196 : }
197 :
198 4083 : if (key_scalar &&
199 998 : (tcategory == JSONTYPE_ARRAY ||
200 995 : tcategory == JSONTYPE_COMPOSITE ||
201 989 : tcategory == JSONTYPE_JSON ||
202 : tcategory == JSONTYPE_CAST))
203 18 : ereport(ERROR,
204 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
205 : errmsg("key value must be scalar, not array, composite, or json")));
206 :
207 4065 : switch (tcategory)
208 : {
209 162 : case JSONTYPE_ARRAY:
210 162 : array_to_json_internal(val, result, false);
211 162 : break;
212 250 : case JSONTYPE_COMPOSITE:
213 250 : composite_to_json(val, result, false);
214 250 : break;
215 20 : case JSONTYPE_BOOL:
216 20 : if (key_scalar)
217 0 : appendStringInfoChar(result, '"');
218 20 : if (DatumGetBool(val))
219 14 : appendBinaryStringInfo(result, "true", strlen("true"));
220 : else
221 6 : appendBinaryStringInfo(result, "false", strlen("false"));
222 20 : if (key_scalar)
223 0 : appendStringInfoChar(result, '"');
224 20 : break;
225 2571 : case JSONTYPE_NUMERIC:
226 2571 : 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 2571 : if (!key_scalar &&
235 1818 : ((*outputstr >= '0' && *outputstr <= '9') ||
236 33 : (*outputstr == '-' &&
237 27 : (outputstr[1] >= '0' && outputstr[1] <= '9'))))
238 1809 : appendStringInfoString(result, outputstr);
239 : else
240 : {
241 762 : appendStringInfoChar(result, '"');
242 762 : appendStringInfoString(result, outputstr);
243 762 : appendStringInfoChar(result, '"');
244 : }
245 2571 : pfree(outputstr);
246 2571 : break;
247 12 : case JSONTYPE_DATE:
248 : {
249 : char buf[MAXDATELEN + 1];
250 :
251 12 : JsonEncodeDateTime(buf, val, DATEOID, NULL);
252 12 : appendStringInfoChar(result, '"');
253 12 : appendStringInfoString(result, buf);
254 12 : appendStringInfoChar(result, '"');
255 : }
256 12 : break;
257 14 : case JSONTYPE_TIMESTAMP:
258 : {
259 : char buf[MAXDATELEN + 1];
260 :
261 14 : JsonEncodeDateTime(buf, val, TIMESTAMPOID, NULL);
262 14 : appendStringInfoChar(result, '"');
263 14 : appendStringInfoString(result, buf);
264 14 : appendStringInfoChar(result, '"');
265 : }
266 14 : break;
267 12 : case JSONTYPE_TIMESTAMPTZ:
268 : {
269 : char buf[MAXDATELEN + 1];
270 :
271 12 : JsonEncodeDateTime(buf, val, TIMESTAMPTZOID, NULL);
272 12 : appendStringInfoChar(result, '"');
273 12 : appendStringInfoString(result, buf);
274 12 : appendStringInfoChar(result, '"');
275 : }
276 12 : break;
277 412 : case JSONTYPE_JSON:
278 : /* JSON and JSONB output will already be escaped */
279 412 : outputstr = OidOutputFunctionCall(outfuncoid, val);
280 412 : appendStringInfoString(result, outputstr);
281 412 : pfree(outputstr);
282 412 : break;
283 2 : case JSONTYPE_CAST:
284 : /* outfuncoid refers to a cast function, not an output function */
285 2 : jsontext = DatumGetTextPP(OidFunctionCall1(outfuncoid, val));
286 2 : appendBinaryStringInfo(result, VARDATA_ANY(jsontext),
287 2 : VARSIZE_ANY_EXHDR(jsontext));
288 2 : pfree(jsontext);
289 2 : break;
290 610 : default:
291 : /* special-case text types to save useless palloc/memcpy cycles */
292 610 : if (outfuncoid == F_TEXTOUT || outfuncoid == F_VARCHAROUT ||
293 : outfuncoid == F_BPCHAROUT)
294 458 : escape_json_text(result, (text *) DatumGetPointer(val));
295 : else
296 : {
297 152 : outputstr = OidOutputFunctionCall(outfuncoid, val);
298 152 : escape_json(result, outputstr);
299 152 : pfree(outputstr);
300 : }
301 610 : 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 866 : JsonEncodeDateTime(char *buf, Datum value, Oid typid, const int *tzp)
312 : {
313 866 : if (!buf)
314 30 : buf = palloc(MAXDATELEN + 1);
315 :
316 866 : switch (typid)
317 : {
318 120 : case DATEOID:
319 : {
320 : DateADT date;
321 : struct pg_tm tm;
322 :
323 120 : date = DatumGetDateADT(value);
324 :
325 : /* Same as date_out(), but forcing DateStyle */
326 120 : if (DATE_NOT_FINITE(date))
327 12 : EncodeSpecialDate(date, buf);
328 : else
329 : {
330 108 : j2date(date + POSTGRES_EPOCH_JDATE,
331 : &(tm.tm_year), &(tm.tm_mon), &(tm.tm_mday));
332 108 : EncodeDateOnly(&tm, USE_XSD_DATES, buf);
333 : }
334 : }
335 120 : break;
336 135 : case TIMEOID:
337 : {
338 135 : TimeADT time = DatumGetTimeADT(value);
339 : struct pg_tm tt,
340 135 : *tm = &tt;
341 : fsec_t fsec;
342 :
343 : /* Same as time_out(), but forcing DateStyle */
344 135 : time2tm(time, tm, &fsec);
345 135 : EncodeTimeOnly(tm, fsec, false, 0, USE_XSD_DATES, buf);
346 : }
347 135 : break;
348 171 : case TIMETZOID:
349 : {
350 171 : TimeTzADT *time = DatumGetTimeTzADTP(value);
351 : struct pg_tm tt,
352 171 : *tm = &tt;
353 : fsec_t fsec;
354 : int tz;
355 :
356 : /* Same as timetz_out(), but forcing DateStyle */
357 171 : timetz2tm(time, tm, &fsec, &tz);
358 171 : EncodeTimeOnly(tm, fsec, true, tz, USE_XSD_DATES, buf);
359 : }
360 171 : break;
361 185 : case TIMESTAMPOID:
362 : {
363 : Timestamp timestamp;
364 : struct pg_tm tm;
365 : fsec_t fsec;
366 :
367 185 : timestamp = DatumGetTimestamp(value);
368 : /* Same as timestamp_out(), but forcing DateStyle */
369 185 : if (TIMESTAMP_NOT_FINITE(timestamp))
370 12 : EncodeSpecialTimestamp(timestamp, buf);
371 173 : else if (timestamp2tm(timestamp, NULL, &tm, &fsec, NULL, NULL) == 0)
372 173 : 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 185 : break;
379 255 : case TIMESTAMPTZOID:
380 : {
381 : TimestampTz timestamp;
382 : struct pg_tm tm;
383 : int tz;
384 : fsec_t fsec;
385 255 : const char *tzn = NULL;
386 :
387 255 : 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 255 : if (tzp)
396 : {
397 231 : tz = *tzp;
398 231 : timestamp -= (TimestampTz) tz * USECS_PER_SEC;
399 : }
400 :
401 : /* Same as timestamptz_out(), but forcing DateStyle */
402 255 : if (TIMESTAMP_NOT_FINITE(timestamp))
403 12 : EncodeSpecialTimestamp(timestamp, buf);
404 243 : else if (timestamp2tm(timestamp, tzp ? NULL : &tz, &tm, &fsec,
405 : tzp ? NULL : &tzn, NULL) == 0)
406 : {
407 243 : if (tzp)
408 231 : tm.tm_isdst = 1; /* set time-zone presence flag */
409 :
410 243 : 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 255 : break;
418 0 : default:
419 0 : elog(ERROR, "unknown jsonb value datetime type oid %u", typid);
420 : return NULL;
421 : }
422 :
423 866 : 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 189 : 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 189 : sep = use_line_feeds ? ",\n " : ",";
442 :
443 189 : appendStringInfoChar(result, '[');
444 :
445 705 : for (i = 1; i <= dims[dim]; i++)
446 : {
447 516 : if (i > 1)
448 327 : appendStringInfoString(result, sep);
449 :
450 516 : if (dim + 1 == ndims)
451 : {
452 510 : datum_to_json_internal(vals[*valcount], nulls[*valcount],
453 : result, tcategory,
454 : outfuncoid, false);
455 510 : (*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 6 : array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
464 : valcount, tcategory, outfuncoid, false);
465 : }
466 : }
467 :
468 189 : appendStringInfoChar(result, ']');
469 189 : }
470 :
471 : /*
472 : * Turn an array into JSON.
473 : */
474 : static void
475 183 : array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
476 : {
477 183 : ArrayType *v = DatumGetArrayTypeP(array);
478 183 : Oid element_type = ARR_ELEMTYPE(v);
479 : int *dim;
480 : int ndim;
481 : int nitems;
482 183 : 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 183 : ndim = ARR_NDIM(v);
492 183 : dim = ARR_DIMS(v);
493 183 : nitems = ArrayGetNItems(ndim, dim);
494 :
495 183 : if (nitems <= 0)
496 : {
497 0 : appendStringInfoString(result, "[]");
498 0 : return;
499 : }
500 :
501 183 : get_typlenbyvalalign(element_type,
502 : &typlen, &typbyval, &typalign);
503 :
504 183 : json_categorize_type(element_type, false,
505 : &tcategory, &outfuncoid);
506 :
507 183 : deconstruct_array(v, element_type, typlen, typbyval,
508 : typalign, &elements, &nulls,
509 : &nitems);
510 :
511 183 : array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
512 : outfuncoid, use_line_feeds);
513 :
514 183 : pfree(elements);
515 183 : pfree(nulls);
516 : }
517 :
518 : /*
519 : * Turn a composite / record into JSON.
520 : */
521 : static void
522 628 : 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 628 : 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 628 : sep = use_line_feeds ? ",\n " : ",";
540 628 : seplen = use_line_feeds ? strlen(",\n ") : strlen(",");
541 :
542 628 : td = DatumGetHeapTupleHeader(composite);
543 :
544 : /* Extract rowtype info and find a tupdesc */
545 628 : tupType = HeapTupleHeaderGetTypeId(td);
546 628 : tupTypmod = HeapTupleHeaderGetTypMod(td);
547 628 : tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
548 :
549 : /* Build a temporary HeapTuple control structure */
550 628 : tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
551 628 : tmptup.t_data = td;
552 628 : tuple = &tmptup;
553 :
554 628 : appendStringInfoChar(result, '{');
555 :
556 1854 : for (i = 0; i < tupdesc->natts; i++)
557 : {
558 : Datum val;
559 : bool isnull;
560 : char *attname;
561 : JsonTypeCategory tcategory;
562 : Oid outfuncoid;
563 1226 : Form_pg_attribute att = TupleDescAttr(tupdesc, i);
564 :
565 1226 : if (att->attisdropped)
566 0 : continue;
567 :
568 1226 : if (needsep)
569 598 : appendBinaryStringInfo(result, sep, seplen);
570 1226 : needsep = true;
571 :
572 1226 : attname = NameStr(att->attname);
573 1226 : escape_json(result, attname);
574 1226 : appendStringInfoChar(result, ':');
575 :
576 1226 : val = heap_getattr(tuple, i + 1, tupdesc, &isnull);
577 :
578 1226 : if (isnull)
579 : {
580 90 : tcategory = JSONTYPE_NULL;
581 90 : outfuncoid = InvalidOid;
582 : }
583 : else
584 1136 : json_categorize_type(att->atttypid, false, &tcategory,
585 : &outfuncoid);
586 :
587 1226 : datum_to_json_internal(val, isnull, result, tcategory, outfuncoid,
588 : false);
589 : }
590 :
591 628 : appendStringInfoChar(result, '}');
592 628 : ReleaseTupleDesc(tupdesc);
593 628 : }
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 689 : 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 689 : if (val_type == InvalidOid)
610 0 : ereport(ERROR,
611 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
612 : errmsg("could not determine input data type")));
613 :
614 689 : if (is_null)
615 : {
616 36 : tcategory = JSONTYPE_NULL;
617 36 : outfuncoid = InvalidOid;
618 : }
619 : else
620 653 : json_categorize_type(val_type, false,
621 : &tcategory, &outfuncoid);
622 :
623 689 : datum_to_json_internal(val, is_null, result, tcategory, outfuncoid,
624 : key_scalar);
625 671 : }
626 :
627 : /*
628 : * SQL function array_to_json(row)
629 : */
630 : Datum
631 9 : array_to_json(PG_FUNCTION_ARGS)
632 : {
633 9 : Datum array = PG_GETARG_DATUM(0);
634 : StringInfoData result;
635 :
636 9 : initStringInfo(&result);
637 :
638 9 : array_to_json_internal(array, &result, false);
639 :
640 9 : 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 12 : array_to_json_pretty(PG_FUNCTION_ARGS)
648 : {
649 12 : Datum array = PG_GETARG_DATUM(0);
650 12 : bool use_line_feeds = PG_GETARG_BOOL(1);
651 : StringInfoData result;
652 :
653 12 : initStringInfo(&result);
654 :
655 12 : array_to_json_internal(array, &result, use_line_feeds);
656 :
657 12 : 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 354 : row_to_json(PG_FUNCTION_ARGS)
665 : {
666 354 : Datum array = PG_GETARG_DATUM(0);
667 : StringInfoData result;
668 :
669 354 : initStringInfo(&result);
670 :
671 354 : composite_to_json(array, &result, false);
672 :
673 354 : 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 24 : row_to_json_pretty(PG_FUNCTION_ARGS)
681 : {
682 24 : Datum array = PG_GETARG_DATUM(0);
683 24 : bool use_line_feeds = PG_GETARG_BOOL(1);
684 : StringInfoData result;
685 :
686 24 : initStringInfo(&result);
687 :
688 24 : composite_to_json(array, &result, use_line_feeds);
689 :
690 24 : 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 90 : to_json(PG_FUNCTION_ARGS)
741 : {
742 90 : Datum val = PG_GETARG_DATUM(0);
743 90 : Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
744 : JsonTypeCategory tcategory;
745 : Oid outfuncoid;
746 :
747 90 : if (val_type == InvalidOid)
748 0 : ereport(ERROR,
749 : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
750 : errmsg("could not determine input data type")));
751 :
752 90 : json_categorize_type(val_type, false,
753 : &tcategory, &outfuncoid);
754 :
755 90 : 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 130 : datum_to_json(Datum val, JsonTypeCategory tcategory, Oid outfuncoid)
765 : {
766 : StringInfoData result;
767 :
768 130 : initStringInfo(&result);
769 130 : datum_to_json_internal(val, false, &result, tcategory, outfuncoid,
770 : false);
771 :
772 130 : 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 277 : json_agg_transfn_worker(FunctionCallInfo fcinfo, bool absent_on_null)
782 : {
783 : MemoryContext aggcontext,
784 : oldcontext;
785 : JsonAggState *state;
786 : Datum val;
787 :
788 277 : 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 277 : if (PG_ARGISNULL(0))
795 : {
796 62 : Oid arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
797 :
798 62 : 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 62 : oldcontext = MemoryContextSwitchTo(aggcontext);
810 62 : state = palloc_object(JsonAggState);
811 62 : state->str = makeStringInfo();
812 62 : MemoryContextSwitchTo(oldcontext);
813 :
814 62 : appendStringInfoChar(state->str, '[');
815 62 : json_categorize_type(arg_type, false, &state->val_category,
816 : &state->val_output_func);
817 : }
818 : else
819 : {
820 215 : state = (JsonAggState *) PG_GETARG_POINTER(0);
821 : }
822 :
823 277 : if (absent_on_null && PG_ARGISNULL(1))
824 45 : PG_RETURN_POINTER(state);
825 :
826 232 : if (state->str->len > 1)
827 173 : appendStringInfoString(state->str, ", ");
828 :
829 : /* fast path for NULLs */
830 232 : if (PG_ARGISNULL(1))
831 : {
832 27 : datum_to_json_internal((Datum) 0, true, state->str, JSONTYPE_NULL,
833 : InvalidOid, false);
834 27 : PG_RETURN_POINTER(state);
835 : }
836 :
837 205 : val = PG_GETARG_DATUM(1);
838 :
839 : /* add some whitespace if structured type and not first item */
840 205 : if (!PG_ARGISNULL(0) && state->str->len > 1 &&
841 152 : (state->val_category == JSONTYPE_ARRAY ||
842 149 : state->val_category == JSONTYPE_COMPOSITE))
843 : {
844 56 : appendStringInfoString(state->str, "\n ");
845 : }
846 :
847 205 : 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 205 : PG_RETURN_POINTER(state);
856 : }
857 :
858 :
859 : /*
860 : * json_agg aggregate function
861 : */
862 : Datum
863 79 : json_agg_transfn(PG_FUNCTION_ARGS)
864 : {
865 79 : return json_agg_transfn_worker(fcinfo, false);
866 : }
867 :
868 : /*
869 : * json_agg_strict aggregate function
870 : */
871 : Datum
872 198 : json_agg_strict_transfn(PG_FUNCTION_ARGS)
873 : {
874 198 : return json_agg_transfn_worker(fcinfo, true);
875 : }
876 :
877 : /*
878 : * json_agg final function
879 : */
880 : Datum
881 68 : 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 136 : state = PG_ARGISNULL(0) ?
889 68 : NULL :
890 62 : (JsonAggState *) PG_GETARG_POINTER(0);
891 :
892 : /* NULL result for no rows in, as is standard with aggregates */
893 68 : if (state == NULL)
894 6 : PG_RETURN_NULL();
895 :
896 : /* Else return state with appropriate array terminator added */
897 62 : PG_RETURN_TEXT_P(catenate_stringinfo_string(state->str, "]"));
898 : }
899 :
900 : /* Functions implementing hash table for key uniqueness check */
901 : static uint32
902 840 : json_unique_hash(const void *key, Size keysize)
903 : {
904 840 : const JsonUniqueHashEntry *entry = (const JsonUniqueHashEntry *) key;
905 840 : uint32 hash = hash_bytes_uint32(entry->object_id);
906 :
907 840 : hash ^= hash_bytes((const unsigned char *) entry->key, entry->key_len);
908 :
909 840 : return hash;
910 : }
911 :
912 : static int
913 60 : json_unique_hash_match(const void *key1, const void *key2, Size keysize)
914 : {
915 60 : const JsonUniqueHashEntry *entry1 = (const JsonUniqueHashEntry *) key1;
916 60 : const JsonUniqueHashEntry *entry2 = (const JsonUniqueHashEntry *) key2;
917 :
918 60 : if (entry1->object_id != entry2->object_id)
919 0 : return entry1->object_id > entry2->object_id ? 1 : -1;
920 :
921 60 : if (entry1->key_len != entry2->key_len)
922 0 : return entry1->key_len > entry2->key_len ? 1 : -1;
923 :
924 60 : 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 162 : json_unique_check_init(JsonUniqueCheckState *cxt)
935 : {
936 : HASHCTL ctl;
937 :
938 162 : memset(&ctl, 0, sizeof(ctl));
939 162 : ctl.keysize = sizeof(JsonUniqueHashEntry);
940 162 : ctl.entrysize = sizeof(JsonUniqueHashEntry);
941 162 : ctl.hcxt = CurrentMemoryContext;
942 162 : ctl.hash = json_unique_hash;
943 162 : ctl.match = json_unique_hash_match;
944 :
945 162 : *cxt = hash_create("json object hashtable",
946 : 32,
947 : &ctl,
948 : HASH_ELEM | HASH_CONTEXT | HASH_FUNCTION | HASH_COMPARE);
949 162 : }
950 :
951 : static void
952 44 : json_unique_builder_init(JsonUniqueBuilderState *cxt)
953 : {
954 44 : json_unique_check_init(&cxt->check);
955 44 : cxt->mcxt = CurrentMemoryContext;
956 44 : cxt->skipped_keys.data = NULL;
957 44 : }
958 :
959 : static bool
960 840 : json_unique_check_key(JsonUniqueCheckState *cxt, const char *key, int object_id)
961 : {
962 : JsonUniqueHashEntry entry;
963 : bool found;
964 :
965 840 : entry.key = key;
966 840 : entry.key_len = strlen(key);
967 840 : entry.object_id = object_id;
968 :
969 840 : (void) hash_search(*cxt, &entry, HASH_ENTER, &found);
970 :
971 840 : 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 21 : json_unique_builder_get_throwawaybuf(JsonUniqueBuilderState *cxt)
981 : {
982 21 : StringInfo out = &cxt->skipped_keys;
983 :
984 21 : if (!out->data)
985 : {
986 15 : MemoryContext oldcxt = MemoryContextSwitchTo(cxt->mcxt);
987 :
988 15 : initStringInfo(out);
989 15 : MemoryContextSwitchTo(oldcxt);
990 : }
991 : else
992 : /* Just reset the string to empty */
993 6 : out->len = 0;
994 :
995 21 : 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 759 : 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 759 : 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 759 : 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 67 : oldcontext = MemoryContextSwitchTo(aggcontext);
1032 67 : state = palloc_object(JsonAggState);
1033 67 : state->str = makeStringInfo();
1034 67 : if (unique_keys)
1035 27 : json_unique_builder_init(&state->unique_check);
1036 : else
1037 40 : memset(&state->unique_check, 0, sizeof(state->unique_check));
1038 67 : MemoryContextSwitchTo(oldcontext);
1039 :
1040 67 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 1);
1041 :
1042 67 : 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 67 : json_categorize_type(arg_type, false, &state->key_category,
1048 : &state->key_output_func);
1049 :
1050 67 : arg_type = get_fn_expr_argtype(fcinfo->flinfo, 2);
1051 :
1052 67 : 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 67 : json_categorize_type(arg_type, false, &state->val_category,
1058 : &state->val_output_func);
1059 :
1060 67 : appendStringInfoString(state->str, "{ ");
1061 : }
1062 : else
1063 : {
1064 692 : 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 759 : if (PG_ARGISNULL(1))
1076 6 : 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 753 : skip = absent_on_null && PG_ARGISNULL(2);
1082 :
1083 753 : 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 27 : if (!unique_keys)
1091 12 : PG_RETURN_POINTER(state);
1092 :
1093 15 : out = json_unique_builder_get_throwawaybuf(&state->unique_check);
1094 : }
1095 : else
1096 : {
1097 726 : out = state->str;
1098 :
1099 : /*
1100 : * Append comma delimiter only if we have already output some fields
1101 : * after the initial string "{ ".
1102 : */
1103 726 : if (out->len > 2)
1104 662 : appendStringInfoString(out, ", ");
1105 : }
1106 :
1107 741 : arg = PG_GETARG_DATUM(1);
1108 :
1109 741 : key_offset = out->len;
1110 :
1111 741 : datum_to_json_internal(arg, false, out, state->key_category,
1112 : state->key_output_func, true);
1113 :
1114 741 : 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 654 : const char *key = MemoryContextStrdup(aggcontext,
1123 654 : &out->data[key_offset]);
1124 :
1125 654 : if (!json_unique_check_key(&state->unique_check.check, key, 0))
1126 18 : ereport(ERROR,
1127 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1128 : errmsg("duplicate JSON object key value: %s", key));
1129 :
1130 636 : if (skip)
1131 9 : PG_RETURN_POINTER(state);
1132 : }
1133 :
1134 714 : appendStringInfoString(state->str, " : ");
1135 :
1136 714 : if (PG_ARGISNULL(2))
1137 6 : arg = (Datum) 0;
1138 : else
1139 708 : arg = PG_GETARG_DATUM(2);
1140 :
1141 714 : datum_to_json_internal(arg, PG_ARGISNULL(2), state->str,
1142 : state->val_category,
1143 : state->val_output_func, false);
1144 :
1145 714 : PG_RETURN_POINTER(state);
1146 : }
1147 :
1148 : /*
1149 : * json_object_agg aggregate function
1150 : */
1151 : Datum
1152 75 : json_object_agg_transfn(PG_FUNCTION_ARGS)
1153 : {
1154 75 : return json_object_agg_transfn_worker(fcinfo, false, false);
1155 : }
1156 :
1157 : /*
1158 : * json_object_agg_strict aggregate function
1159 : */
1160 : Datum
1161 30 : json_object_agg_strict_transfn(PG_FUNCTION_ARGS)
1162 : {
1163 30 : return json_object_agg_transfn_worker(fcinfo, true, false);
1164 : }
1165 :
1166 : /*
1167 : * json_object_agg_unique aggregate function
1168 : */
1169 : Datum
1170 627 : json_object_agg_unique_transfn(PG_FUNCTION_ARGS)
1171 : {
1172 627 : return json_object_agg_transfn_worker(fcinfo, false, true);
1173 : }
1174 :
1175 : /*
1176 : * json_object_agg_unique_strict aggregate function
1177 : */
1178 : Datum
1179 27 : json_object_agg_unique_strict_transfn(PG_FUNCTION_ARGS)
1180 : {
1181 27 : return json_object_agg_transfn_worker(fcinfo, true, true);
1182 : }
1183 :
1184 : /*
1185 : * json_object_agg final function.
1186 : */
1187 : Datum
1188 52 : 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 52 : state = PG_ARGISNULL(0) ? NULL : (JsonAggState *) PG_GETARG_POINTER(0);
1196 :
1197 : /* NULL result for no rows in, as is standard with aggregates */
1198 52 : if (state == NULL)
1199 3 : PG_RETURN_NULL();
1200 :
1201 : /* Else return state with appropriate object terminator added */
1202 49 : 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 111 : catenate_stringinfo_string(StringInfo buffer, const char *addon)
1212 : {
1213 : /* custom version of cstring_to_text_with_len */
1214 111 : int buflen = buffer->len;
1215 111 : int addlen = strlen(addon);
1216 111 : text *result = (text *) palloc(buflen + addlen + VARHDRSZ);
1217 :
1218 111 : SET_VARSIZE(result, buflen + addlen + VARHDRSZ);
1219 111 : memcpy(VARDATA(result), buffer->data, buflen);
1220 111 : memcpy(VARDATA(result) + buflen, addon, addlen);
1221 :
1222 111 : return result;
1223 : }
1224 :
1225 : Datum
1226 221 : 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 221 : const char *sep = "";
1231 : StringInfo result;
1232 : JsonUniqueBuilderState unique_check;
1233 :
1234 221 : if (nargs % 2 != 0)
1235 9 : 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 212 : result = makeStringInfo();
1243 :
1244 212 : appendStringInfoChar(result, '{');
1245 :
1246 212 : if (unique_keys)
1247 17 : json_unique_builder_init(&unique_check);
1248 :
1249 451 : 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 286 : skip = absent_on_null && nulls[i + 1];
1257 :
1258 286 : if (skip)
1259 : {
1260 : /* If key uniqueness check is needed we must save skipped keys */
1261 14 : if (!unique_keys)
1262 8 : continue;
1263 :
1264 6 : out = json_unique_builder_get_throwawaybuf(&unique_check);
1265 : }
1266 : else
1267 : {
1268 272 : appendStringInfoString(result, sep);
1269 272 : sep = ", ";
1270 272 : out = result;
1271 : }
1272 :
1273 : /* process key */
1274 278 : if (nulls[i])
1275 12 : 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 266 : key_offset = out->len;
1281 :
1282 266 : add_json(args[i], false, out, types[i], true);
1283 :
1284 248 : 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 49 : const char *key = pstrdup(&out->data[key_offset]);
1295 :
1296 49 : if (!json_unique_check_key(&unique_check.check, key, 0))
1297 17 : ereport(ERROR,
1298 : errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1299 : errmsg("duplicate JSON object key value: %s", key));
1300 :
1301 32 : if (skip)
1302 3 : continue;
1303 : }
1304 :
1305 228 : appendStringInfoString(result, " : ");
1306 :
1307 : /* process value */
1308 228 : add_json(args[i + 1], nulls[i + 1], result, types[i + 1], false);
1309 : }
1310 :
1311 165 : appendStringInfoChar(result, '}');
1312 :
1313 165 : 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 78 : 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 78 : int nargs = extract_variadic_args(fcinfo, 0, true,
1328 : &args, &types, &nulls);
1329 :
1330 78 : if (nargs < 0)
1331 3 : PG_RETURN_NULL();
1332 :
1333 75 : 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 3 : json_build_object_noargs(PG_FUNCTION_ARGS)
1341 : {
1342 3 : PG_RETURN_TEXT_P(cstring_to_text_with_len("{}", 2));
1343 : }
1344 :
1345 : Datum
1346 116 : 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 116 : const char *sep = "";
1351 : StringInfoData result;
1352 :
1353 116 : initStringInfo(&result);
1354 :
1355 116 : appendStringInfoChar(&result, '[');
1356 :
1357 320 : for (i = 0; i < nargs; i++)
1358 : {
1359 204 : if (absent_on_null && nulls[i])
1360 9 : continue;
1361 :
1362 195 : appendStringInfoString(&result, sep);
1363 195 : sep = ", ";
1364 195 : add_json(args[i], nulls[i], &result, types[i], false);
1365 : }
1366 :
1367 116 : appendStringInfoChar(&result, ']');
1368 :
1369 116 : 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 27 : 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 27 : int nargs = extract_variadic_args(fcinfo, 0, true,
1384 : &args, &types, &nulls);
1385 :
1386 27 : if (nargs < 0)
1387 3 : PG_RETURN_NULL();
1388 :
1389 24 : 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 3 : json_build_array_noargs(PG_FUNCTION_ARGS)
1397 : {
1398 3 : 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 24 : json_object(PG_FUNCTION_ARGS)
1409 : {
1410 24 : ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0);
1411 24 : 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 24 : switch (ndims)
1421 : {
1422 3 : case 0:
1423 3 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1424 : break;
1425 :
1426 9 : case 1:
1427 9 : if ((ARR_DIMS(in_array)[0]) % 2)
1428 3 : ereport(ERROR,
1429 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1430 : errmsg("array must have even number of elements")));
1431 6 : break;
1432 :
1433 9 : case 2:
1434 9 : if ((ARR_DIMS(in_array)[1]) != 2)
1435 6 : ereport(ERROR,
1436 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1437 : errmsg("array must have two columns")));
1438 3 : break;
1439 :
1440 3 : default:
1441 3 : ereport(ERROR,
1442 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1443 : errmsg("wrong number of array subscripts")));
1444 : }
1445 :
1446 9 : deconstruct_array_builtin(in_array, TEXTOID, &in_datums, &in_nulls, &in_count);
1447 :
1448 9 : count = in_count / 2;
1449 :
1450 9 : initStringInfo(&result);
1451 :
1452 9 : appendStringInfoChar(&result, '{');
1453 :
1454 933 : for (i = 0; i < count; ++i)
1455 : {
1456 924 : 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 924 : if (i > 0)
1462 915 : appendStringInfoString(&result, ", ");
1463 924 : escape_json_text(&result, (text *) DatumGetPointer(in_datums[i * 2]));
1464 924 : appendStringInfoString(&result, " : ");
1465 924 : if (in_nulls[i * 2 + 1])
1466 6 : appendStringInfoString(&result, "null");
1467 : else
1468 : {
1469 918 : escape_json_text(&result,
1470 918 : (text *) DatumGetPointer(in_datums[i * 2 + 1]));
1471 : }
1472 : }
1473 :
1474 9 : appendStringInfoChar(&result, '}');
1475 :
1476 9 : pfree(in_datums);
1477 9 : pfree(in_nulls);
1478 :
1479 9 : rval = cstring_to_text_with_len(result.data, result.len);
1480 9 : pfree(result.data);
1481 :
1482 9 : 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 21 : json_object_two_arg(PG_FUNCTION_ARGS)
1493 : {
1494 21 : ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(0);
1495 21 : ArrayType *val_array = PG_GETARG_ARRAYTYPE_P(1);
1496 21 : int nkdims = ARR_NDIM(key_array);
1497 21 : 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 21 : if (nkdims > 1 || nkdims != nvdims)
1509 3 : ereport(ERROR,
1510 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1511 : errmsg("wrong number of array subscripts")));
1512 :
1513 18 : if (nkdims == 0)
1514 3 : PG_RETURN_DATUM(CStringGetTextDatum("{}"));
1515 :
1516 15 : deconstruct_array_builtin(key_array, TEXTOID, &key_datums, &key_nulls, &key_count);
1517 15 : deconstruct_array_builtin(val_array, TEXTOID, &val_datums, &val_nulls, &val_count);
1518 :
1519 15 : if (key_count != val_count)
1520 6 : ereport(ERROR,
1521 : (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
1522 : errmsg("mismatched array dimensions")));
1523 :
1524 9 : initStringInfo(&result);
1525 :
1526 9 : appendStringInfoChar(&result, '{');
1527 :
1528 39 : for (i = 0; i < key_count; ++i)
1529 : {
1530 33 : if (key_nulls[i])
1531 3 : ereport(ERROR,
1532 : (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
1533 : errmsg("null value not allowed for object key")));
1534 :
1535 30 : if (i > 0)
1536 21 : appendStringInfoString(&result, ", ");
1537 30 : escape_json_text(&result, (text *) DatumGetPointer(key_datums[i]));
1538 30 : appendStringInfoString(&result, " : ");
1539 30 : if (val_nulls[i])
1540 0 : appendStringInfoString(&result, "null");
1541 : else
1542 30 : escape_json_text(&result,
1543 30 : (text *) DatumGetPointer(val_datums[i]));
1544 : }
1545 :
1546 6 : appendStringInfoChar(&result, '}');
1547 :
1548 6 : pfree(key_datums);
1549 6 : pfree(key_nulls);
1550 6 : pfree(val_datums);
1551 6 : pfree(val_nulls);
1552 :
1553 6 : rval = cstring_to_text_with_len(result.data, result.len);
1554 6 : pfree(result.data);
1555 :
1556 6 : 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 2279628 : escape_json_char(StringInfo buf, char c)
1565 : {
1566 2279628 : switch (c)
1567 : {
1568 9 : case '\b':
1569 9 : appendStringInfoString(buf, "\\b");
1570 9 : break;
1571 3 : case '\f':
1572 3 : appendStringInfoString(buf, "\\f");
1573 3 : break;
1574 12 : case '\n':
1575 12 : appendStringInfoString(buf, "\\n");
1576 12 : break;
1577 3 : case '\r':
1578 3 : appendStringInfoString(buf, "\\r");
1579 3 : break;
1580 90 : case '\t':
1581 90 : appendStringInfoString(buf, "\\t");
1582 90 : break;
1583 315 : case '"':
1584 315 : appendStringInfoString(buf, "\\\"");
1585 315 : break;
1586 45 : case '\\':
1587 45 : appendStringInfoString(buf, "\\\\");
1588 45 : break;
1589 2279151 : default:
1590 2279151 : if ((unsigned char) c < ' ')
1591 3 : appendStringInfo(buf, "\\u%04x", (int) c);
1592 : else
1593 2279148 : appendStringInfoCharMacro(buf, c);
1594 2279151 : break;
1595 : }
1596 2279628 : }
1597 :
1598 : /*
1599 : * escape_json
1600 : * Produce a JSON string literal, properly escaping the NUL-terminated
1601 : * cstring.
1602 : */
1603 : void
1604 19619 : escape_json(StringInfo buf, const char *str)
1605 : {
1606 19619 : appendStringInfoCharMacro(buf, '"');
1607 :
1608 270121 : for (; *str != '\0'; str++)
1609 250502 : escape_json_char(buf, *str);
1610 :
1611 19619 : appendStringInfoCharMacro(buf, '"');
1612 19619 : }
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 204393 : 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 204393 : 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 204393 : vlen = len & (int) (~(sizeof(Vector8) - 1));
1651 :
1652 204393 : appendStringInfoCharMacro(buf, '"');
1653 :
1654 204393 : 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 220529 : for (; i < vlen; i += sizeof(Vector8))
1667 : {
1668 : Vector8 chunk;
1669 :
1670 16136 : 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 32236 : if (vector8_has_le(chunk, (unsigned char) 0x1F) ||
1677 32197 : vector8_has(chunk, (unsigned char) '"') ||
1678 16097 : 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 16079 : if (i - copypos >= ESCAPE_JSON_FLUSH_AFTER)
1689 : {
1690 15 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1691 15 : 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 204450 : if (copypos < i)
1702 : {
1703 14614 : appendBinaryStringInfo(buf, &str[copypos], i - copypos);
1704 14614 : 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 2233576 : for (int b = 0; b < sizeof(Vector8); b++)
1712 : {
1713 : /* check if we've finished */
1714 2233519 : if (i == len)
1715 204393 : goto done;
1716 :
1717 : Assert(i < len);
1718 :
1719 2029126 : escape_json_char(buf, str[i++]);
1720 : }
1721 :
1722 57 : copypos = i;
1723 : /* We're not done yet. Try the vector search again. */
1724 : }
1725 :
1726 204393 : done:
1727 204393 : appendStringInfoCharMacro(buf, '"');
1728 204393 : }
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 2417 : escape_json_text(StringInfo buf, const text *txt)
1739 : {
1740 : /* must cast away the const, unfortunately */
1741 2417 : text *tunpacked = pg_detoast_datum_packed(unconstify(text *, txt));
1742 2417 : int len = VARSIZE_ANY_EXHDR(tunpacked);
1743 : char *str;
1744 :
1745 2417 : str = VARDATA_ANY(tunpacked);
1746 :
1747 2417 : escape_json_with_len(buf, str, len);
1748 :
1749 : /* pfree any detoasted values */
1750 2417 : if (tunpacked != txt)
1751 0 : pfree(tunpacked);
1752 2417 : }
1753 :
1754 : /* Semantic actions for key uniqueness check */
1755 : static JsonParseErrorType
1756 93 : json_unique_object_start(void *_state)
1757 : {
1758 93 : JsonUniqueParsingState *state = _state;
1759 : JsonUniqueStackEntry *entry;
1760 :
1761 93 : if (!state->unique)
1762 0 : return JSON_SUCCESS;
1763 :
1764 : /* push object entry to stack */
1765 93 : entry = palloc_object(JsonUniqueStackEntry);
1766 93 : entry->object_id = state->id_counter++;
1767 93 : entry->parent = state->stack;
1768 93 : state->stack = entry;
1769 :
1770 93 : return JSON_SUCCESS;
1771 : }
1772 :
1773 : static JsonParseErrorType
1774 90 : json_unique_object_end(void *_state)
1775 : {
1776 90 : JsonUniqueParsingState *state = _state;
1777 : JsonUniqueStackEntry *entry;
1778 :
1779 90 : if (!state->unique)
1780 36 : return JSON_SUCCESS;
1781 :
1782 54 : entry = state->stack;
1783 54 : state->stack = entry->parent; /* pop object from stack */
1784 54 : pfree(entry);
1785 54 : return JSON_SUCCESS;
1786 : }
1787 :
1788 : static JsonParseErrorType
1789 137 : json_unique_object_field_start(void *_state, char *field, bool isnull)
1790 : {
1791 137 : JsonUniqueParsingState *state = _state;
1792 : JsonUniqueStackEntry *entry;
1793 :
1794 137 : if (!state->unique)
1795 0 : return JSON_SUCCESS;
1796 :
1797 : /* find key collision in the current object */
1798 137 : if (json_unique_check_key(&state->check, field, state->stack->object_id))
1799 112 : return JSON_SUCCESS;
1800 :
1801 25 : state->unique = false;
1802 :
1803 : /* pop all objects entries */
1804 61 : while ((entry = state->stack))
1805 : {
1806 36 : state->stack = entry->parent;
1807 36 : pfree(entry);
1808 : }
1809 25 : return JSON_SUCCESS;
1810 : }
1811 :
1812 : /* Validate JSON text and additionally check key uniqueness */
1813 : bool
1814 668 : json_validate(text *json, bool check_unique_keys, bool throw_error)
1815 : {
1816 : JsonLexContext lex;
1817 668 : JsonSemAction uniqueSemAction = {0};
1818 : JsonUniqueParsingState state;
1819 : JsonParseErrorType result;
1820 :
1821 668 : makeJsonLexContext(&lex, json, check_unique_keys);
1822 :
1823 668 : if (check_unique_keys)
1824 : {
1825 118 : state.lex = &lex;
1826 118 : state.stack = NULL;
1827 118 : state.id_counter = 0;
1828 118 : state.unique = true;
1829 118 : json_unique_check_init(&state.check);
1830 :
1831 118 : uniqueSemAction.semstate = &state;
1832 118 : uniqueSemAction.object_start = json_unique_object_start;
1833 118 : uniqueSemAction.object_field_start = json_unique_object_field_start;
1834 118 : uniqueSemAction.object_end = json_unique_object_end;
1835 : }
1836 :
1837 668 : result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
1838 :
1839 668 : if (result != JSON_SUCCESS)
1840 : {
1841 108 : if (throw_error)
1842 0 : json_errsave_error(result, &lex, NULL);
1843 :
1844 108 : return false; /* invalid json */
1845 : }
1846 :
1847 560 : if (check_unique_keys && !state.unique)
1848 : {
1849 25 : if (throw_error)
1850 5 : ereport(ERROR,
1851 : (errcode(ERRCODE_DUPLICATE_JSON_OBJECT_KEY_VALUE),
1852 : errmsg("duplicate JSON object key value")));
1853 :
1854 20 : return false; /* not unique keys */
1855 : }
1856 :
1857 535 : if (check_unique_keys)
1858 81 : freeJsonLexContext(&lex);
1859 :
1860 535 : 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 30 : json_typeof(PG_FUNCTION_ARGS)
1877 : {
1878 30 : 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 30 : makeJsonLexContext(&lex, json, false);
1885 30 : result = json_lex(&lex);
1886 30 : if (result != JSON_SUCCESS)
1887 0 : json_errsave_error(result, &lex, NULL);
1888 :
1889 30 : switch (lex.token_type)
1890 : {
1891 6 : case JSON_TOKEN_OBJECT_START:
1892 6 : type = "object";
1893 6 : break;
1894 6 : case JSON_TOKEN_ARRAY_START:
1895 6 : type = "array";
1896 6 : break;
1897 3 : case JSON_TOKEN_STRING:
1898 3 : type = "string";
1899 3 : break;
1900 6 : case JSON_TOKEN_NUMBER:
1901 6 : type = "number";
1902 6 : break;
1903 6 : case JSON_TOKEN_TRUE:
1904 : case JSON_TOKEN_FALSE:
1905 6 : type = "boolean";
1906 6 : break;
1907 3 : case JSON_TOKEN_NULL:
1908 3 : type = "null";
1909 3 : break;
1910 0 : default:
1911 0 : elog(ERROR, "unexpected json token: %d", lex.token_type);
1912 : }
1913 :
1914 30 : PG_RETURN_TEXT_P(cstring_to_text(type));
1915 : }
|