Skip to content

Commit d57501d

Browse files
committed
improve child path generation for Runtime[Merge]Append (using get_cheapest_parameterized_child_path())
1 parent 3dcf247 commit d57501d

File tree

8 files changed

+149
-136
lines changed

8 files changed

+149
-136
lines changed

expected/pathman_only.out

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,16 +179,26 @@ SELECT * FROM test_only.from_only_test JOIN q1 USING(val);
179179
-> CTE Scan on q1
180180
-> Custom Scan (RuntimeAppend)
181181
-> Seq Scan on from_only_test_1 from_only_test
182+
Filter: (q1.val = val)
182183
-> Seq Scan on from_only_test_2 from_only_test
184+
Filter: (q1.val = val)
183185
-> Seq Scan on from_only_test_3 from_only_test
186+
Filter: (q1.val = val)
184187
-> Seq Scan on from_only_test_4 from_only_test
188+
Filter: (q1.val = val)
185189
-> Seq Scan on from_only_test_5 from_only_test
190+
Filter: (q1.val = val)
186191
-> Seq Scan on from_only_test_6 from_only_test
192+
Filter: (q1.val = val)
187193
-> Seq Scan on from_only_test_7 from_only_test
194+
Filter: (q1.val = val)
188195
-> Seq Scan on from_only_test_8 from_only_test
196+
Filter: (q1.val = val)
189197
-> Seq Scan on from_only_test_9 from_only_test
198+
Filter: (q1.val = val)
190199
-> Seq Scan on from_only_test_10 from_only_test
191-
(15 rows)
200+
Filter: (q1.val = val)
201+
(25 rows)
192202

193203
/* should be OK */
194204
EXPLAIN (COSTS OFF)

