Commit 73ce2a03 authored by Robert Haas's avatar Robert Haas

Move some code from jsonapi.c to jsonfuncs.c.

Specifically, move those functions that depend on ereport()
from jsonapi.c to jsonfuncs.c, in preparation for allowing
jsonapi.c to be used from frontend code.

A few cases where elog(ERROR, ...) is used for can't-happen
conditions are left alone; we can handle those in some other
way in frontend code.

Reviewed by Mark Dilger and Andrew Dunstan.

Discussion: http://postgr.es/m/CA+TgmoYfOXhd27MUDGioVh6QtpD0C1K-f6ObSA10AWiHBAL5bA@mail.gmail.com
parent 1f3a0217
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
#include "utils/date.h" #include "utils/date.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/json.h" #include "utils/json.h"
#include "utils/jsonapi.h" #include "utils/jsonfuncs.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/typcache.h" #include "utils/typcache.h"
......
...@@ -44,7 +44,6 @@ static JsonParseErrorType parse_object(JsonLexContext *lex, JsonSemAction *sem); ...@@ -44,7 +44,6 @@ static JsonParseErrorType parse_object(JsonLexContext *lex, JsonSemAction *sem);
static JsonParseErrorType parse_array_element(JsonLexContext *lex, JsonSemAction *sem); static JsonParseErrorType parse_array_element(JsonLexContext *lex, JsonSemAction *sem);
static JsonParseErrorType parse_array(JsonLexContext *lex, JsonSemAction *sem); static JsonParseErrorType parse_array(JsonLexContext *lex, JsonSemAction *sem);
static JsonParseErrorType report_parse_error(JsonParseContext ctx, JsonLexContext *lex); static JsonParseErrorType report_parse_error(JsonParseContext ctx, JsonLexContext *lex);
static int report_json_context(JsonLexContext *lex);
static char *extract_token(JsonLexContext *lex); static char *extract_token(JsonLexContext *lex);
/* the null action object used for pure validation */ /* the null action object used for pure validation */
...@@ -128,25 +127,13 @@ IsValidJsonNumber(const char *str, int len) ...@@ -128,25 +127,13 @@ IsValidJsonNumber(const char *str, int len)
} }
/* /*
* makeJsonLexContext * makeJsonLexContextCstringLen
* *
* lex constructor, with or without StringInfo object * lex constructor, with or without StringInfo object for de-escaped lexemes.
* for de-escaped lexemes.
* *
* Without is better as it makes the processing faster, so only make one * Without is better as it makes the processing faster, so only make one
* if really required. * if really required.
*
* If you already have the json as a text* value, use the first of these
* functions, otherwise use makeJsonLexContextCstringLen().
*/ */
JsonLexContext *
makeJsonLexContext(text *json, bool need_escapes)
{
return makeJsonLexContextCstringLen(VARDATA_ANY(json),
VARSIZE_ANY_EXHDR(json),
need_escapes);
}
JsonLexContext * JsonLexContext *
makeJsonLexContextCstringLen(char *json, int len, bool need_escapes) makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
{ {
...@@ -202,23 +189,6 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem) ...@@ -202,23 +189,6 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
return result; return result;
} }
/*
* pg_parse_json_or_ereport
*
* This fuction is like pg_parse_json, except that it does not return a
* JsonParseErrorType. Instead, in case of any failure, this function will
* ereport(ERROR).
*/
void
pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
{
JsonParseErrorType result;
result = pg_parse_json(lex, sem);
if (result != JSON_SUCCESS)
json_ereport_error(result, lex);
}
/* /*
* json_count_array_elements * json_count_array_elements
* *
...@@ -1038,27 +1008,6 @@ report_parse_error(JsonParseContext ctx, JsonLexContext *lex) ...@@ -1038,27 +1008,6 @@ report_parse_error(JsonParseContext ctx, JsonLexContext *lex)
} }
} }
/*
* Report a JSON error.
*/
void
json_ereport_error(JsonParseErrorType error, JsonLexContext *lex)
{
if (error == JSON_UNICODE_HIGH_ESCAPE ||
error == JSON_UNICODE_CODE_POINT_ZERO)
ereport(ERROR,
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
errmsg("unsupported Unicode escape sequence"),
errdetail("%s", json_errdetail(error, lex)),
report_json_context(lex)));
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s", "json"),
errdetail("%s", json_errdetail(error, lex)),
report_json_context(lex)));
}
/* /*
* Construct a detail message for a JSON error. * Construct a detail message for a JSON error.
*/ */
...@@ -1118,78 +1067,6 @@ json_errdetail(JsonParseErrorType error, JsonLexContext *lex) ...@@ -1118,78 +1067,6 @@ json_errdetail(JsonParseErrorType error, JsonLexContext *lex)
} }
} }
/*
* Report a CONTEXT line for bogus JSON input.
*
* lex->token_terminator must be set to identify the spot where we detected
* the error. Note that lex->token_start might be NULL, in case we recognized
* error at EOF.
*
* The return value isn't meaningful, but we make it non-void so that this
* can be invoked inside ereport().
*/
static int
report_json_context(JsonLexContext *lex)
{
const char *context_start;
const char *context_end;
const char *line_start;
int line_number;
char *ctxt;
int ctxtlen;
const char *prefix;
const char *suffix;
/* Choose boundaries for the part of the input we will display */
context_start = lex->input;
context_end = lex->token_terminator;
line_start = context_start;
line_number = 1;
for (;;)
{
/* Always advance over newlines */
if (context_start < context_end && *context_start == '\n')
{
context_start++;
line_start = context_start;
line_number++;
continue;
}
/* Otherwise, done as soon as we are close enough to context_end */
if (context_end - context_start < 50)
break;
/* Advance to next multibyte character */
if (IS_HIGHBIT_SET(*context_start))
context_start += pg_mblen(context_start);
else
context_start++;
}
/*
* We add "..." to indicate that the excerpt doesn't start at the
* beginning of the line ... but if we're within 3 characters of the
* beginning of the line, we might as well just show the whole line.
*/
if (context_start - line_start <= 3)
context_start = line_start;
/* Get a null-terminated copy of the data to present */
ctxtlen = context_end - context_start;
ctxt = palloc(ctxtlen + 1);
memcpy(ctxt, context_start, ctxtlen);
ctxt[ctxtlen] = '\0';
/*
* Show the context, prefixing "..." if not starting at start of line, and
* suffixing "..." if not ending at end of line.
*/
prefix = (context_start > line_start) ? "..." : "";
suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : "";
return errcontext("JSON data, line %d: %s%s%s",
line_number, prefix, ctxt, suffix);
}
/* /*
* Extract the current token from a lexing context, for error reporting. * Extract the current token from a lexing context, for error reporting.
*/ */
......
...@@ -23,8 +23,8 @@ ...@@ -23,8 +23,8 @@
#include "utils/date.h" #include "utils/date.h"
#include "utils/datetime.h" #include "utils/datetime.h"
#include "utils/json.h" #include "utils/json.h"
#include "utils/jsonapi.h"
#include "utils/jsonb.h" #include "utils/jsonb.h"
#include "utils/jsonfuncs.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/typcache.h" #include "utils/typcache.h"
......
...@@ -329,6 +329,8 @@ typedef struct JsObject ...@@ -329,6 +329,8 @@ typedef struct JsObject
hash_destroy((jso)->val.json_hash); \ hash_destroy((jso)->val.json_hash); \
} while (0) } while (0)
static int report_json_context(JsonLexContext *lex);
/* semantic action functions for json_object_keys */ /* semantic action functions for json_object_keys */
static void okeys_object_field_start(void *state, char *fname, bool isnull); static void okeys_object_field_start(void *state, char *fname, bool isnull);
static void okeys_array_start(void *state); static void okeys_array_start(void *state);
...@@ -484,6 +486,37 @@ static void transform_string_values_object_field_start(void *state, char *fname, ...@@ -484,6 +486,37 @@ static void transform_string_values_object_field_start(void *state, char *fname,
static void transform_string_values_array_element_start(void *state, bool isnull); static void transform_string_values_array_element_start(void *state, bool isnull);
static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype); static void transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype);
/*
* pg_parse_json_or_ereport
*
* This fuction is like pg_parse_json, except that it does not return a
* JsonParseErrorType. Instead, in case of any failure, this function will
* ereport(ERROR).
*/
void
pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem)
{
JsonParseErrorType result;
result = pg_parse_json(lex, sem);
if (result != JSON_SUCCESS)
json_ereport_error(result, lex);
}
/*
* makeJsonLexContext
*
* This is like makeJsonLexContextCstringLen, but it accepts a text value
* directly.
*/
JsonLexContext *
makeJsonLexContext(text *json, bool need_escapes)
{
return makeJsonLexContextCstringLen(VARDATA_ANY(json),
VARSIZE_ANY_EXHDR(json),
need_escapes);
}
/* /*
* SQL function json_object_keys * SQL function json_object_keys
* *
...@@ -573,6 +606,99 @@ jsonb_object_keys(PG_FUNCTION_ARGS) ...@@ -573,6 +606,99 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
SRF_RETURN_DONE(funcctx); SRF_RETURN_DONE(funcctx);
} }
/*
* Report a JSON error.
*/
void
json_ereport_error(JsonParseErrorType error, JsonLexContext *lex)
{
if (error == JSON_UNICODE_HIGH_ESCAPE ||
error == JSON_UNICODE_CODE_POINT_ZERO)
ereport(ERROR,
(errcode(ERRCODE_UNTRANSLATABLE_CHARACTER),
errmsg("unsupported Unicode escape sequence"),
errdetail("%s", json_errdetail(error, lex)),
report_json_context(lex)));
else
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s", "json"),
errdetail("%s", json_errdetail(error, lex)),
report_json_context(lex)));
}
/*
* Report a CONTEXT line for bogus JSON input.
*
* lex->token_terminator must be set to identify the spot where we detected
* the error. Note that lex->token_start might be NULL, in case we recognized
* error at EOF.
*
* The return value isn't meaningful, but we make it non-void so that this
* can be invoked inside ereport().
*/
static int
report_json_context(JsonLexContext *lex)
{
const char *context_start;
const char *context_end;
const char *line_start;
int line_number;
char *ctxt;
int ctxtlen;
const char *prefix;
const char *suffix;
/* Choose boundaries for the part of the input we will display */
context_start = lex->input;
context_end = lex->token_terminator;
line_start = context_start;
line_number = 1;
for (;;)
{
/* Always advance over newlines */
if (context_start < context_end && *context_start == '\n')
{
context_start++;
line_start = context_start;
line_number++;
continue;
}
/* Otherwise, done as soon as we are close enough to context_end */
if (context_end - context_start < 50)
break;
/* Advance to next multibyte character */
if (IS_HIGHBIT_SET(*context_start))
context_start += pg_mblen(context_start);
else
context_start++;
}
/*
* We add "..." to indicate that the excerpt doesn't start at the
* beginning of the line ... but if we're within 3 characters of the
* beginning of the line, we might as well just show the whole line.
*/
if (context_start - line_start <= 3)
context_start = line_start;
/* Get a null-terminated copy of the data to present */
ctxtlen = context_end - context_start;
ctxt = palloc(ctxtlen + 1);
memcpy(ctxt, context_start, ctxtlen);
ctxt[ctxtlen] = '\0';
/*
* Show the context, prefixing "..." if not starting at start of line, and
* suffixing "..." if not ending at end of line.
*/
prefix = (context_start > line_start) ? "..." : "";
suffix = (lex->token_type != JSON_TOKEN_END && context_end - lex->input < lex->input_length && *context_end != '\n' && *context_end != '\r') ? "..." : "";
return errcontext("JSON data, line %d: %s%s%s",
line_number, prefix, ctxt, suffix);
}
Datum Datum
json_object_keys(PG_FUNCTION_ARGS) json_object_keys(PG_FUNCTION_ARGS)
......
...@@ -126,12 +126,6 @@ typedef struct JsonSemAction ...@@ -126,12 +126,6 @@ typedef struct JsonSemAction
extern JsonParseErrorType pg_parse_json(JsonLexContext *lex, extern JsonParseErrorType pg_parse_json(JsonLexContext *lex,
JsonSemAction *sem); JsonSemAction *sem);
/*
* Same thing, but signal errors via ereport(ERROR) instead of returning
* a result code.
*/
extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
/* the null action object used for pure validation */ /* the null action object used for pure validation */
extern JsonSemAction nullSemAction; extern JsonSemAction nullSemAction;
...@@ -148,15 +142,11 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex, ...@@ -148,15 +142,11 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
int *elements); int *elements);
/* /*
* constructors for JsonLexContext, with or without strval element. * constructor for JsonLexContext, with or without strval element.
* If supplied, the strval element will contain a de-escaped version of * If supplied, the strval element will contain a de-escaped version of
* the lexeme. However, doing this imposes a performance penalty, so * the lexeme. However, doing this imposes a performance penalty, so
* it should be avoided if the de-escaped lexeme is not required. * it should be avoided if the de-escaped lexeme is not required.
*
* If you already have the json as a text* value, use the first of these
* functions, otherwise use makeJsonLexContextCstringLen().
*/ */
extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
extern JsonLexContext *makeJsonLexContextCstringLen(char *json, extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
int len, int len,
bool need_escapes); bool need_escapes);
...@@ -164,9 +154,6 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json, ...@@ -164,9 +154,6 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
/* lex one token */ /* lex one token */
extern JsonParseErrorType json_lex(JsonLexContext *lex); extern JsonParseErrorType json_lex(JsonLexContext *lex);
/* report an error during json lexing or parsing */
extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
/* construct an error detail string for a json error */ /* construct an error detail string for a json error */
extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex); extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex);
......
...@@ -36,6 +36,15 @@ typedef void (*JsonIterateStringValuesAction) (void *state, char *elem_value, in ...@@ -36,6 +36,15 @@ typedef void (*JsonIterateStringValuesAction) (void *state, char *elem_value, in
/* an action that will be applied to each value in transform_json(b)_values functions */ /* an action that will be applied to each value in transform_json(b)_values functions */
typedef text *(*JsonTransformStringValuesAction) (void *state, char *elem_value, int elem_len); typedef text *(*JsonTransformStringValuesAction) (void *state, char *elem_value, int elem_len);
/* build a JsonLexContext from a text datum */
extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
/* try to parse json, and ereport(ERROR) on failure */
extern void pg_parse_json_or_ereport(JsonLexContext *lex, JsonSemAction *sem);
/* report an error during json lexing or parsing */
extern void json_ereport_error(JsonParseErrorType error, JsonLexContext *lex);
extern uint32 parse_jsonb_index_flags(Jsonb *jb); extern uint32 parse_jsonb_index_flags(Jsonb *jb);
extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state, extern void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
JsonIterateStringValuesAction action); JsonIterateStringValuesAction action);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment