Improve JsonLexContext's freeability
Previously, the JSON code didn't have to worry too much about freeing JsonLexContext, because it was never too long-lived. With new features being added for SQL/JSON this is no longer the case. Add a routine that knows how to free this struct and apply that to a few places, to prevent this from becoming problematic. At the same time, we change the API of makeJsonLexContextCstringLen to make it receive a pointer to JsonLexContext for callers that want it to be stack-allocated; it can also be passed as NULL to get the original behavior of a palloc'ed one. This also causes an ABI break due to the addition of flags to JsonLexContext, so we can't easily backpatch it. AFAICS that's not much of a problem; apparently some leaks might exist in JSON usage of text-search, for example via json_to_tsvector, but I haven't seen any complaints about that. Per Coverity complaint about datum_to_jsonb_internal(). Discussion: https://postgr.es/m/20230808174110.oq3iymllsv6amkih@alvherre.pgsql
This commit is contained in:
parent
a8a968a821
commit
1c99cde2f3
|
@ -106,11 +106,11 @@ json_in(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
char *json = PG_GETARG_CSTRING(0);
|
char *json = PG_GETARG_CSTRING(0);
|
||||||
text *result = cstring_to_text(json);
|
text *result = cstring_to_text(json);
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
|
|
||||||
/* validate it */
|
/* validate it */
|
||||||
lex = makeJsonLexContext(result, false);
|
makeJsonLexContext(&lex, result, false);
|
||||||
if (!pg_parse_json_or_errsave(lex, &nullSemAction, fcinfo->context))
|
if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
|
|
||||||
/* Internal representation is the same as text */
|
/* Internal representation is the same as text */
|
||||||
|
@ -152,13 +152,14 @@ json_recv(PG_FUNCTION_ARGS)
|
||||||
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
|
||||||
char *str;
|
char *str;
|
||||||
int nbytes;
|
int nbytes;
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
|
|
||||||
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
|
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
|
||||||
|
|
||||||
/* Validate it. */
|
/* Validate it. */
|
||||||
lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
|
makeJsonLexContextCstringLen(&lex, str, nbytes, GetDatabaseEncoding(),
|
||||||
pg_parse_json_or_ereport(lex, &nullSemAction);
|
false);
|
||||||
|
pg_parse_json_or_ereport(&lex, &nullSemAction);
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
|
||||||
}
|
}
|
||||||
|
@ -1625,14 +1626,16 @@ json_unique_object_field_start(void *_state, char *field, bool isnull)
|
||||||
bool
|
bool
|
||||||
json_validate(text *json, bool check_unique_keys, bool throw_error)
|
json_validate(text *json, bool check_unique_keys, bool throw_error)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
|
JsonLexContext lex;
|
||||||
JsonSemAction uniqueSemAction = {0};
|
JsonSemAction uniqueSemAction = {0};
|
||||||
JsonUniqueParsingState state;
|
JsonUniqueParsingState state;
|
||||||
JsonParseErrorType result;
|
JsonParseErrorType result;
|
||||||
|
|
||||||
|
makeJsonLexContext(&lex, json, check_unique_keys);
|
||||||
|
|
||||||
if (check_unique_keys)
|
if (check_unique_keys)
|
||||||
{
|
{
|
||||||
state.lex = lex;
|
state.lex = &lex;
|
||||||
state.stack = NULL;
|
state.stack = NULL;
|
||||||
state.id_counter = 0;
|
state.id_counter = 0;
|
||||||
state.unique = true;
|
state.unique = true;
|
||||||
|
@ -1644,12 +1647,12 @@ json_validate(text *json, bool check_unique_keys, bool throw_error)
|
||||||
uniqueSemAction.object_end = json_unique_object_end;
|
uniqueSemAction.object_end = json_unique_object_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
|
result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
|
||||||
|
|
||||||
if (result != JSON_SUCCESS)
|
if (result != JSON_SUCCESS)
|
||||||
{
|
{
|
||||||
if (throw_error)
|
if (throw_error)
|
||||||
json_errsave_error(result, lex, NULL);
|
json_errsave_error(result, &lex, NULL);
|
||||||
|
|
||||||
return false; /* invalid json */
|
return false; /* invalid json */
|
||||||
}
|
}
|
||||||
|
@ -1664,6 +1667,9 @@ json_validate(text *json, bool check_unique_keys, bool throw_error)
|
||||||
return false; /* not unique keys */
|
return false; /* not unique keys */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (check_unique_keys)
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
|
|
||||||
return true; /* ok */
|
return true; /* ok */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1683,18 +1689,17 @@ Datum
|
||||||
json_typeof(PG_FUNCTION_ARGS)
|
json_typeof(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, false);
|
JsonLexContext lex;
|
||||||
char *type;
|
char *type;
|
||||||
JsonTokenType tok;
|
|
||||||
JsonParseErrorType result;
|
JsonParseErrorType result;
|
||||||
|
|
||||||
/* Lex exactly one token from the input and check its type. */
|
/* Lex exactly one token from the input and check its type. */
|
||||||
result = json_lex(lex);
|
makeJsonLexContext(&lex, json, false);
|
||||||
|
result = json_lex(&lex);
|
||||||
if (result != JSON_SUCCESS)
|
if (result != JSON_SUCCESS)
|
||||||
json_errsave_error(result, lex, NULL);
|
json_errsave_error(result, &lex, NULL);
|
||||||
tok = lex->token_type;
|
|
||||||
|
|
||||||
switch (tok)
|
switch (lex.token_type)
|
||||||
{
|
{
|
||||||
case JSON_TOKEN_OBJECT_START:
|
case JSON_TOKEN_OBJECT_START:
|
||||||
type = "object";
|
type = "object";
|
||||||
|
@ -1716,7 +1721,7 @@ json_typeof(PG_FUNCTION_ARGS)
|
||||||
type = "null";
|
type = "null";
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
elog(ERROR, "unexpected json token: %d", tok);
|
elog(ERROR, "unexpected json token: %d", lex.token_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(cstring_to_text(type));
|
PG_RETURN_TEXT_P(cstring_to_text(type));
|
||||||
|
|
|
@ -252,13 +252,13 @@ jsonb_typeof(PG_FUNCTION_ARGS)
|
||||||
static inline Datum
|
static inline Datum
|
||||||
jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
|
jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonbInState state;
|
JsonbInState state;
|
||||||
JsonSemAction sem;
|
JsonSemAction sem;
|
||||||
|
|
||||||
memset(&state, 0, sizeof(state));
|
memset(&state, 0, sizeof(state));
|
||||||
memset(&sem, 0, sizeof(sem));
|
memset(&sem, 0, sizeof(sem));
|
||||||
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
|
makeJsonLexContextCstringLen(&lex, json, len, GetDatabaseEncoding(), true);
|
||||||
|
|
||||||
state.unique_keys = unique_keys;
|
state.unique_keys = unique_keys;
|
||||||
state.escontext = escontext;
|
state.escontext = escontext;
|
||||||
|
@ -271,7 +271,7 @@ jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
|
||||||
sem.scalar = jsonb_in_scalar;
|
sem.scalar = jsonb_in_scalar;
|
||||||
sem.object_field_start = jsonb_in_object_field_start;
|
sem.object_field_start = jsonb_in_object_field_start;
|
||||||
|
|
||||||
if (!pg_parse_json_or_errsave(lex, &sem, escontext))
|
if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
|
||||||
return (Datum) 0;
|
return (Datum) 0;
|
||||||
|
|
||||||
/* after parsing, the item member has the composed jsonb structure */
|
/* after parsing, the item member has the composed jsonb structure */
|
||||||
|
@ -755,11 +755,11 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
|
||||||
case JSONTYPE_JSON:
|
case JSONTYPE_JSON:
|
||||||
{
|
{
|
||||||
/* parse the json right into the existing result object */
|
/* parse the json right into the existing result object */
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonSemAction sem;
|
JsonSemAction sem;
|
||||||
text *json = DatumGetTextPP(val);
|
text *json = DatumGetTextPP(val);
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, true);
|
makeJsonLexContext(&lex, json, true);
|
||||||
|
|
||||||
memset(&sem, 0, sizeof(sem));
|
memset(&sem, 0, sizeof(sem));
|
||||||
|
|
||||||
|
@ -772,7 +772,8 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
|
||||||
sem.scalar = jsonb_in_scalar;
|
sem.scalar = jsonb_in_scalar;
|
||||||
sem.object_field_start = jsonb_in_object_field_start;
|
sem.object_field_start = jsonb_in_object_field_start;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, &sem);
|
pg_parse_json_or_ereport(&lex, &sem);
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case JSONTYPE_JSONB:
|
case JSONTYPE_JSONB:
|
||||||
|
|
|
@ -526,7 +526,7 @@ pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
|
||||||
* directly.
|
* directly.
|
||||||
*/
|
*/
|
||||||
JsonLexContext *
|
JsonLexContext *
|
||||||
makeJsonLexContext(text *json, bool need_escapes)
|
makeJsonLexContext(JsonLexContext *lex, text *json, bool need_escapes)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Most callers pass a detoasted datum, but it's not clear that they all
|
* Most callers pass a detoasted datum, but it's not clear that they all
|
||||||
|
@ -534,7 +534,8 @@ makeJsonLexContext(text *json, bool need_escapes)
|
||||||
*/
|
*/
|
||||||
json = pg_detoast_datum_packed(json);
|
json = pg_detoast_datum_packed(json);
|
||||||
|
|
||||||
return makeJsonLexContextCstringLen(VARDATA_ANY(json),
|
return makeJsonLexContextCstringLen(lex,
|
||||||
|
VARDATA_ANY(json),
|
||||||
VARSIZE_ANY_EXHDR(json),
|
VARSIZE_ANY_EXHDR(json),
|
||||||
GetDatabaseEncoding(),
|
GetDatabaseEncoding(),
|
||||||
need_escapes);
|
need_escapes);
|
||||||
|
@ -725,7 +726,7 @@ json_object_keys(PG_FUNCTION_ARGS)
|
||||||
if (SRF_IS_FIRSTCALL())
|
if (SRF_IS_FIRSTCALL())
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, true);
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
MemoryContext oldcontext;
|
MemoryContext oldcontext;
|
||||||
|
|
||||||
|
@ -735,7 +736,7 @@ json_object_keys(PG_FUNCTION_ARGS)
|
||||||
state = palloc(sizeof(OkeysState));
|
state = palloc(sizeof(OkeysState));
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContext(&lex, json, true);
|
||||||
state->result_size = 256;
|
state->result_size = 256;
|
||||||
state->result_count = 0;
|
state->result_count = 0;
|
||||||
state->sent_count = 0;
|
state->sent_count = 0;
|
||||||
|
@ -747,12 +748,10 @@ json_object_keys(PG_FUNCTION_ARGS)
|
||||||
sem->object_field_start = okeys_object_field_start;
|
sem->object_field_start = okeys_object_field_start;
|
||||||
/* remainder are all NULL, courtesy of palloc0 above */
|
/* remainder are all NULL, courtesy of palloc0 above */
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
/* keys are now in state->result */
|
/* keys are now in state->result */
|
||||||
|
|
||||||
pfree(lex->strval->data);
|
freeJsonLexContext(&lex);
|
||||||
pfree(lex->strval);
|
|
||||||
pfree(lex);
|
|
||||||
pfree(sem);
|
pfree(sem);
|
||||||
|
|
||||||
MemoryContextSwitchTo(oldcontext);
|
MemoryContextSwitchTo(oldcontext);
|
||||||
|
@ -1096,13 +1095,13 @@ get_worker(text *json,
|
||||||
int npath,
|
int npath,
|
||||||
bool normalize_results)
|
bool normalize_results)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, true);
|
|
||||||
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
||||||
GetState *state = palloc0(sizeof(GetState));
|
GetState *state = palloc0(sizeof(GetState));
|
||||||
|
|
||||||
Assert(npath >= 0);
|
Assert(npath >= 0);
|
||||||
|
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContext(NULL, json, true);
|
||||||
|
|
||||||
/* is it "_as_text" variant? */
|
/* is it "_as_text" variant? */
|
||||||
state->normalize_results = normalize_results;
|
state->normalize_results = normalize_results;
|
||||||
state->npath = npath;
|
state->npath = npath;
|
||||||
|
@ -1140,7 +1139,8 @@ get_worker(text *json,
|
||||||
sem->array_element_end = get_array_element_end;
|
sem->array_element_end = get_array_element_end;
|
||||||
}
|
}
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(state->lex, sem);
|
||||||
|
freeJsonLexContext(state->lex);
|
||||||
|
|
||||||
return state->tresult;
|
return state->tresult;
|
||||||
}
|
}
|
||||||
|
@ -1842,25 +1842,23 @@ json_array_length(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
AlenState *state;
|
AlenState *state;
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, false);
|
|
||||||
state = palloc0(sizeof(AlenState));
|
state = palloc0(sizeof(AlenState));
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
state->lex = makeJsonLexContext(&lex, json, false);
|
||||||
|
|
||||||
/* palloc0 does this for us */
|
/* palloc0 does this for us */
|
||||||
#if 0
|
#if 0
|
||||||
state->count = 0;
|
state->count = 0;
|
||||||
#endif
|
#endif
|
||||||
state->lex = lex;
|
|
||||||
|
|
||||||
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
sem->semstate = (void *) state;
|
sem->semstate = (void *) state;
|
||||||
sem->object_start = alen_object_start;
|
sem->object_start = alen_object_start;
|
||||||
sem->scalar = alen_scalar;
|
sem->scalar = alen_scalar;
|
||||||
sem->array_element_start = alen_array_element_start;
|
sem->array_element_start = alen_array_element_start;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(state->lex, sem);
|
||||||
|
|
||||||
PG_RETURN_INT32(state->count);
|
PG_RETURN_INT32(state->count);
|
||||||
}
|
}
|
||||||
|
@ -2049,12 +2047,11 @@ static Datum
|
||||||
each_worker(FunctionCallInfo fcinfo, bool as_text)
|
each_worker(FunctionCallInfo fcinfo, bool as_text)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
ReturnSetInfo *rsi;
|
ReturnSetInfo *rsi;
|
||||||
EachState *state;
|
EachState *state;
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, true);
|
|
||||||
state = palloc0(sizeof(EachState));
|
state = palloc0(sizeof(EachState));
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
|
||||||
|
@ -2072,14 +2069,15 @@ each_worker(FunctionCallInfo fcinfo, bool as_text)
|
||||||
|
|
||||||
state->normalize_results = as_text;
|
state->normalize_results = as_text;
|
||||||
state->next_scalar = false;
|
state->next_scalar = false;
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContext(&lex, json, true);
|
||||||
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
|
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"json_each temporary cxt",
|
"json_each temporary cxt",
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
|
||||||
MemoryContextDelete(state->tmp_cxt);
|
MemoryContextDelete(state->tmp_cxt);
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
|
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
}
|
}
|
||||||
|
@ -2299,13 +2297,14 @@ static Datum
|
||||||
elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
|
elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
|
JsonLexContext lex;
|
||||||
/* elements only needs escaped strings when as_text */
|
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, as_text);
|
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
ReturnSetInfo *rsi;
|
ReturnSetInfo *rsi;
|
||||||
ElementsState *state;
|
ElementsState *state;
|
||||||
|
|
||||||
|
/* elements only needs escaped strings when as_text */
|
||||||
|
makeJsonLexContext(&lex, json, as_text);
|
||||||
|
|
||||||
state = palloc0(sizeof(ElementsState));
|
state = palloc0(sizeof(ElementsState));
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
|
||||||
|
@ -2323,14 +2322,15 @@ elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
|
||||||
state->function_name = funcname;
|
state->function_name = funcname;
|
||||||
state->normalize_results = as_text;
|
state->normalize_results = as_text;
|
||||||
state->next_scalar = false;
|
state->next_scalar = false;
|
||||||
state->lex = lex;
|
state->lex = &lex;
|
||||||
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
|
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
|
||||||
"json_array_elements temporary cxt",
|
"json_array_elements temporary cxt",
|
||||||
ALLOCSET_DEFAULT_SIZES);
|
ALLOCSET_DEFAULT_SIZES);
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
|
||||||
MemoryContextDelete(state->tmp_cxt);
|
MemoryContextDelete(state->tmp_cxt);
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
|
|
||||||
PG_RETURN_NULL();
|
PG_RETURN_NULL();
|
||||||
}
|
}
|
||||||
|
@ -2704,7 +2704,8 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
|
||||||
PopulateArrayState state;
|
PopulateArrayState state;
|
||||||
JsonSemAction sem;
|
JsonSemAction sem;
|
||||||
|
|
||||||
state.lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
|
state.lex = makeJsonLexContextCstringLen(NULL, json, len,
|
||||||
|
GetDatabaseEncoding(), true);
|
||||||
state.ctx = ctx;
|
state.ctx = ctx;
|
||||||
|
|
||||||
memset(&sem, 0, sizeof(sem));
|
memset(&sem, 0, sizeof(sem));
|
||||||
|
@ -2720,7 +2721,7 @@ populate_array_json(PopulateArrayContext *ctx, char *json, int len)
|
||||||
/* number of dimensions should be already known */
|
/* number of dimensions should be already known */
|
||||||
Assert(ctx->ndims > 0 && ctx->dims);
|
Assert(ctx->ndims > 0 && ctx->dims);
|
||||||
|
|
||||||
pfree(state.lex);
|
freeJsonLexContext(state.lex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -3547,7 +3548,6 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
|
||||||
HASHCTL ctl;
|
HASHCTL ctl;
|
||||||
HTAB *tab;
|
HTAB *tab;
|
||||||
JHashState *state;
|
JHashState *state;
|
||||||
JsonLexContext *lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
|
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
|
|
||||||
ctl.keysize = NAMEDATALEN;
|
ctl.keysize = NAMEDATALEN;
|
||||||
|
@ -3563,7 +3563,8 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
|
||||||
|
|
||||||
state->function_name = funcname;
|
state->function_name = funcname;
|
||||||
state->hash = tab;
|
state->hash = tab;
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContextCstringLen(NULL, json, len,
|
||||||
|
GetDatabaseEncoding(), true);
|
||||||
|
|
||||||
sem->semstate = (void *) state;
|
sem->semstate = (void *) state;
|
||||||
sem->array_start = hash_array_start;
|
sem->array_start = hash_array_start;
|
||||||
|
@ -3571,7 +3572,9 @@ get_json_object_as_hash(char *json, int len, const char *funcname)
|
||||||
sem->object_field_start = hash_object_field_start;
|
sem->object_field_start = hash_object_field_start;
|
||||||
sem->object_field_end = hash_object_field_end;
|
sem->object_field_end = hash_object_field_end;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(state->lex, sem);
|
||||||
|
|
||||||
|
freeJsonLexContext(state->lex);
|
||||||
|
|
||||||
return tab;
|
return tab;
|
||||||
}
|
}
|
||||||
|
@ -3863,12 +3866,12 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
|
||||||
if (is_json)
|
if (is_json)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(json_arg_num);
|
text *json = PG_GETARG_TEXT_PP(json_arg_num);
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
|
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, true);
|
makeJsonLexContext(&lex, json, true);
|
||||||
|
|
||||||
sem->semstate = (void *) state;
|
sem->semstate = (void *) state;
|
||||||
sem->array_start = populate_recordset_array_start;
|
sem->array_start = populate_recordset_array_start;
|
||||||
|
@ -3879,9 +3882,12 @@ populate_recordset_worker(FunctionCallInfo fcinfo, const char *funcname,
|
||||||
sem->object_start = populate_recordset_object_start;
|
sem->object_start = populate_recordset_object_start;
|
||||||
sem->object_end = populate_recordset_object_end;
|
sem->object_end = populate_recordset_object_end;
|
||||||
|
|
||||||
state->lex = lex;
|
state->lex = &lex;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
|
state->lex = NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -4217,16 +4223,15 @@ json_strip_nulls(PG_FUNCTION_ARGS)
|
||||||
{
|
{
|
||||||
text *json = PG_GETARG_TEXT_PP(0);
|
text *json = PG_GETARG_TEXT_PP(0);
|
||||||
StripnullState *state;
|
StripnullState *state;
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem;
|
JsonSemAction *sem;
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, true);
|
|
||||||
state = palloc0(sizeof(StripnullState));
|
state = palloc0(sizeof(StripnullState));
|
||||||
sem = palloc0(sizeof(JsonSemAction));
|
sem = palloc0(sizeof(JsonSemAction));
|
||||||
|
|
||||||
|
state->lex = makeJsonLexContext(&lex, json, true);
|
||||||
state->strval = makeStringInfo();
|
state->strval = makeStringInfo();
|
||||||
state->skip_next_null = false;
|
state->skip_next_null = false;
|
||||||
state->lex = lex;
|
|
||||||
|
|
||||||
sem->semstate = (void *) state;
|
sem->semstate = (void *) state;
|
||||||
sem->object_start = sn_object_start;
|
sem->object_start = sn_object_start;
|
||||||
|
@ -4237,7 +4242,7 @@ json_strip_nulls(PG_FUNCTION_ARGS)
|
||||||
sem->array_element_start = sn_array_element_start;
|
sem->array_element_start = sn_array_element_start;
|
||||||
sem->object_field_start = sn_object_field_start;
|
sem->object_field_start = sn_object_field_start;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data,
|
PG_RETURN_TEXT_P(cstring_to_text_with_len(state->strval->data,
|
||||||
state->strval->len));
|
state->strval->len));
|
||||||
|
@ -5433,11 +5438,11 @@ void
|
||||||
iterate_json_values(text *json, uint32 flags, void *action_state,
|
iterate_json_values(text *json, uint32 flags, void *action_state,
|
||||||
JsonIterateStringValuesAction action)
|
JsonIterateStringValuesAction action)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, true);
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
||||||
IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
|
IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
|
||||||
|
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContext(&lex, json, true);
|
||||||
state->action = action;
|
state->action = action;
|
||||||
state->action_state = action_state;
|
state->action_state = action_state;
|
||||||
state->flags = flags;
|
state->flags = flags;
|
||||||
|
@ -5446,7 +5451,8 @@ iterate_json_values(text *json, uint32 flags, void *action_state,
|
||||||
sem->scalar = iterate_values_scalar;
|
sem->scalar = iterate_values_scalar;
|
||||||
sem->object_field_start = iterate_values_object_field_start;
|
sem->object_field_start = iterate_values_object_field_start;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -5553,11 +5559,11 @@ text *
|
||||||
transform_json_string_values(text *json, void *action_state,
|
transform_json_string_values(text *json, void *action_state,
|
||||||
JsonTransformStringValuesAction transform_action)
|
JsonTransformStringValuesAction transform_action)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex = makeJsonLexContext(json, true);
|
JsonLexContext lex;
|
||||||
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
|
||||||
TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
|
TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
|
||||||
|
|
||||||
state->lex = lex;
|
state->lex = makeJsonLexContext(&lex, json, true);
|
||||||
state->strval = makeStringInfo();
|
state->strval = makeStringInfo();
|
||||||
state->action = transform_action;
|
state->action = transform_action;
|
||||||
state->action_state = action_state;
|
state->action_state = action_state;
|
||||||
|
@ -5571,7 +5577,8 @@ transform_json_string_values(text *json, void *action_state,
|
||||||
sem->array_element_start = transform_string_values_array_element_start;
|
sem->array_element_start = transform_string_values_array_element_start;
|
||||||
sem->object_field_start = transform_string_values_object_field_start;
|
sem->object_field_start = transform_string_values_object_field_start;
|
||||||
|
|
||||||
pg_parse_json_or_ereport(lex, sem);
|
pg_parse_json_or_ereport(&lex, sem);
|
||||||
|
freeJsonLexContext(&lex);
|
||||||
|
|
||||||
return cstring_to_text_with_len(state->strval->data, state->strval->len);
|
return cstring_to_text_with_len(state->strval->data, state->strval->len);
|
||||||
}
|
}
|
||||||
|
@ -5670,19 +5677,19 @@ transform_string_values_scalar(void *state, char *token, JsonTokenType tokentype
|
||||||
JsonTokenType
|
JsonTokenType
|
||||||
json_get_first_token(text *json, bool throw_error)
|
json_get_first_token(text *json, bool throw_error)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex;
|
JsonLexContext lex;
|
||||||
JsonParseErrorType result;
|
JsonParseErrorType result;
|
||||||
|
|
||||||
lex = makeJsonLexContext(json, false);
|
makeJsonLexContext(&lex, json, false);
|
||||||
|
|
||||||
/* Lex exactly one token from the input and check its type. */
|
/* Lex exactly one token from the input and check its type. */
|
||||||
result = json_lex(lex);
|
result = json_lex(&lex);
|
||||||
|
|
||||||
if (result == JSON_SUCCESS)
|
if (result == JSON_SUCCESS)
|
||||||
return lex->token_type;
|
return lex.token_type;
|
||||||
|
|
||||||
if (throw_error)
|
if (throw_error)
|
||||||
json_errsave_error(result, lex, NULL);
|
json_errsave_error(result, &lex, NULL);
|
||||||
|
|
||||||
return JSON_TOKEN_INVALID; /* invalid json */
|
return JSON_TOKEN_INVALID; /* invalid json */
|
||||||
}
|
}
|
||||||
|
|
|
@ -130,7 +130,7 @@ json_parse_manifest(JsonManifestParseContext *context, char *buffer,
|
||||||
parse.saw_version_field = false;
|
parse.saw_version_field = false;
|
||||||
|
|
||||||
/* Create a JSON lexing context. */
|
/* Create a JSON lexing context. */
|
||||||
lex = makeJsonLexContextCstringLen(buffer, size, PG_UTF8, true);
|
lex = makeJsonLexContextCstringLen(NULL, buffer, size, PG_UTF8, true);
|
||||||
|
|
||||||
/* Set up semantic actions. */
|
/* Set up semantic actions. */
|
||||||
sem.semstate = &parse;
|
sem.semstate = &parse;
|
||||||
|
@ -153,6 +153,8 @@ json_parse_manifest(JsonManifestParseContext *context, char *buffer,
|
||||||
|
|
||||||
/* Verify the manifest checksum. */
|
/* Verify the manifest checksum. */
|
||||||
verify_manifest_checksum(&parse, buffer, size);
|
verify_manifest_checksum(&parse, buffer, size);
|
||||||
|
|
||||||
|
freeJsonLexContext(lex);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -135,26 +135,62 @@ IsValidJsonNumber(const char *str, int len)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* makeJsonLexContextCstringLen
|
* makeJsonLexContextCstringLen
|
||||||
|
* Initialize the given JsonLexContext object, or create one
|
||||||
*
|
*
|
||||||
* lex constructor, with or without StringInfo object for de-escaped lexemes.
|
* If a valid 'lex' pointer is given, it is initialized. This can
|
||||||
|
* be used for stack-allocated structs, saving overhead. If NULL is
|
||||||
|
* given, a new struct is allocated.
|
||||||
*
|
*
|
||||||
* Without is better as it makes the processing faster, so only make one
|
* If need_escapes is true, ->strval stores the unescaped lexemes.
|
||||||
* if really required.
|
* Unescaping is expensive, so only request it when necessary.
|
||||||
|
*
|
||||||
|
* If need_escapes is true or lex was given as NULL, then caller is
|
||||||
|
* responsible for freeing the returned struct, either by calling
|
||||||
|
* freeJsonLexContext() or (in backend environment) via memory context
|
||||||
|
* cleanup.
|
||||||
*/
|
*/
|
||||||
JsonLexContext *
|
JsonLexContext *
|
||||||
makeJsonLexContextCstringLen(char *json, int len, int encoding, bool need_escapes)
|
makeJsonLexContextCstringLen(JsonLexContext *lex, char *json,
|
||||||
|
int len, int encoding, bool need_escapes)
|
||||||
{
|
{
|
||||||
JsonLexContext *lex = palloc0(sizeof(JsonLexContext));
|
if (lex == NULL)
|
||||||
|
{
|
||||||
|
lex = palloc0(sizeof(JsonLexContext));
|
||||||
|
lex->flags |= JSONLEX_FREE_STRUCT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
memset(lex, 0, sizeof(JsonLexContext));
|
||||||
|
|
||||||
lex->input = lex->token_terminator = lex->line_start = json;
|
lex->input = lex->token_terminator = lex->line_start = json;
|
||||||
lex->line_number = 1;
|
lex->line_number = 1;
|
||||||
lex->input_length = len;
|
lex->input_length = len;
|
||||||
lex->input_encoding = encoding;
|
lex->input_encoding = encoding;
|
||||||
if (need_escapes)
|
if (need_escapes)
|
||||||
|
{
|
||||||
lex->strval = makeStringInfo();
|
lex->strval = makeStringInfo();
|
||||||
|
lex->flags |= JSONLEX_FREE_STRVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return lex;
|
return lex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Free memory in a JsonLexContext. There's no need for this if a *lex
|
||||||
|
* pointer was given when the object was made and need_escapes was false,
|
||||||
|
* or (in backend environment) a memory context delete/reset is imminent.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
freeJsonLexContext(JsonLexContext *lex)
|
||||||
|
{
|
||||||
|
if (lex->flags & JSONLEX_FREE_STRVAL)
|
||||||
|
{
|
||||||
|
pfree(lex->strval->data);
|
||||||
|
pfree(lex->strval);
|
||||||
|
}
|
||||||
|
if (lex->flags & JSONLEX_FREE_STRUCT)
|
||||||
|
pfree(lex);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pg_parse_json
|
* pg_parse_json
|
||||||
*
|
*
|
||||||
|
|
|
@ -70,7 +70,11 @@ typedef enum JsonParseErrorType
|
||||||
* token_terminator and prev_token_terminator point to the character
|
* token_terminator and prev_token_terminator point to the character
|
||||||
* AFTER the end of the token, i.e. where there would be a nul byte
|
* AFTER the end of the token, i.e. where there would be a nul byte
|
||||||
* if we were using nul-terminated strings.
|
* if we were using nul-terminated strings.
|
||||||
|
*
|
||||||
|
* JSONLEX_FREE_STRUCT/STRVAL are used to drive freeJsonLexContext.
|
||||||
*/
|
*/
|
||||||
|
#define JSONLEX_FREE_STRUCT (1 << 0)
|
||||||
|
#define JSONLEX_FREE_STRVAL (1 << 1)
|
||||||
typedef struct JsonLexContext
|
typedef struct JsonLexContext
|
||||||
{
|
{
|
||||||
char *input;
|
char *input;
|
||||||
|
@ -81,6 +85,7 @@ typedef struct JsonLexContext
|
||||||
char *prev_token_terminator;
|
char *prev_token_terminator;
|
||||||
JsonTokenType token_type;
|
JsonTokenType token_type;
|
||||||
int lex_level;
|
int lex_level;
|
||||||
|
bits32 flags;
|
||||||
int line_number; /* line number, starting from 1 */
|
int line_number; /* line number, starting from 1 */
|
||||||
char *line_start; /* where that line starts within input */
|
char *line_start; /* where that line starts within input */
|
||||||
StringInfo strval;
|
StringInfo strval;
|
||||||
|
@ -151,15 +156,26 @@ extern JsonParseErrorType json_count_array_elements(JsonLexContext *lex,
|
||||||
int *elements);
|
int *elements);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* constructor for JsonLexContext, with or without strval element.
|
* initializer for JsonLexContext.
|
||||||
* If supplied, the strval element will contain a de-escaped version of
|
*
|
||||||
* the lexeme. However, doing this imposes a performance penalty, so
|
* If a valid 'lex' pointer is given, it is initialized. This can be used
|
||||||
* it should be avoided if the de-escaped lexeme is not required.
|
* for stack-allocated structs, saving overhead. If NULL is given, a new
|
||||||
|
* struct is allocated.
|
||||||
|
*
|
||||||
|
* If need_escapes is true, ->strval stores the unescaped lexemes.
|
||||||
|
* Unescaping is expensive, so only request it when necessary.
|
||||||
|
*
|
||||||
|
* If need_escapes is true or lex was given as NULL, then the caller is
|
||||||
|
* responsible for freeing the returned struct, either by calling
|
||||||
|
* freeJsonLexContext() or (in backend environment) via memory context
|
||||||
|
* cleanup.
|
||||||
*/
|
*/
|
||||||
extern JsonLexContext *makeJsonLexContextCstringLen(char *json,
|
extern JsonLexContext *makeJsonLexContextCstringLen(JsonLexContext *lex,
|
||||||
|
char *json,
|
||||||
int len,
|
int len,
|
||||||
int encoding,
|
int encoding,
|
||||||
bool need_escapes);
|
bool need_escapes);
|
||||||
|
extern void freeJsonLexContext(JsonLexContext *lex);
|
||||||
|
|
||||||
/* lex one token */
|
/* lex one token */
|
||||||
extern JsonParseErrorType json_lex(JsonLexContext *lex);
|
extern JsonParseErrorType json_lex(JsonLexContext *lex);
|
||||||
|
|
|
@ -36,8 +36,8 @@ 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 */
|
/* build a JsonLexContext from a text datum; see also freeJsonLexContext */
|
||||||
extern JsonLexContext *makeJsonLexContext(text *json, bool need_escapes);
|
extern JsonLexContext *makeJsonLexContext(JsonLexContext *lex, text *json, bool need_escapes);
|
||||||
|
|
||||||
/* try to parse json, and errsave(escontext) on failure */
|
/* try to parse json, and errsave(escontext) on failure */
|
||||||
extern bool pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
|
extern bool pg_parse_json_or_errsave(JsonLexContext *lex, JsonSemAction *sem,
|
||||||
|
|
Loading…
Reference in New Issue