identity_projection_v3_20130212.patch
application/octet-stream
Filename: identity_projection_v3_20130212.patch
Type: application/octet-stream
Part: 0
Message:
Re: Identity projection
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: context
Series: patch v3
| File | + | − |
|---|---|---|
| src/backend/optimizer/plan/createplan.c | 53 | 0 |
| src/backend/optimizer/plan/planner.c | 24 | 0 |
| src/include/optimizer/planmain.h | 1 | 0 |
| src/test/regress/expected/alter_table.out | 42 | 0 |
| src/test/regress/expected/inherit.out | 36 | 0 |
| src/test/regress/expected/union.out | 13 | 0 |
*** a/src/backend/optimizer/plan/createplan.c
--- b/src/backend/optimizer/plan/createplan.c
***************
*** 936,945 **** create_unique_plan(PlannerInfo *root, UniquePath *best_path)
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 (!is_projection_capable_plan(subplan))
subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
else
subplan->targetlist = newtlist;
--- 936,947 ----
if (newitems || best_path->umethod == UNIQUE_PATH_SORT)
{
/*
! * If the top plan node can't do projections and target list of top
! * plan node is not equivalent to target list expected from plan, we
! * need to add a Result node to help it along.
*/
! if (!is_projection_capable_plan(subplan) &&
! !equivalent_tlists(newtlist, subplan->targetlist))
subplan = (Plan *) make_result(root, newtlist, NULL, subplan);
else
subplan->targetlist = newtlist;
***************
*** 3953,3959 **** prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys,
elog(ERROR, "could not find pathkey item to sort");
/*
! * Do we need to insert a Result node?
*/
if (!adjust_tlist_in_place &&
!is_projection_capable_plan(lefttree))
--- 3955,3964 ----
elog(ERROR, "could not find pathkey item to sort");
/*
! * Do we need to insert a Result node? Here we can use
! * equivalent_tlists to avoid result node for cases where the
! * projection is identity, but for that we need to make sure
! * sortexpr is a Var.
*/
if (!adjust_tlist_in_place &&
!is_projection_capable_plan(lefttree))
***************
*** 4776,4778 **** is_projection_capable_plan(Plan *plan)
--- 4781,4827 ----
}
return true;
}
+
+
+ /*
+ * equivalent_tlists
+ * returns whether two traget lists are equivalent
+ *
+ * We consider two target lists equivalent if both have
+ * only Var entries and resjunk of each target entry is same.
+ *
+ * This function is used to decide whether to create a result node.
+ * We need to ensure that each entry is Var as below node will not be
+ * projection capable, so it will not be able to compute expressions.
+ */
+ bool
+ equivalent_tlists(List *tlist1, List *tlist2)
+ {
+ ListCell *lp,
+ *lc;
+
+ if (list_length(tlist1) != list_length(tlist2))
+ return false; /* tlists not same length */
+
+ forboth(lp, tlist1, lc, tlist2)
+ {
+ TargetEntry *tle1 = (TargetEntry *) lfirst(lp);
+ TargetEntry *tle2 = (TargetEntry *) lfirst(lc);
+
+ if (tle1->resjunk != tle1->resjunk)
+ return false; /* tlist doesn't match junk status */
+
+ if (tle1->expr && IsA(tle1->expr, Var) &&
+ tle2->expr && IsA(tle2->expr, Var))
+ {
+ Var *var1 = (Var *) tle1->expr;
+ Var *var2 = (Var *) tle2->expr;
+
+ if (var1->varattno != var2->varattno)
+ return false; /* different order */
+ }
+ else
+ return false;
+ }
+ return true;
+ }
*** a/src/backend/optimizer/plan/planner.c
--- b/src/backend/optimizer/plan/planner.c
***************
*** 1377,1386 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
{
/*
* If the top-level plan node is one that cannot do expression
! * evaluation, we must insert a Result node to project the
! * desired tlist.
*/
! if (!is_projection_capable_plan(result_plan))
{
result_plan = (Plan *) make_result(root,
sub_tlist,
--- 1377,1388 ----
{
/*
* If the top-level plan node is one that cannot do expression
! * evaluation and target list of top-level plan node is not
! * equivalent to target list expected from this level, we must
! * insert a Result node to project the desired tlist.
*/
! if (!is_projection_capable_plan(result_plan) &&
! !equivalent_tlists(sub_tlist, result_plan->targetlist))
{
result_plan = (Plan *) make_result(root,
sub_tlist,
***************
*** 1537,1559 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
ListCell *l;
/*
- * 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.
- */
- if (!is_projection_capable_plan(result_plan))
- {
- result_plan = (Plan *) make_result(root,
- NIL,
- NULL,
- result_plan);
- }
-
- /*
* The "base" targetlist for all steps of the windowing process is
* a flat tlist of all Vars and Aggs needed in the result. (In
* some cases we wouldn't need to propagate all of these all the
--- 1539,1544 ----
***************
*** 1570,1575 **** grouping_planner(PlannerInfo *root, double tuple_fraction)
--- 1555,1579 ----
activeWindows);
/*
+ * If the top-level plan node is one that cannot do expression
+ * evaluation and target list of top-level plan node is not
+ * equivalent to generated windows_tlist, we must insert a Result
+ * node to project the desiredtlist. (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.
+ */
+ if (!is_projection_capable_plan(result_plan) &&
+ !equivalent_tlists(window_tlist, result_plan->targetlist))
+ {
+ result_plan = (Plan *) make_result(root,
+ NIL,
+ NULL,
+ result_plan);
+ }
+
+ /*
* The copyObject steps here are needed to ensure that each plan
* node has a separately modifiable tlist. (XXX wouldn't a
* shallow list copy do for that?)
*** a/src/include/optimizer/planmain.h
--- b/src/include/optimizer/planmain.h
***************
*** 83,88 **** extern ModifyTable *make_modifytable(CmdType operation, bool canSetTag,
--- 83,89 ----
List *resultRelations, List *subplans, List *returningLists,
List *rowMarks, int epqParam);
extern bool is_projection_capable_plan(Plan *plan);
+ extern bool equivalent_tlists(List *tlist1, List *tlist2);
/*
* prototypes for plan/initsplan.c
*** a/src/test/regress/expected/alter_table.out
--- b/src/test/regress/expected/alter_table.out
***************
*** 385,445 **** create table nv_child_2011 () inherits (nv_parent);
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)
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)
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)
-- 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)
-- Foreign key adding test with mixed types
-- Note: these tables are TEMP to avoid name conflicts when this test
--- 385,441 ----
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
! ---------------------------------------------------------------------------
! 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
! ---------------------------------------------------------------------------
! 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
! ---------------------------------------------------------------------------
! 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
! ---------------------------------------------------------------------------
! 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
*** a/src/test/regress/expected/inherit.out
--- b/src/test/regress/expected/inherit.out
***************
*** 1258,1305 **** SELECT thousand, tenthous FROM tenk1
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)
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)
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)
-- Check min/max aggregate optimization
explain (costs off)
--- 1258,1302 ----
UNION ALL
SELECT thousand, thousand FROM tenk1
ORDER BY thousand, tenthous;
! 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
! ------------------------------------------------------------------
! 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
! -------------------------------------------------------------------------
! 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)
***************
*** 1345,1360 **** SELECT x, y FROM
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)
reset enable_seqscan;
reset enable_indexscan;
--- 1342,1356 ----
UNION ALL
SELECT unique2 AS x, unique2 AS y FROM tenk1 b) s
ORDER BY x, y;
! 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;
*** a/src/test/regress/expected/union.out
--- b/src/test/regress/expected/union.out
***************
*** 474,488 **** explain (costs off)
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)
explain (costs off)
SELECT * FROM
--- 474,487 ----
UNION ALL
SELECT * FROM t2) t
WHERE ab = 'ab';
! 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
***************
*** 510,519 **** explain (costs off)
UNION ALL
SELECT 2 AS t, * FROM tenk1 b) c
WHERE t = 2;
! QUERY PLAN
! ---------------------------------
! Result
! -> Append
! -> Seq Scan on tenk1 b
! (3 rows)
--- 509,517 ----
UNION ALL
SELECT 2 AS t, * FROM tenk1 b) c
WHERE t = 2;
! QUERY PLAN
! ---------------------------
! Append
! -> Seq Scan on tenk1 b
! (2 rows)