if (newitems || best_path->umethod == UNIQUE_PATH_SORT)
{
/*
- * If the top plan node can't do projections, we need to add a Result
- * node to help it along.
+ * If the top plan node can't do projections and its existing target
+ * list isn't already what we need, we need to add a Result node to
+ * help it along.
*/
- if (!is_projection_capable_plan(subplan))
+ if (!is_projection_capable_plan(subplan) &&
+ !tlist_same_exprs(newtlist, subplan->targetlist))
subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
else
subplan->targetlist = newtlist;
{
/*
* If the top-level plan node is one that cannot do expression
- * evaluation, we must insert a Result node to project the
+ * evaluation and its existing target list isn't already what
+ * we need, we must insert a Result node to project the
* desired tlist.
*/
- if (!is_projection_capable_plan(result_plan))
+ if (!is_projection_capable_plan(result_plan) &&
+ !tlist_same_exprs(sub_tlist, result_plan->targetlist))
{
result_plan = (Plan *) make_result(root,
sub_tlist,
* If the top-level plan node is one that cannot do expression
* evaluation, we must insert a Result node to project the desired
* tlist. (In some cases this might not really be required, but
- * it's not worth trying to avoid it.) Note that on second and
- * subsequent passes through the following loop, the top-level
- * node will be a WindowAgg which we know can project; so we only
- * need to check once.
+ * it's not worth trying to avoid it. In particular, think not to
+ * skip adding the Result if the initial window_tlist matches the
+ * top-level plan node's output, because we might change the tlist
+ * inside the following loop.) Note that on second and subsequent
+ * passes through the following loop, the top-level node will be a
+ * WindowAgg which we know can project; so we only need to check
+ * once.
*/
if (!is_projection_capable_plan(result_plan))
{
}
+/*
+ * tlist_same_exprs
+ * Check whether two target lists contain the same expressions
+ *
+ * Note: this function is used to decide whether it's safe to jam a new tlist
+ * into a non-projection-capable plan node. Obviously we can't do that unless
+ * the node's tlist shows it already returns the column values we want.
+ * However, we can ignore the TargetEntry attributes resname, ressortgroupref,
+ * resorigtbl, resorigcol, and resjunk, because those are only labelings that
+ * don't affect the row values computed by the node. (Moreover, if we didn't
+ * ignore them, we'd frequently fail to make the desired optimization, since
+ * the planner tends to not bother to make resname etc. valid in intermediate
+ * plan nodes.) Note that on success, the caller must still jam the desired
+ * tlist into the plan node, else it won't have the desired labeling fields.
+ */
+bool
+tlist_same_exprs(List *tlist1, List *tlist2)
+{
+ ListCell *lc1,
+ *lc2;
+
+ if (list_length(tlist1) != list_length(tlist2))
+ return false; /* not same length, so can't match */
+
+ forboth(lc1, tlist1, lc2, tlist2)
+ {
+ TargetEntry *tle1 = (TargetEntry *) lfirst(lc1);
+ TargetEntry *tle2 = (TargetEntry *) lfirst(lc2);
+
+ if (!equal(tle1->expr, tle2->expr))
+ return false;
+ }
+
+ return true;
+}
+
+
/*
* Does tlist have same output datatypes as listed in colTypes?
*
extern List *add_to_flat_tlist(List *tlist, List *exprs);
extern List *get_tlist_exprs(List *tlist, bool includeJunk);
+
+extern bool tlist_same_exprs(List *tlist1, List *tlist2);
+
extern bool tlist_same_datatypes(List *tlist, List *colTypes, bool junkOK);
extern bool tlist_same_collations(List *tlist, List *colCollations, bool junkOK);
alter table nv_child_2010 add check (d between '2010-01-01'::date and '2010-12-31'::date) not valid;
alter table nv_child_2011 add check (d between '2011-01-01'::date and '2011-12-31'::date) not valid;
explain (costs off) select * from nv_parent where d between '2011-08-01' and '2011-08-31';
- QUERY PLAN
----------------------------------------------------------------------------------
- Result
- -> Append
- -> Seq Scan on nv_parent
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
- -> Seq Scan on nv_child_2010
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
- -> Seq Scan on nv_child_2011
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-(8 rows)
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Append
+ -> Seq Scan on nv_parent
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+ -> Seq Scan on nv_child_2010
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+ -> Seq Scan on nv_child_2011
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+(7 rows)
create table nv_child_2009 (check (d between '2009-01-01'::date and '2009-12-31'::date)) inherits (nv_parent);
explain (costs off) select * from nv_parent where d between '2011-08-01'::date and '2011-08-31'::date;
- QUERY PLAN
----------------------------------------------------------------------------------
- Result
- -> Append
- -> Seq Scan on nv_parent
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
- -> Seq Scan on nv_child_2010
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
- -> Seq Scan on nv_child_2011
- Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
-(8 rows)
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Append
+ -> Seq Scan on nv_parent
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+ -> Seq Scan on nv_child_2010
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+ -> Seq Scan on nv_child_2011
+ Filter: ((d >= '08-01-2011'::date) AND (d <= '08-31-2011'::date))
+(7 rows)
explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date;
- QUERY PLAN
----------------------------------------------------------------------------------
- Result
- -> Append
- -> Seq Scan on nv_parent
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
- -> Seq Scan on nv_child_2010
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
- -> Seq Scan on nv_child_2011
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
- -> Seq Scan on nv_child_2009
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-(10 rows)
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Append
+ -> Seq Scan on nv_parent
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+ -> Seq Scan on nv_child_2010
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+ -> Seq Scan on nv_child_2011
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+ -> Seq Scan on nv_child_2009
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+(9 rows)
-- after validation, the constraint should be used
alter table nv_child_2011 VALIDATE CONSTRAINT nv_child_2011_d_check;
explain (costs off) select * from nv_parent where d between '2009-08-01'::date and '2009-08-31'::date;
- QUERY PLAN
----------------------------------------------------------------------------------
- Result
- -> Append
- -> Seq Scan on nv_parent
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
- -> Seq Scan on nv_child_2010
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
- -> Seq Scan on nv_child_2009
- Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
-(8 rows)
+ QUERY PLAN
+---------------------------------------------------------------------------
+ Append
+ -> Seq Scan on nv_parent
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+ -> Seq Scan on nv_child_2010
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+ -> Seq Scan on nv_child_2009
+ Filter: ((d >= '08-01-2009'::date) AND (d <= '08-31-2009'::date))
+(7 rows)
-- Foreign key adding test with mixed types
-- Note: these tables are TEMP to avoid name conflicts when this test
reset enable_indexscan;
set enable_seqscan = off; -- plan with fewest seqscans should be merge
explain (verbose, costs off) select * from matest0 order by 1-id;
- QUERY PLAN
-------------------------------------------------------------------------
- Result
- Output: matest0.id, matest0.name, ((1 - matest0.id))
- -> Merge Append
- Sort Key: ((1 - matest0.id))
- -> Index Scan using matest0i on public.matest0
- Output: matest0.id, matest0.name, (1 - matest0.id)
- -> Index Scan using matest1i on public.matest1
- Output: matest1.id, matest1.name, (1 - matest1.id)
- -> Sort
- Output: matest2.id, matest2.name, ((1 - matest2.id))
- Sort Key: ((1 - matest2.id))
- -> Seq Scan on public.matest2
- Output: matest2.id, matest2.name, (1 - matest2.id)
- -> Index Scan using matest3i on public.matest3
- Output: matest3.id, matest3.name, (1 - matest3.id)
-(15 rows)
+ QUERY PLAN
+------------------------------------------------------------------
+ Merge Append
+ Sort Key: ((1 - matest0.id))
+ -> Index Scan using matest0i on public.matest0
+ Output: matest0.id, matest0.name, (1 - matest0.id)
+ -> Index Scan using matest1i on public.matest1
+ Output: matest1.id, matest1.name, (1 - matest1.id)
+ -> Sort
+ Output: matest2.id, matest2.name, ((1 - matest2.id))
+ Sort Key: ((1 - matest2.id))
+ -> Seq Scan on public.matest2
+ Output: matest2.id, matest2.name, (1 - matest2.id)
+ -> Index Scan using matest3i on public.matest3
+ Output: matest3.id, matest3.name, (1 - matest3.id)
+(13 rows)
select * from matest0 order by 1-id;
id | name
UNION ALL
SELECT thousand, thousand FROM tenk1
ORDER BY thousand, tenthous;
- QUERY PLAN
--------------------------------------------------------------------------------
- Result
- -> Merge Append
- Sort Key: tenk1.thousand, tenk1.tenthous
- -> Index Only Scan using tenk1_thous_tenthous on tenk1
- -> Sort
- Sort Key: tenk1_1.thousand, tenk1_1.thousand
- -> Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
-(7 rows)
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Merge Append
+ Sort Key: tenk1.thousand, tenk1.tenthous
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ -> Sort
+ Sort Key: tenk1_1.thousand, tenk1_1.thousand
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
+(6 rows)
explain (costs off)
SELECT thousand, tenthous, thousand+tenthous AS x FROM tenk1
UNION ALL
SELECT 42, 42, hundred FROM tenk1
ORDER BY thousand, tenthous;
- QUERY PLAN
-------------------------------------------------------------------------
- Result
- -> Merge Append
- Sort Key: tenk1.thousand, tenk1.tenthous
- -> Index Only Scan using tenk1_thous_tenthous on tenk1
- -> Sort
- Sort Key: (42), (42)
- -> Index Only Scan using tenk1_hundred on tenk1 tenk1_1
-(7 rows)
+ QUERY PLAN
+------------------------------------------------------------------
+ Merge Append
+ Sort Key: tenk1.thousand, tenk1.tenthous
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ -> Sort
+ Sort Key: (42), (42)
+ -> Index Only Scan using tenk1_hundred on tenk1 tenk1_1
+(6 rows)
explain (costs off)
SELECT thousand, tenthous FROM tenk1
UNION ALL
SELECT thousand, random()::integer FROM tenk1
ORDER BY thousand, tenthous;
- QUERY PLAN
--------------------------------------------------------------------------------
- Result
- -> Merge Append
- Sort Key: tenk1.thousand, tenk1.tenthous
- -> Index Only Scan using tenk1_thous_tenthous on tenk1
- -> Sort
- Sort Key: tenk1_1.thousand, ((random())::integer)
- -> Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
-(7 rows)
+ QUERY PLAN
+-------------------------------------------------------------------------
+ Merge Append
+ Sort Key: tenk1.thousand, tenk1.tenthous
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1
+ -> Sort
+ Sort Key: tenk1_1.thousand, ((random())::integer)
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1 tenk1_1
+(6 rows)
-- Check min/max aggregate optimization
explain (costs off)
UNION ALL
SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s
ORDER BY x, y;
- QUERY PLAN
--------------------------------------------------------------------
- Result
- -> Merge Append
- Sort Key: a.thousand, a.tenthous
- -> Index Only Scan using tenk1_thous_tenthous on tenk1 a
- -> Sort
- Sort Key: b.unique2, b.unique2
- -> Index Only Scan using tenk1_unique2 on tenk1 b
-(7 rows)
+ QUERY PLAN
+-------------------------------------------------------------
+ Merge Append
+ Sort Key: a.thousand, a.tenthous
+ -> Index Only Scan using tenk1_thous_tenthous on tenk1 a
+ -> Sort
+ Sort Key: b.unique2, b.unique2
+ -> Index Only Scan using tenk1_unique2 on tenk1 b
+(6 rows)
reset enable_seqscan;
reset enable_indexscan;
UNION ALL
SELECT * FROM t2) t
WHERE ab = 'ab';
- QUERY PLAN
----------------------------------------------------
- Result
- -> Append
- -> Index Scan using t1_ab_idx on t1
- Index Cond: ((a || b) = 'ab'::text)
- -> Index Only Scan using t2_pkey on t2
- Index Cond: (ab = 'ab'::text)
-(6 rows)
+ QUERY PLAN
+---------------------------------------------
+ Append
+ -> Index Scan using t1_ab_idx on t1
+ Index Cond: ((a || b) = 'ab'::text)
+ -> Index Only Scan using t2_pkey on t2
+ Index Cond: (ab = 'ab'::text)
+(5 rows)
explain (costs off)
SELECT * FROM
UNION ALL
SELECT 2 AS t, * FROM tenk1 b) c
WHERE t = 2;
- QUERY PLAN
----------------------------------
- Result
- -> Append
- -> Seq Scan on tenk1 b
-(3 rows)
+ QUERY PLAN
+---------------------------
+ Append
+ -> Seq Scan on tenk1 b
+(2 rows)