Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 4be058f

Browse files
committed
In the planner, replace an empty FROM clause with a dummy RTE.
The fact that "SELECT expression" has no base relations has long been a thorn in the side of the planner. It makes it hard to flatten a sub-query that looks like that, or is a trivial VALUES() item, because the planner generally uses relid sets to identify sub-relations, and such a sub-query would have an empty relid set if we flattened it. prepjointree.c contains some baroque logic that works around this in certain special cases --- but there is a much better answer. We can replace an empty FROM clause with a dummy RTE that acts like a table of one row and no columns, and then there are no such corner cases to worry about. Instead we need some logic to get rid of useless dummy RTEs, but that's simpler and covers more cases than what was there before. For really trivial cases, where the query is just "SELECT expression" and nothing else, there's a hazard that adding the extra RTE makes for a noticeable slowdown; even though it's not much processing, there's not that much for the planner to do overall. However testing says that the penalty is very small, close to the noise level. In more complex queries, this is able to find optimizations that we could not find before. The new RTE type is called RTE_RESULT, since the "scan" plan type it gives rise to is a Result node (the same plan we produced for a "SELECT expression" query before). To avoid confusion, rename the old ResultPath path type to GroupResultPath, reflecting that it's only used in degenerate grouping cases where we know the query produces just one grouped row. (It wouldn't work to unify the two cases, because there are different rules about where the associated quals live during query_planner.) Note: although this touches readfuncs.c, I don't think a catversion bump is required, because the added case can't occur in stored rules, only plans. Patch by me, reviewed by David Rowley and Mark Dilger Discussion: https://postgr.es/m/15944.1521127664@sss.pgh.pa.us
1 parent 5c11867 commit 4be058f

35 files changed

+1171
-446
lines changed

contrib/pg_stat_statements/pg_stat_statements.c

+2
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,8 @@ JumbleRangeTable(pgssJumbleState *jstate, List *rtable)
24762476
case RTE_NAMEDTUPLESTORE:
24772477
APP_JUMB_STRING(rte->enrname);
24782478
break;
2479+
case RTE_RESULT:
2480+
break;
24792481
default:
24802482
elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
24812483
break;

contrib/postgres_fdw/expected/postgres_fdw.out

+1-1
Original file line numberDiff line numberDiff line change
@@ -5361,7 +5361,7 @@ INSERT INTO ft2 (c1,c2,c3) VALUES (1200,999,'foo') RETURNING tableoid::regclass;
53615361
QUERY PLAN
53625362
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
53635363
Insert on public.ft2
5364-
Output: (tableoid)::regclass
5364+
Output: (ft2.tableoid)::regclass
53655365
Remote SQL: INSERT INTO "S 1"."T 1"("C 1", c2, c3, c4, c5, c6, c7, c8) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)
53665366
-> Result
53675367
Output: 1200, 999, NULL::integer, 'foo'::text, NULL::timestamp with time zone, NULL::timestamp without time zone, NULL::character varying, 'ft2 '::character(10), NULL::user_enum

src/backend/executor/execAmi.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -437,9 +437,12 @@ ExecSupportsMarkRestore(Path *pathnode)
437437
return ExecSupportsMarkRestore(((ProjectionPath *) pathnode)->subpath);
438438
else if (IsA(pathnode, MinMaxAggPath))
439439
return false; /* childless Result */
440+
else if (IsA(pathnode, GroupResultPath))
441+
return false; /* childless Result */
440442
else
441443
{
442-
Assert(IsA(pathnode, ResultPath));
444+
/* Simple RTE_RESULT base relation */
445+
Assert(IsA(pathnode, Path));
443446
return false; /* childless Result */
444447
}
445448

src/backend/nodes/nodeFuncs.c

