public inbox for [email protected]
help / color / mirror / Atom feedEXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
9+ messages / 2 participants
[nested] [flat]
* EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
@ 2026-07-02 16:58 Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Thom Brown @ 2026-07-02 16:58 UTC (permalink / raw)
To: pgsql-bugs
Hi,
EXPLAIN (VERBOSE) throws an internal error when a query's target list
contains an SQL/JSON aggregate (JSON_ARRAYAGG or JSON_OBJECTAGG)
together with a window function (although I suspect it's for anything
that forces a plan node on top of the aggregation node). I ran into it
when trying to construct an obscenely complex SELECT statement.
The query itself executes fine. Only the attempt to deparse its plan
for EXPLAIN (VERBOSE) fails.
I'm using the latest master branch, but I believe the bug is also
present in 16, 17 and 18, since the responsible code has been
unchanged since SQL/JSON constructors were added by commit 7081ac46ace
in 2023.
Debian, Linux 6.12.90 (6.12.90+deb13.1-amd64), x86_64
gcc (Debian 14.2.0-19) 14.2.0
configure options:
--with-llvm --with-libxml --with-ossp-uuid --with-lz4
--with-liburing --with-openssl
No non-default server settings are required to reproduce.
Reproducible test case:
CREATE TEMP TABLE t (g int, name text);
INSERT INTO t VALUES (1, 'a'), (1, 'b'), (2, 'c');
EXPLAIN (VERBOSE)
SELECT g,
JSON_ARRAYAGG(name RETURNING jsonb) AS names,
row_number() OVER (ORDER BY g) AS rn
FROM t
GROUP BY g;
Result:
ERROR: invalid JsonConstructorExpr underlying node type: 6
The same happens with JSON_OBJECTAGG. Note that without EXPLAIN
(VERBOSE), or just using EXPLAIN with any options except VERBOSE, the
query runs and returns correct results.
Removing the window function makes EXPLAIN (VERBOSE) work, because
then a single Aggregate node computes the aggregate and there is no
upper node to reference it from.
Expected result:
WindowAgg
Output: g, JSON_ARRAYAGG(name RETURNING jsonb), row_number() OVER w1
...
-> HashAggregate
Output: g, jsonb_agg_strict(name)
Regards
Thom
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
@ 2026-07-02 22:10 ` Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Richard Guo @ 2026-07-02 22:10 UTC (permalink / raw)
To: Thom Brown <[email protected]>; +Cc: pgsql-bugs
On Fri, Jul 3, 2026 at 1:58 AM Thom Brown <[email protected]> wrote:
> CREATE TEMP TABLE t (g int, name text);
> INSERT INTO t VALUES (1, 'a'), (1, 'b'), (2, 'c');
>
> EXPLAIN (VERBOSE)
> SELECT g,
> JSON_ARRAYAGG(name RETURNING jsonb) AS names,
> row_number() OVER (ORDER BY g) AS rn
> FROM t
> GROUP BY g;
> ERROR: invalid JsonConstructorExpr underlying node type: 6
Reproduced here. get_json_agg_constructor() expects that ctor->func
is Aggref or WindowFunc, but what it gets here is a Var.
This is because the query has both window functions and grouped
aggregates. make_window_input_target() flattens the final tlist using
pull_var_clause, which pulls the Aggref out of its JsonConstructorExpr
wrapper. Then fix_upper_expr() matches that inner Aggref against the
Agg subplan's tlist and replaces it with an OUTER Var.
A simple fix is:
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -12389,6 +12389,8 @@ get_json_agg_constructor(JsonConstructorExpr
*ctor, deparse_context *context,
get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
funcname, options.data,
is_json_objectagg);
+ else if (IsA(ctor->func, Var))
+ get_rule_expr((Node *) ctor->func, context, false);
else
elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
nodeTag(ctor->func));
Will work on a patch on it.
- Richard
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
@ 2026-07-03 03:06 ` Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Richard Guo @ 2026-07-03 03:06 UTC (permalink / raw)
To: Thom Brown <[email protected]>; +Cc: pgsql-bugs
On Fri, Jul 3, 2026 at 7:10 AM Richard Guo <[email protected]> wrote:
> Reproduced here. get_json_agg_constructor() expects that ctor->func
> is Aggref or WindowFunc, but what it gets here is a Var.
>
> This is because the query has both window functions and grouped
> aggregates. make_window_input_target() flattens the final tlist using
> pull_var_clause, which pulls the Aggref out of its JsonConstructorExpr
> wrapper. Then fix_upper_expr() matches that inner Aggref against the
> Agg subplan's tlist and replaces it with an OUTER Var.
Here is the patch. It's a bit annoying that the original JSON agg
syntax then appears nowhere in the EXPLAIN output. All we get is the
bare jsonb_agg_strict Aggref.
WindowAgg
Output: g, (jsonb_agg_strict(name)), row_number() OVER w1
Window: w1 AS (ROWS UNBOUNDED PRECEDING)
-> HashAggregate
Output: g, jsonb_agg_strict(name)
The reason is that the JsonConstructorExpr wrapper and the Aggref it
wraps end up in different plan nodes, and neither alone suffices to
reconstruct the syntax.
I had an attempt to reconstruct the JSON syntax for the WindowAgg node
by leveraging resolve_special_varno(), and that works. But I don't
know how to do that for the HashAggregate node, because the
JsonConstructorExpr wrapper simply doesn't exist at that plan level.
Maybe we can hack the planner to make make_window_input_target() keep
the JsonConstructorExpr together with its Aggref. But I think that is
too invasive and it changes which node evaluates the wrapper.
So that attempt ended up with:
WindowAgg
Output: g, JSON_ARRAYAGG(name RETURNING jsonb), row_number() OVER w1
Window: w1 AS (ROWS UNBOUNDED PRECEDING)
-> HashAggregate
Output: g, jsonb_agg_strict(name)
But I don't think this is good. It fails to state the fact that the
WindowAgg doesn't compute a JSON aggregate; it passes through a value
that the HashAggregate computed. So I gave up this idea.
- Richard
Attachments:
[application/octet-stream] v1-0001-Fix-EXPLAIN-failure-when-deparsing-SQL-JSON-aggre.patch (7.9K, ../../CAMbWs4-b2By5XFEn-_ZJKgN4-8SBhAoRZJrt4gwmM2ctOQzAyw@mail.gmail.com/2-v1-0001-Fix-EXPLAIN-failure-when-deparsing-SQL-JSON-aggre.patch)
download | inline diff:
From b917e5c2a20dd469da6a9deb248cb5dc42bca511 Mon Sep 17 00:00:00 2001
From: Richard Guo <[email protected]>
Date: Fri, 3 Jul 2026 10:52:29 +0900
Subject: [PATCH v1] Fix EXPLAIN failure when deparsing SQL/JSON aggregates
If an expression containing an aggregate is evaluated above the plan
node that computes the aggregate, as happens with window functions or
with expressions postponed to above the final sort, setrefs.c replaces
the Aggref or WindowFunc with a Var referencing the lower node's
output. For SQL/JSON aggregates such as JSON_ARRAYAGG, deparsing the
containing JsonConstructorExpr then failed with "invalid
JsonConstructorExpr underlying node type", since
get_json_agg_constructor() did not expect a Var there.
Fix by printing the Var, matching how other expressions evaluated
above an aggregate are displayed. It's a bit annoying that the
original JSON aggregate syntax then appears nowhere in the EXPLAIN
output, but there is no good way to print it: the JsonConstructorExpr
wrapper and the Aggref it decorates end up in different plan nodes,
and neither alone suffices to reconstruct the syntax.
---
src/backend/utils/adt/ruleutils.c | 9 +++
src/test/regress/expected/sqljson.out | 95 +++++++++++++++++++++++++++
src/test/regress/sql/sqljson.sql | 35 ++++++++++
3 files changed, 139 insertions(+)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 88de5c0481c..05b769dce78 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -12389,6 +12389,15 @@ get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
funcname, options.data,
is_json_objectagg);
+ else if (IsA(ctor->func, Var))
+ {
+ /*
+ * If the aggregate is computed by a lower plan node, setrefs.c will
+ * have replaced the Aggref or WindowFunc with a Var referencing that
+ * node's output. Just print the Var.
+ */
+ (void) get_variable((Var *) ctor->func, 0, false, context);
+ }
else
elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
nodeTag(ctor->func));
diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out
index 0f337bda325..3e5a0508ab0 100644
--- a/src/test/regress/expected/sqljson.out
+++ b/src/test/regress/expected/sqljson.out
@@ -1757,3 +1757,98 @@ SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
(1 row)
DROP FUNCTION volatile_one, stable_one;
+-- Test deparsing of a JSON aggregate that is computed below a WindowAgg
+-- node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) AS ja,
+ JSON_OBJECTAGG(i: i RETURNING jsonb) AS jo,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+ QUERY PLAN
+--------------------------------------------------------------------------------------------
+ WindowAgg
+ Output: ((i % 2)), (jsonb_agg_strict(i)), (jsonb_object_agg(i, i)), row_number() OVER w1
+ Window: w1 AS (ORDER BY ((i.i % 2)) ROWS UNBOUNDED PRECEDING)
+ -> GroupAggregate
+ Output: ((i % 2)), jsonb_agg_strict(i), jsonb_object_agg(i, i)
+ Group Key: ((i.i % 2))
+ -> Sort
+ Output: ((i % 2)), i
+ Sort Key: ((i.i % 2))
+ -> Function Scan on pg_catalog.generate_series i
+ Output: (i % 2), i
+ Function Call: generate_series(1, 3)
+(12 rows)
+
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) AS ja,
+ JSON_OBJECTAGG(i: i RETURNING jsonb) AS jo,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+ g | ja | jo | rn
+---+--------+------------------+----
+ 0 | [2] | {"2": 2} | 1
+ 1 | [1, 3] | {"1": 1, "3": 3} | 2
+(2 rows)
+
+-- The same, but with the JSON aggregate used as a window function that is
+-- computed below another WindowAgg node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(i RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+ QUERY PLAN
+-----------------------------------------------------------------------
+ WindowAgg
+ Output: (jsonb_agg_strict(i) OVER w1), row_number() OVER w2, i
+ Window: w2 AS (ORDER BY i.i ROWS UNBOUNDED PRECEDING)
+ -> Sort
+ Output: i, (jsonb_agg_strict(i) OVER w1)
+ Sort Key: i.i
+ -> WindowAgg
+ Output: i, jsonb_agg_strict(i) OVER w1
+ Window: w1 AS (ORDER BY i.i)
+ -> Sort
+ Output: i
+ Sort Key: i.i DESC
+ -> Function Scan on pg_catalog.generate_series i
+ Output: i
+ Function Call: generate_series(1, 3)
+(15 rows)
+
+SELECT JSON_ARRAYAGG(i RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+ ja | rn
+-----------+----
+ [3, 2, 1] | 1
+ [3, 2] | 2
+ [3] | 3
+(3 rows)
+
+-- The same, but with the expression containing the JSON aggregate postponed
+-- to above the final sort due to being volatile.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) || to_jsonb(random()) AS ja
+FROM generate_series(1, 3) i
+GROUP BY i % 2
+ORDER BY count(*);
+ QUERY PLAN
+--------------------------------------------------------------------------------
+ Result
+ Output: ((i % 2)), ((jsonb_agg_strict(i)) || to_jsonb(random())), (count(*))
+ -> Sort
+ Output: ((i % 2)), (count(*)), (jsonb_agg_strict(i))
+ Sort Key: (count(*))
+ -> HashAggregate
+ Output: ((i % 2)), count(*), jsonb_agg_strict(i)
+ Group Key: (i.i % 2)
+ -> Function Scan on pg_catalog.generate_series i
+ Output: (i % 2), i
+ Function Call: generate_series(1, 3)
+(11 rows)
+
diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql
index a68747733a1..3a666eca128 100644
--- a/src/test/regress/sql/sqljson.sql
+++ b/src/test/regress/sql/sqljson.sql
@@ -706,3 +706,38 @@ SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': stable_one() RETURNING text) FORMAT
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
DROP FUNCTION volatile_one, stable_one;
+
+-- Test deparsing of a JSON aggregate that is computed below a WindowAgg
+-- node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) AS ja,
+ JSON_OBJECTAGG(i: i RETURNING jsonb) AS jo,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) AS ja,
+ JSON_OBJECTAGG(i: i RETURNING jsonb) AS jo,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+
+-- The same, but with the JSON aggregate used as a window function that is
+-- computed below another WindowAgg node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(i RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+SELECT JSON_ARRAYAGG(i RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+
+-- The same, but with the expression containing the JSON aggregate postponed
+-- to above the final sort due to being volatile.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING jsonb) || to_jsonb(random()) AS ja
+FROM generate_series(1, 3) i
+GROUP BY i % 2
+ORDER BY count(*);
--
2.39.5 (Apple Git-154)
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
@ 2026-07-03 08:36 ` Thom Brown <[email protected]>
2026-07-06 02:17 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Thom Brown @ 2026-07-03 08:36 UTC (permalink / raw)
To: Richard Guo <[email protected]>; +Cc: pgsql-bugs
On Fri, 3 Jul 2026 at 04:06, Richard Guo <[email protected]> wrote:
>
> On Fri, Jul 3, 2026 at 7:10 AM Richard Guo <[email protected]> wrote:
> > Reproduced here. get_json_agg_constructor() expects that ctor->func
> > is Aggref or WindowFunc, but what it gets here is a Var.
> >
> > This is because the query has both window functions and grouped
> > aggregates. make_window_input_target() flattens the final tlist using
> > pull_var_clause, which pulls the Aggref out of its JsonConstructorExpr
> > wrapper. Then fix_upper_expr() matches that inner Aggref against the
> > Agg subplan's tlist and replaces it with an OUTER Var.
>
> Here is the patch. It's a bit annoying that the original JSON agg
> syntax then appears nowhere in the EXPLAIN output. All we get is the
> bare jsonb_agg_strict Aggref.
>
> WindowAgg
> Output: g, (jsonb_agg_strict(name)), row_number() OVER w1
> Window: w1 AS (ROWS UNBOUNDED PRECEDING)
> -> HashAggregate
> Output: g, jsonb_agg_strict(name)
>
> The reason is that the JsonConstructorExpr wrapper and the Aggref it
> wraps end up in different plan nodes, and neither alone suffices to
> reconstruct the syntax.
>
> I had an attempt to reconstruct the JSON syntax for the WindowAgg node
> by leveraging resolve_special_varno(), and that works. But I don't
> know how to do that for the HashAggregate node, because the
> JsonConstructorExpr wrapper simply doesn't exist at that plan level.
> Maybe we can hack the planner to make make_window_input_target() keep
> the JsonConstructorExpr together with its Aggref. But I think that is
> too invasive and it changes which node evaluates the wrapper.
>
> So that attempt ended up with:
>
> WindowAgg
> Output: g, JSON_ARRAYAGG(name RETURNING jsonb), row_number() OVER w1
> Window: w1 AS (ROWS UNBOUNDED PRECEDING)
> -> HashAggregate
> Output: g, jsonb_agg_strict(name)
>
> But I don't think this is good. It fails to state the fact that the
> WindowAgg doesn't compute a JSON aggregate; it passes through a value
> that the HashAggregate computed. So I gave up this idea.
Thanks for taking a look at this. It's unfortunate that reconstructing
the syntax is problematic, because in the case of the original query
that tripped on this bug, I lose the following from my original query:
JSON_ARRAYAGG(i RETURNING jsonb)
is instead:
jsonb_agg_strict(i)
And the same with: JSON_ARRAYAGG(…)
JSON_ARRAYAGG(i RETURNING json)
is instead:
json_agg_strict(i)
And the same with: JSON_ARRAYAGG(i RETURNING text)
WITH UNIQUE KEYS
is instead:
json_object_agg_unique(i,i)
NULL ON NULL
This isn't reconstructed
I guess the relevant information can be gleaned from this, but not for
JSON_ARRAYAGG(i RETURNING text) because we get the same output whether
we're returning text or json.
Thom
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
@ 2026-07-06 02:17 ` Richard Guo <[email protected]>
2026-07-07 13:49 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Richard Guo @ 2026-07-06 02:17 UTC (permalink / raw)
To: Thom Brown <[email protected]>; +Cc: pgsql-bugs
On Fri, Jul 3, 2026 at 5:36 PM Thom Brown <[email protected]> wrote:
> Thanks for taking a look at this. It's unfortunate that reconstructing
> the syntax is problematic, because in the case of the original query
> that tripped on this bug, I lose the following from my original query:
> ...
> I guess the relevant information can be gleaned from this, but not for
> JSON_ARRAYAGG(i RETURNING text) because we get the same output whether
> we're returning text or json.
Yeah, the v1 patch is lossy, and the lost information cannot be
recovered from the plan at all.
So I'm switching back to the resolve_special_varno() reconstruction I
had tried and set aside. My earlier objection was that it makes the
WindowAgg look like it computes a JSON aggregate when it only passes
through a value computed below. But the lower node still prints
jsonb_agg_strict(name), so the plan does show where the aggregate is
computed, and I think that is a smaller cost than losing the
information outright. Also, partial aggregation already deparses this
way. The combining aggregate's argument is a Var referencing the
partial aggregate's output, and get_agg_expr() resolves it back with
resolve_special_varno() to reprint the aggregate at the finalizing
node.
I still don't want to hack the planner to keep the JsonConstructorExpr
with its Aggref in make_window_input_target(), because I still think
that that is too invasive and changes which node evaluates the
wrapper.
Hence, I end up with the attached v2 patch.
- Richard
Attachments:
[application/octet-stream] v2-0001-Fix-EXPLAIN-failure-when-deparsing-SQL-JSON-aggre.patch (11.2K, ../../CAMbWs4_+Xh6JRTUrxBHhr2-5e+5Y74vne_xR_Dr_=Y0t=5cjgg@mail.gmail.com/2-v2-0001-Fix-EXPLAIN-failure-when-deparsing-SQL-JSON-aggre.patch)
download | inline diff:
From 1cc7e77c4bf6bede72fe5756390ac86e77092a7b Mon Sep 17 00:00:00 2001
From: Richard Guo <[email protected]>
Date: Fri, 3 Jul 2026 10:52:29 +0900
Subject: [PATCH v2] Fix EXPLAIN failure when deparsing SQL/JSON aggregates
If an expression containing an aggregate is evaluated above the plan
node that computes the aggregate, as happens with window functions or
with expressions postponed to above the final sort, setrefs.c replaces
the Aggref or WindowFunc with a Var referencing the lower node's
output. For SQL/JSON aggregates such as JSON_ARRAYAGG, deparsing the
containing JsonConstructorExpr then failed with "invalid
JsonConstructorExpr underlying node type", since
get_json_agg_constructor() did not expect a Var there.
Fix by resolving the Var back to the underlying Aggref or WindowFunc
and deparsing the constructor as if the aggregate were computed at the
current node. The JsonConstructorExpr retains the RETURNING clause
and the ABSENT/NULL ON NULL and WITH UNIQUE options, and the arguments
come from the resolved aggregate, so the original JSON aggregate
syntax is reproduced in full. This mirrors how get_agg_expr() already
looks through such a Var when deparsing a combining aggregate.
---
src/backend/utils/adt/ruleutils.c | 32 ++++++++
src/test/regress/expected/sqljson.out | 101 ++++++++++++++++++++++++++
src/test/regress/sql/sqljson.sql | 41 +++++++++++
3 files changed, 174 insertions(+)
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 88de5c0481c..819631781c0 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -512,6 +512,8 @@ static void get_json_agg_constructor(JsonConstructorExpr *ctor,
deparse_context *context,
const char *funcname,
bool is_json_objectagg);
+static void get_json_agg_constructor_expr(Node *node, deparse_context *context,
+ void *callback_arg);
static void simple_quote_literal(StringInfo buf, const char *val);
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
static void get_tablefunc(TableFunc *tf, deparse_context *context,
@@ -12389,11 +12391,41 @@ get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
funcname, options.data,
is_json_objectagg);
+ else if (IsA(ctor->func, Var))
+ {
+ /*
+ * If the aggregate is computed by a lower plan node, setrefs.c will
+ * have replaced the Aggref or WindowFunc with a Var referencing that
+ * node's output. Chase the Var back to it so we can still print the
+ * original JSON aggregate syntax. This only happens in EXPLAIN.
+ */
+ resolve_special_varno((Node *) ctor->func, context,
+ get_json_agg_constructor_expr, ctor);
+ }
else
elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
nodeTag(ctor->func));
}
+/*
+ * Deparse a JsonConstructorExpr whose aggregate is computed by a lower plan
+ * node; resolve_special_varno has located the underlying Aggref/WindowFunc.
+ */
+static void
+get_json_agg_constructor_expr(Node *node, deparse_context *context,
+ void *callback_arg)
+{
+ JsonConstructorExpr ctor;
+
+ if (!IsA(node, Aggref) && !IsA(node, WindowFunc))
+ elog(ERROR, "JSON aggregate constructor does not point to an Aggref or WindowFunc");
+
+ /* Flat copy suffices; we only replace func. */
+ ctor = *(JsonConstructorExpr *) callback_arg;
+ ctor.func = (Expr *) node;
+ get_json_constructor(&ctor, context, false);
+}
+
/*
* simple_quote_literal - Format a string as a SQL literal, append to buf
*/
diff --git a/src/test/regress/expected/sqljson.out b/src/test/regress/expected/sqljson.out
index 0f337bda325..091a0b98574 100644
--- a/src/test/regress/expected/sqljson.out
+++ b/src/test/regress/expected/sqljson.out
@@ -1757,3 +1757,104 @@ SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
(1 row)
DROP FUNCTION volatile_one, stable_one;
+-- Test deparsing of JSON aggregates that are computed below a WindowAgg
+-- node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING jsonb) AS ja,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING text) AS ja_text,
+ JSON_ARRAYAGG(i ORDER BY i NULL ON NULL RETURNING jsonb) AS ja_null,
+ JSON_OBJECTAGG(i: i ABSENT ON NULL RETURNING jsonb) AS jo_absent,
+ JSON_OBJECTAGG(i: i WITH UNIQUE RETURNING jsonb) AS jo_unique,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+ QUERY PLAN
+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
+ WindowAgg
+ Output: ((i % 2)), JSON_ARRAYAGG(i ORDER BY i RETURNING jsonb), JSON_ARRAYAGG(i ORDER BY i RETURNING text), JSON_ARRAYAGG(i ORDER BY i NULL ON NULL RETURNING jsonb), JSON_OBJECTAGG(i : i ABSENT ON NULL RETURNING jsonb), JSON_OBJECTAGG(i : i WITH UNIQUE KEYS RETURNING jsonb), row_number() OVER w1
+ Window: w1 AS (ORDER BY ((i.i % 2)) ROWS UNBOUNDED PRECEDING)
+ -> GroupAggregate
+ Output: ((i % 2)), jsonb_agg_strict(i ORDER BY i), json_agg_strict(i ORDER BY i), jsonb_agg(i ORDER BY i), jsonb_object_agg_strict(i, i), jsonb_object_agg_unique(i, i)
+ Group Key: ((i.i % 2))
+ -> Sort
+ Output: ((i % 2)), i
+ Sort Key: ((i.i % 2)), i.i
+ -> Function Scan on pg_catalog.generate_series i
+ Output: (i % 2), i
+ Function Call: generate_series(1, 3)
+(12 rows)
+
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING jsonb) AS ja,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING text) AS ja_text,
+ JSON_ARRAYAGG(i ORDER BY i NULL ON NULL RETURNING jsonb) AS ja_null,
+ JSON_OBJECTAGG(i: i ABSENT ON NULL RETURNING jsonb) AS jo_absent,
+ JSON_OBJECTAGG(i: i WITH UNIQUE RETURNING jsonb) AS jo_unique,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+ g | ja | ja_text | ja_null | jo_absent | jo_unique | rn
+---+--------+---------+---------+------------------+------------------+----
+ 0 | [2] | [2] | [2] | {"2": 2} | {"2": 2} | 1
+ 1 | [1, 3] | [1, 3] | [1, 3] | {"1": 1, "3": 3} | {"1": 1, "3": 3} | 2
+(2 rows)
+
+-- The same, but with the JSON aggregate used as a window function that is
+-- computed below another WindowAgg node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(i NULL ON NULL RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+ QUERY PLAN
+------------------------------------------------------------------------------------------
+ WindowAgg
+ Output: JSON_ARRAYAGG(i NULL ON NULL RETURNING jsonb) OVER w1, row_number() OVER w2, i
+ Window: w2 AS (ORDER BY i.i ROWS UNBOUNDED PRECEDING)
+ -> Sort
+ Output: i, (jsonb_agg(i) OVER w1)
+ Sort Key: i.i
+ -> WindowAgg
+ Output: i, jsonb_agg(i) OVER w1
+ Window: w1 AS (ORDER BY i.i)
+ -> Sort
+ Output: i
+ Sort Key: i.i DESC
+ -> Function Scan on pg_catalog.generate_series i
+ Output: i
+ Function Call: generate_series(1, 3)
+(15 rows)
+
+SELECT JSON_ARRAYAGG(i NULL ON NULL RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+ ja | rn
+-----------+----
+ [3, 2, 1] | 1
+ [3, 2] | 2
+ [3] | 3
+(3 rows)
+
+-- The same, but with the expression containing the JSON aggregate postponed
+-- to above the final sort due to being volatile.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING text) || random()::text AS ja
+FROM generate_series(1, 3) i
+GROUP BY i % 2
+ORDER BY count(*);
+ QUERY PLAN
+----------------------------------------------------------------------------------------
+ Result
+ Output: ((i % 2)), (JSON_ARRAYAGG(i RETURNING text) || (random())::text), (count(*))
+ -> Sort
+ Output: ((i % 2)), (count(*)), (json_agg_strict(i))
+ Sort Key: (count(*))
+ -> HashAggregate
+ Output: ((i % 2)), count(*), json_agg_strict(i)
+ Group Key: (i.i % 2)
+ -> Function Scan on pg_catalog.generate_series i
+ Output: (i % 2), i
+ Function Call: generate_series(1, 3)
+(11 rows)
+
diff --git a/src/test/regress/sql/sqljson.sql b/src/test/regress/sql/sqljson.sql
index a68747733a1..2550da15c45 100644
--- a/src/test/regress/sql/sqljson.sql
+++ b/src/test/regress/sql/sqljson.sql
@@ -706,3 +706,44 @@ SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': stable_one() RETURNING text) FORMAT
EXPLAIN (VERBOSE, COSTS OFF) SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
SELECT JSON_OBJECT('a': JSON_OBJECTAGG('b': 1 RETURNING text) FORMAT JSON);
DROP FUNCTION volatile_one, stable_one;
+
+-- Test deparsing of JSON aggregates that are computed below a WindowAgg
+-- node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING jsonb) AS ja,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING text) AS ja_text,
+ JSON_ARRAYAGG(i ORDER BY i NULL ON NULL RETURNING jsonb) AS ja_null,
+ JSON_OBJECTAGG(i: i ABSENT ON NULL RETURNING jsonb) AS jo_absent,
+ JSON_OBJECTAGG(i: i WITH UNIQUE RETURNING jsonb) AS jo_unique,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING jsonb) AS ja,
+ JSON_ARRAYAGG(i ORDER BY i RETURNING text) AS ja_text,
+ JSON_ARRAYAGG(i ORDER BY i NULL ON NULL RETURNING jsonb) AS ja_null,
+ JSON_OBJECTAGG(i: i ABSENT ON NULL RETURNING jsonb) AS jo_absent,
+ JSON_OBJECTAGG(i: i WITH UNIQUE RETURNING jsonb) AS jo_unique,
+ row_number() OVER (ORDER BY i % 2) AS rn
+FROM generate_series(1, 3) i
+GROUP BY i % 2;
+
+-- The same, but with the JSON aggregate used as a window function that is
+-- computed below another WindowAgg node.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT JSON_ARRAYAGG(i NULL ON NULL RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+SELECT JSON_ARRAYAGG(i NULL ON NULL RETURNING jsonb) OVER (ORDER BY i DESC) AS ja,
+ row_number() OVER (ORDER BY i) AS rn
+FROM generate_series(1, 3) i;
+
+-- The same, but with the expression containing the JSON aggregate postponed
+-- to above the final sort due to being volatile.
+EXPLAIN (VERBOSE, COSTS OFF)
+SELECT i % 2 AS g,
+ JSON_ARRAYAGG(i RETURNING text) || random()::text AS ja
+FROM generate_series(1, 3) i
+GROUP BY i % 2
+ORDER BY count(*);
--
2.39.5 (Apple Git-154)
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-06 02:17 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
@ 2026-07-07 13:49 ` Thom Brown <[email protected]>
2026-07-07 14:20 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Thom Brown @ 2026-07-07 13:49 UTC (permalink / raw)
To: Richard Guo <[email protected]>; +Cc: pgsql-bugs
On Mon, 6 Jul 2026 at 03:17, Richard Guo <[email protected]> wrote:
>
> On Fri, Jul 3, 2026 at 5:36 PM Thom Brown <[email protected]> wrote:
> > Thanks for taking a look at this. It's unfortunate that reconstructing
> > the syntax is problematic, because in the case of the original query
> > that tripped on this bug, I lose the following from my original query:
> > ...
> > I guess the relevant information can be gleaned from this, but not for
> > JSON_ARRAYAGG(i RETURNING text) because we get the same output whether
> > we're returning text or json.
>
> Yeah, the v1 patch is lossy, and the lost information cannot be
> recovered from the plan at all.
>
> So I'm switching back to the resolve_special_varno() reconstruction I
> had tried and set aside. My earlier objection was that it makes the
> WindowAgg look like it computes a JSON aggregate when it only passes
> through a value computed below. But the lower node still prints
> jsonb_agg_strict(name), so the plan does show where the aggregate is
> computed, and I think that is a smaller cost than losing the
> information outright. Also, partial aggregation already deparses this
> way. The combining aggregate's argument is a Var referencing the
> partial aggregate's output, and get_agg_expr() resolves it back with
> resolve_special_varno() to reprint the aggregate at the finalizing
> node.
>
> I still don't want to hack the planner to keep the JsonConstructorExpr
> with its Aggref in make_window_input_target(), because I still think
> that that is too invasive and changes which node evaluates the
> wrapper.
>
> Hence, I end up with the attached v2 patch.
I've tested it against the output from the 1st patch.
Patch 1:
((jsonb_agg_strict(e.emp_name ORDER BY e.emp_name)))
Patch 2:
(JSON_ARRAYAGG(e.emp_name ORDER BY e.emp_name RETURNING jsonb))
1:
((jsonb_object_agg_strict(e.emp_name, e.salary)))
2:
(JSON_OBJECTAGG(e.emp_name : e.salary ABSENT ON NULL RETURNING jsonb))
With that last one, why is that colon there?
Thom
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-06 02:17 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-07 13:49 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
@ 2026-07-07 14:20 ` Thom Brown <[email protected]>
2026-07-08 00:08 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Thom Brown @ 2026-07-07 14:20 UTC (permalink / raw)
To: Richard Guo <[email protected]>; +Cc: pgsql-bugs
On Tue, 7 Jul 2026 at 14:49, Thom Brown <[email protected]> wrote:
>
> On Mon, 6 Jul 2026 at 03:17, Richard Guo <[email protected]> wrote:
> >
> > On Fri, Jul 3, 2026 at 5:36 PM Thom Brown <[email protected]> wrote:
> > > Thanks for taking a look at this. It's unfortunate that reconstructing
> > > the syntax is problematic, because in the case of the original query
> > > that tripped on this bug, I lose the following from my original query:
> > > ...
> > > I guess the relevant information can be gleaned from this, but not for
> > > JSON_ARRAYAGG(i RETURNING text) because we get the same output whether
> > > we're returning text or json.
> >
> > Yeah, the v1 patch is lossy, and the lost information cannot be
> > recovered from the plan at all.
> >
> > So I'm switching back to the resolve_special_varno() reconstruction I
> > had tried and set aside. My earlier objection was that it makes the
> > WindowAgg look like it computes a JSON aggregate when it only passes
> > through a value computed below. But the lower node still prints
> > jsonb_agg_strict(name), so the plan does show where the aggregate is
> > computed, and I think that is a smaller cost than losing the
> > information outright. Also, partial aggregation already deparses this
> > way. The combining aggregate's argument is a Var referencing the
> > partial aggregate's output, and get_agg_expr() resolves it back with
> > resolve_special_varno() to reprint the aggregate at the finalizing
> > node.
> >
> > I still don't want to hack the planner to keep the JsonConstructorExpr
> > with its Aggref in make_window_input_target(), because I still think
> > that that is too invasive and changes which node evaluates the
> > wrapper.
> >
> > Hence, I end up with the attached v2 patch.
>
> I've tested it against the output from the 1st patch.
>
> Patch 1:
> ((jsonb_agg_strict(e.emp_name ORDER BY e.emp_name)))
>
> Patch 2:
> (JSON_ARRAYAGG(e.emp_name ORDER BY e.emp_name RETURNING jsonb))
>
> 1:
> ((jsonb_object_agg_strict(e.emp_name, e.salary)))
>
> 2:
> (JSON_OBJECTAGG(e.emp_name : e.salary ABSENT ON NULL RETURNING jsonb))
>
> With that last one, why is that colon there?
Actually, I don't know what I was thinking. This is clearly a
key:value pairing, so ignore that remark. But we do get more info with
this line in the 2nd patch.
So all looks good to me.
Thanks
Thom
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-06 02:17 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-07 13:49 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-07 14:20 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
@ 2026-07-08 00:08 ` Richard Guo <[email protected]>
2026-07-08 00:42 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
0 siblings, 1 reply; 9+ messages in thread
From: Richard Guo @ 2026-07-08 00:08 UTC (permalink / raw)
To: Thom Brown <[email protected]>; +Cc: pgsql-bugs
On Tue, Jul 7, 2026 at 11:20 PM Thom Brown <[email protected]> wrote:
> So all looks good to me.
Pushed and back-patched.
- Richard
^ permalink raw reply [nested|flat] 9+ messages in thread
* Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 03:06 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-03 08:36 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-06 02:17 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
2026-07-07 13:49 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-07 14:20 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-08 00:08 ` Re: EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Richard Guo <[email protected]>
@ 2026-07-08 00:42 ` Thom Brown <[email protected]>
0 siblings, 0 replies; 9+ messages in thread
From: Thom Brown @ 2026-07-08 00:42 UTC (permalink / raw)
To: Richard Guo <[email protected]>; +Cc: pgsql-bugs
On Wed, 8 Jul 2026 at 01:08, Richard Guo <[email protected]> wrote:
>
> On Tue, Jul 7, 2026 at 11:20 PM Thom Brown <[email protected]> wrote:
> > So all looks good to me.
>
> Pushed and back-patched.
Thanks. Much appreciate you working on this.
Thom
^ permalink raw reply [nested|flat] 9+ messages in thread
end of thread, other threads:[~2026-07-08 00:42 UTC | newest]
Thread overview: 9+ messages (download: mbox mbox.gz follow: Atom feed)
-- links below jump to the message on this page --
2026-07-02 16:58 EXPLAIN (VERBOSE) fails with for JSON_ARRAYAGG/JSON_OBJECTAGG + window function Thom Brown <[email protected]>
2026-07-02 22:10 ` Richard Guo <[email protected]>
2026-07-03 03:06 ` Richard Guo <[email protected]>
2026-07-03 08:36 ` Thom Brown <[email protected]>
2026-07-06 02:17 ` Richard Guo <[email protected]>
2026-07-07 13:49 ` Thom Brown <[email protected]>
2026-07-07 14:20 ` Thom Brown <[email protected]>
2026-07-08 00:08 ` Richard Guo <[email protected]>
2026-07-08 00:42 ` Thom Brown <[email protected]>
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox