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:
parent
1f3a021730
commit
73ce2a03f3
@ -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);
|
|||||||
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.
|
||||||
*/
|
*/
|
||||||
|
@ -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
|
|||||||
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)
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user