+10-8
Original file line numberDiff line numberDiff line change
@@ -2329,10 +2329,6 @@ range_table_walker(List *rtable,
23292329
if (walker(rte->tablesample, context))
23302330
return true;
23312331
break;
2332-
case RTE_CTE:
2333-
case RTE_NAMEDTUPLESTORE:
2334-
/* nothing to do */
2335-
break;
23362332
case RTE_SUBQUERY:
23372333
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
23382334
if (walker(rte->subquery, context))
@@ -2355,6 +2351,11 @@ range_table_walker(List *rtable,
23552351
if (walker(rte->values_lists, context))
23562352
return true;
23572353
break;
2354+
case RTE_CTE:
2355+
case RTE_NAMEDTUPLESTORE:
2356+
case RTE_RESULT:
2357+
/* nothing to do */
2358+
break;
23582359
}
23592360

23602361
if (walker(rte->securityQuals, context))
@@ -3164,10 +3165,6 @@ range_table_mutator(List *rtable,
31643165
TableSampleClause *);
31653166
/* we don't bother to copy eref, aliases, etc; OK? */
31663167
break;
3167-
case RTE_CTE:
3168-
case RTE_NAMEDTUPLESTORE:
3169-
/* nothing to do */
3170-
break;
31713168
case RTE_SUBQUERY:
31723169
if (!(flags & QTW_IGNORE_RT_SUBQUERIES))
31733170
{
@@ -3198,6 +3195,11 @@ range_table_mutator(List *rtable,
31983195
case RTE_VALUES:
31993196
MUTATE(newrte->values_lists, rte->values_lists, List *);
32003197
break;
3198+
case RTE_CTE:
3199+
case RTE_NAMEDTUPLESTORE:
3200+
case RTE_RESULT:
3201+
/* nothing to do */
3202+
break;
32013203
}
32023204
MUTATE(newrte->securityQuals, rte->securityQuals, List *);
32033205
newrt = lappend(newrt, newrte);

src/backend/nodes/outfuncs.c

+7-5
Original file line numberDiff line numberDiff line change
@@ -1855,9 +1855,9 @@ _outMergeAppendPath(StringInfo str, const MergeAppendPath *node)
18551855
}
18561856

18571857
static void
1858-
_outResultPath(StringInfo str, const ResultPath *node)
1858+
_outGroupResultPath(StringInfo str, const GroupResultPath *node)
18591859
{
1860-
WRITE_NODE_TYPE("RESULTPATH");
1860+
WRITE_NODE_TYPE("GROUPRESULTPATH");
18611861

18621862
_outPathInfo(str, (const Path *) node);
18631863

@@ -2213,7 +2213,6 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
22132213
WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind);
22142214
WRITE_BOOL_FIELD(hasJoinRTEs);
22152215
WRITE_BOOL_FIELD(hasLateralRTEs);
2216-
WRITE_BOOL_FIELD(hasDeletedRTEs);
22172216
WRITE_BOOL_FIELD(hasHavingQual);
22182217
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
22192218
WRITE_BOOL_FIELD(hasRecursion);
@@ -3060,6 +3059,9 @@ _outRangeTblEntry(StringInfo str, const RangeTblEntry *node)
30603059
WRITE_NODE_FIELD(coltypmods);
30613060
WRITE_NODE_FIELD(colcollations);
30623061
break;
3062+
case RTE_RESULT:
3063+
/* no extra fields */
3064+
break;
30633065
default:
30643066
elog(ERROR, "unrecognized RTE kind: %d", (int) node->rtekind);
30653067
break;
@@ -3943,8 +3945,8 @@ outNode(StringInfo str, const void *obj)
39433945
case T_MergeAppendPath:
39443946
_outMergeAppendPath(str, obj);
39453947
break;
3946-
case T_ResultPath:
3947-
_outResultPath(str, obj);
3948+
case T_GroupResultPath:
3949+
_outGroupResultPath(str, obj);
39483950
break;
39493951
case T_MaterialPath:
39503952
_outMaterialPath(str, obj);

src/backend/nodes/print.c

+4
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,10 @@ print_rt(const List *rtable)
295295
printf("%d\t%s\t[tuplestore]",
296296
i, rte->eref->aliasname);
297297
break;
298+
case RTE_RESULT:
299+
printf("%d\t%s\t[result]",
300+
i, rte->eref->aliasname);
301+
break;
298302
default:
299303
printf("%d\t%s\t[unknown rtekind]",
300304
i, rte->eref->aliasname);

src/backend/nodes/readfuncs.c

+3
Original file line numberDiff line numberDiff line change
@@ -1411,6 +1411,9 @@ _readRangeTblEntry(void)
14111411
READ_NODE_FIELD(coltypmods);
14121412
READ_NODE_FIELD(colcollations);
14131413
break;
1414+
case RTE_RESULT:
1415+
/* no extra fields */
1416+
break;
14141417
default:
14151418
elog(ERROR, "unrecognized RTE kind: %d",
14161419
(int) local_node->rtekind);

src/backend/optimizer/README

+11-2
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,16 @@ RelOptInfo - a relation or joined relations
361361
join clauses)
362362

