Skip to content

Commit 7d2c7f0

Browse files
committed
Fix query pullup issue with WindowClause runCondition
94985c2 added code to detect when WindowFuncs were monotonic and allowed additional quals to be "pushed down" into the subquery to be used as WindowClause runConditions in order to short-circuit execution in nodeWindowAgg.c. The Node representation of runConditions wasn't well selected and because we do qual pushdown before planning the subquery, the planning of the subquery could perform subquery pull-up of nested subqueries. For WindowFuncs with args, the arguments could be changed after pushing the qual down to the subquery. This was made more difficult by the fact that the code duplicated the WindowFunc inside an OpExpr to include in the WindowClauses runCondition field. This could result in duplication of subqueries and a pull-up of such a subquery could result in another initplan parameter being issued for the 2nd version of the subplan. This could result in errors such as: ERROR: WindowFunc not found in subplan target lists To fix this, we change the node representation of these run conditions and instead of storing an OpExpr containing the WindowFunc in a list inside WindowClause, we now store a new node type named WindowFuncRunCondition within a new field in the WindowFunc. These get transformed into OpExprs later in planning once subquery pull-up has been performed. This problem did exist in v15 and v16, but that was fixed by 9d36b88 and e5d20bbd. Cat version bump due to new node type and modifying WindowFunc struct. Bug: #18305 Reported-by: Zuming Jiang Discussion: https://postgr.es/m/18305-33c49b4c830b37b3%40postgresql.org
1 parent 01df147 commit 7d2c7f0

File tree

18 files changed

+143
-53
lines changed

18 files changed

+143
-53
lines changed

src/backend/nodes/nodeFuncs.c

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,16 @@ expression_tree_walker_impl(Node *node,
21632163
return true;
21642164
if (WALK(expr->aggfilter))
21652165
return true;
2166+
if (WALK(expr->runCondition))
2167+
return true;
2168+
}
2169+
break;
2170+
case T_WindowFuncRunCondition:
2171+
{
2172+
WindowFuncRunCondition *expr = (WindowFuncRunCondition *) node;
2173+
2174+
if (WALK(expr->arg))
2175+
return true;
21662176
}
21672177
break;
21682178
case T_SubscriptingRef:
@@ -2400,8 +2410,6 @@ expression_tree_walker_impl(Node *node,
24002410
return true;
24012411
if (WALK(wc->endOffset))
24022412
return true;
2403-
if (WALK(wc->runCondition))
2404-
return true;
24052413
}
24062414
break;
24072415
case T_CTECycleClause:
@@ -2752,8 +2760,6 @@ query_tree_walker_impl(Query *query,
27522760
return true;
27532761
if (WALK(wc->endOffset))
27542762
return true;
2755-
if (WALK(wc->runCondition))
2756-
return true;
27572763
}
27582764
}
27592765

