diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2019-01-28 17:54:10 -0500 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2019-01-28 17:54:23 -0500 |
commit | 4be058fe9ec5e630239b656af21fc083371f30ed (patch) | |
tree | 1e422ec3af7f08b7a1cdd7c57b71cd3215cec152 /src/backend/optimizer/plan/planner.c | |
parent | 5c1186751214416fdf88f33a89c3dc88391d2d60 (diff) | |
download | postgresql-4be058fe9ec5e630239b656af21fc083371f30ed.tar.gz postgresql-4be058fe9ec5e630239b656af21fc083371f30ed.zip |
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
Diffstat (limited to 'src/backend/optimizer/plan/planner.c')
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 49 |
1 files changed, 35 insertions, 14 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 4465f002c8f..272710eed2d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -611,6 +611,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, List *newWithCheckOptions; List *newHaving; bool hasOuterJoins; + bool hasResultRTEs; RelOptInfo *final_rel; ListCell *l; @@ -652,6 +653,12 @@ subquery_planner(PlannerGlobal *glob, Query *parse, SS_process_ctes(root); /* + * If the FROM clause is empty, replace it with a dummy RTE_RESULT RTE, so + * that we don't need so many special cases to deal with that situation. + */ + replace_empty_jointree(parse); + + /* * Look for ANY and EXISTS SubLinks in WHERE and JOIN/ON clauses, and try * to transform them into joins. Note that this step does not descend * into subqueries; if we pull up any subqueries below, their SubLinks are @@ -684,14 +691,16 @@ subquery_planner(PlannerGlobal *glob, Query *parse, /* * Detect whether any rangetable entries are RTE_JOIN kind; if not, we can - * avoid the expense of doing flatten_join_alias_vars(). Also check for - * outer joins --- if none, we can skip reduce_outer_joins(). And check - * for LATERAL RTEs, too. This must be done after we have done - * pull_up_subqueries(), of course. + * avoid the expense of doing flatten_join_alias_vars(). Likewise check + * whether any are RTE_RESULT kind; if not, we can skip + * remove_useless_result_rtes(). Also check for outer joins --- if none, + * we can skip reduce_outer_joins(). And check for LATERAL RTEs, too. + * This must be done after we have done pull_up_subqueries(), of course. */ root->hasJoinRTEs = false; root->hasLateralRTEs = false; hasOuterJoins = false; + hasResultRTEs = false; foreach(l, parse->rtable) { RangeTblEntry *rte = lfirst_node(RangeTblEntry, l); @@ -702,6 +711,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, if (IS_OUTER_JOIN(rte->jointype)) hasOuterJoins = true; } + else if (rte->rtekind == RTE_RESULT) + { + hasResultRTEs = true; + } if (rte->lateral) root->hasLateralRTEs = true; } @@ -717,10 +730,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, /* * Expand any rangetable entries that are inheritance sets into "append * relations". This can add entries to the rangetable, but they must be - * plain base relations not joins, so it's OK (and marginally more - * efficient) to do it after checking for join RTEs. We must do it after - * pulling up subqueries, else we'd fail to handle inherited tables in - * subqueries. + * plain RTE_RELATION entries, so it's OK (and marginally more efficient) + * to do it after checking for joins and other special RTEs. We must do + * this after pulling up subqueries, else we'd fail to handle inherited + * tables in subqueries. */ expand_inherited_tables(root); @@ -968,6 +981,14 @@ subquery_planner(PlannerGlobal *glob, Query *parse, reduce_outer_joins(root); /* + * If we have any RTE_RESULT relations, see if they can be deleted from + * the jointree. This step is most effectively done after we've done + * expression preprocessing and outer join reduction. + */ + if (hasResultRTEs) + remove_useless_result_rtes(root); + + /* * Do the main planning. If we have an inherited target relation, that * needs special processing, else go straight to grouping_planner. */ @@ -3894,9 +3915,9 @@ create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, while (--nrows >= 0) { path = (Path *) - create_result_path(root, grouped_rel, - grouped_rel->reltarget, - (List *) parse->havingQual); + create_group_result_path(root, grouped_rel, + grouped_rel->reltarget, + (List *) parse->havingQual); paths = lappend(paths, path); } path = (Path *) @@ -3914,9 +3935,9 @@ create_degenerate_grouping_paths(PlannerInfo *root, RelOptInfo *input_rel, { /* No grouping sets, or just one, so one output row */ path = (Path *) - create_result_path(root, grouped_rel, - grouped_rel->reltarget, - (List *) parse->havingQual); + create_group_result_path(root, grouped_rel, + grouped_rel->reltarget, + (List *) parse->havingQual); } add_path(grouped_rel, path); |