SQL/JSON: Correct jsonpath variable name matching
authorAmit Langote <amitlan@postgresql.org>
Wed, 19 Jun 2024 06:22:06 +0000 (15:22 +0900)
committerAmit Langote <amitlan@postgresql.org>
Wed, 19 Jun 2024 06:22:06 +0000 (15:22 +0900)
Previously, GetJsonPathVar() allowed a jsonpath expression to
reference any prefix of a PASSING variable's name. For example, the
following query would incorrectly work:

SELECT JSON_QUERY(context_item, jsonpath '$xy' PASSING val AS xyz);

The fix ensures that the length of the variable name mentioned in a
jsonpath expression matches exactly with the name of the PASSING
variable before comparing the strings using strncmp().

Reported-by: Alvaro Herrera (off-list)
Discussion: https://postgr.es/m/CA+HiwqFGkLWMvELBH6E4SQ45qUHthgcRH6gCJL20OsYDRtFx_w@mail.gmail.com

src/backend/executor/execExpr.c
src/backend/utils/adt/jsonpath_exec.c
src/include/utils/jsonpath.h
src/test/regress/expected/sqljson_queryfuncs.out
src/test/regress/sql/sqljson_queryfuncs.sql

index b9ebc827a74bcc739218feec718e1de0660ae882..2bf86d06ef5d5010724137e061e2058e11ad0d9a 100644 (file)
@@ -4278,6 +4278,7 @@ ExecInitJsonExpr(JsonExpr *jsexpr, ExprState *state,
                JsonPathVariable *var = palloc(sizeof(*var));
 
                var->name = argname->sval;
+               var->namelen = strlen(var->name);
                var->typid = exprType((Node *) argexpr);
                var->typmod = exprTypmod((Node *) argexpr);
 
index c30d059a7627292a6cf2cc9e2985efea24e3721a..d79c9298227a064740764a2581322991d9aea3da 100644 (file)
@@ -2994,7 +2994,8 @@ GetJsonPathVar(void *cxt, char *varName, int varNameLen,
        {
                JsonPathVariable *curvar = lfirst(lc);
 
-               if (!strncmp(curvar->name, varName, varNameLen))
+               if (curvar->namelen == varNameLen &&
+                       strncmp(curvar->name, varName, varNameLen) == 0)
                {
                        var = curvar;
                        break;
@@ -4118,6 +4119,7 @@ JsonTableInitOpaque(TableFuncScanState *state, int natts)
                        JsonPathVariable *var = palloc(sizeof(*var));
 
                        var->name = pstrdup(name->sval);
+                       var->namelen = strlen(var->name);
                        var->typid = exprType((Node *) state->expr);
                        var->typmod = exprTypmod((Node *) state->expr);
 
index 9d2b8533d54852d07a5525b9c3478f032e3f104b..ee35698d083effb8dd2a2e480da35cfc730f40bb 100644 (file)
@@ -287,6 +287,7 @@ extern bool jspConvertRegexFlags(uint32 xflags, int *result,
 typedef struct JsonPathVariable
 {
        char       *name;
+       int                     namelen;                /* strlen(name) as cache for GetJsonPathVar() */
        Oid                     typid;
        int32           typmod;
        Datum           value;
index 21e0fc64170baaa55c33b8ff695f16b7ae23ff9b..98117b346d46de5d7c2b42a3ace84c418fd276c1 100644 (file)
@@ -1334,3 +1334,14 @@ SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
  "aaa"
 (1 row)
 
+-- Test PASSING argument parsing
+SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xy);
+ERROR:  could not find jsonpath variable "xyz"
+SELECT JSON_QUERY(jsonb 'null', '$xy' PASSING 1 AS xyz);
+ERROR:  could not find jsonpath variable "xy"
+SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xyz);
+ json_query 
+------------
+ 1
+(1 row)
+
index c46489e2dd703d23fd4f9a1fac8f2317b4312c2c..d9dbb1ceaacf02051a11a4d5e4ee300a3c48c2e8 100644 (file)
@@ -454,3 +454,8 @@ SELECT JSON_QUERY(NULL FORMAT JSON, '$');
 -- Test non-const jsonpath
 CREATE TEMP TABLE jsonpaths (path) AS SELECT '$';
 SELECT json_value('"aaa"', path RETURNING json) FROM jsonpaths;
+
+-- Test PASSING argument parsing
+SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xy);
+SELECT JSON_QUERY(jsonb 'null', '$xy' PASSING 1 AS xyz);
+SELECT JSON_QUERY(jsonb 'null', '$xyz' PASSING 1 AS xyz);