Re: SQL/JSON: JSON_TRANSFORM (SQL standard, subclause 6.44)

From: Srinath Reddy Sadipiralla <srinath2133(at)gmail(dot)com>
To: Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
Cc: pgsql-hackers(at)lists(dot)postgresql(dot)org
Subject: Re: SQL/JSON: JSON_TRANSFORM (SQL standard, subclause 6.44)
Date: 2026-07-08 17:23:46
Message-ID: CAFC+b6oAK75SV_1TRz9RDA7D-LeR9VDXSoLS1NSXdUSy4nYAYw@mail.gmail.com
Views: Whole Thread | Raw Message | Download mbox | Resend email
Thread:
Lists: pgsql-hackers

Hi Zsolt, thanks for looking into this.

On Fri, Jul 3, 2026 at 3:48 AM Zsolt Parragi <zsolt(dot)parragi(at)percona(dot)com>
wrote:

>
> + analyzed_jst_action->pathspec = coerced_path_spec;
> + jsexpr->action = analyzed_jst_action;
>
> Shouldn't jsexpr->patch_spec also be set? This currently crashes:
>
> CREATE TABLE t (id int, j jsonb);
> CREATE INDEX ON t ((JSON_TRANSFORM(j, REMOVE '$.a'))); -- crash
>

JSON_TRANSFORM doesn't set jsexpr->patch_spec, it because doesn't
use it, but yeah the same reason caused the segfault crash in
contain_mutable_functions_walker, so i used the jsexpr->action->pathspec
which JSON_TRANSFORM uses for each operation.

@@ -432,11 +432,17 @@ contain_mutable_functions_walker(Node *node, void
*context)
{
JsonExpr *jexpr = castNode(JsonExpr, node);
Const *cnst;
+ Node *path_spec;

- if (!IsA(jexpr->path_spec, Const))
+ if(jexpr->action)
+ path_spec = jexpr->action->pathspec;
+ else
+ path_spec = jexpr->path_spec;
+
+ if (!IsA(path_spec, Const))
return true;

- cnst = castNode(Const, jexpr->path_spec);
+ cnst = castNode(Const, path_spec);

Assert(cnst->consttype == JSONPATHOID);
if (cnst->constisnull)

>
> Another thing I noticed is that deparse support is missing:
>
> EXPLAIN (VERBOSE) SELECT JSON_TRANSFORM('{"a":1}'::jsonb, REMOVE '$.a');
>

This was missing because I didn't teach get_rule_expr about JSON_TRANSFORM
at all,
so now I added OPs, pathspec, value_expr and behaviours for each action
into the
expression parser.

EXPLAIN (VERBOSE) SELECT JSON_TRANSFORM('{"a":1}'::jsonb, REMOVE '$.a');
QUERY PLAN

-------------------------------------------------------------------------------
Result (cost=0.00..0.01 rows=1 width=32)
Output: JSON_TRANSFORM('{"a": 1}'::jsonb, REMOVE '$."a"' IGNORE ON
MISSING)
(2 rows)

>
>
> Shouldn't the following statement work?
>
> SELECT JSON_TRANSFORM('{"arr":[{"a":1}]}', REPLACE 'lax $.arr.a' = '9');
>

This is a valid statement, target path walker currently ignores the mode
and only
goes through objects, so it silently no-ops, that's the gap. To close it,
the walker needs
when a .key/.* step lands on an array, unwrap in lax mode (recurse into
each element),
error in strict mode, will work on this and will include the above changes
into next patch
set, along with some other todos.

--
Thanks :)
Srinath Reddy Sadipiralla
EDB: https://www.enterprisedb.com/

In response to

Browse pgsql-hackers by date

  From Date Subject
Next Message Jacob Champion 2026-07-08 17:29:45 Re: PG20 Minimum Dependency Thread
Previous Message Christoph Berg 2026-07-08 17:15:47 Re: libpq: decouple the .pgpass lookup port from the connection port