From: Andrey Rachitskiy Date: Sat, 1 Aug 2026 21:23:00 +0500 Subject: [PATCH] Fix DCH format-cache recycle to update std flag DCH_cache_getnew() set ent->std only when allocating a new entry, not when recycling an old one. DCH_cache_search() matches on (str, std), so a format tree parsed in one mode could be reused under the other. That let jsonpath .datetime() (SQL/JSON standard mode) reuse a format tree built by lenient to_char(), once enough distinct format strings had filled the cache and forced an eviction. Author: Andrey Rachitskiy Reported-by: Michael Malis Discussion: https://www.postgresql.org/message-id/19594-5d9bdc019e3f7f6e%40postgresql.org --- diff --git a/src/backend/utils/adt/formatting.c b/src/backend/utils/adt/formatting.c index d52d71b0a8c..93a2336d951 100644 --- a/src/backend/utils/adt/formatting.c +++ b/src/backend/utils/adt/formatting.c @@ -3814,6 +3814,7 @@ DCH_cache_getnew(const char *str, bool std) #endif old->valid = false; strlcpy(old->str, str, DCH_CACHE_SIZE + 1); + old->std = std; old->age = (++DCHCounter); /* caller is expected to fill format, then set valid */ return old; diff --git a/src/test/regress/expected/jsonb_jsonpath.out b/src/test/regress/expected/jsonb_jsonpath.out index c7b8c36c842..d512328e836 100644 --- a/src/test/regress/expected/jsonb_jsonpath.out +++ b/src/test/regress/expected/jsonb_jsonpath.out @@ -1782,6 +1782,25 @@ select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2 ERROR: unmatched format character "T" select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")'); ERROR: unmatched format character "T" +-- DCH cache recycle must preserve std vs lenient mode +SELECT count(*) FROM ( + SELECT jsonb_path_query(to_jsonb('x' || g::text || '1234'), + format('$.datetime("\"x%s\"HH24MI")', g)::jsonpath) + FROM generate_series(1, 20) g +) s; + count +------- + 20 +(1 row) + +SELECT to_char(timestamp '2000-01-01 12:34:00', 'HH24zMI'); + to_char +--------- + 12z34 +(1 row) + +SELECT jsonb_path_query('"12z34"'::jsonb, '$.datetime("HH24zMI")'); +ERROR: invalid datetime format separator: "z" -- Test .bigint() select jsonb_path_query('null', '$.bigint()'); ERROR: jsonpath item method .bigint() can only be applied to a string or numeric value diff --git a/src/test/regress/sql/jsonb_jsonpath.sql b/src/test/regress/sql/jsonb_jsonpath.sql index c37dc3817ff..209ca470a9f 100644 --- a/src/test/regress/sql/jsonb_jsonpath.sql +++ b/src/test/regress/sql/jsonb_jsonpath.sql @@ -383,6 +383,15 @@ select jsonb_path_query('"10-03-2017T12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH2 select jsonb_path_query('"10-03-2017t12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")'); select jsonb_path_query('"10-03-2017 12:34:56"', '$.datetime("dd-mm-yyyy\"T\"HH24:MI:SS")'); +-- DCH cache recycle must preserve std vs lenient mode +SELECT count(*) FROM ( + SELECT jsonb_path_query(to_jsonb('x' || g::text || '1234'), + format('$.datetime("\"x%s\"HH24MI")', g)::jsonpath) + FROM generate_series(1, 20) g +) s; +SELECT to_char(timestamp '2000-01-01 12:34:00', 'HH24zMI'); +SELECT jsonb_path_query('"12z34"'::jsonb, '$.datetime("HH24zMI")'); + -- Test .bigint() select jsonb_path_query('null', '$.bigint()'); select jsonb_path_query('true', '$.bigint()');