Skip to content

Commit 9ff79b9

Browse files
committed
Fix up planner infrastructure to support LATERAL properly.
This patch takes care of a number of problems having to do with failure to choose valid join orders and incorrect handling of lateral references pulled up from subqueries. Notable changes: * Add a LateralJoinInfo data structure similar to SpecialJoinInfo, to represent join ordering constraints created by lateral references. (I first considered extending the SpecialJoinInfo structure, but the semantics are different enough that a separate data structure seems better.) Extend join_is_legal() and related functions to prevent trying to form unworkable joins, and to ensure that we will consider joins that satisfy lateral references even if the joins would be clauseless. * Fill in the infrastructure needed for the last few types of relation scan paths to support parameterization. We'd have wanted this eventually anyway, but it is necessary now because a relation that gets pulled up out of a UNION ALL subquery may acquire a reltargetlist containing lateral references, meaning that its paths *have* to be parameterized whether or not we have any code that can push join quals down into the scan. * Compute data about lateral references early in query_planner(), and save in RelOptInfo nodes, to avoid repetitive calculations later. * Assorted corner-case bug fixes. There's probably still some bugs left, but this is a lot closer to being real than it was before.
1 parent de87d47 commit 9ff79b9

30 files changed

+817
-182
lines changed

src/backend/nodes/copyfuncs.c

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,20 @@ _copySpecialJoinInfo(const SpecialJoinInfo *from)
19061906
return newnode;
19071907
}
19081908

1909+
/*
1910+
* _copyLateralJoinInfo
1911+
*/
1912+
static LateralJoinInfo *
1913+
_copyLateralJoinInfo(const LateralJoinInfo *from)
1914+
{
1915+
LateralJoinInfo *newnode = makeNode(LateralJoinInfo);
1916+
1917+
COPY_SCALAR_FIELD(lateral_rhs);
1918+
COPY_BITMAPSET_FIELD(lateral_lhs);
1919+
1920+
return newnode;
1921+
}
1922+
19091923
/*
19101924
* _copyAppendRelInfo
19111925
*/
@@ -4082,6 +4096,9 @@ copyObject(const void *from)
40824096
case T_SpecialJoinInfo:
40834097
retval = _copySpecialJoinInfo(from);
40844098
break;
4099+
case T_LateralJoinInfo:
4100+
retval = _copyLateralJoinInfo(from);
4101+
break;
40854102
case T_AppendRelInfo:
40864103
retval = _copyAppendRelInfo(from);
40874104
break;

src/backend/nodes/equalfuncs.c

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -862,6 +862,15 @@ _equalSpecialJoinInfo(const SpecialJoinInfo *a, const SpecialJoinInfo *b)
862862
return true;
863863
}
864864

