{
char *json = PG_GETARG_CSTRING(0);
text *result = cstring_to_text(json);
- JsonLexContext *lex;
+ JsonLexContext lex;
/* validate it */
- lex = makeJsonLexContext(result, false);
- if (!pg_parse_json_or_errsave(lex, &nullSemAction, fcinfo->context))
+ makeJsonLexContext(&lex, result, false);
+ if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
PG_RETURN_NULL();
/* Internal representation is the same as text */
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
char *str;
int nbytes;
- JsonLexContext *lex;
+ JsonLexContext lex;
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
/* Validate it. */
- lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
- pg_parse_json_or_ereport(lex, &nullSemAction);
+ makeJsonLexContextCstringLen(&lex, str, nbytes, GetDatabaseEncoding(),
+ false);
+ pg_parse_json_or_ereport(&lex, &nullSemAction);
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
}
bool
json_validate(text *json, bool check_unique_keys, bool throw_error)
{
- JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
+ JsonLexContext lex;
JsonSemAction uniqueSemAction = {0};
JsonUniqueParsingState state;
JsonParseErrorType result;
+ makeJsonLexContext(&lex, json, check_unique_keys);
+
if (check_unique_keys)
{
- state.lex = lex;
+ state.lex = &lex;
state.stack = NULL;
state.id_counter = 0;
state.unique = true;
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 (throw_error)
- json_errsave_error(result, lex, NULL);
+ json_errsave_error(result, &lex, NULL);
return false; /* invalid json */
}
return false; /* not unique keys */
}
+ if (check_unique_keys)
+ freeJsonLexContext(&lex);
+
return true; /* ok */
}
json_typeof(PG_FUNCTION_ARGS)
{
text *json = PG_GETARG_TEXT_PP(0);
- JsonLexContext *lex = makeJsonLexContext(json, false);
+ JsonLexContext lex;
char *type;
- JsonTokenType tok;
JsonParseErrorType result;
/* 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)
- json_errsave_error(result, lex, NULL);
- tok = lex->token_type;
+ json_errsave_error(result, &lex, NULL);
- switch (tok)
+ switch (lex.token_type)
{
case JSON_TOKEN_OBJECT_START:
type = "object";
type = "null";
break;
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));
* directly.
*/
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
*/
json = pg_detoast_datum_packed(json);
- return makeJsonLexContextCstringLen(VARDATA_ANY(json),
+ return makeJsonLexContextCstringLen(lex,
+ VARDATA_ANY(json),
VARSIZE_ANY_EXHDR(json),
GetDatabaseEncoding(),
need_escapes);
if (SRF_IS_FIRSTCALL())
{
text *json = PG_GETARG_TEXT_PP(0);
- JsonLexContext *lex = makeJsonLexContext(json, true);
+ JsonLexContext lex;
JsonSemAction *sem;
MemoryContext oldcontext;
state = palloc(sizeof(OkeysState));
sem = palloc0(sizeof(JsonSemAction));
- state->lex = lex;
+ state->lex = makeJsonLexContext(&lex, json, true);
state->result_size = 256;
state->result_count = 0;
state->sent_count = 0;
sem->object_field_start = okeys_object_field_start;
/* 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 */
- pfree(lex->strval->data);
- pfree(lex->strval);
- pfree(lex);
+ freeJsonLexContext(&lex);
pfree(sem);
MemoryContextSwitchTo(oldcontext);
int npath,
bool normalize_results)
{
- JsonLexContext *lex = makeJsonLexContext(json, true);
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
GetState *state = palloc0(sizeof(GetState));
Assert(npath >= 0);
- state->lex = lex;
+ state->lex = makeJsonLexContext(NULL, json, true);
+
/* is it "_as_text" variant? */
state->normalize_results = normalize_results;
state->npath = npath;
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;
}
{
text *json = PG_GETARG_TEXT_PP(0);
AlenState *state;
- JsonLexContext *lex;
+ JsonLexContext lex;
JsonSemAction *sem;
- lex = makeJsonLexContext(json, false);
state = palloc0(sizeof(AlenState));
- sem = palloc0(sizeof(JsonSemAction));
-
+ state->lex = makeJsonLexContext(&lex, json, false);
/* palloc0 does this for us */
#if 0
state->count = 0;
#endif
- state->lex = lex;
+ sem = palloc0(sizeof(JsonSemAction));
sem->semstate = (void *) state;
sem->object_start = alen_object_start;
sem->scalar = alen_scalar;
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);
}
each_worker(FunctionCallInfo fcinfo, bool as_text)
{
text *json = PG_GETARG_TEXT_PP(0);
- JsonLexContext *lex;
+ JsonLexContext lex;
JsonSemAction *sem;
ReturnSetInfo *rsi;
EachState *state;
- lex = makeJsonLexContext(json, true);
state = palloc0(sizeof(EachState));
sem = palloc0(sizeof(JsonSemAction));
state->normalize_results = as_text;
state->next_scalar = false;
- state->lex = lex;
+ state->lex = makeJsonLexContext(&lex, json, true);
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"json_each temporary cxt",
ALLOCSET_DEFAULT_SIZES);
- pg_parse_json_or_ereport(lex, sem);
+ pg_parse_json_or_ereport(&lex, sem);
MemoryContextDelete(state->tmp_cxt);
+ freeJsonLexContext(&lex);
PG_RETURN_NULL();
}
elements_worker(FunctionCallInfo fcinfo, const char *funcname, bool as_text)
{
text *json = PG_GETARG_TEXT_PP(0);
-
- /* elements only needs escaped strings when as_text */
- JsonLexContext *lex = makeJsonLexContext(json, as_text);
+ JsonLexContext lex;
JsonSemAction *sem;
ReturnSetInfo *rsi;
ElementsState *state;
+ /* elements only needs escaped strings when as_text */
+ makeJsonLexContext(&lex, json, as_text);
+
state = palloc0(sizeof(ElementsState));
sem = palloc0(sizeof(JsonSemAction));
state->function_name = funcname;
state->normalize_results = as_text;
state->next_scalar = false;
- state->lex = lex;
+ state->lex = &lex;
state->tmp_cxt = AllocSetContextCreate(CurrentMemoryContext,
"json_array_elements temporary cxt",
ALLOCSET_DEFAULT_SIZES);
- pg_parse_json_or_ereport(lex, sem);
+ pg_parse_json_or_ereport(&lex, sem);
MemoryContextDelete(state->tmp_cxt);
+ freeJsonLexContext(&lex);
PG_RETURN_NULL();
}
PopulateArrayState state;
JsonSemAction sem;
- state.lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
+ state.lex = makeJsonLexContextCstringLen(NULL, json, len,
+ GetDatabaseEncoding(), true);
state.ctx = ctx;
memset(&sem, 0, sizeof(sem));
/* number of dimensions should be already known */
Assert(ctx->ndims > 0 && ctx->dims);
- pfree(state.lex);
+ freeJsonLexContext(state.lex);
}
/*
HASHCTL ctl;
HTAB *tab;
JHashState *state;
- JsonLexContext *lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
JsonSemAction *sem;
ctl.keysize = NAMEDATALEN;
state->function_name = funcname;
state->hash = tab;
- state->lex = lex;
+ state->lex = makeJsonLexContextCstringLen(NULL, json, len,
+ GetDatabaseEncoding(), true);
sem->semstate = (void *) state;
sem->array_start = hash_array_start;
sem->object_field_start = hash_object_field_start;
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;
}
if (is_json)
{
text *json = PG_GETARG_TEXT_PP(json_arg_num);
- JsonLexContext *lex;
+ JsonLexContext lex;
JsonSemAction *sem;
sem = palloc0(sizeof(JsonSemAction));
- lex = makeJsonLexContext(json, true);
+ makeJsonLexContext(&lex, json, true);
sem->semstate = (void *) state;
sem->array_start = populate_recordset_array_start;
sem->object_start = populate_recordset_object_start;
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
{
{
text *json = PG_GETARG_TEXT_PP(0);
StripnullState *state;
- JsonLexContext *lex;
+ JsonLexContext lex;
JsonSemAction *sem;
- lex = makeJsonLexContext(json, true);
state = palloc0(sizeof(StripnullState));
sem = palloc0(sizeof(JsonSemAction));
+ state->lex = makeJsonLexContext(&lex, json, true);
state->strval = makeStringInfo();
state->skip_next_null = false;
- state->lex = lex;
sem->semstate = (void *) state;
sem->object_start = sn_object_start;
sem->array_element_start = sn_array_element_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,
state->strval->len));
iterate_json_values(text *json, uint32 flags, void *action_state,
JsonIterateStringValuesAction action)
{
- JsonLexContext *lex = makeJsonLexContext(json, true);
+ JsonLexContext lex;
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
IterateJsonStringValuesState *state = palloc0(sizeof(IterateJsonStringValuesState));
- state->lex = lex;
+ state->lex = makeJsonLexContext(&lex, json, true);
state->action = action;
state->action_state = action_state;
state->flags = flags;
sem->scalar = iterate_values_scalar;
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);
}
/*
transform_json_string_values(text *json, void *action_state,
JsonTransformStringValuesAction transform_action)
{
- JsonLexContext *lex = makeJsonLexContext(json, true);
+ JsonLexContext lex;
JsonSemAction *sem = palloc0(sizeof(JsonSemAction));
TransformJsonStringValuesState *state = palloc0(sizeof(TransformJsonStringValuesState));
- state->lex = lex;
+ state->lex = makeJsonLexContext(&lex, json, true);
state->strval = makeStringInfo();
state->action = transform_action;
state->action_state = action_state;
sem->array_element_start = transform_string_values_array_element_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);
}
JsonTokenType
json_get_first_token(text *json, bool throw_error)
{
- JsonLexContext *lex;
+ JsonLexContext lex;
JsonParseErrorType result;
- lex = makeJsonLexContext(json, false);
+ makeJsonLexContext(&lex, json, false);
/* Lex exactly one token from the input and check its type. */
- result = json_lex(lex);
+ result = json_lex(&lex);
if (result == JSON_SUCCESS)
- return lex->token_type;
+ return lex.token_type;
if (throw_error)
- json_errsave_error(result, lex, NULL);
+ json_errsave_error(result, &lex, NULL);
return JSON_TOKEN_INVALID; /* invalid json */
}
/*
* 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 really required.
+ * 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 caller is
+ * responsible for freeing the returned struct, either by calling
+ * freeJsonLexContext() or (in backend environment) via memory context
+ * cleanup.
*/
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->line_number = 1;
lex->input_length = len;
lex->input_encoding = encoding;
if (need_escapes)
+ {
lex->strval = makeStringInfo();
+ lex->flags |= JSONLEX_FREE_STRVAL;
+ }
+
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
*