]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/json.c
Correctly handle NULLs in JSON output.
[postgresql] / src / backend / utils / adt / json.c
1 /*-------------------------------------------------------------------------
2  *
3  * json.c
4  *              JSON data type support.
5  *
6  * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *        src/backend/utils/adt/json.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "catalog/pg_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"
27
28 typedef enum
29 {
30         JSON_VALUE_INVALID,
31         JSON_VALUE_STRING,
32         JSON_VALUE_NUMBER,
33         JSON_VALUE_OBJECT,
34         JSON_VALUE_ARRAY,
35         JSON_VALUE_TRUE,
36         JSON_VALUE_FALSE,
37         JSON_VALUE_NULL
38 } JsonValueType;
39
40 typedef struct
41 {
42         char       *input;
43         char       *token_start;
44         char       *token_terminator;
45         JsonValueType   token_type;
46         int                     line_number;
47         char       *line_start;
48 } JsonLexContext;
49
50 typedef enum
51 {
52         JSON_PARSE_VALUE,                       /* expecting a value */
53         JSON_PARSE_ARRAY_START,         /* saw '[', expecting value or ']' */
54         JSON_PARSE_ARRAY_NEXT,          /* saw array element, expecting ',' or ']' */
55         JSON_PARSE_OBJECT_START,        /* saw '{', expecting label or '}' */
56         JSON_PARSE_OBJECT_LABEL,        /* saw object label, expecting ':' */
57         JSON_PARSE_OBJECT_NEXT,         /* saw object value, expecting ',' or '}' */
58         JSON_PARSE_OBJECT_COMMA         /* saw object ',', expecting next label */
59 } JsonParseState;
60
61 typedef struct JsonParseStack
62 {
63         JsonParseState  state;
64 } JsonParseStack;
65
66 typedef enum
67 {
68         JSON_STACKOP_NONE,
69         JSON_STACKOP_PUSH,
70         JSON_STACKOP_PUSH_WITH_PUSHBACK,
71         JSON_STACKOP_POP
72 } JsonStackOp;
73
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 char *extract_mb_char(char *s);
81 static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds);
82 static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
83                                                           Datum *vals, bool *nulls, int *valcount, 
84                                                           TYPCATEGORY tcategory, Oid typoutputfunc, 
85                                                           bool use_line_feeds);
86 static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds);
87
88 /* fake type category for JSON so we can distinguish it in datum_to_json */
89 #define TYPCATEGORY_JSON 'j'
90 /* letters appearing in numeric output that aren't valid in a JSON number */
91 #define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
92 /*
93  * Input.
94  */
95 Datum
96 json_in(PG_FUNCTION_ARGS)
97 {
98         char    *text = PG_GETARG_CSTRING(0);
99
100         json_validate_cstring(text);
101
102         PG_RETURN_TEXT_P(cstring_to_text(text));
103 }
104
105 /*
106  * Output.
107  */
108 Datum
109 json_out(PG_FUNCTION_ARGS)
110 {
111         Datum   txt = PG_GETARG_DATUM(0);
112
113         PG_RETURN_CSTRING(TextDatumGetCString(txt));
114 }
115
116 /*
117  * Binary send.
118  */
119 Datum
120 json_send(PG_FUNCTION_ARGS)
121 {
122         StringInfoData buf;
123         text   *t = PG_GETARG_TEXT_PP(0);
124
125         pq_begintypsend(&buf);
126         pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
127         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
128 }
129
130 /*
131  * Binary receive.
132  */
133 Datum
134 json_recv(PG_FUNCTION_ARGS)
135 {
136         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
137         text       *result;
138         char       *str;
139         int                     nbytes;
140
141         str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
142
143         /*
144          * We need a null-terminated string to pass to json_validate_cstring().
145          * Rather than make a separate copy, make the temporary result one byte
146          * bigger than it needs to be.
147          */
148         result = palloc(nbytes + 1 + VARHDRSZ);
149         SET_VARSIZE(result, nbytes + VARHDRSZ);
150         memcpy(VARDATA(result), str, nbytes);
151         str = VARDATA(result);
152         str[nbytes] = '\0';
153
154         /* Validate it. */
155         json_validate_cstring(str);
156
157         PG_RETURN_TEXT_P(result);
158 }
159
160 /*
161  * Check whether supplied input is valid JSON.
162  */
163 static void
164 json_validate_cstring(char *input)
165 {
166         JsonLexContext  lex;
167         JsonParseStack *stack,
168                                    *stacktop;
169         int                             stacksize;
170
171         /* Set up lexing context. */
172         lex.input = input;
173         lex.token_terminator = lex.input;
174         lex.line_number = 1;
175         lex.line_start = input;
176
177         /* Set up parse stack. */
178         stacksize = 32;
179         stacktop = palloc(sizeof(JsonParseStack) * stacksize);
180         stack = stacktop;
181         stack->state = JSON_PARSE_VALUE;
182
183         /* Main parsing loop. */
184         for (;;)
185         {
186                 JsonStackOp     op;
187
188                 /* Fetch next token. */
189                 json_lex(&lex);
190
191                 /* Check for unexpected end of input. */
192                 if (lex.token_start == NULL)
193                         report_parse_error(stack, &lex);
194
195 redo:
196                 /* Figure out what to do with this token. */
197                 op = JSON_STACKOP_NONE;
198                 switch (stack->state)
199                 {
200                         case JSON_PARSE_VALUE:
201                                 if (lex.token_type != JSON_VALUE_INVALID)
202                                         op = JSON_STACKOP_POP;
203                                 else if (lex.token_start[0] == '[')
204                                         stack->state = JSON_PARSE_ARRAY_START;
205                                 else if (lex.token_start[0] == '{')
206                                         stack->state = JSON_PARSE_OBJECT_START;
207                                 else
208                                         report_parse_error(stack, &lex);
209                                 break;
210                         case JSON_PARSE_ARRAY_START:
211                                 if (lex.token_type != JSON_VALUE_INVALID)
212                                         stack->state = JSON_PARSE_ARRAY_NEXT;
213                                 else if (lex.token_start[0] == ']')
214                                         op = JSON_STACKOP_POP;
215                                 else if (lex.token_start[0] == '['
216                                         || lex.token_start[0] == '{')
217                                 {
218                                         stack->state = JSON_PARSE_ARRAY_NEXT;
219                                         op = JSON_STACKOP_PUSH_WITH_PUSHBACK;
220                                 }
221                                 else
222                                         report_parse_error(stack, &lex);
223                                 break;
224                         case JSON_PARSE_ARRAY_NEXT:
225                                 if (lex.token_type != JSON_VALUE_INVALID)
226                                         report_parse_error(stack, &lex);
227                                 else if (lex.token_start[0] == ']')
228                                         op = JSON_STACKOP_POP;
229                                 else if (lex.token_start[0] == ',')
230                                         op = JSON_STACKOP_PUSH;
231                                 else
232                                         report_parse_error(stack, &lex);
233                                 break;
234                         case JSON_PARSE_OBJECT_START:
235                                 if (lex.token_type == JSON_VALUE_STRING)
236                                         stack->state = JSON_PARSE_OBJECT_LABEL;
237                                 else if (lex.token_type == JSON_VALUE_INVALID
238                                         && lex.token_start[0] == '}')
239                                         op = JSON_STACKOP_POP;
240                                 else
241                                         report_parse_error(stack, &lex);
242                                 break;
243                         case JSON_PARSE_OBJECT_LABEL:
244                                 if (lex.token_type == JSON_VALUE_INVALID
245                                         && lex.token_start[0] == ':')
246                                 {
247                                         stack->state = JSON_PARSE_OBJECT_NEXT;
248                                         op = JSON_STACKOP_PUSH;
249                                 }
250                                 else
251                                         report_parse_error(stack, &lex);
252                                 break;
253                         case JSON_PARSE_OBJECT_NEXT:
254                                 if (lex.token_type != JSON_VALUE_INVALID)
255                                         report_parse_error(stack, &lex);
256                                 else if (lex.token_start[0] == '}')
257                                         op = JSON_STACKOP_POP;
258                                 else if (lex.token_start[0] == ',')
259                                         stack->state = JSON_PARSE_OBJECT_COMMA;
260                                 else
261                                         report_parse_error(stack, &lex);
262                                 break;
263                         case JSON_PARSE_OBJECT_COMMA:
264                                 if (lex.token_type == JSON_VALUE_STRING)
265                                         stack->state = JSON_PARSE_OBJECT_LABEL;
266                                 else
267                                         report_parse_error(stack, &lex);
268                                 break;
269                         default:
270                                 elog(ERROR, "unexpected json parse state: %d",
271                                                 (int) stack->state);
272                 }
273
274                 /* Push or pop the stack, if needed. */
275                 switch (op)
276                 {
277                         case JSON_STACKOP_PUSH:
278                         case JSON_STACKOP_PUSH_WITH_PUSHBACK:
279                                 ++stack;
280                                 if (stack >= &stacktop[stacksize])
281                                 {
282                                         int             stackoffset = stack - stacktop;
283                                         stacksize = stacksize + 32;
284                                         stacktop = repalloc(stacktop,
285                                                                                 sizeof(JsonParseStack) * stacksize);
286                                         stack = stacktop + stackoffset;
287                                 }
288                                 stack->state = JSON_PARSE_VALUE;
289                                 if (op == JSON_STACKOP_PUSH_WITH_PUSHBACK)
290                                         goto redo;
291                                 break;
292                         case JSON_STACKOP_POP:
293                                 if (stack == stacktop)
294                                 {
295                                         /* Expect end of input. */
296                                         json_lex(&lex);
297                                         if (lex.token_start != NULL)
298                                                 report_parse_error(NULL, &lex);
299                                         return;
300                                 }
301                                 --stack;
302                                 break;
303                         case JSON_STACKOP_NONE:
304                                 /* nothing to do */
305                                 break;
306                 }
307         }
308 }
309
310 /*
311  * Lex one token from the input stream.
312  */
313 static void
314 json_lex(JsonLexContext *lex)
315 {
316         char       *s;
317
318         /* Skip leading whitespace. */
319         s = lex->token_terminator;
320         while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
321         {
322                 if (*s == '\n')
323                         ++lex->line_number;
324                 ++s;
325         }
326         lex->token_start = s;
327
328         /* Determine token type. */
329         if (strchr("{}[],:", s[0]))
330         {
331                 /* strchr() doesn't return false on a NUL input. */
332                 if (s[0] == '\0')
333                 {
334                         /* End of string. */
335                         lex->token_start = NULL;
336                         lex->token_terminator = NULL;
337                 }
338                 else
339                 {
340                         /* Single-character token, some kind of punctuation mark. */
341                         lex->token_terminator = s + 1;
342                 }
343                 lex->token_type = JSON_VALUE_INVALID;
344         }
345         else if (*s == '"')
346         {
347                 /* String. */
348                 json_lex_string(lex);
349                 lex->token_type = JSON_VALUE_STRING;
350         }
351         else if (*s == '-')
352         {
353                 /* Negative number. */
354                 json_lex_number(lex, s + 1);
355                 lex->token_type = JSON_VALUE_NUMBER;
356         }
357         else if (*s >= '0' && *s <= '9')
358         {
359                 /* Positive number. */
360                 json_lex_number(lex, s);
361                 lex->token_type = JSON_VALUE_NUMBER;
362         }
363         else
364         {
365                 char   *p;
366
367                 /*
368                  * We're not dealing with a string, number, legal punctuation mark,
369                  * or end of string.  The only legal tokens we might find here are
370                  * true, false, and null, but for error reporting purposes we scan
371                  * until we see a non-alphanumeric character.  That way, we can report
372                  * the whole word as an unexpected token, rather than just some
373                  * unintuitive prefix thereof.
374                  */
375                 for (p = s; (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')
376                         || (*p >= '0' && *p <= '9') || *p == '_' || IS_HIGHBIT_SET(*p);
377                         ++p)
378                         ;
379
380                 /*
381                  * We got some sort of unexpected punctuation or an otherwise
382                  * unexpected character, so just complain about that one character.
383                  */
384                 if (p == s)
385                 {
386                         lex->token_terminator = s + 1;
387                         report_invalid_token(lex);
388                 }
389
390                 /*
391                  * We've got a real alphanumeric token here.  If it happens to be
392                  * true, false, or null, all is well.  If not, error out.
393                  */
394                 lex->token_terminator = p;
395                 if (p - s == 4)
396                 {
397                         if (memcmp(s, "true", 4) == 0)
398                                 lex->token_type = JSON_VALUE_TRUE;
399                         else if (memcmp(s, "null", 4) == 0)
400                                 lex->token_type = JSON_VALUE_NULL;
401                         else
402                                 report_invalid_token(lex);
403                 }
404                 else if (p - s == 5 && memcmp(s, "false", 5) == 0)
405                         lex->token_type = JSON_VALUE_FALSE;
406                 else
407                         report_invalid_token(lex);
408         }
409 }
410
411 /*
412  * The next token in the input stream is known to be a string; lex it.
413  */
414 static void
415 json_lex_string(JsonLexContext *lex)
416 {
417         char       *s = lex->token_start + 1;
418
419         for (s = lex->token_start + 1; *s != '"'; ++s)
420         {
421                 /* Per RFC4627, these characters MUST be escaped. */
422                 if (*s < 32)
423                 {
424                         /* A NUL byte marks the (premature) end of the string. */
425                         if (*s == '\0')
426                         {
427                                 lex->token_terminator = s;
428                                 report_invalid_token(lex);
429                         }
430                         ereport(ERROR,
431                                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
432                                          errmsg("invalid input syntax for type json"),
433                                          errdetail_internal("line %d: Character \"%c\" must be escaped.",
434                                                 lex->line_number, *s)));
435                 }
436                 else if (*s == '\\')
437                 {
438                         /* OK, we have an escape character. */
439                         ++s;
440                         if (*s == '\0')
441                         {
442                                 lex->token_terminator = s;
443                                 report_invalid_token(lex);
444                         }
445                         else if (*s == 'u')
446                         {
447                                 int             i;
448                                 int             ch = 0;
449
450                                 for (i = 1; i <= 4; ++i)
451                                 {
452                                         if (s[i] == '\0')
453                                         {
454                                                 lex->token_terminator = s + i;
455                                                 report_invalid_token(lex);
456                                         }
457                                         else if (s[i] >= '0' && s[i] <= '9')
458                                                 ch = (ch * 16) + (s[i] - '0');
459                                         else if (s[i] >= 'a' && s[i] <= 'f')
460                                                 ch = (ch * 16) + (s[i] - 'a') + 10;
461                                         else if (s[i] >= 'A' && s[i] <= 'F')
462                                                 ch = (ch * 16) + (s[i] - 'A') + 10;
463                                         else
464                                         {
465                                                 ereport(ERROR,
466                                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
467                                                                  errmsg("invalid input syntax for type json"),
468                                                                  errdetail_internal("line %d: \"\\u\" must be followed by four hexadecimal digits.",
469                                                                         lex->line_number)));
470                                         }
471                                 }
472
473                                 /* Account for the four additional bytes we just parsed. */
474                                 s += 4;
475                         }
476                         else if (!strchr("\"\\/bfnrt", *s))
477                         {
478                                 /* Error out. */
479                                 ereport(ERROR,
480                                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
481                                                  errmsg("invalid input syntax for type json"),
482                                                  errdetail_internal("line %d: Invalid escape \"\\%s\".",
483                                                         lex->line_number, extract_mb_char(s))));
484                         }
485                 }
486         }
487
488         /* Hooray, we found the end of the string! */
489         lex->token_terminator = s + 1;
490 }
491
492 /*-------------------------------------------------------------------------
493  * The next token in the input stream is known to be a number; lex it.
494  *
495  * In JSON, a number consists of four parts:
496  *
497  * (1) An optional minus sign ('-').
498  *
499  * (2) Either a single '0', or a string of one or more digits that does not
500  *     begin with a '0'.
501  *
502  * (3) An optional decimal part, consisting of a period ('.') followed by
503  *     one or more digits.  (Note: While this part can be omitted
504  *     completely, it's not OK to have only the decimal point without
505  *     any digits afterwards.)
506  *
507  * (4) An optional exponent part, consisting of 'e' or 'E', optionally
508  *     followed by '+' or '-', followed by one or more digits.  (Note:
509  *     As with the decimal part, if 'e' or 'E' is present, it must be
510  *     followed by at least one digit.)
511  *
512  * The 's' argument to this function points to the ostensible beginning
513  * of part 2 - i.e. the character after any optional minus sign, and the
514  * first character of the string if there is none.
515  *
516  *-------------------------------------------------------------------------
517  */
518 static void
519 json_lex_number(JsonLexContext *lex, char *s)
520 {
521         bool    error = false;
522         char   *p;
523
524         /* Part (1): leading sign indicator. */
525         /* Caller already did this for us; so do nothing. */
526
527         /* Part (2): parse main digit string. */
528         if (*s == '0')
529                 ++s;
530         else if (*s >= '1' && *s <= '9')
531         {
532                 do
533                 {
534                         ++s;
535                 } while (*s >= '0' && *s <= '9');
536         }
537         else
538                 error = true;
539
540         /* Part (3): parse optional decimal portion. */
541         if (*s == '.')
542         {
543                 ++s;
544                 if (*s < '0' && *s > '9')
545                         error = true;
546                 else
547                 {
548                         do
549                         {
550                                 ++s;
551                         } while (*s >= '0' && *s <= '9');
552                 }
553         }
554
555         /* Part (4): parse optional exponent. */
556         if (*s == 'e' || *s == 'E')
557         {
558                 ++s;
559                 if (*s == '+' || *s == '-')
560                         ++s;
561                 if (*s < '0' && *s > '9')
562                         error = true;
563                 else
564                 {
565                         do
566                         {
567                                 ++s;
568                         } while (*s >= '0' && *s <= '9');
569                 }
570         }
571
572         /* Check for trailing garbage. */
573         for (p = s; (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')
574                 || (*p >= '0' && *p <= '9') || *p == '_' || IS_HIGHBIT_SET(*p); ++p)
575                 ;
576         lex->token_terminator = p;
577         if (p > s || error)
578                 report_invalid_token(lex);
579 }
580
581 /*
582  * Report a parse error.
583  */
584 static void
585 report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
586 {
587         char   *detail = NULL;
588         char   *token = NULL;
589         int             toklen;
590
591         /* Handle case where the input ended prematurely. */
592         if (lex->token_start == NULL)
593                 ereport(ERROR,
594                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
595                                  errmsg("invalid input syntax for type json: \"%s\"",
596                                         lex->input),
597                                  errdetail_internal("The input string ended unexpectedly.")));
598
599         /* Work out the offending token. */
600         toklen = lex->token_terminator - lex->token_start;
601         token = palloc(toklen + 1);
602         memcpy(token, lex->token_start, toklen);
603         token[toklen] = '\0';
604
605         /* Select correct detail message. */
606         if (stack == NULL)
607                 detail = "line %d: Expected end of input, but found \"%s\".";
608         else
609         {
610                 switch (stack->state)
611                 {
612                         case JSON_PARSE_VALUE:
613                                 detail = "line %d: Expected string, number, object, array, true, false, or null, but found \"%s\".";
614                                 break;
615                         case JSON_PARSE_ARRAY_START:
616                                 detail = "line %d: Expected array element or \"]\", but found \"%s\".";
617                                 break;
618                         case JSON_PARSE_ARRAY_NEXT:
619                                 detail = "line %d: Expected \",\" or \"]\", but found \"%s\".";
620                                 break;
621                         case JSON_PARSE_OBJECT_START:
622                                 detail = "line %d: Expected string or \"}\", but found \"%s\".";
623                                 break;
624                         case JSON_PARSE_OBJECT_LABEL:
625                                 detail = "line %d: Expected \":\", but found \"%s\".";
626                                 break;
627                         case JSON_PARSE_OBJECT_NEXT:
628                                 detail = "line %d: Expected \",\" or \"}\", but found \"%s\".";
629                                 break;
630                         case JSON_PARSE_OBJECT_COMMA:
631                                 detail = "line %d: Expected string, but found \"%s\".";
632                                 break;
633                 }
634         }
635
636         ereport(ERROR,
637                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
638                          errmsg("invalid input syntax for type json: \"%s\"",
639                                 lex->input),
640                          errdetail_internal(detail, lex->line_number, token)));
641 }
642
643 /*
644  * Report an invalid input token.
645  */
646 static void
647 report_invalid_token(JsonLexContext *lex)
648 {
649         char   *token;
650         int             toklen;
651
652         toklen = lex->token_terminator - lex->token_start;
653         token = palloc(toklen + 1);
654         memcpy(token, lex->token_start, toklen);
655         token[toklen] = '\0';
656
657         ereport(ERROR,
658                         (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
659                          errmsg("invalid input syntax for type json"),
660                          errdetail_internal("line %d: Token \"%s\" is invalid.",
661                                 lex->line_number, token)));
662 }
663
664 /*
665  * Extract a single, possibly multi-byte char from the input string.
666  */
667 static char *
668 extract_mb_char(char *s)
669 {
670         char   *res;
671         int             len;
672
673         len = pg_mblen(s);
674         res = palloc(len + 1);
675         memcpy(res, s, len);
676         res[len] = '\0';
677
678         return res;
679 }
680
681 /*
682  * Turn a scalar Datum into JSON. Hand off a non-scalar datum to
683  * composite_to_json or array_to_json_internal as appropriate.
684  */
685 static inline void
686 datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory,
687                           Oid typoutputfunc)
688 {
689
690         char *outputstr;
691
692         if (is_null)
693         {
694                 appendStringInfoString(result,"null");
695                 return;
696         }
697
698         switch (tcategory)
699         {
700                 case TYPCATEGORY_ARRAY:
701                         array_to_json_internal(val, result, false);
702                         break;
703                 case TYPCATEGORY_COMPOSITE:
704                         composite_to_json(val, result, false);
705                         break;
706                 case TYPCATEGORY_BOOLEAN:
707                         if (DatumGetBool(val))
708                                 appendStringInfoString(result,"true");
709                         else
710                                 appendStringInfoString(result,"false");
711                         break;
712                 case TYPCATEGORY_NUMERIC:
713                         outputstr = OidOutputFunctionCall(typoutputfunc, val);
714                         /*
715                          * Don't call escape_json here if it's a valid JSON
716                          * number. Numeric output should usually be a valid 
717                          * JSON number and JSON numbers shouldn't be quoted. 
718                          * Quote cases like "Nan" and "Infinity", however.
719                          */
720                         if (strpbrk(outputstr,NON_NUMERIC_LETTER) == NULL)
721                                 appendStringInfoString(result, outputstr);
722                         else
723                                 escape_json(result, outputstr);
724                         pfree(outputstr);
725                         break;
726                 case TYPCATEGORY_JSON:
727                         /* JSON will already be escaped */
728                         outputstr = OidOutputFunctionCall(typoutputfunc, val);
729                         appendStringInfoString(result, outputstr);
730                         pfree(outputstr);
731                         break;
732                 default:
733                         outputstr = OidOutputFunctionCall(typoutputfunc, val);
734                         escape_json(result, outputstr);
735                         pfree(outputstr);
736         }
737 }
738
739 /*
740  * Process a single dimension of an array.
741  * If it's the innermost dimension, output the values, otherwise call
742  * ourselves recursively to process the next dimension.
743  */
744 static void
745 array_dim_to_json(StringInfo result, int dim, int ndims,int * dims, Datum *vals,
746                                   bool *nulls, int * valcount, TYPCATEGORY tcategory, 
747                                   Oid typoutputfunc, bool use_line_feeds)
748 {
749
750         int i;
751         char *sep;
752
753         Assert(dim < ndims);
754
755         sep = use_line_feeds ? ",\n " : ",";
756
757         appendStringInfoChar(result, '[');
758
759         for (i = 1; i <= dims[dim]; i++)
760         {
761                 if (i > 1)
762                         appendStringInfoString(result,sep);
763
764                 if (dim + 1 == ndims)
765                 {
766                         datum_to_json(vals[*valcount], nulls[*valcount], result, tcategory,
767                                                   typoutputfunc);
768                         (*valcount)++;
769                 }
770                 else
771                 {
772                         /*
773                          * Do we want line feeds on inner dimensions of arrays?
774                          * For now we'll say no.
775                          */
776                         array_dim_to_json(result, dim+1, ndims, dims, vals, nulls,
777                                                           valcount, tcategory, typoutputfunc, false);
778                 }
779         }
780
781         appendStringInfoChar(result, ']');
782 }
783
784 /*
785  * Turn an array into JSON.
786  */
787 static void
788 array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
789 {
790         ArrayType  *v = DatumGetArrayTypeP(array);
791         Oid                     element_type = ARR_ELEMTYPE(v);
792         int                *dim;
793         int                     ndim;
794         int                     nitems;
795         int         count = 0;
796         Datum      *elements;
797         bool       *nulls;
798
799         int16           typlen;
800         bool            typbyval;
801         char            typalign,
802                                 typdelim;
803         Oid                     typioparam;
804         Oid                     typoutputfunc;
805         TYPCATEGORY tcategory;
806
807         ndim = ARR_NDIM(v);
808         dim = ARR_DIMS(v);
809         nitems = ArrayGetNItems(ndim, dim);
810
811         if (nitems <= 0)
812         {
813                 appendStringInfoString(result,"[]");
814                 return;
815         }
816
817         get_type_io_data(element_type, IOFunc_output,
818                                          &typlen, &typbyval, &typalign,
819                                          &typdelim, &typioparam, &typoutputfunc);
820
821         deconstruct_array(v, element_type, typlen, typbyval,
822                                           typalign, &elements, &nulls,
823                                           &nitems);
824
825         if (element_type == RECORDOID)
826                 tcategory = TYPCATEGORY_COMPOSITE;
827         else if (element_type == JSONOID)
828                 tcategory = TYPCATEGORY_JSON;
829         else
830                 tcategory = TypeCategory(element_type);
831
832         array_dim_to_json(result, 0, ndim, dim, elements, nulls, &count, tcategory,
833                                           typoutputfunc, use_line_feeds);
834
835         pfree(elements);
836         pfree(nulls);
837 }
838
839 /*
840  * Turn a composite / record into JSON.
841  */
842 static void
843 composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
844 {
845     HeapTupleHeader td;
846     Oid         tupType;
847     int32       tupTypmod;
848     TupleDesc   tupdesc;
849     HeapTupleData tmptup, *tuple;
850         int         i;
851         bool        needsep = false;
852         char       *sep;
853
854         sep = use_line_feeds ? ",\n " : ",";
855
856     td = DatumGetHeapTupleHeader(composite);
857
858     /* Extract rowtype info and find a tupdesc */
859     tupType = HeapTupleHeaderGetTypeId(td);
860     tupTypmod = HeapTupleHeaderGetTypMod(td);
861     tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
862
863     /* Build a temporary HeapTuple control structure */
864     tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
865     tmptup.t_data = td;
866         tuple = &tmptup;
867
868         appendStringInfoChar(result,'{');
869
870     for (i = 0; i < tupdesc->natts; i++)
871     {
872         Datum       val, origval;
873         bool        isnull;
874         char       *attname;
875                 TYPCATEGORY tcategory;
876                 Oid                     typoutput;
877                 bool            typisvarlena;
878
879                 if (tupdesc->attrs[i]->attisdropped)
880             continue;
881
882                 if (needsep)
883                         appendStringInfoString(result,sep);
884                 needsep = true;
885
886         attname = NameStr(tupdesc->attrs[i]->attname);
887                 escape_json(result,attname);
888                 appendStringInfoChar(result,':');
889
890         origval = heap_getattr(tuple, i + 1, tupdesc, &isnull);
891
892                 if (tupdesc->attrs[i]->atttypid == RECORDARRAYOID)
893                         tcategory = TYPCATEGORY_ARRAY;
894                 else if (tupdesc->attrs[i]->atttypid == RECORDOID)
895                         tcategory = TYPCATEGORY_COMPOSITE;
896                 else if (tupdesc->attrs[i]->atttypid == JSONOID)
897                         tcategory = TYPCATEGORY_JSON;
898                 else
899                         tcategory = TypeCategory(tupdesc->attrs[i]->atttypid);
900
901                 getTypeOutputInfo(tupdesc->attrs[i]->atttypid,
902                                                   &typoutput, &typisvarlena);
903
904                 /*
905                  * If we have a toasted datum, forcibly detoast it here to avoid memory
906                  * leakage inside the type's output routine.
907                  */
908                 if (typisvarlena && ! isnull)
909                         val = PointerGetDatum(PG_DETOAST_DATUM(origval));
910                 else
911                         val = origval;
912
913                 datum_to_json(val, isnull, result, tcategory, typoutput);
914
915                 /* Clean up detoasted copy, if any */
916                 if (val != origval)
917                         pfree(DatumGetPointer(val));
918         }
919
920         appendStringInfoChar(result,'}');
921     ReleaseTupleDesc(tupdesc);
922 }
923
924 /*
925  * SQL function array_to_json(row)
926  */
927 extern Datum
928 array_to_json(PG_FUNCTION_ARGS)
929 {
930         Datum    array = PG_GETARG_DATUM(0);
931         StringInfo      result;
932
933         result = makeStringInfo();
934
935         array_to_json_internal(array, result, false);
936
937         PG_RETURN_TEXT_P(cstring_to_text(result->data));
938 };
939
940 /*
941  * SQL function array_to_json(row, prettybool)
942  */
943 extern Datum
944 array_to_json_pretty(PG_FUNCTION_ARGS)
945 {
946         Datum    array = PG_GETARG_DATUM(0);
947         bool     use_line_feeds = PG_GETARG_BOOL(1);
948         StringInfo      result;
949
950         result = makeStringInfo();
951
952         array_to_json_internal(array, result, use_line_feeds);
953
954         PG_RETURN_TEXT_P(cstring_to_text(result->data));
955 };
956
957 /*
958  * SQL function row_to_json(row)
959  */
960 extern Datum
961 row_to_json(PG_FUNCTION_ARGS)
962 {
963         Datum    array = PG_GETARG_DATUM(0);
964         StringInfo      result;
965
966         result = makeStringInfo();
967
968         composite_to_json(array, result, false);
969
970         PG_RETURN_TEXT_P(cstring_to_text(result->data));
971 };
972
973 /*
974  * SQL function row_to_json(row, prettybool)
975  */
976 extern Datum
977 row_to_json_pretty(PG_FUNCTION_ARGS)
978 {
979         Datum    array = PG_GETARG_DATUM(0);
980         bool     use_line_feeds = PG_GETARG_BOOL(1);
981         StringInfo      result;
982
983         result = makeStringInfo();
984
985         composite_to_json(array, result, use_line_feeds);
986
987         PG_RETURN_TEXT_P(cstring_to_text(result->data));
988 };
989
990 /*
991  * Produce a JSON string literal, properly escaping characters in the text.
992  */
993 void
994 escape_json(StringInfo buf, const char *str)
995 {
996         const char *p;
997
998         appendStringInfoCharMacro(buf, '\"');
999         for (p = str; *p; p++)
1000         {
1001                 switch (*p)
1002                 {
1003                         case '\b':
1004                                 appendStringInfoString(buf, "\\b");
1005                                 break;
1006                         case '\f':
1007                                 appendStringInfoString(buf, "\\f");
1008                                 break;
1009                         case '\n':
1010                                 appendStringInfoString(buf, "\\n");
1011                                 break;
1012                         case '\r':
1013                                 appendStringInfoString(buf, "\\r");
1014                                 break;
1015                         case '\t':
1016                                 appendStringInfoString(buf, "\\t");
1017                                 break;
1018                         case '"':
1019                                 appendStringInfoString(buf, "\\\"");
1020                                 break;
1021                         case '\\':
1022                                 appendStringInfoString(buf, "\\\\");
1023                                 break;
1024                         default:
1025                                 if ((unsigned char) *p < ' ')
1026                                         appendStringInfo(buf, "\\u%04x", (int) *p);
1027                                 else
1028                                         appendStringInfoCharMacro(buf, *p);
1029                                 break;
1030                 }
1031         }
1032         appendStringInfoCharMacro(buf, '\"');
1033 }
1034