865+
static bool
866+
_equalLateralJoinInfo(const LateralJoinInfo *a, const LateralJoinInfo *b)
867+
{
868+
COMPARE_SCALAR_FIELD(lateral_rhs);
869+
COMPARE_BITMAPSET_FIELD(lateral_lhs);
870+
871+
return true;
872+
}
873+
865874
static bool
866875
_equalAppendRelInfo(const AppendRelInfo *a, const AppendRelInfo *b)
867876
{
@@ -2646,6 +2655,9 @@ equal(const void *a, const void *b)
26462655
case T_SpecialJoinInfo:
26472656
retval = _equalSpecialJoinInfo(a, b);
26482657
break;
2658+
case T_LateralJoinInfo:
2659+
retval = _equalLateralJoinInfo(a, b);
2660+
break;
26492661
case T_AppendRelInfo:
26502662
retval = _equalAppendRelInfo(a, b);
26512663
break;

src/backend/nodes/outfuncs.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
16991699
WRITE_NODE_FIELD(right_join_clauses);
17001700
WRITE_NODE_FIELD(full_join_clauses);
17011701
WRITE_NODE_FIELD(join_info_list);
1702+
WRITE_NODE_FIELD(lateral_info_list);
17021703
WRITE_NODE_FIELD(append_rel_list);
17031704
WRITE_NODE_FIELD(rowMarks);
17041705
WRITE_NODE_FIELD(placeholder_list);
@@ -1713,6 +1714,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
17131714
WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
17141715
WRITE_BOOL_FIELD(hasInheritedTarget);
17151716
WRITE_BOOL_FIELD(hasJoinRTEs);
1717+
WRITE_BOOL_FIELD(hasLateralRTEs);
17161718
WRITE_BOOL_FIELD(hasHavingQual);
17171719
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
17181720
WRITE_BOOL_FIELD(hasRecursion);
@@ -1743,6 +1745,8 @@ _outRelOptInfo(StringInfo str, const RelOptInfo *node)
17431745
WRITE_ENUM_FIELD(rtekind, RTEKind);
17441746
WRITE_INT_FIELD(min_attr);
17451747
WRITE_INT_FIELD(max_attr);
1748+
WRITE_NODE_FIELD(lateral_vars);
1749+
WRITE_BITMAPSET_FIELD(lateral_relids);
17461750
WRITE_NODE_FIELD(indexlist);
17471751
WRITE_UINT_FIELD(pages);
17481752
WRITE_FLOAT_FIELD(tuples, "%.0f");
@@ -1890,6 +1894,15 @@ _outSpecialJoinInfo(StringInfo str, const SpecialJoinInfo *node)
18901894
WRITE_NODE_FIELD(join_quals);
18911895
}
18921896

