Skip to content
Snippets Groups Projects
Commit f871ef74 authored by Tom Lane's avatar Tom Lane
Browse files

Minor code review for json.c.

Improve commenting, conform to project style for use of ++ etc.
No functional changes.
parent 36b7e3da
No related branches found
No related tags found
No related merge requests found
...@@ -25,9 +25,9 @@ ...@@ -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 ...@@ -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 ...@@ -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); ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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: ...@@ -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: ...@@ -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,18 +285,20 @@ redo: ...@@ -271,18 +285,20 @@ 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 *)
repalloc(stacktop,
sizeof(JsonParseStack) * stacksize); sizeof(JsonParseStack) * stacksize);
stack = stacktop + stackoffset; stack = stacktop + stackoffset;
} }
...@@ -299,7 +315,7 @@ redo: ...@@ -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) ...@@ -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) ...@@ -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)
;
if (p == s)
{
/* /*
* We got some sort of unexpected punctuation or an otherwise * We got some sort of unexpected punctuation or an otherwise
* unexpected character, so just complain about that one character. * unexpected character, so just complain about that one
* character.
*/ */
if (p == s)
{
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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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, ...@@ -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) ...@@ -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) ...@@ -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 " : ",";
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment