1 /*-------------------------------------------------------------------------
4 * JSON data type support.
6 * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
7 * Portions Copyright (c) 1994, Regents of the University of California
10 * src/backend/utils/adt/json.c
12 *-------------------------------------------------------------------------
16 #include "access/htup_details.h"
17 #include "catalog/pg_type.h"
18 #include "executor/spi.h"
19 #include "lib/stringinfo.h"
20 #include "libpq/pqformat.h"
21 #include "mb/pg_wchar.h"
22 #include "parser/parse_coerce.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/json.h"
27 #include "utils/typcache.h"
29 typedef enum /* types of JSON values */
31 JSON_VALUE_INVALID, /* non-value tokens are reported as this */
41 typedef struct /* state of JSON lexer */
43 char *input; /* whole string being parsed */
44 char *token_start; /* start of current token within input */
45 char *token_terminator; /* end of previous or current token */
46 JsonValueType token_type; /* type of current token, once it's known */
49 typedef enum /* states of JSON parser */
51 JSON_PARSE_VALUE, /* expecting a value */
52 JSON_PARSE_ARRAY_START, /* saw '[', expecting value or ']' */
53 JSON_PARSE_ARRAY_NEXT, /* saw array element, expecting ',' or ']' */
54 JSON_PARSE_OBJECT_START, /* saw '{', expecting label or '}' */
55 JSON_PARSE_OBJECT_LABEL, /* saw object label, expecting ':' */
56 JSON_PARSE_OBJECT_NEXT, /* saw object value, expecting ',' or '}' */
57 JSON_PARSE_OBJECT_COMMA /* saw object ',', expecting next label */
60 typedef struct JsonParseStack /* the parser state has to be stackable */
63 /* currently only need the state enum, but maybe someday more stuff */
66 typedef enum /* required operations on state stack */
68 JSON_STACKOP_NONE, /* no-op */
69 JSON_STACKOP_PUSH, /* push new JSON_PARSE_VALUE stack item */
70 JSON_STACKOP_PUSH_WITH_PUSHBACK, /* push, then rescan current token */
71 JSON_STACKOP_POP /* pop, or expect end of input if no stack */
74 static void json_validate_cstring(char *input);
75 static void json_lex(JsonLexContext *lex);
76 static void json_lex_string(JsonLexContext *lex);
77 static void json_lex_number(JsonLexContext *lex, char *s);
78 static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
79 static void report_invalid_token(JsonLexContext *lex);
80 static int report_json_context(JsonLexContext *lex);
81 static char *extract_mb_char(char *s);
82 static void composite_to_json(Datum composite, StringInfo result,
84 static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
85 Datum *vals, bool *nulls, int *valcount,
86 TYPCATEGORY tcategory, Oid typoutputfunc,
88 static void array_to_json_internal(Datum array, StringInfo result,
91 /* fake type category for JSON so we can distinguish it in datum_to_json */
92 #define TYPCATEGORY_JSON 'j'
93 /* letters appearing in numeric output that aren't valid in a JSON number */
94 #define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
95 /* chars to consider as part of an alphanumeric token */
96 #define JSON_ALPHANUMERIC_CHAR(c) \
97 (((c) >= 'a' && (c) <= 'z') || \
98 ((c) >= 'A' && (c) <= 'Z') || \
99 ((c) >= '0' && (c) <= '9') || \
108 json_in(PG_FUNCTION_ARGS)
110 char *text = PG_GETARG_CSTRING(0);
112 json_validate_cstring(text);
114 /* Internal representation is the same as text, for now */
115 PG_RETURN_TEXT_P(cstring_to_text(text));
122 json_out(PG_FUNCTION_ARGS)
124 /* we needn't detoast because text_to_cstring will handle that */
125 Datum txt = PG_GETARG_DATUM(0);
127 PG_RETURN_CSTRING(TextDatumGetCString(txt));
134 json_send(PG_FUNCTION_ARGS)
136 text *t = PG_GETARG_TEXT_PP(0);
139 pq_begintypsend(&buf);
140 pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
141 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
148 json_recv(PG_FUNCTION_ARGS)
150 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
155 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
158 * We need a null-terminated string to pass to json_validate_cstring().
159 * Rather than make a separate copy, make the temporary result one byte
160 * bigger than it needs to be.
162 result = palloc(nbytes + 1 + VARHDRSZ);
163 SET_VARSIZE(result, nbytes + VARHDRSZ);
164 memcpy(VARDATA(result), str, nbytes);
165 str = VARDATA(result);
169 json_validate_cstring(str);
171 PG_RETURN_TEXT_P(result);
175 * Check whether supplied input is valid JSON.
178 json_validate_cstring(char *input)
181 JsonParseStack *stack,
185 /* Set up lexing context. */
187 lex.token_terminator = lex.input;
189 /* Set up parse stack. */
191 stacktop = (JsonParseStack *) palloc(sizeof(JsonParseStack) * stacksize);
193 stack->state = JSON_PARSE_VALUE;
195 /* Main parsing loop. */
200 /* Fetch next token. */
203 /* Check for unexpected end of input. */
204 if (lex.token_start == NULL)
205 report_parse_error(stack, &lex);
208 /* Figure out what to do with this token. */
209 op = JSON_STACKOP_NONE;
210 switch (stack->state)
212 case JSON_PARSE_VALUE:
213 if (lex.token_type != JSON_VALUE_INVALID)
214 op = JSON_STACKOP_POP;
215 else if (lex.token_start[0] == '[')
216 stack->state = JSON_PARSE_ARRAY_START;
217 else if (lex.token_start[0] == '{')
218 stack->state = JSON_PARSE_OBJECT_START;
220 report_parse_error(stack, &lex);
222 case JSON_PARSE_ARRAY_START:
223 if (lex.token_type != JSON_VALUE_INVALID)
224 stack->state = JSON_PARSE_ARRAY_NEXT;
225 else if (lex.token_start[0] == ']')
226 op = JSON_STACKOP_POP;
227 else if (lex.token_start[0] == '[' ||
228 lex.token_start[0] == '{')
230 stack->state = JSON_PARSE_ARRAY_NEXT;
231 op = JSON_STACKOP_PUSH_WITH_PUSHBACK;
234 report_parse_error(stack, &lex);
236 case JSON_PARSE_ARRAY_NEXT:
237 if (lex.token_type != JSON_VALUE_INVALID)
238 report_parse_error(stack, &lex);
239 else if (lex.token_start[0] == ']')
240 op = JSON_STACKOP_POP;
241 else if (lex.token_start[0] == ',')
242 op = JSON_STACKOP_PUSH;
244 report_parse_error(stack, &lex);
246 case JSON_PARSE_OBJECT_START:
247 if (lex.token_type == JSON_VALUE_STRING)
248 stack->state = JSON_PARSE_OBJECT_LABEL;
249 else if (lex.token_type == JSON_VALUE_INVALID &&
250 lex.token_start[0] == '}')
251 op = JSON_STACKOP_POP;
253 report_parse_error(stack, &lex);
255 case JSON_PARSE_OBJECT_LABEL:
256 if (lex.token_type == JSON_VALUE_INVALID &&
257 lex.token_start[0] == ':')
259 stack->state = JSON_PARSE_OBJECT_NEXT;
260 op = JSON_STACKOP_PUSH;
263 report_parse_error(stack, &lex);
265 case JSON_PARSE_OBJECT_NEXT:
266 if (lex.token_type != JSON_VALUE_INVALID)
267 report_parse_error(stack, &lex);
268 else if (lex.token_start[0] == '}')
269 op = JSON_STACKOP_POP;
270 else if (lex.token_start[0] == ',')
271 stack->state = JSON_PARSE_OBJECT_COMMA;
273 report_parse_error(stack, &lex);
275 case JSON_PARSE_OBJECT_COMMA:
276 if (lex.token_type == JSON_VALUE_STRING)
277 stack->state = JSON_PARSE_OBJECT_LABEL;
279 report_parse_error(stack, &lex);
282 elog(ERROR, "unexpected json parse state: %d",
286 /* Push or pop the state stack, if needed. */
289 case JSON_STACKOP_PUSH:
290 case JSON_STACKOP_PUSH_WITH_PUSHBACK:
292 if (stack >= &stacktop[stacksize])
294 /* Need to enlarge the stack. */
295 int stackoffset = stack - stacktop;
298 stacktop = (JsonParseStack *)
300 sizeof(JsonParseStack) * stacksize);
301 stack = stacktop + stackoffset;
303 stack->state = JSON_PARSE_VALUE;
304 if (op == JSON_STACKOP_PUSH_WITH_PUSHBACK)
307 case JSON_STACKOP_POP:
308 if (stack == stacktop)
310 /* Expect end of input. */
312 if (lex.token_start != NULL)
313 report_parse_error(NULL, &lex);
318 case JSON_STACKOP_NONE:
326 * Lex one token from the input stream.
329 json_lex(JsonLexContext *lex)
333 /* Skip leading whitespace. */
334 s = lex->token_terminator;
335 while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
337 lex->token_start = s;
339 /* Determine token type. */
340 if (strchr("{}[],:", s[0]) != NULL)
342 /* strchr() is willing to match a zero byte, so test for that. */
346 lex->token_start = NULL;
347 lex->token_terminator = s;
351 /* Single-character token, some kind of punctuation mark. */
352 lex->token_terminator = s + 1;
354 lex->token_type = JSON_VALUE_INVALID;
359 json_lex_string(lex);
360 lex->token_type = JSON_VALUE_STRING;
364 /* Negative number. */
365 json_lex_number(lex, s + 1);
366 lex->token_type = JSON_VALUE_NUMBER;
368 else if (*s >= '0' && *s <= '9')
370 /* Positive number. */
371 json_lex_number(lex, s);
372 lex->token_type = JSON_VALUE_NUMBER;
379 * We're not dealing with a string, number, legal punctuation mark, or
380 * end of string. The only legal tokens we might find here are true,
381 * false, and null, but for error reporting purposes we scan until we
382 * see a non-alphanumeric character. That way, we can report the
383 * whole word as an unexpected token, rather than just some
384 * unintuitive prefix thereof.
386 for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
392 * We got some sort of unexpected punctuation or an otherwise
393 * unexpected character, so just complain about that one
394 * character. (It can't be multibyte because the above loop
395 * will advance over any multibyte characters.)
397 lex->token_terminator = s + 1;
398 report_invalid_token(lex);
402 * We've got a real alphanumeric token here. If it happens to be
403 * true, false, or null, all is well. If not, error out.
405 lex->token_terminator = p;
408 if (memcmp(s, "true", 4) == 0)
409 lex->token_type = JSON_VALUE_TRUE;
410 else if (memcmp(s, "null", 4) == 0)
411 lex->token_type = JSON_VALUE_NULL;
413 report_invalid_token(lex);
415 else if (p - s == 5 && memcmp(s, "false", 5) == 0)
416 lex->token_type = JSON_VALUE_FALSE;
418 report_invalid_token(lex);
423 * The next token in the input stream is known to be a string; lex it.
426 json_lex_string(JsonLexContext *lex)
430 for (s = lex->token_start + 1; *s != '"'; s++)
432 /* Per RFC4627, these characters MUST be escaped. */
433 if ((unsigned char) *s < 32)
435 /* A NUL byte marks the (premature) end of the string. */
438 lex->token_terminator = s;
439 report_invalid_token(lex);
441 /* Since *s isn't printable, exclude it from the context string */
442 lex->token_terminator = s;
444 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
445 errmsg("invalid input syntax for type json"),
446 errdetail("Character with value 0x%02x must be escaped.",
448 report_json_context(lex)));
452 /* OK, we have an escape character. */
456 lex->token_terminator = s;
457 report_invalid_token(lex);
464 for (i = 1; i <= 4; i++)
469 lex->token_terminator = s;
470 report_invalid_token(lex);
472 else if (*s >= '0' && *s <= '9')
473 ch = (ch * 16) + (*s - '0');
474 else if (*s >= 'a' && *s <= 'f')
475 ch = (ch * 16) + (*s - 'a') + 10;
476 else if (*s >= 'A' && *s <= 'F')
477 ch = (ch * 16) + (*s - 'A') + 10;
480 lex->token_terminator = s + pg_mblen(s);
482 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
483 errmsg("invalid input syntax for type json"),
484 errdetail("\"\\u\" must be followed by four hexadecimal digits."),
485 report_json_context(lex)));
489 else if (strchr("\"\\/bfnrt", *s) == NULL)
491 /* Not a valid string escape, so error out. */
492 lex->token_terminator = s + pg_mblen(s);
494 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
495 errmsg("invalid input syntax for type json"),
496 errdetail("Escape sequence \"\\%s\" is invalid.",
498 report_json_context(lex)));
503 /* Hooray, we found the end of the string! */
504 lex->token_terminator = s + 1;
507 /*-------------------------------------------------------------------------
508 * The next token in the input stream is known to be a number; lex it.
510 * In JSON, a number consists of four parts:
512 * (1) An optional minus sign ('-').
514 * (2) Either a single '0', or a string of one or more digits that does not
517 * (3) An optional decimal part, consisting of a period ('.') followed by
518 * one or more digits. (Note: While this part can be omitted
519 * completely, it's not OK to have only the decimal point without
520 * any digits afterwards.)
522 * (4) An optional exponent part, consisting of 'e' or 'E', optionally
523 * followed by '+' or '-', followed by one or more digits. (Note:
524 * As with the decimal part, if 'e' or 'E' is present, it must be
525 * followed by at least one digit.)
527 * The 's' argument to this function points to the ostensible beginning
528 * of part 2 - i.e. the character after any optional minus sign, and the
529 * first character of the string if there is none.
531 *-------------------------------------------------------------------------
534 json_lex_number(JsonLexContext *lex, char *s)
539 /* Part (1): leading sign indicator. */
540 /* Caller already did this for us; so do nothing. */
542 /* Part (2): parse main digit string. */
545 else if (*s >= '1' && *s <= '9')
550 } while (*s >= '0' && *s <= '9');
555 /* Part (3): parse optional decimal portion. */
559 if (*s < '0' || *s > '9')
566 } while (*s >= '0' && *s <= '9');
570 /* Part (4): parse optional exponent. */
571 if (*s == 'e' || *s == 'E')
574 if (*s == '+' || *s == '-')
576 if (*s < '0' || *s > '9')
583 } while (*s >= '0' && *s <= '9');
588 * Check for trailing garbage. As in json_lex(), any alphanumeric stuff
589 * here should be considered part of the token for error-reporting
592 for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
594 lex->token_terminator = p;
596 report_invalid_token(lex);
600 * Report a parse error.
602 * lex->token_start and lex->token_terminator must identify the current token.
605 report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
610 /* Handle case where the input ended prematurely. */
611 if (lex->token_start == NULL)
613 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
614 errmsg("invalid input syntax for type json"),
615 errdetail("The input string ended unexpectedly."),
616 report_json_context(lex)));
618 /* Separate out the current token. */
619 toklen = lex->token_terminator - lex->token_start;
620 token = palloc(toklen + 1);
621 memcpy(token, lex->token_start, toklen);
622 token[toklen] = '\0';
624 /* Complain, with the appropriate detail message. */
627 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
628 errmsg("invalid input syntax for type json"),
629 errdetail("Expected end of input, but found \"%s\".",
631 report_json_context(lex)));
634 switch (stack->state)
636 case JSON_PARSE_VALUE:
638 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
639 errmsg("invalid input syntax for type json"),
640 errdetail("Expected JSON value, but found \"%s\".",
642 report_json_context(lex)));
644 case JSON_PARSE_ARRAY_START:
646 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
647 errmsg("invalid input syntax for type json"),
648 errdetail("Expected array element or \"]\", but found \"%s\".",
650 report_json_context(lex)));
652 case JSON_PARSE_ARRAY_NEXT:
654 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
655 errmsg("invalid input syntax for type json"),
656 errdetail("Expected \",\" or \"]\", but found \"%s\".",
658 report_json_context(lex)));
660 case JSON_PARSE_OBJECT_START:
662 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
663 errmsg("invalid input syntax for type json"),
664 errdetail("Expected string or \"}\", but found \"%s\".",
666 report_json_context(lex)));
668 case JSON_PARSE_OBJECT_LABEL:
670 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
671 errmsg("invalid input syntax for type json"),
672 errdetail("Expected \":\", but found \"%s\".",
674 report_json_context(lex)));
676 case JSON_PARSE_OBJECT_NEXT:
678 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
679 errmsg("invalid input syntax for type json"),
680 errdetail("Expected \",\" or \"}\", but found \"%s\".",
682 report_json_context(lex)));
684 case JSON_PARSE_OBJECT_COMMA:
686 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
687 errmsg("invalid input syntax for type json"),
688 errdetail("Expected string, but found \"%s\".",
690 report_json_context(lex)));
693 elog(ERROR, "unexpected json parse state: %d",
700 * Report an invalid input token.
702 * lex->token_start and lex->token_terminator must identify the token.
705 report_invalid_token(JsonLexContext *lex)
710 /* Separate out the offending token. */
711 toklen = lex->token_terminator - lex->token_start;
712 token = palloc(toklen + 1);
713 memcpy(token, lex->token_start, toklen);
714 token[toklen] = '\0';
717 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
718 errmsg("invalid input syntax for type json"),
719 errdetail("Token \"%s\" is invalid.", token),
720 report_json_context(lex)));
724 * Report a CONTEXT line for bogus JSON input.
726 * lex->token_terminator must be set to identify the spot where we detected
727 * the error. Note that lex->token_start might be NULL, in case we recognized
730 * The return value isn't meaningful, but we make it non-void so that this
731 * can be invoked inside ereport().
734 report_json_context(JsonLexContext *lex)
736 const char *context_start;
737 const char *context_end;
738 const char *line_start;
745 /* Choose boundaries for the part of the input we will display */
746 context_start = lex->input;
747 context_end = lex->token_terminator;
748 line_start = context_start;
752 /* Always advance over newlines (context_end test is just paranoia) */
753 if (*context_start == '\n' && context_start < context_end)
756 line_start = context_start;
760 /* Otherwise, done as soon as we are close enough to context_end */
761 if (context_end - context_start < 50)
763 /* Advance to next multibyte character */
764 if (IS_HIGHBIT_SET(*context_start))
765 context_start += pg_mblen(context_start);
771 * We add "..." to indicate that the excerpt doesn't start at the
772 * beginning of the line ... but if we're within 3 characters of the
773 * beginning of the line, we might as well just show the whole line.
775 if (context_start - line_start <= 3)
776 context_start = line_start;
778 /* Get a null-terminated copy of the data to present */
779 ctxtlen = context_end - context_start;
780 ctxt = palloc(ctxtlen + 1);
781 memcpy(ctxt, context_start, ctxtlen);
782 ctxt[ctxtlen] = '\0';
785 * Show the context, prefixing "..." if not starting at start of line, and
786 * suffixing "..." if not ending at end of line.
788 prefix = (context_start > line_start) ? "..." : "";
789 suffix = (*context_end != '\0' && *context_end != '\n' && *context_end != '\r') ? "..." : "";
791 return errcontext("JSON data, line %d: %s%s%s",
792 line_number, prefix, ctxt, suffix);
796 * Extract a single, possibly multi-byte char from the input string.
799 extract_mb_char(char *s)
805 res = palloc(len + 1);
813 * Turn a scalar Datum into JSON, appending the string to "result".
815 * Hand off a non-scalar datum to composite_to_json or array_to_json_internal
819 datum_to_json(Datum val, bool is_null, StringInfo result,
820 TYPCATEGORY tcategory, Oid typoutputfunc)
826 appendStringInfoString(result, "null");
832 case TYPCATEGORY_ARRAY:
833 array_to_json_internal(val, result, false);
835 case TYPCATEGORY_COMPOSITE:
836 composite_to_json(val, result, false);
838 case TYPCATEGORY_BOOLEAN:
839 if (DatumGetBool(val))
840 appendStringInfoString(result, "true");
842 appendStringInfoString(result, "false");
844 case TYPCATEGORY_NUMERIC:
845 outputstr = OidOutputFunctionCall(typoutputfunc, val);
848 * Don't call escape_json here if it's a valid JSON number.
849 * Numeric output should usually be a valid JSON number and JSON
850 * numbers shouldn't be quoted. Quote cases like "Nan" and
851 * "Infinity", however.
853 if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL)
854 appendStringInfoString(result, outputstr);
856 escape_json(result, outputstr);
859 case TYPCATEGORY_JSON:
860 /* JSON will already be escaped */
861 outputstr = OidOutputFunctionCall(typoutputfunc, val);
862 appendStringInfoString(result, outputstr);
866 outputstr = OidOutputFunctionCall(typoutputfunc, val);
867 escape_json(result, outputstr);
874 * Process a single dimension of an array.
875 * If it's the innermost dimension, output the values, otherwise call
876 * ourselves recursively to process the next dimension.
879 array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
880 bool *nulls, int *valcount, TYPCATEGORY tcategory,
881 Oid typoutputfunc, bool use_line_feeds)
888 sep = use_line_feeds ? ",\n " : ",";
890 appendStringInfoChar(result, '[');
892 for (i = 1; i <= dims[dim]; i++)
895 appendStringInfoString(result, sep);
897 if (dim + 1 == ndims)
899 datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
906 * Do we want line feeds on inner dimensions of arrays? For now
909 array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
910 valcount, tcategory, typoutputfunc, false);
914 appendStringInfoChar(result, ']');
918 * Turn an array into JSON.
921 array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
923 ArrayType *v = DatumGetArrayTypeP(array);
924 Oid element_type = ARR_ELEMTYPE(v);
937 TYPCATEGORY tcategory;
941 nitems = ArrayGetNItems(ndim, dim);
945 appendStringInfoString(result, "[]");
949 get_type_io_data(element_type, IOFunc_output,
950 &typlen, &typbyval, &typalign,
951 &typdelim, &typioparam, &typoutputfunc);
953 deconstruct_array(v, element_type, typlen, typbyval,
954 typalign, &elements, &nulls,
957 if (element_type == RECORDOID)
958 tcategory = TYPCATEGORY_COMPOSITE;
959 else if (element_type == JSONOID)
960 tcategory = TYPCATEGORY_JSON;
962 tcategory = TypeCategory(element_type);
964 array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
965 typoutputfunc, use_line_feeds);
972 * Turn a composite / record into JSON.
975 composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
981 HeapTupleData tmptup,
984 bool needsep = false;
987 sep = use_line_feeds ? ",\n " : ",";
989 td = DatumGetHeapTupleHeader(composite);
991 /* Extract rowtype info and find a tupdesc */
992 tupType = HeapTupleHeaderGetTypeId(td);
993 tupTypmod = HeapTupleHeaderGetTypMod(td);
994 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
996 /* Build a temporary HeapTuple control structure */
997 tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
1001 appendStringInfoChar(result, '{');
1003 for (i = 0; i < tupdesc->natts; i++)
1009 TYPCATEGORY tcategory;
1013 if (tupdesc->attrs[i]->attisdropped)
1017 appendStringInfoString(result, sep);
1020 attname = NameStr(tupdesc->attrs[i]->attname);
1021 escape_json(result, attname);
1022 appendStringInfoChar(result, ':');
1024 origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);
1026 if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
1027 tcategory = TYPCATEGORY_ARRAY;
1028 else if (tupdesc->attrs[i]->atttypid == RECORDOID)
1029 tcategory = TYPCATEGORY_COMPOSITE;
1030 else if (tupdesc->attrs[i]->atttypid == JSONOID)
1031 tcategory = TYPCATEGORY_JSON;
1033 tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
1035 getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
1036 &typoutput, &typisvarlena);
1039 * If we have a toasted datum, forcibly detoast it here to avoid
1040 * memory leakage inside the type's output routine.
1042 if (typisvarlena && !isnull)
1043 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
1047 datum_to_json(val, isnull, result, tcategory, typoutput);
1049 /* Clean up detoasted copy, if any */
1051 pfree(DatumGetPointer(val));
1054 appendStringInfoChar(result, '}');
1055 ReleaseTupleDesc(tupdesc);
1059 * SQL function array_to_json(row)
1062 array_to_json(PG_FUNCTION_ARGS)
1064 Datum array = PG_GETARG_DATUM(0);
1067 result = makeStringInfo();
1069 array_to_json_internal(array, result, false);
1071 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1075 * SQL function array_to_json(row, prettybool)
1078 array_to_json_pretty(PG_FUNCTION_ARGS)
1080 Datum array = PG_GETARG_DATUM(0);
1081 bool use_line_feeds = PG_GETARG_BOOL(1);
1084 result = makeStringInfo();
1086 array_to_json_internal(array, result, use_line_feeds);
1088 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1092 * SQL function row_to_json(row)
1095 row_to_json(PG_FUNCTION_ARGS)
1097 Datum array = PG_GETARG_DATUM(0);
1100 result = makeStringInfo();
1102 composite_to_json(array, result, false);
1104 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1108 * SQL function row_to_json(row, prettybool)
1111 row_to_json_pretty(PG_FUNCTION_ARGS)
1113 Datum array = PG_GETARG_DATUM(0);
1114 bool use_line_feeds = PG_GETARG_BOOL(1);
1117 result = makeStringInfo();
1119 composite_to_json(array, result, use_line_feeds);
1121 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1125 * Produce a JSON string literal, properly escaping characters in the text.
1128 escape_json(StringInfo buf, const char *str)
1132 appendStringInfoCharMacro(buf, '\"');
1133 for (p = str; *p; p++)
1138 appendStringInfoString(buf, "\\b");
1141 appendStringInfoString(buf, "\\f");
1144 appendStringInfoString(buf, "\\n");
1147 appendStringInfoString(buf, "\\r");
1150 appendStringInfoString(buf, "\\t");
1153 appendStringInfoString(buf, "\\\"");
1156 appendStringInfoString(buf, "\\\\");
1159 if ((unsigned char) *p < ' ')
1160 appendStringInfo(buf, "\\u%04x", (int) *p);
1162 appendStringInfoCharMacro(buf, *p);
1166 appendStringInfoCharMacro(buf, '\"');