1897+
static void
1898+
_outLateralJoinInfo(StringInfo str, const LateralJoinInfo *node)
1899+
{
1900+
WRITE_NODE_TYPE("LATERALJOININFO");
1901+
1902+
WRITE_UINT_FIELD(lateral_rhs);
1903+
WRITE_BITMAPSET_FIELD(lateral_lhs);
1904+
}
1905+
18931906
static void
18941907
_outAppendRelInfo(StringInfo str, const AppendRelInfo *node)
18951908
{
@@ -3036,6 +3049,9 @@ _outNode(StringInfo str, const void *obj)
30363049
case T_SpecialJoinInfo:
30373050
_outSpecialJoinInfo(str, obj);
30383051
break;
3052+
case T_LateralJoinInfo:
3053+
_outLateralJoinInfo(str, obj);
3054+
break;
30393055
case T_AppendRelInfo:
30403056
_outAppendRelInfo(str, obj);
30413057
break;

src/backend/optimizer/path/allpaths.c

Lines changed: 55 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,9 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
268268
case RTE_CTE:
269269

270270
/*
271-
* CTEs don't support parameterized paths, so just go ahead
272-
* and build their paths immediately.
271+
* CTEs don't support making a choice between parameterized
272+
* and unparameterized paths, so just go ahead and build their
273+
* paths immediately.
273274
*/
274275
if (rte->self_reference)
275276
set_worktable_pathlist(root, rel, rte);
@@ -376,8 +377,18 @@ set_plain_rel_size(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
376377
static void
377378
set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
378379
{
380+
Relids required_outer;
381+
382+
/*
383+
* We don't support pushing join clauses into the quals of a seqscan, but
384+
* it could still have required parameterization due to LATERAL refs in
385+
* its tlist. (That can only happen if the seqscan is on a relation
386+
* pulled up out of a UNION ALL appendrel.)
387+
*/
388+
required_outer = rel->lateral_relids;
389+
379390
/* Consider sequential scan */
380-
add_path(rel, create_seqscan_path(root, rel, NULL));
391+
add_path(rel, create_seqscan_path(root, rel, required_outer));
381392

382393
/* Consider index scans */
383394
create_index_paths(root, rel);
@@ -536,10 +547,10 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
536547
* CE failed, so finish copying/modifying targetlist and join quals.
537548
*
538549
* Note: the resulting childrel->reltargetlist may contain arbitrary
539-
* expressions, which normally would not occur in a reltargetlist.
540-
* That is okay because nothing outside of this routine will look at
541-
* the child rel's reltargetlist. We do have to cope with the case
542-
* while constructing attr_widths estimates below, though.
550+
* expressions, which otherwise would not occur in a reltargetlist.
551+
* Code that might be looking at an appendrel child must cope with
552+
* such. Note in particular that "arbitrary expression" can include
553+
* "Var belonging to another relation", due to LATERAL references.
543554
*/
544555
childrel->joininfo = (List *)
545556
adjust_appendrel_attrs(root,
@@ -610,7 +621,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
610621
int pndx = parentvar->varattno - rel->min_attr;
611622
int32 child_width = 0;
612623

613-
if (IsA(childvar, Var))
624+
if (IsA(childvar, Var) &&
625+
((Var *) childvar)->varno == childrel->relid)
614626
{
615627
int cndx = ((Var *) childvar)->varattno - childrel->min_attr;
616628

@@ -1054,17 +1066,10 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
10541066

10551067
/*
10561068
* If it's a LATERAL subquery, it might contain some Vars of the current
1057-
* query level, requiring it to be treated as parameterized.
1069+
* query level, requiring it to be treated as parameterized, even though
1070+
* we don't support pushing down join quals into subqueries.
10581071
*/
1059-
if (rte->lateral)
1060-
{
1061-
required_outer = pull_varnos_of_level((Node *) subquery, 1);
1062-
/* Enforce convention that empty required_outer is exactly NULL */
1063-
if (bms_is_empty(required_outer))
1064-
required_outer = NULL;
1065-
}
1066-
else
1067-
required_outer = NULL;
1072+
required_outer = rel->lateral_relids;
10681073

10691074
/* We need a workspace for keeping track of set-op type coercions */
10701075
differentTypes = (bool *)
@@ -1175,29 +1180,18 @@ set_subquery_pathlist(PlannerInfo *root, RelOptInfo *rel,
11751180
/*
11761181
* set_function_pathlist
11771182
* Build the (single) access path for a function RTE
1178-
*
1179-
* As with subqueries, a function RTE's path might be parameterized due to
1180-
* LATERAL references, but that's inherent in the function expression and
1181-
* not a result of pushing down join quals.
11821183
*/
11831184
static void
11841185
set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
11851186
{
11861187
Relids required_outer;
11871188

11881189
/*
1189-
* If it's a LATERAL function, it might contain some Vars of the current
1190-
* query level, requiring it to be treated as parameterized.
1190+
* We don't support pushing join clauses into the quals of a function
1191+
* scan, but it could still have required parameterization due to LATERAL
1192+
* refs in the function expression.
11911193
*/
1192-
if (rte->lateral)
1193-
{
1194-
required_outer = pull_varnos_of_level(rte->funcexpr, 0);
1195-
/* Enforce convention that empty required_outer is exactly NULL */
1196-
if (bms_is_empty(required_outer))
1197-
required_outer = NULL;
1198-
}
1199-
else
1200-
required_outer = NULL;
1194+
required_outer = rel->lateral_relids;
12011195

12021196
/* Generate appropriate path */
12031197
add_path(rel, create_functionscan_path(root, rel, required_outer));
@@ -1209,29 +1203,18 @@ set_function_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12091203
/*
12101204
* set_values_pathlist
12111205
* Build the (single) access path for a VALUES RTE
1212-
*
1213-
* As with subqueries, a VALUES RTE's path might be parameterized due to
1214-
* LATERAL references, but that's inherent in the values expressions and
1215-
* not a result of pushing down join quals.
12161206
*/
12171207
static void
12181208
set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12191209
{
12201210
Relids required_outer;
12211211

12221212
/*
1223-
* If it's a LATERAL RTE, it might contain some Vars of the current query
1224-
* level, requiring it to be treated as parameterized.
1213+
* We don't support pushing join clauses into the quals of a values scan,
1214+
* but it could still have required parameterization due to LATERAL refs
1215+
* in the values expressions.
12251216
*/
1226-
if (rte->lateral)
1227-
{
1228-
required_outer = pull_varnos_of_level((Node *) rte->values_lists, 0);
1229-
/* Enforce convention that empty required_outer is exactly NULL */
1230-
if (bms_is_empty(required_outer))
1231-
required_outer = NULL;
1232-
}
1233-
else
1234-
required_outer = NULL;
1217+
required_outer = rel->lateral_relids;
12351218

12361219
/* Generate appropriate path */
12371220
add_path(rel, create_valuesscan_path(root, rel, required_outer));
@@ -1245,7 +1228,7 @@ set_values_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12451228
* Build the (single) access path for a non-self-reference CTE RTE
12461229
*
12471230
* There's no need for a separate set_cte_size phase, since we don't
1248-
* support parameterized paths for CTEs.
1231+
* support join-qual-parameterized paths for CTEs.
12491232
*/
12501233
static void
12511234
set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
@@ -1256,6 +1239,7 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12561239
int ndx;
12571240
ListCell *lc;
12581241
int plan_id;
1242+
Relids required_outer;
12591243

12601244
/*
12611245
* Find the referenced CTE, and locate the plan previously made for it.
@@ -1294,8 +1278,16 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
12941278
/* Mark rel with estimated output rows, width, etc */
12951279
set_cte_size_estimates(root, rel, cteplan);
12961280

1281+
/*
1282+
* We don't support pushing join clauses into the quals of a CTE scan, but
1283+
* it could still have required parameterization due to LATERAL refs in
1284+
* its tlist. (That can only happen if the CTE scan is on a relation
1285+
* pulled up out of a UNION ALL appendrel.)
1286+
*/
1287+
required_outer = rel->lateral_relids;
1288+
12971289
/* Generate appropriate path */
1298-
add_path(rel, create_ctescan_path(root, rel));
1290+
add_path(rel, create_ctescan_path(root, rel, required_outer));
12991291

13001292
/* Select cheapest path (pretty easy in this case...) */
13011293
set_cheapest(rel);
@@ -1306,14 +1298,15 @@ set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
13061298
* Build the (single) access path for a self-reference CTE RTE
13071299
*
13081300
* There's no need for a separate set_worktable_size phase, since we don't
1309-
* support parameterized paths for CTEs.
1301+
* support join-qual-parameterized paths for CTEs.
13101302
*/
13111303
static void
13121304
set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
13131305
{
13141306
Plan *cteplan;
13151307
PlannerInfo *cteroot;
13161308
Index levelsup;
1309+
Relids required_outer;
13171310

13181311
/*
13191312
* We need to find the non-recursive term's plan, which is in the plan
@@ -1338,8 +1331,18 @@ set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
13381331
/* Mark rel with estimated output rows, width, etc */
13391332
set_cte_size_estimates(root, rel, cteplan);
13401333

1334+
/*
1335+
* We don't support pushing join clauses into the quals of a worktable
1336+
* scan, but it could still have required parameterization due to LATERAL
1337+
* refs in its tlist. (That can only happen if the worktable scan is on a
1338+
* relation pulled up out of a UNION ALL appendrel. I'm not sure this is
1339+
* actually possible given the restrictions on recursive references, but
1340+
* it's easy enough to support.)
1341+
*/
1342+
required_outer = rel->lateral_relids;
1343+
13411344
/* Generate appropriate path */
1342-
add_path(rel, create_worktablescan_path(root, rel));
1345+
add_path(rel, create_worktablescan_path(root, rel, required_outer));
13431346

13441347
/* Select cheapest path (pretty easy in this case...) */
13451348
set_cheapest(rel);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy