1
0
mirror of https://github.com/postgres/postgres.git synced 2025-04-21 12:05:57 +03:00

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
This commit is contained in:
Robert Haas 2020-01-27 11:22:13 -05:00
parent 1f3a021730
commit 73ce2a03f3
6 changed files with 140 additions and 141 deletions

View File

@ -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"

View File

@ -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)
} }
/* /*
* 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)
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)
} }
} }
/*
* 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)
} }
} }
/*
* 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.
*/ */

View File

@ -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"

View File

@ -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,
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)
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)

View File

@ -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,
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,
/* 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);

View File

@ -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);