mirror of
https://github.com/postgres/postgres.git
synced 2025-04-25 21:42:33 +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:
parent
1f3a021730
commit
73ce2a03f3
@ -23,7 +23,7 @@
|
||||
#include "utils/date.h"
|
||||
#include "utils/datetime.h"
|
||||
#include "utils/json.h"
|
||||
#include "utils/jsonapi.h"
|
||||
#include "utils/jsonfuncs.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
||||
|
@ -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(JsonLexContext *lex, JsonSemAction *sem);
|
||||
static JsonParseErrorType report_parse_error(JsonParseContext ctx, JsonLexContext *lex);
|
||||
static int report_json_context(JsonLexContext *lex);
|
||||
static char *extract_token(JsonLexContext *lex);
|
||||
|
||||
/* 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
|
||||
* for de-escaped lexemes.
|
||||
* lex constructor, with or without StringInfo object for de-escaped lexemes.
|
||||
*
|
||||
* Without is better as it makes the processing faster, so only make one
|
||||
* 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 *
|
||||
makeJsonLexContextCstringLen(char *json, int len, bool need_escapes)
|
||||
{
|
||||
@ -202,23 +189,6 @@ pg_parse_json(JsonLexContext *lex, JsonSemAction *sem)
|
||||
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
|
||||
*
|
||||
@ -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.
|
||||
*/
|
||||
@ -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.
|
||||
*/
|
||||
|
@ -23,8 +23,8 @@
|
||||
#include "utils/date.h"
|
||||
#include "utils/datetime.h"
|
||||
#include "utils/json.h"
|
||||
#include "utils/jsonapi.h"
|
||||
#include "utils/jsonb.h"
|
||||
#include "utils/jsonfuncs.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
#include "utils/typcache.h"
|
||||
|
@ -329,6 +329,8 @@ typedef struct JsObject
|
||||
hash_destroy((jso)->val.json_hash); \
|
||||
} while (0)
|
||||
|
||||
static int report_json_context(JsonLexContext *lex);
|
||||
|
||||
/* semantic action functions for json_object_keys */
|
||||
static void okeys_object_field_start(void *state, char *fname, bool isnull);
|
||||
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_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
|
||||
*
|
||||
@ -573,6 +606,99 @@ jsonb_object_keys(PG_FUNCTION_ARGS)
|
||||
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
|
||||
json_object_keys(PG_FUNCTION_ARGS)
|
||||
|
@ -126,12 +126,6 @@ typedef struct JsonSemAction
|
||||
extern JsonParseErrorType pg_parse_json(JsonLexContext *lex,
|
||||
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 */
|
||||
extern JsonSemAction nullSemAction;
|
||||
|
||||
@ -148,15 +142,11 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
|
||||
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
|
||||
* the lexeme. However, doing this imposes a performance penalty, so
|
||||
* 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,
|
||||
int len,
|
||||
bool need_escapes);
|
||||
@ -164,9 +154,6 @@ extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
|
||||
/* lex one token */
|
||||
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 */
|
||||
extern char *json_errdetail(JsonParseErrorType error, JsonLexContext *lex);
|
||||
|
||||
|
@ -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 */
|
||||
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 void iterate_jsonb_values(Jsonb *jb, uint32 flags, void *state,
|
||||
JsonIterateStringValuesAction action);
|
||||
|
Loading…
x
Reference in New Issue
Block a user