SQL/JSON: Correct jsonpath variable name matching
authorAmit Langote <[email protected]>
Wed, 19 Jun 2024 06:22:06 +0000 (15:22 +0900)
committerAmit Langote <[email protected]>
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);