363363
Path - every way to generate a RelOptInfo(sequential,index,joins)
364-
SeqScan - represents a sequential scan plan
364+
A plain Path node can represent several simple plans, per its pathtype:
365+
T_SeqScan - sequential scan
366+
T_SampleScan - tablesample scan
367+
T_FunctionScan - function-in-FROM scan
368+
T_TableFuncScan - table function scan
369+
T_ValuesScan - VALUES scan
370+
T_CteScan - CTE (WITH) scan
371+
T_NamedTuplestoreScan - ENR scan
372+
T_WorkTableScan - scan worktable of a recursive CTE
373+
T_Result - childless Result plan node (used for FROM-less SELECT)
365374
IndexPath - index scan
366375
BitmapHeapPath - top of a bitmapped index scan
367376
TidPath - scan by CTID
@@ -370,7 +379,7 @@ RelOptInfo - a relation or joined relations
370379
CustomPath - for custom scan providers
371380
AppendPath - append multiple subpaths together
372381
MergeAppendPath - merge multiple subpaths, preserving their common sort order
373-
ResultPath - a childless Result plan node (used for FROM-less SELECT)
382+
GroupResultPath - childless Result plan node (used for degenerate grouping)
374383
MaterialPath - a Material plan node
375384
UniquePath - remove duplicate rows (either by hashing or sorting)
376385
GatherPath - collect the results of parallel workers

src/backend/optimizer/path/allpaths.c

