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