Minor code review for json.c.

Improve commenting, conform to project style for use of ++ etc.
No functional changes.
This commit is contained in:
Tom Lane 2012-06-12 16:23:45 -04:00
parent 36b7e3da17
commit f871ef74a5
1 changed files with 91 additions and 73 deletions

View File

@ -25,9 +25,9 @@
#include "utils/json.h" #include "utils/json.h"
#include "utils/typcache.h" #include "utils/typcache.h"
typedef enum typedef enum /* types of JSON values */
{ {
JSON_VALUE_INVALID, JSON_VALUE_INVALID, /* non-value tokens are reported as this */
JSON_VALUE_STRING, JSON_VALUE_STRING,
JSON_VALUE_NUMBER, JSON_VALUE_NUMBER,
JSON_VALUE_OBJECT, JSON_VALUE_OBJECT,
@ -37,17 +37,17 @@ typedef enum
JSON_VALUE_NULL JSON_VALUE_NULL
} JsonValueType; } JsonValueType;
typedef struct typedef struct /* state of JSON lexer */
{ {
char *input; char *input; /* whole string being parsed */
char *token_start; char *token_start; /* start of current token within input */
char *token_terminator; char *token_terminator; /* end of previous or current token */
JsonValueType token_type; JsonValueType token_type; /* type of current token, once it's known */
int line_number; int line_number; /* current line number (counting from 1) */
char *line_start; char *line_start; /* start of current line within input (BROKEN!!) */
} JsonLexContext; } JsonLexContext;
typedef enum typedef enum /* states of JSON parser */
{ {
JSON_PARSE_VALUE, /* expecting a value */ JSON_PARSE_VALUE, /* expecting a value */
JSON_PARSE_ARRAY_START, /* saw '[', expecting value or ']' */ JSON_PARSE_ARRAY_START, /* saw '[', expecting value or ']' */
@ -58,17 +58,18 @@ typedef enum
JSON_PARSE_OBJECT_COMMA /* saw object ',', expecting next label */ JSON_PARSE_OBJECT_COMMA /* saw object ',', expecting next label */
} JsonParseState; } JsonParseState;
typedef struct JsonParseStack typedef struct JsonParseStack /* the parser state has to be stackable */
{ {
JsonParseState state; JsonParseState state;
/* currently only need the state enum, but maybe someday more stuff */
} JsonParseStack; } JsonParseStack;
typedef enum typedef enum /* required operations on state stack */
{ {
JSON_STACKOP_NONE, JSON_STACKOP_NONE, /* no-op */
JSON_STACKOP_PUSH, JSON_STACKOP_PUSH, /* push new JSON_PARSE_VALUE stack item */
JSON_STACKOP_PUSH_WITH_PUSHBACK, JSON_STACKOP_PUSH_WITH_PUSHBACK, /* push, then rescan current token */
JSON_STACKOP_POP JSON_STACKOP_POP /* pop, or expect end of input if no stack */
} JsonStackOp; } JsonStackOp;
static void json_validate_cstring(char *input); static void json_validate_cstring(char *input);
@ -78,17 +79,28 @@ static void json_lex_number(JsonLexContext *lex, char *s);
static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex); static void report_parse_error(JsonParseStack *stack, JsonLexContext *lex);
static void report_invalid_token(JsonLexContext *lex); static void report_invalid_token(JsonLexContext *lex);
static char *extract_mb_char(char *s); static char *extract_mb_char(char *s);
static void composite_to_json(Datum composite, StringInfo result, bool use_line_feeds); static void composite_to_json(Datum composite, StringInfo result,
bool use_line_feeds);
static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, static void array_dim_to_json(StringInfo result, int dim, int ndims, int *dims,
Datum *vals, bool *nulls, int *valcount, Datum *vals, bool *nulls, int *valcount,
TYPCATEGORY tcategory, Oid typoutputfunc, TYPCATEGORY tcategory, Oid typoutputfunc,
bool use_line_feeds); bool use_line_feeds);
static void array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds); static void array_to_json_internal(Datum array, StringInfo result,
bool use_line_feeds);
/* fake type category for JSON so we can distinguish it in datum_to_json */ /* fake type category for JSON so we can distinguish it in datum_to_json */
#define TYPCATEGORY_JSON 'j' #define TYPCATEGORY_JSON 'j'
/* letters appearing in numeric output that aren't valid in a JSON number */ /* letters appearing in numeric output that aren't valid in a JSON number */
#define NON_NUMERIC_LETTER "NnAaIiFfTtYy" #define NON_NUMERIC_LETTER "NnAaIiFfTtYy"
/* chars to consider as part of an alphanumeric token */
#define JSON_ALPHANUMERIC_CHAR(c) \
(((c) >= 'a' && (c) <= 'z') || \
((c) >= 'A' && (c) <= 'Z') || \
((c) >= '0' && (c) <= '9') || \
(c) == '_' || \
IS_HIGHBIT_SET(c))
/* /*
* Input. * Input.
*/ */
@ -99,6 +111,7 @@ json_in(PG_FUNCTION_ARGS)
json_validate_cstring(text); json_validate_cstring(text);
/* Internal representation is the same as text, for now */
PG_RETURN_TEXT_P(cstring_to_text(text)); PG_RETURN_TEXT_P(cstring_to_text(text));
} }
@ -108,6 +121,7 @@ json_in(PG_FUNCTION_ARGS)
Datum Datum
json_out(PG_FUNCTION_ARGS) json_out(PG_FUNCTION_ARGS)
{ {
/* we needn't detoast because text_to_cstring will handle that */
Datum txt = PG_GETARG_DATUM(0); Datum txt = PG_GETARG_DATUM(0);
PG_RETURN_CSTRING(TextDatumGetCString(txt)); PG_RETURN_CSTRING(TextDatumGetCString(txt));
@ -119,8 +133,8 @@ json_out(PG_FUNCTION_ARGS)
Datum Datum
json_send(PG_FUNCTION_ARGS) json_send(PG_FUNCTION_ARGS)
{ {
StringInfoData buf;
text *t = PG_GETARG_TEXT_PP(0); text *t = PG_GETARG_TEXT_PP(0);
StringInfoData buf;
pq_begintypsend(&buf); pq_begintypsend(&buf);
pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t)); pq_sendtext(&buf, VARDATA_ANY(t), VARSIZE_ANY_EXHDR(t));
@ -176,7 +190,7 @@ json_validate_cstring(char *input)
/* Set up parse stack. */ /* Set up parse stack. */
stacksize = 32; stacksize = 32;
stacktop = palloc(sizeof(JsonParseStack) * stacksize); stacktop = (JsonParseStack *) palloc(sizeof(JsonParseStack) * stacksize);
stack = stacktop; stack = stacktop;
stack->state = JSON_PARSE_VALUE; stack->state = JSON_PARSE_VALUE;
@ -212,8 +226,8 @@ redo:
stack->state = JSON_PARSE_ARRAY_NEXT; stack->state = JSON_PARSE_ARRAY_NEXT;
else if (lex.token_start[0] == ']') else if (lex.token_start[0] == ']')
op = JSON_STACKOP_POP; op = JSON_STACKOP_POP;
else if (lex.token_start[0] == '[' else if (lex.token_start[0] == '[' ||
|| lex.token_start[0] == '{') lex.token_start[0] == '{')
{ {
stack->state = JSON_PARSE_ARRAY_NEXT; stack->state = JSON_PARSE_ARRAY_NEXT;
op = JSON_STACKOP_PUSH_WITH_PUSHBACK; op = JSON_STACKOP_PUSH_WITH_PUSHBACK;
@ -234,15 +248,15 @@ redo:
case JSON_PARSE_OBJECT_START: case JSON_PARSE_OBJECT_START:
if (lex.token_type == JSON_VALUE_STRING) if (lex.token_type == JSON_VALUE_STRING)
stack->state = JSON_PARSE_OBJECT_LABEL; stack->state = JSON_PARSE_OBJECT_LABEL;
else if (lex.token_type == JSON_VALUE_INVALID else if (lex.token_type == JSON_VALUE_INVALID &&
&& lex.token_start[0] == '}') lex.token_start[0] == '}')
op = JSON_STACKOP_POP; op = JSON_STACKOP_POP;
else else
report_parse_error(stack, &lex); report_parse_error(stack, &lex);
break; break;
case JSON_PARSE_OBJECT_LABEL: case JSON_PARSE_OBJECT_LABEL:
if (lex.token_type == JSON_VALUE_INVALID if (lex.token_type == JSON_VALUE_INVALID &&
&& lex.token_start[0] == ':') lex.token_start[0] == ':')
{ {
stack->state = JSON_PARSE_OBJECT_NEXT; stack->state = JSON_PARSE_OBJECT_NEXT;
op = JSON_STACKOP_PUSH; op = JSON_STACKOP_PUSH;
@ -271,19 +285,21 @@ redo:
(int) stack->state); (int) stack->state);
} }
/* Push or pop the stack, if needed. */ /* Push or pop the state stack, if needed. */
switch (op) switch (op)
{ {
case JSON_STACKOP_PUSH: case JSON_STACKOP_PUSH:
case JSON_STACKOP_PUSH_WITH_PUSHBACK: case JSON_STACKOP_PUSH_WITH_PUSHBACK:
++stack; stack++;
if (stack >= &stacktop[stacksize]) if (stack >= &stacktop[stacksize])
{ {
/* Need to enlarge the stack. */
int stackoffset = stack - stacktop; int stackoffset = stack - stacktop;
stacksize = stacksize + 32; stacksize += 32;
stacktop = repalloc(stacktop, stacktop = (JsonParseStack *)
sizeof(JsonParseStack) * stacksize); repalloc(stacktop,
sizeof(JsonParseStack) * stacksize);
stack = stacktop + stackoffset; stack = stacktop + stackoffset;
} }
stack->state = JSON_PARSE_VALUE; stack->state = JSON_PARSE_VALUE;
@ -299,7 +315,7 @@ redo:
report_parse_error(NULL, &lex); report_parse_error(NULL, &lex);
return; return;
} }
--stack; stack--;
break; break;
case JSON_STACKOP_NONE: case JSON_STACKOP_NONE:
/* nothing to do */ /* nothing to do */
@ -321,15 +337,15 @@ json_lex(JsonLexContext *lex)
while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r')
{ {
if (*s == '\n') if (*s == '\n')
++lex->line_number; lex->line_number++;
++s; s++;
} }
lex->token_start = s; lex->token_start = s;
/* Determine token type. */ /* Determine token type. */
if (strchr("{}[],:", s[0])) if (strchr("{}[],:", s[0]) != NULL)
{ {
/* strchr() doesn't return false on a NUL input. */ /* strchr() is willing to match a zero byte, so test for that. */
if (s[0] == '\0') if (s[0] == '\0')
{ {
/* End of string. */ /* End of string. */
@ -373,17 +389,16 @@ json_lex(JsonLexContext *lex)
* whole word as an unexpected token, rather than just some * whole word as an unexpected token, rather than just some
* unintuitive prefix thereof. * unintuitive prefix thereof.
*/ */
for (p = s; (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
|| (*p >= '0' && *p <= '9') || *p == '_' || IS_HIGHBIT_SET(*p); /* skip */ ;
++p)
;
/*
* We got some sort of unexpected punctuation or an otherwise
* unexpected character, so just complain about that one character.
*/
if (p == s) if (p == s)
{ {
/*
* We got some sort of unexpected punctuation or an otherwise
* unexpected character, so just complain about that one
* character.
*/
lex->token_terminator = s + 1; lex->token_terminator = s + 1;
report_invalid_token(lex); report_invalid_token(lex);
} }
@ -415,9 +430,9 @@ json_lex(JsonLexContext *lex)
static void static void
json_lex_string(JsonLexContext *lex) json_lex_string(JsonLexContext *lex)
{ {
char *s = lex->token_start + 1; char *s;
for (s = lex->token_start + 1; *s != '"'; ++s) for (s = lex->token_start + 1; *s != '"'; s++)
{ {
/* Per RFC4627, these characters MUST be escaped. */ /* Per RFC4627, these characters MUST be escaped. */
if ((unsigned char) *s < 32) if ((unsigned char) *s < 32)
@ -437,7 +452,7 @@ json_lex_string(JsonLexContext *lex)
else if (*s == '\\') else if (*s == '\\')
{ {
/* OK, we have an escape character. */ /* OK, we have an escape character. */
++s; s++;
if (*s == '\0') if (*s == '\0')
{ {
lex->token_terminator = s; lex->token_terminator = s;
@ -448,7 +463,7 @@ json_lex_string(JsonLexContext *lex)
int i; int i;
int ch = 0; int ch = 0;
for (i = 1; i <= 4; ++i) for (i = 1; i <= 4; i++)
{ {
if (s[i] == '\0') if (s[i] == '\0')
{ {
@ -474,9 +489,9 @@ json_lex_string(JsonLexContext *lex)
/* Account for the four additional bytes we just parsed. */ /* Account for the four additional bytes we just parsed. */
s += 4; s += 4;
} }
else if (!strchr("\"\\/bfnrt", *s)) else if (strchr("\"\\/bfnrt", *s) == NULL)
{ {
/* Error out. */ /* Not a valid string escape, so error out. */
ereport(ERROR, ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type json"), errmsg("invalid input syntax for type json"),
@ -527,12 +542,12 @@ json_lex_number(JsonLexContext *lex, char *s)
/* Part (2): parse main digit string. */ /* Part (2): parse main digit string. */
if (*s == '0') if (*s == '0')
++s; s++;
else if (*s >= '1' && *s <= '9') else if (*s >= '1' && *s <= '9')
{ {
do do
{ {
++s; s++;
} while (*s >= '0' && *s <= '9'); } while (*s >= '0' && *s <= '9');
} }
else else
@ -541,14 +556,14 @@ json_lex_number(JsonLexContext *lex, char *s)
/* Part (3): parse optional decimal portion. */ /* Part (3): parse optional decimal portion. */
if (*s == '.') if (*s == '.')
{ {
++s; s++;
if (*s < '0' || *s > '9') if (*s < '0' || *s > '9')
error = true; error = true;
else else
{ {
do do
{ {
++s; s++;
} while (*s >= '0' && *s <= '9'); } while (*s >= '0' && *s <= '9');
} }
} }
@ -556,26 +571,29 @@ json_lex_number(JsonLexContext *lex, char *s)
/* Part (4): parse optional exponent. */ /* Part (4): parse optional exponent. */
if (*s == 'e' || *s == 'E') if (*s == 'e' || *s == 'E')
{ {
++s; s++;
if (*s == '+' || *s == '-') if (*s == '+' || *s == '-')
++s; s++;
if (*s < '0' || *s > '9') if (*s < '0' || *s > '9')
error = true; error = true;
else else
{ {
do do
{ {
++s; s++;
} while (*s >= '0' && *s <= '9'); } while (*s >= '0' && *s <= '9');
} }
} }
/* Check for trailing garbage. */ /*
for (p = s; (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') * Check for trailing garbage. As in json_lex(), any alphanumeric stuff
|| (*p >= '0' && *p <= '9') || *p == '_' || IS_HIGHBIT_SET(*p); ++p) * here should be considered part of the token for error-reporting
; * purposes.
*/
for (p = s; JSON_ALPHANUMERIC_CHAR(*p); p++)
error = true;
lex->token_terminator = p; lex->token_terminator = p;
if (p > s || error) if (error)
report_invalid_token(lex); report_invalid_token(lex);
} }
@ -597,7 +615,7 @@ report_parse_error(JsonParseStack *stack, JsonLexContext *lex)
lex->input), lex->input),
errdetail("The input string ended unexpectedly."))); errdetail("The input string ended unexpectedly.")));
/* Work out the offending token. */ /* Separate out the offending token. */
toklen = lex->token_terminator - lex->token_start; toklen = lex->token_terminator - lex->token_start;
token = palloc(toklen + 1); token = palloc(toklen + 1);
memcpy(token, lex->token_start, toklen); memcpy(token, lex->token_start, toklen);
@ -680,14 +698,15 @@ extract_mb_char(char *s)
} }
/* /*
* Turn a scalar Datum into JSON. Hand off a non-scalar datum to * Turn a scalar Datum into JSON, appending the string to "result".
* composite_to_json or array_to_json_internal as appropriate. *
* Hand off a non-scalar datum to composite_to_json or array_to_json_internal
* as appropriate.
*/ */
static inline void static void
datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory, datum_to_json(Datum val, bool is_null, StringInfo result,
Oid typoutputfunc) TYPCATEGORY tcategory, Oid typoutputfunc)
{ {
char *outputstr; char *outputstr;
if (is_null) if (is_null)
@ -735,6 +754,7 @@ datum_to_json(Datum val, bool is_null, StringInfo result, TYPCATEGORY tcategory,
outputstr = OidOutputFunctionCall(typoutputfunc, val); outputstr = OidOutputFunctionCall(typoutputfunc, val);
escape_json(result, outputstr); escape_json(result, outputstr);
pfree(outputstr); pfree(outputstr);
break;
} }
} }
@ -748,9 +768,8 @@ array_dim_to_json(StringInfo result, int dim, int ndims, int *dims, Datum *vals,
bool *nulls, int *valcount, TYPCATEGORY tcategory, bool *nulls, int *valcount, TYPCATEGORY tcategory,
Oid typoutputfunc, bool use_line_feeds) Oid typoutputfunc, bool use_line_feeds)
{ {
int i; int i;
char *sep; const char *sep;
Assert(dim < ndims); Assert(dim < ndims);
@ -797,7 +816,6 @@ array_to_json_internal(Datum array, StringInfo result, bool use_line_feeds)
int count = 0; int count = 0;
Datum *elements; Datum *elements;
bool *nulls; bool *nulls;
int16 typlen; int16 typlen;
bool typbyval; bool typbyval;
char typalign, char typalign,
@ -852,7 +870,7 @@ composite_to_json(Datum composite, StringInfo result, bool use_line_feeds)
*tuple; *tuple;
int i; int i;
bool needsep = false; bool needsep = false;
char *sep; const char *sep;
sep = use_line_feeds ? ",\n " : ","; sep = use_line_feeds ? ",\n " : ",";