+53-6
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ static void set_cte_pathlist(PlannerInfo *root, RelOptInfo *rel,
117117
RangeTblEntry *rte);
118118
static void set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
119119
RangeTblEntry *rte);
120+
static void set_result_pathlist(PlannerInfo *root, RelOptInfo *rel,
121+
RangeTblEntry *rte);
120122
static void set_worktable_pathlist(PlannerInfo *root, RelOptInfo *rel,
121123
RangeTblEntry *rte);
122124
static RelOptInfo *make_rel_from_joinlist(PlannerInfo *root, List *joinlist);
@@ -437,8 +439,13 @@ set_rel_size(PlannerInfo *root, RelOptInfo *rel,
437439
set_cte_pathlist(root, rel, rte);
438440
break;
439441
case RTE_NAMEDTUPLESTORE:
442+
/* Might as well just build the path immediately */
440443
set_namedtuplestore_pathlist(root, rel, rte);
441444
break;
445+
case RTE_RESULT:
446+
/* Might as well just build the path immediately */
447+
set_result_pathlist(root, rel, rte);
448+
break;
442449
default:
443450
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
444451
break;
@@ -510,6 +517,9 @@ set_rel_pathlist(PlannerInfo *root, RelOptInfo *rel,
510517
case RTE_NAMEDTUPLESTORE:
511518
/* tuplestore reference --- fully handled during set_rel_size */
512519
break;
520+
case RTE_RESULT:
521+
/* simple Result --- fully handled during set_rel_size */
522+
break;
513523
default:
514524
elog(ERROR, "unexpected rtekind: %d", (int) rel->rtekind);
515525
break;
@@ -712,6 +722,10 @@ set_rel_consider_parallel(PlannerInfo *root, RelOptInfo *rel,
712722
* infrastructure to support that.
713723
*/
714724
return;
725+
726+
case RTE_RESULT:
727+
/* RESULT RTEs, in themselves, are no problem. */
728+
break;
715729
}
716730

717731
/*
@@ -2509,6 +2523,36 @@ set_namedtuplestore_pathlist(PlannerInfo *root, RelOptInfo *rel,
25092523
set_cheapest(rel);
25102524
}
25112525

2526+
/*
2527+
* set_result_pathlist
2528+
* Build the (single) access path for an RTE_RESULT RTE
2529+
*
2530+
* There's no need for a separate set_result_size phase, since we
2531+
* don't support join-qual-parameterized paths for these RTEs.
2532+
*/
2533+
static void
2534+
set_result_pathlist(PlannerInfo *root, RelOptInfo *rel,
2535+
RangeTblEntry *rte)
2536+
{
2537+
Relids required_outer;
2538+
2539+
/* Mark rel with estimated output rows, width, etc */
2540+
set_result_size_estimates(root, rel);
2541+
2542+
/*
2543+
* We don't support pushing join clauses into the quals of a Result scan,
2544+
* but it could still have required parameterization due to LATERAL refs
2545+
* in its tlist.
2546+
*/
2547+
required_outer = rel->lateral_relids;
2548+
2549+
/* Generate appropriate path */
2550+
add_path(rel, create_resultscan_path(root, rel, required_outer));
2551+
2552+
/* Select cheapest path (pretty easy in this case...) */
2553+
set_cheapest(rel);
2554+
}
2555+
25122556
/*
25132557
* set_worktable_pathlist
25142558
* Build the (single) access path for a self-reference CTE RTE
@@ -3677,9 +3721,6 @@ print_path(PlannerInfo *root, Path *path, int indent)
36773721
case T_SampleScan:
36783722
ptype = "SampleScan";
36793723
break;
3680-
case T_SubqueryScan:
3681-
ptype = "SubqueryScan";
3682-
break;
36833724
case T_FunctionScan:
36843725
ptype = "FunctionScan";
36853726
break;
@@ -3692,6 +3733,12 @@ print_path(PlannerInfo *root, Path *path, int indent)
36923733
case T_CteScan:
36933734
ptype = "CteScan";
36943735
break;
3736+
case T_NamedTuplestoreScan:
3737+
ptype = "NamedTuplestoreScan";
3738+
break;
3739+
case T_Result:
3740+
ptype = "Result";
3741+
break;
36953742
case T_WorkTableScan:
36963743
ptype = "WorkTableScan";
36973744
break;
@@ -3716,7 +3763,7 @@ print_path(PlannerInfo *root, Path *path, int indent)
37163763
ptype = "TidScan";
37173764
break;
37183765
case T_SubqueryScanPath:
3719-
ptype = "SubqueryScanScan";
3766+
ptype = "SubqueryScan";
37203767
break;
37213768
case T_ForeignPath:
37223769
ptype = "ForeignScan";
@@ -3742,8 +3789,8 @@ print_path(PlannerInfo *root, Path *path, int indent)
37423789
case T_MergeAppendPath:
37433790
ptype = "MergeAppend";
37443791
break;
3745-
case T_ResultPath:
3746-
ptype = "Result";
3792+
case T_GroupResultPath:
3793+
ptype = "GroupResult";
37473794
break;
37483795
case T_MaterialPath:
37493796
ptype = "Material";

src/backend/optimizer/path/costsize.c

+57
Original file line numberDiff line numberDiff line change
@@ -1570,6 +1570,40 @@ cost_namedtuplestorescan(Path *path, PlannerInfo *root,
15701570
path->total_cost = startup_cost + run_cost;
15711571
}
15721572

1573+
/*
1574+
* cost_resultscan
1575+
* Determines and returns the cost of scanning an RTE_RESULT relation.
1576+
*/
1577+
void
1578+
cost_resultscan(Path *path, PlannerInfo *root,
1579+
RelOptInfo *baserel, ParamPathInfo *param_info)
1580+
{
1581+
Cost startup_cost = 0;
1582+
Cost run_cost = 0;
1583+
QualCost qpqual_cost;
1584+
Cost cpu_per_tuple;
1585+
1586+
/* Should only be applied to RTE_RESULT base relations */
1587+
Assert(baserel->relid > 0);
1588+
Assert(baserel->rtekind == RTE_RESULT);
1589+
1590+
/* Mark the path with the correct row estimate */
1591+
if (param_info)
1592+
path->rows = param_info->ppi_rows;
1593+
else
1594+
path->rows = baserel->rows;
1595+
1596+
/* We charge qual cost plus cpu_tuple_cost */
1597+
get_restriction_qual_cost(root, baserel, param_info, &qpqual_cost);
1598+
1599+
startup_cost += qpqual_cost.startup;
1600+
cpu_per_tuple = cpu_tuple_cost + qpqual_cost.per_tuple;
1601+
run_cost += cpu_per_tuple * baserel->tuples;
1602+
1603+
path->startup_cost = startup_cost;
1604+
path->total_cost = startup_cost + run_cost;
1605+
}
1606+
15731607
/*
15741608
* cost_recursive_union
15751609
* Determines and returns the cost of performing a recursive union,
@@ -5044,6 +5078,29 @@ set_namedtuplestore_size_estimates(PlannerInfo *root, RelOptInfo *rel)
50445078
set_baserel_size_estimates(root, rel);
50455079
}
50465080

5081+
/*
5082+
* set_result_size_estimates
5083+
* Set the size estimates for an RTE_RESULT base relation
5084+
*
5085+
* The rel's targetlist and restrictinfo list must have been constructed
5086+
* already.
5087+
*
5088+
* We set the same fields as set_baserel_size_estimates.
5089+
*/
5090+
void
5091+
set_result_size_estimates(PlannerInfo *root, RelOptInfo *rel)
5092+
{
5093+
/* Should only be applied to RTE_RESULT base relations */
5094+
Assert(rel->relid > 0);
5095+
Assert(planner_rt_fetch(rel->relid, root)->rtekind == RTE_RESULT);
5096+
5097+
/* RTE_RESULT always generates a single row, natively */
5098+
rel->tuples = 1;
5099+
5100+
/* Now estimate number of output rows, etc */
5101+
set_baserel_size_estimates(root, rel);
5102+
}
5103+
50475104
/*
50485105
* set_foreign_size_estimates
50495106
* Set the size estimates for a base relation that is a foreign table.

0 commit comments

Comments
 (0)