src/hooks.c

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -120,27 +120,39 @@ pathman_join_pathlist_hook(PlannerInfo *root,
120120
NestPath *nest_path; /* NestLoop we're creating */
121121
ParamPathInfo *ppi; /* parameterization info */
122122
Relids inner_required; /* required paremeterization relids */
123-
List *filtered_joinclauses = NIL;
123+
List *filtered_joinclauses = NIL,
124+
*saved_ppi_list;
124125
ListCell *rinfo_lc;
125126

126127
if (!IsA(cur_inner_path, AppendPath))
127128
continue;
128129

129130
/* Select cheapest path for outerrel */
130131
outer = outerrel->cheapest_total_path;
132+
133+
/* Wrap outer path with Unique if needed */
131134
if (saved_jointype == JOIN_UNIQUE_OUTER)
132135
{
133136
outer = (Path *) create_unique_path(root, outerrel,
134137
outer, extra->sjinfo);
135138
Assert(outer);
136139
}
137140

141+
/* No way to do this in a parameterized inner path */
142+
if (saved_jointype == JOIN_UNIQUE_INNER)
143+
return;
144+
138145
/* Make innerrel path depend on outerrel's column */
139146
inner_required = bms_union(PATH_REQ_OUTER((Path *) cur_inner_path),
140147
bms_make_singleton(outerrel->relid));
141148

149+
/* Preserve existing ppis built by get_appendrel_parampathinfo() */
150+
saved_ppi_list = innerrel->ppilist;
151+
142152
/* Get the ParamPathInfo for a parameterized path */
153+
innerrel->ppilist = NIL;
143154
ppi = get_baserel_parampathinfo(root, innerrel, inner_required);
155+
innerrel->ppilist = saved_ppi_list;
144156

145157
/* Skip ppi->ppi_clauses don't reference partition attribute */
146158
if (!(ppi && get_partitioned_attr_clauses(ppi->ppi_clauses,
@@ -149,8 +161,8 @@ pathman_join_pathlist_hook(PlannerInfo *root,
149161
continue;
150162

151163
inner = create_runtimeappend_path(root, cur_inner_path, ppi, paramsel);
152-
if (saved_jointype == JOIN_UNIQUE_INNER)
153-
return; /* No way to do this with a parameterized inner path */
164+
if (!inner)
165+
return; /* could not build it, retreat! */
154166

155167
initial_cost_nestloop(root, &workspace, jointype,
156168
outer, inner, /* built paths */
@@ -388,13 +400,6 @@ pathman_rel_pathlist_hook(PlannerInfo *root,
388400
Relids inner_required = PATH_REQ_OUTER((Path *) cur_path);
389401
Path *inner_path = NULL;
390402
ParamPathInfo *ppi;
391-
List *ppi_part_clauses = NIL;
392-
393-
/* Fetch ParamPathInfo & try to extract part-related clauses */
394-
ppi = get_baserel_parampathinfo(root, rel, inner_required);
395-
if (ppi && ppi->ppi_clauses)
396-
ppi_part_clauses = get_partitioned_attr_clauses(ppi->ppi_clauses,
397-
prel, rel->relid);
398403

399404
/* Skip if rel contains some join-related stuff or path type mismatched */
400405
if (!(IsA(cur_path, AppendPath) || IsA(cur_path, MergeAppendPath)) ||
@@ -403,13 +408,13 @@ pathman_rel_pathlist_hook(PlannerInfo *root,
403408
continue;
404409
}
405410

406-
/*
407-
* Skip if neither rel->baserestrictinfo nor
408-
* ppi->ppi_clauses reference partition attribute
409-
*/
410-
if (!(rel_part_clauses || ppi_part_clauses))
411+
/* Skip if rel->baserestrictinfo doesn't reference partition attribute */
412+
if (!rel_part_clauses)
411413
continue;
412414

415+
/* Get existing parameterization */
416+
ppi = get_appendrel_parampathinfo(rel, inner_required);
417+
413418
if (IsA(cur_path, AppendPath) && pg_pathman_enable_runtimeappend)
414419
inner_path = create_runtimeappend_path(root, cur_path,
415420
ppi, paramsel);

src/include/pathman.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ Bitmapset *translate_col_privs(const Bitmapset *parent_privs,
123123
void set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti,
124124
PathKey *pathkeyAsc, PathKey *pathkeyDesc);
125125

126+
Path *get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
127+
Relids required_outer);
128+
126129

127130
typedef struct
128131
{

src/include/runtimeappend.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ typedef struct
3737

3838
/* Restrictions to be checked during ReScan and Exec */
3939
List *custom_exprs;
40-
List *custom_expr_states;
4140

4241
/* All available plans \ plan states */
4342
HTAB *children_table;

src/nodes_common.c

Lines changed: 35 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -350,22 +350,53 @@ create_append_path_common(PlannerInfo *root,
350350

351351
result->nchildren = list_length(inner_append->subpaths);
352352
result->children = (ChildScanCommon *)
353-
palloc(result->nchildren * sizeof(ChildScanCommon));
353+
palloc(result->nchildren * sizeof(ChildScanCommon));
354+
354355
i = 0;
355356
foreach (lc, inner_append->subpaths)
356357
{
357-
Path *path = lfirst(lc);
358-
Index relindex = path->parent->relid;
358+
Path *path = (Path *) lfirst(lc);
359+
RelOptInfo *childrel = path->parent;
359360
ChildScanCommon child;
360361

362+
/* Do we have parameterization? */
363+
if (param_info)
364+
{
365+
Relids required_outer = param_info->ppi_req_outer;
366+
367+
/* Rebuild path using new 'required_outer' */
368+
path = get_cheapest_parameterized_child_path(root, childrel,
369+
required_outer);
370+
}
371+
372+
/*
373+
* We were unable to re-parameterize child path,
374+
* which means that we can't use Runtime[Merge]Append,
375+
* since its children can't evaluate join quals.
376+
*/
377+
if (!path)
378+
{
379+
int j;
380+
381+
for (j = 0; j < i; j++)
382+
pfree(result->children[j]);
383+
pfree(result->children);
384+
385+
list_free_deep(result->cpath.custom_paths);
386+
387+
pfree(result);
388+
389+
return NULL; /* notify caller */
390+
}
391+
361392
child = (ChildScanCommon) palloc(sizeof(ChildScanCommonData));
362393

363394
result->cpath.path.startup_cost += path->startup_cost;
364395
result->cpath.path.total_cost += path->total_cost;
365396

366397
child->content_type = CHILD_PATH;
367398
child->content.path = path;
368-
child->relid = root->simple_rte_array[relindex]->relid;
399+
child->relid = root->simple_rte_array[childrel->relid]->relid;
369400
Assert(child->relid != InvalidOid);
370401

371402
result->cpath.custom_paths = lappend(result->cpath.custom_paths,
@@ -476,12 +507,6 @@ create_append_scan_state_common(CustomScan *node,
476507
void
477508
begin_append_common(CustomScanState *node, EState *estate, int eflags)
478509
{
479-
RuntimeAppendState *scan_state = (RuntimeAppendState *) node;
480-
481-
scan_state->custom_expr_states =
482-
(List *) ExecInitExpr((Expr *) scan_state->custom_exprs,
483-
(PlanState *) scan_state);
484-
485510
node->ss.ps.ps_TupFromTlist = false;
486511
}
487512

src/pg_pathman.c

Lines changed: 74 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,6 @@ static void generate_mergeappend_paths(PlannerInfo *root,
9292
PathKey *pathkeyAsc,
9393
PathKey *pathkeyDesc);
9494

95-
static Path *get_cheapest_parameterized_child_path(PlannerInfo *root,
96-
RelOptInfo *rel,
97-
Relids required_outer);
98-
9995

10096
/* We can transform Param into Const provided that 'econtext' is available */
10197
#define IsConstValue(wcxt, node) \
@@ -293,7 +289,7 @@ append_child_relation(PlannerInfo *root, Relation parent_relation,
293289
{
294290
childquals = NIL;
295291

296-
forboth(lc1, wrappers, lc2, parent_rel->baserestrictinfo)
292+
forboth (lc1, wrappers, lc2, parent_rel->baserestrictinfo)
297293
{
298294
WrapperNode *wrap = (WrapperNode *) lfirst(lc1);
299295
Node *new_clause;
@@ -1333,78 +1329,6 @@ accumulate_append_subpath(List *subpaths, Path *path)
13331329
return lappend(subpaths, path);
13341330
}
13351331

1336-
/*
1337-
* get_cheapest_parameterized_child_path
1338-
* Get cheapest path for this relation that has exactly the requested
1339-
* parameterization.
1340-
*
1341-
* Returns NULL if unable to create such a path.
1342-
*/
1343-
static Path *
1344-
get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
1345-
Relids required_outer)
1346-
{
1347-
Path *cheapest;
1348-
ListCell *lc;
1349-
1350-
/*
1351-
* Look up the cheapest existing path with no more than the needed
1352-
* parameterization. If it has exactly the needed parameterization, we're
1353-
* done.
1354-
*/
1355-
cheapest = get_cheapest_path_for_pathkeys(rel->pathlist,
1356-
NIL,
1357-
required_outer,
1358-
TOTAL_COST);
1359-
Assert(cheapest != NULL);
1360-
if (bms_equal(PATH_REQ_OUTER(cheapest), required_outer))
1361-
return cheapest;
1362-
1363-
/*
1364-
* Otherwise, we can "reparameterize" an existing path to match the given
1365-
* parameterization, which effectively means pushing down additional
1366-
* joinquals to be checked within the path's scan. However, some existing
1367-
* paths might check the available joinquals already while others don't;
1368-
* therefore, it's not clear which existing path will be cheapest after
1369-
* reparameterization. We have to go through them all and find out.
1370-
*/
1371-
cheapest = NULL;
1372-
foreach(lc, rel->pathlist)
1373-
{
1374-
Path *path = (Path *) lfirst(lc);
1375-
1376-
/* Can't use it if it needs more than requested parameterization */
1377-
if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer))
1378-
continue;
1379-
1380-
/*
1381-
* Reparameterization can only increase the path's cost, so if it's
1382-
* already more expensive than the current cheapest, forget it.
1383-
*/
1384-
if (cheapest != NULL &&
1385-
compare_path_costs(cheapest, path, TOTAL_COST) <= 0)
1386-
continue;
1387-
1388-
/* Reparameterize if needed, then recheck cost */
1389-
if (!bms_equal(PATH_REQ_OUTER(path), required_outer))
1390-
{
1391-
path = reparameterize_path(root, path, required_outer, 1.0);
1392-
if (path == NULL)
1393-
continue; /* failed to reparameterize this one */
1394-
Assert(bms_equal(PATH_REQ_OUTER(path), required_outer));
1395-
1396-
if (cheapest != NULL &&
1397-
compare_path_costs(cheapest, path, TOTAL_COST) <= 0)
1398-
continue;
1399-
}
1400-
1401-
/* We have a new best path */
1402-
cheapest = path;
1403-
}
1404-
1405-
/* Return the best path, or NULL if we found no suitable candidate */
1406-
return cheapest;
1407-
}
14081332

14091333
/*
14101334
* generate_mergeappend_paths
@@ -1945,7 +1869,6 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti,
19451869

19461870
if (parallel_workers > 0)
19471871
{
1948-
19491872
/* Generate a partial append path. */
19501873
appendpath = create_append_path_compat(rel, partial_subpaths, NULL,
19511874
parallel_workers);
@@ -2006,3 +1929,76 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Index rti,
20061929
create_append_path_compat(rel, subpaths, required_outer, 0));
20071930
}
20081931
}
1932+
1933+
/*
1934+
* get_cheapest_parameterized_child_path
1935+
* Get cheapest path for this relation that has exactly the requested
1936+
* parameterization.
1937+
*
1938+
* Returns NULL if unable to create such a path.
1939+
*/
1940+
Path *
1941+
get_cheapest_parameterized_child_path(PlannerInfo *root, RelOptInfo *rel,
1942+
Relids required_outer)
1943+
{
1944+
Path *cheapest;
1945+
ListCell *lc;
1946+
1947+
/*
1948+
* Look up the cheapest existing path with no more than the needed
1949+
* parameterization. If it has exactly the needed parameterization, we're
1950+
* done.
1951+
*/
1952+
cheapest = get_cheapest_path_for_pathkeys(rel->pathlist,
1953+
NIL,
1954+
required_outer,
1955+
TOTAL_COST);
1956+
Assert(cheapest != NULL);
1957+
if (bms_equal(PATH_REQ_OUTER(cheapest), required_outer))
1958+
return cheapest;
1959+
1960+
/*
1961+
* Otherwise, we can "reparameterize" an existing path to match the given
1962+
* parameterization, which effectively means pushing down additional
1963+
* joinquals to be checked within the path's scan. However, some existing
1964+
* paths might check the available joinquals already while others don't;
1965+
* therefore, it's not clear which existing path will be cheapest after
1966+
* reparameterization. We have to go through them all and find out.
1967+
*/
1968+
cheapest = NULL;
1969+
foreach(lc, rel->pathlist)
1970+
{
1971+
Path *path = (Path *) lfirst(lc);
1972+
1973+
/* Can't use it if it needs more than requested parameterization */
1974+
if (!bms_is_subset(PATH_REQ_OUTER(path), required_outer))
1975+
continue;
1976+
1977+
/*
1978+
* Reparameterization can only increase the path's cost, so if it's
1979+
* already more expensive than the current cheapest, forget it.
1980+
*/
1981+
if (cheapest != NULL &&
1982+
compare_path_costs(cheapest, path, TOTAL_COST) <= 0)
1983+
continue;
1984+
1985+
/* Reparameterize if needed, then recheck cost */
1986+
if (!bms_equal(PATH_REQ_OUTER(path), required_outer))
1987+
{
1988+
path = reparameterize_path(root, path, required_outer, 1.0);
1989+
if (path == NULL)
1990+
continue; /* failed to reparameterize this one */
1991+
Assert(bms_equal(PATH_REQ_OUTER(path), required_outer));
1992+
1993+
if (cheapest != NULL &&
1994+
compare_path_costs(cheapest, path, TOTAL_COST) <= 0)
1995+
continue;
1996+
}
1997+
1998+
/* We have a new best path */
1999+
cheapest = path;
2000+
}
2001+
2002+
/* Return the best path, or NULL if we found no suitable candidate */
2003+
return cheapest;
2004+
}

0 commit comments

Comments
 (0)