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