Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
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

+17
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

+12
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

+16
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

+55-52
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)