bool is_sorted;
int presorted_keys;
+ /* If the input rel is dummy, propagate that to this query level */
+ if (is_dummy_rel(final_rel))
+ {
+ mark_dummy_rel(rel);
+ continue;
+ }
+
/*
* Include the cheapest path as-is so that the set operation can be
* cheaply implemented using a method which does not require the input
RelOptInfo *rel = lfirst(lc);
Path *ordered_path;
+ /* Skip any UNION children that are proven not to yield any rows */
+ if (is_dummy_rel(rel))
+ continue;
+
cheapest_pathlist = lappend(cheapest_pathlist,
rel->cheapest_total_path);
result_rel->consider_parallel = consider_parallel;
result_rel->consider_startup = (root->tuple_fraction > 0);
+ /* If all UNION children were dummy rels, make the resulting rel dummy */
+ if (cheapest_pathlist == NIL)
+ {
+ result_rel->reltarget = create_pathtarget(root, list_nth(tlist_list, 0));
+ mark_dummy_rel(result_rel);
+
+ return result_rel;
+ }
+
/*
* Append the child results together using the cheapest paths from each
* union child.
bool can_sort = grouping_is_sortable(groupList);
bool can_hash = grouping_is_hashable(groupList);
- /*
- * XXX for the moment, take the number of distinct groups as equal to
- * the total input size, i.e., the worst case. This is too
- * conservative, but it's not clear how to get a decent estimate of
- * the true size. One should note as well the propensity of novices
- * to write UNION rather than UNION ALL even when they don't expect
- * any duplicates...
- */
- dNumGroups = apath->rows;
+ if (list_length(cheapest_pathlist) == 1)
+ {
+ Path *path = linitial(cheapest_pathlist);
+
+ /*
+ * In the case where only one union child remains due to the
+ * detection of one or more dummy union children, obtain an
+ * estimate on the surviving child directly.
+ */
+ dNumGroups = estimate_num_groups(root,
+ path->pathtarget->exprs,
+ path->rows,
+ NULL,
+ NULL);
+ }
+ else
+ {
+ /*
+ * Otherwise, for the moment, take the number of distinct groups
+ * as equal to the total input size, i.e., the worst case. This
+ * is too conservative, but it's not clear how to get a decent
+ * estimate of the true size. One should note as well the
+ * propensity of novices to write UNION rather than UNION ALL even
+ * when they don't expect any duplicates...
+ */
+ dNumGroups = apath->rows;
+ }
if (can_hash)
{
drop table events_child, events, other_events;
reset enable_indexonlyscan;
+--
+-- Test handling of UNION with provably empty inputs
+--
+-- Ensure the empty UNION input is pruned and de-duplication is done for the
+-- remaining relation.
+EXPLAIN (COSTS OFF, VERBOSE)
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1
+ORDER BY 1;
+ QUERY PLAN
+--------------------------------------
+ Sort
+ Output: tenk1.four
+ Sort Key: tenk1.four
+ -> HashAggregate
+ Output: tenk1.four
+ Group Key: tenk1.four
+ -> Seq Scan on public.tenk1
+ Output: tenk1.four
+(8 rows)
+
+-- Validate that the results of the above are correct
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1
+ORDER BY 1;
+ two
+-----
+ 0
+ 1
+ 2
+ 3
+(4 rows)
+
+-- All UNION inputs are proven empty. Ensure the planner provides a
+-- const-false Result node
+EXPLAIN (COSTS OFF, VERBOSE)
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1 WHERE 1=2
+UNION
+SELECT ten FROM tenk1 WHERE 1=2;
+ QUERY PLAN
+--------------------------------
+ Result
+ Output: unnamed_subquery.two
+ Replaces: Aggregate
+ One-Time Filter: false
+(4 rows)
+
-- Test constraint exclusion of UNION ALL subqueries
explain (costs off)
SELECT * FROM
reset enable_indexonlyscan;
+--
+-- Test handling of UNION with provably empty inputs
+--
+
+-- Ensure the empty UNION input is pruned and de-duplication is done for the
+-- remaining relation.
+EXPLAIN (COSTS OFF, VERBOSE)
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1
+ORDER BY 1;
+
+-- Validate that the results of the above are correct
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1
+ORDER BY 1;
+
+-- All UNION inputs are proven empty. Ensure the planner provides a
+-- const-false Result node
+EXPLAIN (COSTS OFF, VERBOSE)
+SELECT two FROM tenk1 WHERE 1=2
+UNION
+SELECT four FROM tenk1 WHERE 1=2
+UNION
+SELECT ten FROM tenk1 WHERE 1=2;
+
-- Test constraint exclusion of UNION ALL subqueries
explain (costs off)
SELECT * FROM