@@ -3053,6 +3059,16 @@ expression_tree_mutator_impl(Node *node,
30533059
return (Node *) newnode;
30543060
}
30553061
break;
3062+
case T_WindowFuncRunCondition:
3063+
{
3064+
WindowFuncRunCondition *wfuncrc = (WindowFuncRunCondition *) node;
3065+
WindowFuncRunCondition *newnode;
3066+
3067+
FLATCOPY(newnode, wfuncrc, WindowFuncRunCondition);
3068+
MUTATE(newnode->arg, wfuncrc->arg, Expr *);
3069+
return (Node *) newnode;
3070+
}
3071+
break;
30563072
case T_SubscriptingRef:
30573073
{
30583074
SubscriptingRef *sbsref = (SubscriptingRef *) node;
@@ -3466,7 +3482,6 @@ expression_tree_mutator_impl(Node *node,
34663482
MUTATE(newnode->orderClause, wc->orderClause, List *);
34673483
MUTATE(newnode->startOffset, wc->startOffset, Node *);
34683484
MUTATE(newnode->endOffset, wc->endOffset, Node *);
3469-
MUTATE(newnode->runCondition, wc->runCondition, List *);
34703485
return (Node *) newnode;
34713486
}
34723487
break;
@@ -3799,7 +3814,6 @@ query_tree_mutator_impl(Query *query,
37993814
FLATCOPY(newnode, wc, WindowClause);
38003815
MUTATE(newnode->startOffset, wc->startOffset, Node *);
38013816
MUTATE(newnode->endOffset, wc->endOffset, Node *);
3802-
MUTATE(newnode->runCondition, wc->runCondition, List *);
38033817

38043818
resultlist = lappend(resultlist, (Node *) newnode);
38053819
}

src/backend/optimizer/path/allpaths.c

Lines changed: 12 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2205,7 +2205,7 @@ set_dummy_rel_pathlist(RelOptInfo *rel)
22052205
* the run condition will handle all of the required filtering.
22062206
*
22072207
* Returns true if 'opexpr' was found to be useful and was added to the
2208-
* WindowClauses runCondition. We also set *keep_original accordingly and add
2208+
* WindowFunc's runCondition. We also set *keep_original accordingly and add
22092209
* 'attno' to *run_cond_attrs offset by FirstLowInvalidHeapAttributeNumber.
22102210
* If the 'opexpr' cannot be used then we set *keep_original to true and
22112211
* return false.
@@ -2358,7 +2358,7 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
23582358
*keep_original = true;
23592359
runopexpr = opexpr;
23602360

2361-
/* determine the operator to use for the runCondition qual */
2361+
/* determine the operator to use for the WindowFuncRunCondition */
23622362
runoperator = get_opfamily_member(opinfo->opfamily_id,
23632363
opinfo->oplefttype,
23642364
opinfo->oprighttype,
@@ -2369,27 +2369,15 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
23692369

23702370
if (runopexpr != NULL)
23712371
{
2372-
Expr *newexpr;
2372+
WindowFuncRunCondition *wfuncrc;
23732373

2374-
/*
2375-
* Build the qual required for the run condition keeping the
2376-
* WindowFunc on the same side as it was originally.
2377-
*/
2378-
if (wfunc_left)
2379-
newexpr = make_opclause(runoperator,
2380-
runopexpr->opresulttype,
2381-
runopexpr->opretset, (Expr *) wfunc,
2382-
otherexpr, runopexpr->opcollid,
2383-
runopexpr->inputcollid);
2384-
else
2385-
newexpr = make_opclause(runoperator,
2386-
runopexpr->opresulttype,
2387-
runopexpr->opretset,
2388-
otherexpr, (Expr *) wfunc,
2389-
runopexpr->opcollid,
2390-
runopexpr->inputcollid);
2374+
wfuncrc = makeNode(WindowFuncRunCondition);
2375+
wfuncrc->opno = runoperator;
2376+
wfuncrc->inputcollid = runopexpr->inputcollid;
2377+
wfuncrc->wfunc_left = wfunc_left;
2378+
wfuncrc->arg = copyObject(otherexpr);
23912379

2392-
wclause->runCondition = lappend(wclause->runCondition, newexpr);
2380+
wfunc->runCondition = lappend(wfunc->runCondition, wfuncrc);
23932381

23942382
/* record that this attno was used in a run condition */
23952383
*run_cond_attrs = bms_add_member(*run_cond_attrs,
@@ -2403,9 +2391,9 @@ find_window_run_conditions(Query *subquery, RangeTblEntry *rte, Index rti,
24032391

24042392
/*
24052393
* check_and_push_window_quals
2406-
* Check if 'clause' is a qual that can be pushed into a WindowFunc's
2407-
* WindowClause as a 'runCondition' qual. These, when present, allow
2408-
* some unnecessary work to be skipped during execution.
2394+
* Check if 'clause' is a qual that can be pushed into a WindowFunc
2395+
* as a 'runCondition' qual. These, when present, allow some unnecessary
2396+
* work to be skipped during execution.
24092397
*
24102398
* 'run_cond_attrs' will be populated with all targetlist resnos of subquery
24112399
* targets (offset by FirstLowInvalidHeapAttributeNumber) that we pushed

src/backend/optimizer/plan/createplan.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2699,7 +2699,7 @@ create_windowagg_plan(PlannerInfo *root, WindowAggPath *best_path)
26992699
wc->inRangeColl,
27002700
wc->inRangeAsc,
27012701
wc->inRangeNullsFirst,
2702-
wc->runCondition,
2702+
best_path->runCondition,
27032703
best_path->qual,
27042704
best_path->topwindow,
27052705
subplan);

src/backend/optimizer/plan/planner.c

Lines changed: 44 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -870,9 +870,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse, PlannerInfo *parent_root,
870870
EXPRKIND_LIMIT);
871871
wc->endOffset = preprocess_expression(root, wc->endOffset,
872872
EXPRKIND_LIMIT);
873-
wc->runCondition = (List *) preprocess_expression(root,
874-
(Node *) wc->runCondition,
875-
EXPRKIND_TARGET);
876873
}
877874

878875
parse->limitOffset = preprocess_expression(root, parse->limitOffset,
@@ -4527,9 +4524,11 @@ create_one_window_path(PlannerInfo *root,
45274524
{
45284525
WindowClause *wc = lfirst_node(WindowClause, l);
45294526
List *window_pathkeys;
4527+
List *runcondition = NIL;
45304528
int presorted_keys;
45314529
bool is_sorted;
45324530
bool topwindow;
4531+
ListCell *lc2;
45334532

45344533
window_pathkeys = make_pathkeys_for_window(root,
45354534
wc,
@@ -4577,7 +4576,6 @@ create_one_window_path(PlannerInfo *root,
45774576
* we do need to account for the increase in tlist width.
45784577
*/
45794578
int64 tuple_width = window_target->width;
4580-
ListCell *lc2;
45814579

45824580
window_target = copy_pathtarget(window_target);
45834581
foreach(lc2, wflists->windowFuncs[wc->winref])
@@ -4599,17 +4597,53 @@ create_one_window_path(PlannerInfo *root,
45994597
topwindow = foreach_current_index(l) == list_length(activeWindows) - 1;
46004598

46014599
/*
4602-
* Accumulate all of the runConditions from each intermediate
4603-
* WindowClause. The top-level WindowAgg must pass these as a qual so
4604-
* that it filters out unwanted tuples correctly.
4600+
* Collect the WindowFuncRunConditions from each WindowFunc and
4601+
* convert them into OpExprs
46054602
*/
4606-
if (!topwindow)
4607-
topqual = list_concat(topqual, wc->runCondition);
4603+
foreach(lc2, wflists->windowFuncs[wc->winref])
4604+
{
4605+
ListCell *lc3;
4606+
WindowFunc *wfunc = lfirst_node(WindowFunc, lc2);
4607+
4608+
foreach(lc3, wfunc->runCondition)
4609+
{
4610+
WindowFuncRunCondition *wfuncrc =
4611+
lfirst_node(WindowFuncRunCondition, lc3);
4612+
Expr *opexpr;
4613+
Expr *leftop;
4614+
Expr *rightop;
4615+
4616+
if (wfuncrc->wfunc_left)
4617+
{
4618+
leftop = (Expr *) copyObject(wfunc);
4619+
rightop = copyObject(wfuncrc->arg);
4620+
}
4621+
else
4622+
{
4623+
leftop = copyObject(wfuncrc->arg);
4624+
rightop = (Expr *) copyObject(wfunc);
4625+
}
4626+
4627+
opexpr = make_opclause(wfuncrc->opno,
4628+
BOOLOID,
4629+
false,
4630+
leftop,
4631+
rightop,
4632+
InvalidOid,
4633+
wfuncrc->inputcollid);
4634+
4635+
runcondition = lappend(runcondition, opexpr);
4636+
4637+
if (!topwindow)
4638+
topqual = lappend(topqual, opexpr);
4639+
}
4640+
}
46084641

46094642
path = (Path *)
46104643
create_windowagg_path(root, window_rel, path, window_target,
46114644
wflists->windowFuncs[wc->winref],
4612-
wc, topwindow ? topqual : NIL, topwindow);
4645+
runcondition, wc,
4646+
topwindow ? topqual : NIL, topwindow);
46134647
}
46144648

46154649
add_path(window_rel, path);

src/backend/optimizer/prep/prepjointree.c

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2175,14 +2175,6 @@ perform_pullup_replace_vars(PlannerInfo *root,
21752175
parse->returningList = (List *)
21762176
pullup_replace_vars((Node *) parse->returningList, rvcontext);
21772177

2178-
foreach(lc, parse->windowClause)
2179-
{
2180-
WindowClause *wc = lfirst_node(WindowClause, lc);
2181-
2182-
if (wc->runCondition != NIL)
2183-
wc->runCondition = (List *)
2184-
pullup_replace_vars((Node *) wc->runCondition, rvcontext);
2185-
}
21862178
if (parse->onConflict)
21872179
{
21882180
parse->onConflict->onConflictSet = (List *)

src/backend/optimizer/util/clauses.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2566,6 +2566,7 @@ eval_const_expressions_mutator(Node *node,
25662566
newexpr->inputcollid = expr->inputcollid;
25672567
newexpr->args = args;
25682568
newexpr->aggfilter = aggfilter;
2569+
newexpr->runCondition = expr->runCondition;
25692570
newexpr->winref = expr->winref;
25702571
newexpr->winstar = expr->winstar;
25712572
newexpr->winagg = expr->winagg;

src/backend/optimizer/util/pathnode.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3471,6 +3471,7 @@ create_minmaxagg_path(PlannerInfo *root,
34713471
* 'subpath' is the path representing the source of data
34723472
* 'target' is the PathTarget to be computed
34733473
* 'windowFuncs' is a list of WindowFunc structs
3474+
* 'runCondition' is a list of OpExprs to short-circuit WindowAgg execution
34743475
* 'winclause' is a WindowClause that is common to all the WindowFuncs
34753476
* 'qual' WindowClause.runconditions from lower-level WindowAggPaths.
34763477
* Must always be NIL when topwindow == false
@@ -3486,6 +3487,7 @@ create_windowagg_path(PlannerInfo *root,
34863487
Path *subpath,
34873488
PathTarget *target,
34883489
List *windowFuncs,
3490+
List *runCondition,
34893491
WindowClause *winclause,
34903492
List *qual,
34913493
bool topwindow)
@@ -3510,6 +3512,7 @@ create_windowagg_path(PlannerInfo *root,
35103512
pathnode->subpath = subpath;
35113513
pathnode->winclause = winclause;
35123514
pathnode->qual = qual;
3515+
pathnode->runCondition = runCondition;
35133516
pathnode->topwindow = topwindow;
35143517

35153518
/*

src/backend/parser/parse_clause.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2956,7 +2956,6 @@ transformWindowDefinitions(ParseState *pstate,
29562956
rangeopfamily, rangeopcintype,
29572957
&wc->endInRangeFunc,
29582958
windef->endOffset);
2959-
wc->runCondition = NIL;
29602959
wc->winref = winref;
29612960

29622961
result = lappend(result, wc);

src/backend/parser/parse_expr.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3826,6 +3826,7 @@ transformJsonAggConstructor(ParseState *pstate, JsonAggConstructor *agg_ctor,
38263826
/* wincollid and inputcollid will be set by parse_collate.c */
38273827
wfunc->args = args;
38283828
wfunc->aggfilter = aggfilter;
3829+
wfunc->runCondition = NIL;
38293830
/* winref will be set by transformWindowFuncCall */
38303831
wfunc->winstar = false;
38313832
wfunc->winagg = true;

src/backend/parser/parse_func.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,6 +834,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
834834
wfunc->winstar = agg_star;
835835
wfunc->winagg = (fdresult == FUNCDETAIL_AGGREGATE);
836836
wfunc->aggfilter = agg_filter;
837+
wfunc->runCondition = NIL;
837838
wfunc->location = location;
838839

839840
/*

src/include/catalog/catversion.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,6 @@
5757
*/
5858

5959
/* yyyymmddN */
60-
#define CATALOG_VERSION_NO 202404291
60+
#define CATALOG_VERSION_NO 202405051
6161

6262
#endif

src/include/nodes/parsenodes.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1549,8 +1549,6 @@ typedef struct WindowClause
15491549
int frameOptions; /* frame_clause options, see WindowDef */
15501550
Node *startOffset; /* expression for starting bound, if any */
15511551
Node *endOffset; /* expression for ending bound, if any */
1552-
/* qual to help short-circuit execution */
1553-
List *runCondition pg_node_attr(query_jumble_ignore);
15541552
/* in_range function for startOffset */
15551553
Oid startInRangeFunc pg_node_attr(query_jumble_ignore);
15561554
/* in_range function for endOffset */

src/include/nodes/pathnodes.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2308,6 +2308,7 @@ typedef struct WindowAggPath
23082308
Path *subpath; /* path representing input source */
23092309
WindowClause *winclause; /* WindowClause we'll be using */
23102310
List *qual; /* lower-level WindowAgg runconditions */
2311+
List *runCondition; /* OpExpr List to short-circuit execution */
23112312
bool topwindow; /* false for all apart from the WindowAgg
23122313
* that's closest to the root of the plan */
23132314
} WindowAggPath;

src/include/nodes/primnodes.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,8 @@ typedef struct WindowFunc
575575
List *args;
576576
/* FILTER expression, if any */
577577
Expr *aggfilter;
578+
/* List of WindowFuncRunConditions to help short-circuit execution */
579+
List *runCondition pg_node_attr(query_jumble_ignore);
578580
/* index of associated WindowClause */
579581
Index winref;
580582
/* true if argument list was really '*' */
@@ -585,6 +587,34 @@ typedef struct WindowFunc
585587
ParseLoc location;
586588
} WindowFunc;
587589

590+
/*
591+
* WindowFuncRunCondition
592+
*
593+
* Represents intermediate OpExprs which will be used by WindowAgg to
594+
* short-circuit execution.
595+
*/
596+
typedef struct WindowFuncRunCondition
597+
{
598+
Expr xpr;
599+
600+
/* PG_OPERATOR OID of the operator */
601+
Oid opno;
602+
/* OID of collation that operator should use */
603+
Oid inputcollid pg_node_attr(query_jumble_ignore);
604+
605+
/*
606+
* true of WindowFunc belongs on the left of the resulting OpExpr or false
607+
* if the WindowFunc is on the right.
608+
*/
609+
bool wfunc_left;
610+
611+
/*
612+
* The Expr being compared to the WindowFunc to use in the OpExpr in the
613+
* WindowAgg's runCondition
614+
*/
615+
Expr *arg;
616+
} WindowFuncRunCondition;
617+
588618
/*
589619
* MergeSupportFunc
590620
*

src/include/optimizer/pathnode.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ extern WindowAggPath *create_windowagg_path(PlannerInfo *root,
250250
Path *subpath,
251251
PathTarget *target,
252252
List *windowFuncs,
253+
List *runCondition,
253254
WindowClause *winclause,
254255
List *qual,
255256
bool topwindow);

0 commit comments

Comments
 (0)