Skip to content

Commit 1c99cde

Browse files
committed
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
1 parent a8a968a commit 1c99cde

File tree

7 files changed

+153
-86
lines changed

7 files changed

+153
-86
lines changed

src/backend/utils/adt/json.c

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ json_in(PG_FUNCTION_ARGS)
106106
{
107107
char *json = PG_GETARG_CSTRING(0);
108108
text *result = cstring_to_text(json);
109-
JsonLexContext *lex;
109+
JsonLexContext lex;
110110

111111
/* validate it */
112-
lex = makeJsonLexContext(result, false);
113-
if (!pg_parse_json_or_errsave(lex, &nullSemAction, fcinfo->context))
112+
makeJsonLexContext(&lex, result, false);
113+
if (!pg_parse_json_or_errsave(&lex, &nullSemAction, fcinfo->context))
114114
PG_RETURN_NULL();
115115

116116
/* Internal representation is the same as text */
@@ -152,13 +152,14 @@ json_recv(PG_FUNCTION_ARGS)
152152
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
153153
char *str;
154154
int nbytes;
155-
JsonLexContext *lex;
155+
JsonLexContext lex;
156156

157157
str = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
158158

159159
/* Validate it. */
160-
lex = makeJsonLexContextCstringLen(str, nbytes, GetDatabaseEncoding(), false);
161-
pg_parse_json_or_ereport(lex, &nullSemAction);
160+
makeJsonLexContextCstringLen(&lex, str, nbytes, GetDatabaseEncoding(),
161+
false);
162+
pg_parse_json_or_ereport(&lex, &nullSemAction);
162163

163164
PG_RETURN_TEXT_P(cstring_to_text_with_len(str, nbytes));
164165
}
@@ -1625,14 +1626,16 @@ json_unique_object_field_start(void *_state, char *field, bool isnull)
16251626
bool
16261627
json_validate(text *json, bool check_unique_keys, bool throw_error)
16271628
{
1628-
JsonLexContext *lex = makeJsonLexContext(json, check_unique_keys);
1629+
JsonLexContext lex;
16291630
JsonSemAction uniqueSemAction = {0};
16301631
JsonUniqueParsingState state;
16311632
JsonParseErrorType result;
16321633

1634+
makeJsonLexContext(&lex, json, check_unique_keys);
1635+
16331636
if (check_unique_keys)
16341637
{
1635-
state.lex = lex;
1638+
state.lex = &lex;
16361639
state.stack = NULL;
16371640
state.id_counter = 0;
16381641
state.unique = true;
@@ -1644,12 +1647,12 @@ json_validate(text *json, bool check_unique_keys, bool throw_error)
16441647
uniqueSemAction.object_end = json_unique_object_end;
16451648
}
16461649

1647-
result = pg_parse_json(lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
1650+
result = pg_parse_json(&lex, check_unique_keys ? &uniqueSemAction : &nullSemAction);
16481651

16491652
if (result != JSON_SUCCESS)
16501653
{
16511654
if (throw_error)
1652-
json_errsave_error(result, lex, NULL);
1655+
json_errsave_error(result, &lex, NULL);
16531656

16541657
return false; /* invalid json */
16551658
}
@@ -1664,6 +1667,9 @@ json_validate(text *json, bool check_unique_keys, bool throw_error)
16641667
return false; /* not unique keys */
16651668
}
16661669

1670+
if (check_unique_keys)
1671+
freeJsonLexContext(&lex);
1672+
16671673
return true; /* ok */
16681674
}
16691675

@@ -1683,18 +1689,17 @@ Datum
16831689
json_typeof(PG_FUNCTION_ARGS)
16841690
{
16851691
text *json = PG_GETARG_TEXT_PP(0);
1686-
JsonLexContext *lex = makeJsonLexContext(json, false);
1692+
JsonLexContext lex;
16871693
char *type;
1688-
JsonTokenType tok;
16891694
JsonParseErrorType result;
16901695

16911696
/* Lex exactly one token from the input and check its type. */
1692-
result = json_lex(lex);
1697+
makeJsonLexContext(&lex, json, false);
1698+
result = json_lex(&lex);
16931699
if (result != JSON_SUCCESS)
1694-
json_errsave_error(result, lex, NULL);
1695-
tok = lex->token_type;
1700+
json_errsave_error(result, &lex, NULL);
16961701

1697-
switch (tok)
1702+
switch (lex.token_type)
16981703
{
16991704
case JSON_TOKEN_OBJECT_START:
17001705
type = "object";
@@ -1716,7 +1721,7 @@ json_typeof(PG_FUNCTION_ARGS)
17161721
type = "null";
17171722
break;
17181723
default:
1719-
elog(ERROR, "unexpected json token: %d", tok);
1724+
elog(ERROR, "unexpected json token: %d", lex.token_type);
17201725
}
17211726

17221727
PG_RETURN_TEXT_P(cstring_to_text(type));

src/backend/utils/adt/jsonb.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,13 +252,13 @@ jsonb_typeof(PG_FUNCTION_ARGS)
252252
static inline Datum
253253
jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
254254
{
255-
JsonLexContext *lex;
255+
JsonLexContext lex;
256256
JsonbInState state;
257257
JsonSemAction sem;
258258

259259
memset(&state, 0, sizeof(state));
260260
memset(&sem, 0, sizeof(sem));
261-
lex = makeJsonLexContextCstringLen(json, len, GetDatabaseEncoding(), true);
261+
makeJsonLexContextCstringLen(&lex, json, len, GetDatabaseEncoding(), true);
262262

263263
state.unique_keys = unique_keys;
264264
state.escontext = escontext;
@@ -271,7 +271,7 @@ jsonb_from_cstring(char *json, int len, bool unique_keys, Node *escontext)
271271
sem.scalar = jsonb_in_scalar;
272272
sem.object_field_start = jsonb_in_object_field_start;
273273

274-
if (!pg_parse_json_or_errsave(lex, &sem, escontext))
274+
if (!pg_parse_json_or_errsave(&lex, &sem, escontext))
275275
return (Datum) 0;
276276

277277
/* 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,
755755
case JSONTYPE_JSON:
756756
{
757757
/* parse the json right into the existing result object */
758-
JsonLexContext *lex;
758+
JsonLexContext lex;
759759
JsonSemAction sem;
760760
text *json = DatumGetTextPP(val);
761761

762-
lex = makeJsonLexContext(json, true);
762+
makeJsonLexContext(&lex, json, true);
763763

764764
memset(&sem, 0, sizeof(sem));
765765

@@ -772,7 +772,8 @@ datum_to_jsonb_internal(Datum val, bool is_null, JsonbInState *result,
772772
sem.scalar = jsonb_in_scalar;
773773
sem.object_field_start = jsonb_in_object_field_start;
774774

775-
pg_parse_json_or_ereport(lex, &sem);
775+
pg_parse_json_or_ereport(&lex, &sem);
776+
freeJsonLexContext(&lex);
776777
}
777778
break;
778779
case JSONTYPE_JSONB:

0 commit comments

Comments
 (0)