1 /*-------------------------------------------------------------------------
4 * JSON data type support.
6 * Portions Copyright (c) 1996-2012, 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 "catalog/pg_type.h"
17 #include "executor/spi.h"
18 #include "lib/stringinfo.h"
19 #include "libpq/pqformat.h"
20 #include "mb/pg_wchar.h"
21 #include "parser/parse_coerce.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/lsyscache.h"
25 #include "utils/json.h"
26 #include "utils/typcache.h"
28 typedef enum /* types of JSON values */
30 JSON_VALUE_INVALID, /* non-value tokens are reported as this */
40 typedef struct /* state of JSON lexer */
42 char *input; /* whole string being parsed */
43 char *token_start; /* start of current token within input */
44 char *token_terminator; /* end of previous or current token */
45 JsonValueType token_type; /* type of current token, once it's known */
48 typedef enum /* states of JSON parser */
50 JSON_PARSE_VALUE, /* expecting a value */
51 JSON_PARSE_ARRAY_START, /* saw '[', expecting value or ']' */
52 JSON_PARSE_ARRAY_NEXT, /* saw array element, expecting ',' or ']' */
53 JSON_PARSE_OBJECT_START, /* saw '{', expecting label or '}' */
54 JSON_PARSE_OBJECT_LABEL, /* saw object label, expecting ':' */
55 JSON_PARSE_OBJECT_NEXT, /* saw object value, expecting ',' or '}' */
56 JSON_PARSE_OBJECT_COMMA /* saw object ',', expecting next label */
59 typedef struct JsonParseStack /* the parser state has to be stackable */
62 /* currently only need the state enum, but maybe someday more stuff */
65 typedef enum /* required operations on state stack */
67 JSON_STACKOP_NONE, /* no-op */
68 JSON_STACKOP_PUSH, /* push new JSON_PARSE_VALUE stack item */
69 JSON_STACKOP_PUSH_WITH_PUSHBACK, /* push, then rescan current token */
70 JSON_STACKOP_POP /* pop, or expect end of input if no stack */
73 static void json_validate_cstring(char *input);
74 static void json_lex(JsonLexContext *lex);
75 static void json_lex_string(JsonLexContext *lex);
76 static void json_lex_number(JsonLexContext *lex, char *s);
77 static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
78 static void report_invalid_token(JsonLexContext *lex);
79 static int report_json_context(JsonLexContext *lex);
80 static char *extract_mb_char(char *s);
81 static void composite_to_json(Datum composite, StringInfo result,
83 static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
84 Datum *vals, bool *nulls, int *valcount,
85 TYPCATEGORY tcategory, Oid typoutputfunc,
87 static void array_to_json_internal(Datum array, StringInfo result,
90 /* fake type category for JSON so we can distinguish it in datum_to_json */
91 #define TYPCATEGORY_JSON 'j'
92 /* letters appearing in numeric output that aren't valid in a JSON number */
93 #define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
94 /* chars to consider as part of an alphanumeric token */
95 #define JSON_ALPHANUMERIC_CHAR(c) \
96 (((c) >= 'a' && (c) <= 'z') || \
97 ((c) >= 'A' && (c) <= 'Z') || \
98 ((c) >= '0' && (c) <= '9') || \
107 json_in(PG_FUNCTION_ARGS)
109 char *text = PG_GETARG_CSTRING(0);
111 json_validate_cstring(text);
113 /* Internal representation is the same as text, for now */
114 PG_RETURN_TEXT_P(cstring_to_text(text));
121 json_out(PG_FUNCTION_ARGS)
123 /* we needn't detoast because text_to_cstring will handle that */
124 Datum txt = PG_GETARG_DATUM(0);
126 PG_RETURN_CSTRING(TextDatumGetCString(txt));
133 json_send(PG_FUNCTION_ARGS)
135 text *t = PG_GETARG_TEXT_PP(0);
138 pq_begintypsend(&buf);
139 pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
140 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
147 json_recv(PG_FUNCTION_ARGS)
149 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
154 str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
157 * We need a null-terminated string to pass to json_validate_cstring().
158 * Rather than make a separate copy, make the temporary result one byte
159 * bigger than it needs to be.
161 result = palloc(nbytes + 1 + VARHDRSZ);
162 SET_VARSIZE(result, nbytes + VARHDRSZ);
163 memcpy(VARDATA(result), str, nbytes);
164 str = VARDATA(result);
168 json_validate_cstring(str);
170 PG_RETURN_TEXT_P(result);
174 * Check whether supplied input is valid JSON.
177 json_validate_cstring(char *input)
180 JsonParseStack *stack,
184 /* Set up lexing context. */
186 lex.token_terminator = lex.input;
188 /* Set up parse stack. */
190 stacktop = (JsonParseStack *) palloc(sizeof(JsonParseStack) * stacksize);
192 stack->state = JSON_PARSE_VALUE;
194 /* Main parsing loop. */
199 /* Fetch next token. */
202 /* Check for unexpected end of input. */
203 if (lex.token_start == NULL)
204 report_parse_error(stack, &lex);
207 /* Figure out what to do with this token. */
208 op = JSON_STACKOP_NONE;
209 switch (stack->state)
211 case JSON_PARSE_VALUE:
212 if (lex.token_type != JSON_VALUE_INVALID)
213 op = JSON_STACKOP_POP;
214 else if (lex.token_start[0] == '[')
215 stack->state = JSON_PARSE_ARRAY_START;
216 else if (lex.token_start[0] == '{')
217 stack->state = JSON_PARSE_OBJECT_START;
219 report_parse_error(stack, &lex);
221 case JSON_PARSE_ARRAY_START:
222 if (lex.token_type != JSON_VALUE_INVALID)
223 stack->state = JSON_PARSE_ARRAY_NEXT;
224 else if (lex.token_start[0] == ']')
225 op = JSON_STACKOP_POP;
226 else if (lex.token_start[0] == '[' ||
227 lex.token_start[0] == '{')
229 stack->state = JSON_PARSE_ARRAY_NEXT;
230 op = JSON_STACKOP_PUSH_WITH_PUSHBACK;
233 report_parse_error(stack, &lex);
235 case JSON_PARSE_ARRAY_NEXT:
236 if (lex.token_type != JSON_VALUE_INVALID)
237 report_parse_error(stack, &lex);
238 else if (lex.token_start[0] == ']')
239 op = JSON_STACKOP_POP;
240 else if (lex.token_start[0] == ',')
241 op = JSON_STACKOP_PUSH;
243 report_parse_error(stack, &lex);
245 case JSON_PARSE_OBJECT_START:
246 if (lex.token_type == JSON_VALUE_STRING)
247 stack->state = JSON_PARSE_OBJECT_LABEL;
248 else if (lex.token_type == JSON_VALUE_INVALID &&
249 lex.token_start[0] == '}')
250 op = JSON_STACKOP_POP;
252 report_parse_error(stack, &lex);
254 case JSON_PARSE_OBJECT_LABEL:
255 if (lex.token_type == JSON_VALUE_INVALID &&
256 lex.token_start[0] == ':')
258 stack->state = JSON_PARSE_OBJECT_NEXT;
259 op = JSON_STACKOP_PUSH;
262 report_parse_error(stack, &lex);
264 case JSON_PARSE_OBJECT_NEXT:
265 if (lex.token_type != JSON_VALUE_INVALID)
266 report_parse_error(stack, &lex);
267 else if (lex.token_start[0] == '}')
268 op = JSON_STACKOP_POP;
269 else if (lex.token_start[0] == ',')
270 stack->state = JSON_PARSE_OBJECT_COMMA;
272 report_parse_error(stack, &lex);
274 case JSON_PARSE_OBJECT_COMMA:
275 if (lex.token_type == JSON_VALUE_STRING)
276 stack->state = JSON_PARSE_OBJECT_LABEL;
278 report_parse_error(stack, &lex);
281 elog(ERROR, "unexpected json parse state: %d",
285 /* Push or pop the state stack, if needed. */
288 case JSON_STACKOP_PUSH:
289 case JSON_STACKOP_PUSH_WITH_PUSHBACK:
291 if (stack >= &stacktop[stacksize])
293 /* Need to enlarge the stack. */
294 int stackoffset = stack - stacktop;
297 stacktop = (JsonParseStack *)
299 sizeof(JsonParseStack) * stacksize);
300 stack = stacktop + stackoffset;
302 stack->state = JSON_PARSE_VALUE;
303 if (op == JSON_STACKOP_PUSH_WITH_PUSHBACK)
306 case JSON_STACKOP_POP:
307 if (stack == stacktop)
309 /* Expect end of input. */
311 if (lex.token_start != NULL)
312 report_parse_error(NULL, &lex);
317 case JSON_STACKOP_NONE:
325 * Lex one token from the input stream.
328 json_lex(JsonLexContext *lex)
332 /* Skip leading whitespace. */
333 s = lex->token_terminator;
334 while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
336 lex->token_start = s;
338 /* Determine token type. */
339 if (strchr("{}[],:", s[0]) != NULL)
341 /* strchr() is willing to match a zero byte, so test for that. */
345 lex->token_start = NULL;
346 lex->token_terminator = s;
350 /* Single-character token, some kind of punctuation mark. */
351 lex->token_terminator = s + 1;
353 lex->token_type = JSON_VALUE_INVALID;
358 json_lex_string(lex);
359 lex->token_type = JSON_VALUE_STRING;
363 /* Negative number. */
364 json_lex_number(lex, s + 1);
365 lex->token_type = JSON_VALUE_NUMBER;
367 else if (*s >= '0' && *s <= '9')
369 /* Positive number. */
370 json_lex_number(lex, s);
371 lex->token_type = JSON_VALUE_NUMBER;
378 * We're not dealing with a string, number, legal punctuation mark, or
379 * end of string. The only legal tokens we might find here are true,
380 * false, and null, but for error reporting purposes we scan until we
381 * see a non-alphanumeric character. That way, we can report the
382 * whole word as an unexpected token, rather than just some
383 * unintuitive prefix thereof.
385 for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
391 * We got some sort of unexpected punctuation or an otherwise
392 * unexpected character, so just complain about that one
393 * character. (It can't be multibyte because the above loop
394 * will advance over any multibyte characters.)
396 lex->token_terminator = s + 1;
397 report_invalid_token(lex);
401 * We've got a real alphanumeric token here. If it happens to be
402 * true, false, or null, all is well. If not, error out.
404 lex->token_terminator = p;
407 if (memcmp(s, "true", 4) == 0)
408 lex->token_type = JSON_VALUE_TRUE;
409 else if (memcmp(s, "null", 4) == 0)
410 lex->token_type = JSON_VALUE_NULL;
412 report_invalid_token(lex);
414 else if (p - s == 5 && memcmp(s, "false", 5) == 0)
415 lex->token_type = JSON_VALUE_FALSE;
417 report_invalid_token(lex);
422 * The next token in the input stream is known to be a string; lex it.
425 json_lex_string(JsonLexContext *lex)
429 for (s = lex->token_start + 1; *s != '"'; s++)
431 /* Per RFC4627, these characters MUST be escaped. */
432 if ((unsigned char) *s < 32)
434 /* A NUL byte marks the (premature) end of the string. */
437 lex->token_terminator = s;
438 report_invalid_token(lex);
440 /* Since *s isn't printable, exclude it from the context string */
441 lex->token_terminator = s;
443 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
444 errmsg("invalid input syntax for type json"),
445 errdetail("Character with value 0x%02x must be escaped.",
447 report_json_context(lex)));
451 /* OK, we have an escape character. */
455 lex->token_terminator = s;
456 report_invalid_token(lex);
463 for (i = 1; i <= 4; i++)
468 lex->token_terminator = s;
469 report_invalid_token(lex);
471 else if (*s >= '0' && *s <= '9')
472 ch = (ch * 16) + (*s - '0');
473 else if (*s >= 'a' && *s <= 'f')
474 ch = (ch * 16) + (*s - 'a') + 10;
475 else if (*s >= 'A' && *s <= 'F')
476 ch = (ch * 16) + (*s - 'A') + 10;
479 lex->token_terminator = s + pg_mblen(s);
481 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
482 errmsg("invalid input syntax for type json"),
483 errdetail("\"\\u\" must be followed by four hexadecimal digits."),
484 report_json_context(lex)));
488 else if (strchr("\"\\/bfnrt", *s) == NULL)
490 /* Not a valid string escape, so error out. */
491 lex->token_terminator = s + pg_mblen(s);
493 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
494 errmsg("invalid input syntax for type json"),
495 errdetail("Escape sequence \"\\%s\" is invalid.",
497 report_json_context(lex)));
502 /* Hooray, we found the end of the string! */
503 lex->token_terminator = s + 1;
506 /*-------------------------------------------------------------------------
507 * The next token in the input stream is known to be a number; lex it.
509 * In JSON, a number consists of four parts:
511 * (1) An optional minus sign ('-').
513 * (2) Either a single '0', or a string of one or more digits that does not
516 * (3) An optional decimal part, consisting of a period ('.') followed by
517 * one or more digits. (Note: While this part can be omitted
518 * completely, it's not OK to have only the decimal point without
519 * any digits afterwards.)
521 * (4) An optional exponent part, consisting of 'e' or 'E', optionally
522 * followed by '+' or '-', followed by one or more digits. (Note:
523 * As with the decimal part, if 'e' or 'E' is present, it must be
524 * followed by at least one digit.)
526 * The 's' argument to this function points to the ostensible beginning
527 * of part 2 - i.e. the character after any optional minus sign, and the
528 * first character of the string if there is none.
530 *-------------------------------------------------------------------------
533 json_lex_number(JsonLexContext *lex, char *s)
538 /* Part (1): leading sign indicator. */
539 /* Caller already did this for us; so do nothing. */
541 /* Part (2): parse main digit string. */
544 else if (*s >= '1' && *s <= '9')
549 } while (*s >= '0' && *s <= '9');
554 /* Part (3): parse optional decimal portion. */
558 if (*s < '0' || *s > '9')
565 } while (*s >= '0' && *s <= '9');
569 /* Part (4): parse optional exponent. */
570 if (*s == 'e' || *s == 'E')
573 if (*s == '+' || *s == '-')
575 if (*s < '0' || *s > '9')
582 } while (*s >= '0' && *s <= '9');
587 * Check for trailing garbage. As in json_lex(), any alphanumeric stuff
588 * here should be considered part of the token for error-reporting
591 for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
593 lex->token_terminator = p;
595 report_invalid_token(lex);
599 * Report a parse error.
601 * lex->token_start and lex->token_terminator must identify the current token.
604 report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
609 /* Handle case where the input ended prematurely. */
610 if (lex->token_start == NULL)
612 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
613 errmsg("invalid input syntax for type json"),
614 errdetail("The input string ended unexpectedly."),
615 report_json_context(lex)));
617 /* Separate out the current token. */
618 toklen = lex->token_terminator - lex->token_start;
619 token = palloc(toklen + 1);
620 memcpy(token, lex->token_start, toklen);
621 token[toklen] = '\0';
623 /* Complain, with the appropriate detail message. */
626 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
627 errmsg("invalid input syntax for type json"),
628 errdetail("Expected end of input, but found \"%s\".",
630 report_json_context(lex)));
633 switch (stack->state)
635 case JSON_PARSE_VALUE:
637 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
638 errmsg("invalid input syntax for type json"),
639 errdetail("Expected JSON value, but found \"%s\".",
641 report_json_context(lex)));
643 case JSON_PARSE_ARRAY_START:
645 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
646 errmsg("invalid input syntax for type json"),
647 errdetail("Expected array element or \"]\", but found \"%s\".",
649 report_json_context(lex)));
651 case JSON_PARSE_ARRAY_NEXT:
653 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
654 errmsg("invalid input syntax for type json"),
655 errdetail("Expected \",\" or \"]\", but found \"%s\".",
657 report_json_context(lex)));
659 case JSON_PARSE_OBJECT_START:
661 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
662 errmsg("invalid input syntax for type json"),
663 errdetail("Expected string or \"}\", but found \"%s\".",
665 report_json_context(lex)));
667 case JSON_PARSE_OBJECT_LABEL:
669 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
670 errmsg("invalid input syntax for type json"),
671 errdetail("Expected \":\", but found \"%s\".",
673 report_json_context(lex)));
675 case JSON_PARSE_OBJECT_NEXT:
677 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
678 errmsg("invalid input syntax for type json"),
679 errdetail("Expected \",\" or \"}\", but found \"%s\".",
681 report_json_context(lex)));
683 case JSON_PARSE_OBJECT_COMMA:
685 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
686 errmsg("invalid input syntax for type json"),
687 errdetail("Expected string, but found \"%s\".",
689 report_json_context(lex)));
692 elog(ERROR, "unexpected json parse state: %d",
699 * Report an invalid input token.
701 * lex->token_start and lex->token_terminator must identify the token.
704 report_invalid_token(JsonLexContext *lex)
709 /* Separate out the offending token. */
710 toklen = lex->token_terminator - lex->token_start;
711 token = palloc(toklen + 1);
712 memcpy(token, lex->token_start, toklen);
713 token[toklen] = '\0';
716 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
717 errmsg("invalid input syntax for type json"),
718 errdetail("Token \"%s\" is invalid.", token),
719 report_json_context(lex)));
723 * Report a CONTEXT line for bogus JSON input.
725 * lex->token_terminator must be set to identify the spot where we detected
726 * the error. Note that lex->token_start might be NULL, in case we recognized
729 * The return value isn't meaningful, but we make it non-void so that this
730 * can be invoked inside ereport().
733 report_json_context(JsonLexContext *lex)
735 const char *context_start;
736 const char *context_end;
737 const char *line_start;
744 /* Choose boundaries for the part of the input we will display */
745 context_start = lex->input;
746 context_end = lex->token_terminator;
747 line_start = context_start;
751 /* Always advance over newlines (context_end test is just paranoia) */
752 if (*context_start == '\n' && context_start < context_end)
755 line_start = context_start;
759 /* Otherwise, done as soon as we are close enough to context_end */
760 if (context_end - context_start < 50)
762 /* Advance to next multibyte character */
763 if (IS_HIGHBIT_SET(*context_start))
764 context_start += pg_mblen(context_start);
770 * We add "..." to indicate that the excerpt doesn't start at the
771 * beginning of the line ... but if we're within 3 characters of the
772 * beginning of the line, we might as well just show the whole line.
774 if (context_start - line_start <= 3)
775 context_start = line_start;
777 /* Get a null-terminated copy of the data to present */
778 ctxtlen = context_end - context_start;
779 ctxt = palloc(ctxtlen + 1);
780 memcpy(ctxt, context_start, ctxtlen);
781 ctxt[ctxtlen] = '\0';
784 * Show the context, prefixing "..." if not starting at start of line, and
785 * suffixing "..." if not ending at end of line.
787 prefix = (context_start > line_start) ? "..." : "";
788 suffix = (*context_end != '\0' && *context_end != '\n' && *context_end != '\r') ? "..." : "";
790 return errcontext("JSON data, line %d: %s%s%s",
791 line_number, prefix, ctxt, suffix);
795 * Extract a single, possibly multi-byte char from the input string.
798 extract_mb_char(char *s)
804 res = palloc(len + 1);
812 * Turn a scalar Datum into JSON, appending the string to "result".
814 * Hand off a non-scalar datum to composite_to_json or array_to_json_internal
818 datum_to_json(Datum val, bool is_null, StringInfo result,
819 TYPCATEGORY tcategory, Oid typoutputfunc)
825 appendStringInfoString(result, "null");
831 case TYPCATEGORY_ARRAY:
832 array_to_json_internal(val, result, false);
834 case TYPCATEGORY_COMPOSITE:
835 composite_to_json(val, result, false);
837 case TYPCATEGORY_BOOLEAN:
838 if (DatumGetBool(val))
839 appendStringInfoString(result, "true");
841 appendStringInfoString(result, "false");
843 case TYPCATEGORY_NUMERIC:
844 outputstr = OidOutputFunctionCall(typoutputfunc, val);
847 * Don't call escape_json here if it's a valid JSON number.
848 * Numeric output should usually be a valid JSON number and JSON
849 * numbers shouldn't be quoted. Quote cases like "Nan" and
850 * "Infinity", however.
852 if (strpbrk(outputstr, NON_NUMERIC_LETTER) == NULL)
853 appendStringInfoString(result, outputstr);
855 escape_json(result, outputstr);
858 case TYPCATEGORY_JSON:
859 /* JSON will already be escaped */
860 outputstr = OidOutputFunctionCall(typoutputfunc, val);
861 appendStringInfoString(result, outputstr);
865 outputstr = OidOutputFunctionCall(typoutputfunc, val);
866 escape_json(result, outputstr);
873 * Process a single dimension of an array.
874 * If it's the innermost dimension, output the values, otherwise call
875 * ourselves recursively to process the next dimension.
878 array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
879 bool *nulls, int *valcount, TYPCATEGORY tcategory,
880 Oid typoutputfunc, bool use_line_feeds)
887 sep = use_line_feeds ? ",\n " : ",";
889 appendStringInfoChar(result, '[');
891 for (i = 1; i <= dims[dim]; i++)
894 appendStringInfoString(result, sep);
896 if (dim + 1 == ndims)
898 datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
905 * Do we want line feeds on inner dimensions of arrays? For now
908 array_dim_to_json(result, dim + 1, ndims, dims, vals, nulls,
909 valcount, tcategory, typoutputfunc, false);
913 appendStringInfoChar(result, ']');
917 * Turn an array into JSON.
920 array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
922 ArrayType *v = DatumGetArrayTypeP(array);
923 Oid element_type = ARR_ELEMTYPE(v);
936 TYPCATEGORY tcategory;
940 nitems = ArrayGetNItems(ndim, dim);
944 appendStringInfoString(result, "[]");
948 get_type_io_data(element_type, IOFunc_output,
949 &typlen, &typbyval, &typalign,
950 &typdelim, &typioparam, &typoutputfunc);
952 deconstruct_array(v, element_type, typlen, typbyval,
953 typalign, &elements, &nulls,
956 if (element_type == RECORDOID)
957 tcategory = TYPCATEGORY_COMPOSITE;
958 else if (element_type == JSONOID)
959 tcategory = TYPCATEGORY_JSON;
961 tcategory = TypeCategory(element_type);
963 array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
964 typoutputfunc, use_line_feeds);
971 * Turn a composite / record into JSON.
974 composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
980 HeapTupleData tmptup,
983 bool needsep = false;
986 sep = use_line_feeds ? ",\n " : ",";
988 td = DatumGetHeapTupleHeader(composite);
990 /* Extract rowtype info and find a tupdesc */
991 tupType = HeapTupleHeaderGetTypeId(td);
992 tupTypmod = HeapTupleHeaderGetTypMod(td);
993 tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
995 /* Build a temporary HeapTuple control structure */
996 tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
1000 appendStringInfoChar(result, '{');
1002 for (i = 0; i < tupdesc->natts; i++)
1008 TYPCATEGORY tcategory;
1012 if (tupdesc->attrs[i]->attisdropped)
1016 appendStringInfoString(result, sep);
1019 attname = NameStr(tupdesc->attrs[i]->attname);
1020 escape_json(result, attname);
1021 appendStringInfoChar(result, ':');
1023 origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);
1025 if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
1026 tcategory = TYPCATEGORY_ARRAY;
1027 else if (tupdesc->attrs[i]->atttypid == RECORDOID)
1028 tcategory = TYPCATEGORY_COMPOSITE;
1029 else if (tupdesc->attrs[i]->atttypid == JSONOID)
1030 tcategory = TYPCATEGORY_JSON;
1032 tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
1034 getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
1035 &typoutput, &typisvarlena);
1038 * If we have a toasted datum, forcibly detoast it here to avoid
1039 * memory leakage inside the type's output routine.
1041 if (typisvarlena && !isnull)
1042 val = PointerGetDatum(PG_DETOAST_DATUM(origval));
1046 datum_to_json(val, isnull, result, tcategory, typoutput);
1048 /* Clean up detoasted copy, if any */
1050 pfree(DatumGetPointer(val));
1053 appendStringInfoChar(result, '}');
1054 ReleaseTupleDesc(tupdesc);
1058 * SQL function array_to_json(row)
1061 array_to_json(PG_FUNCTION_ARGS)
1063 Datum array = PG_GETARG_DATUM(0);
1066 result = makeStringInfo();
1068 array_to_json_internal(array, result, false);
1070 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1074 * SQL function array_to_json(row, prettybool)
1077 array_to_json_pretty(PG_FUNCTION_ARGS)
1079 Datum array = PG_GETARG_DATUM(0);
1080 bool use_line_feeds = PG_GETARG_BOOL(1);
1083 result = makeStringInfo();
1085 array_to_json_internal(array, result, use_line_feeds);
1087 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1091 * SQL function row_to_json(row)
1094 row_to_json(PG_FUNCTION_ARGS)
1096 Datum array = PG_GETARG_DATUM(0);
1099 result = makeStringInfo();
1101 composite_to_json(array, result, false);
1103 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1107 * SQL function row_to_json(row, prettybool)
1110 row_to_json_pretty(PG_FUNCTION_ARGS)
1112 Datum array = PG_GETARG_DATUM(0);
1113 bool use_line_feeds = PG_GETARG_BOOL(1);
1116 result = makeStringInfo();
1118 composite_to_json(array, result, use_line_feeds);
1120 PG_RETURN_TEXT_P(cstring_to_text(result->data));
1124 * Produce a JSON string literal, properly escaping characters in the text.
1127 escape_json(StringInfo buf, const char *str)
1131 appendStringInfoCharMacro(buf, '\"');
1132 for (p = str; *p; p++)
1137 appendStringInfoString(buf, "\\b");
1140 appendStringInfoString(buf, "\\f");
1143 appendStringInfoString(buf, "\\n");
1146 appendStringInfoString(buf, "\\r");
1149 appendStringInfoString(buf, "\\t");
1152 appendStringInfoString(buf, "\\\"");
1155 appendStringInfoString(buf, "\\\\");
1158 if ((unsigned char) *p < ' ')
1159 appendStringInfo(buf, "\\u%04x", (int) *p);
1161 appendStringInfoCharMacro(buf, *p);
1165 appendStringInfoCharMacro(buf, '\"');