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