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