HEAD-get_relation_constraints-fix_v2.patch
text/plain
Filename: HEAD-get_relation_constraints-fix_v2.patch
Type: text/plain
Part: 0
Patch
Format: unified
Series: patch v2
| File | + | − |
|---|---|---|
| src/backend/optimizer/plan/planner.c | 3 | 2 |
| src/backend/optimizer/util/plancat.c | 51 | 55 |
| src/test/regress/expected/partition_prune.out | 48 | 0 |
| src/test/regress/sql/partition_prune.sql | 19 | 0 |
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c
index 3a1b846217..de655892f6 100644
--- a/src/backend/optimizer/plan/planner.c
+++ b/src/backend/optimizer/plan/planner.c
@@ -1513,8 +1513,9 @@ inheritance_planner(PlannerInfo *root)
parent_rte->securityQuals = NIL;
/*
- * Mark whether we're planning a query to a partitioned table or an
- * inheritance parent.
+ * HACK: setting this to a value other than INHKIND_NONE signals to
+ * relation_excluded_by_constraints() to process the result relation as
+ * a partition; see that function for more details.
*/
subroot->inhTargetKind =
(rootRelation != 0) ? INHKIND_PARTITIONED : INHKIND_INHERITED;
diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c
index 31a3784536..0698bafd04 100644
--- a/src/backend/optimizer/util/plancat.c
+++ b/src/backend/optimizer/util/plancat.c
@@ -66,7 +66,7 @@ static void get_relation_foreign_keys(PlannerInfo *root, RelOptInfo *rel,
static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel,
List *idxExprs);
static List *get_relation_constraints(PlannerInfo *root,
- Oid relationObjectId, RelOptInfo *rel,
+ Relation relation, RelOptInfo *rel,
bool include_notnull);
static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index,
Relation heapRelation);
@@ -1155,19 +1155,13 @@ get_relation_data_width(Oid relid, int32 *attr_widths)
*/
static List *
get_relation_constraints(PlannerInfo *root,
- Oid relationObjectId, RelOptInfo *rel,
+ Relation relation, RelOptInfo *rel,
bool include_notnull)
{
List *result = NIL;
Index varno = rel->relid;
- Relation relation;
TupleConstr *constr;
- /*
- * We assume the relation has already been safely locked.
- */
- relation = table_open(relationObjectId, NoLock);
-
constr = relation->rd_att->constr;
if (constr != NULL)
{
@@ -1247,38 +1241,6 @@ get_relation_constraints(PlannerInfo *root,
}
}
- /*
- * Append partition predicates, if any.
- *
- * For selects, partition pruning uses the parent table's partition bound
- * descriptor, instead of constraint exclusion which is driven by the
- * individual partition's partition constraint.
- */
- if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
- {
- List *pcqual = RelationGetPartitionQual(relation);
-
- if (pcqual)
- {
- /*
- * Run the partition quals through const-simplification similar to
- * check constraints. We skip canonicalize_qual, though, because
- * partition quals should be in canonical form already; also,
- * since the qual is in implicit-AND format, we'd have to
- * explicitly convert it to explicit-AND format and back again.
- */
- pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);
-
- /* Fix Vars to have the desired varno */
- if (varno != 1)
- ChangeVarNodes((Node *) pcqual, 1, varno, 0);
-
- result = list_concat(result, pcqual);
- }
- }
-
- table_close(relation, NoLock);
-
return result;
}
@@ -1385,6 +1347,7 @@ relation_excluded_by_constraints(PlannerInfo *root,
List *constraint_pred;
List *safe_constraints;
ListCell *lc;
+ Relation relation;
/* As of now, constraint exclusion works only with simple relations. */
Assert(IS_SIMPLE_REL(rel));
@@ -1415,26 +1378,14 @@ relation_excluded_by_constraints(PlannerInfo *root,
switch (constraint_exclusion)
{
case CONSTRAINT_EXCLUSION_OFF:
-
- /*
- * Don't prune if feature turned off -- except if the relation is
- * a partition. While partprune.c-style partition pruning is not
- * yet in use for all cases (update/delete is not handled), it
- * would be a UI horror to use different user-visible controls
- * depending on such a volatile implementation detail. Therefore,
- * for partitioned tables we use enable_partition_pruning to
- * control this behavior.
- */
- if (root->inhTargetKind == INHKIND_PARTITIONED)
- break;
return false;
case CONSTRAINT_EXCLUSION_PARTITION:
/*
* When constraint_exclusion is set to 'partition' we only handle
- * OTHER_MEMBER_RELs, or BASERELs in cases where the result target
- * is an inheritance parent or a partitioned table.
+ * OTHER_MEMBER_RELs, or BASERELs in cases where the relation is
+ * is an inherited target relation.
*/
if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&
!(rel->reloptkind == RELOPT_BASEREL &&
@@ -1486,10 +1437,55 @@ relation_excluded_by_constraints(PlannerInfo *root,
return false;
/*
+ * We assume the relation has already been safely locked.
+ */
+ relation = table_open(rte->relid, NoLock);
+
+ /*
* OK to fetch the constraint expressions. Include "col IS NOT NULL"
* expressions for attnotnull columns, in case we can refute those.
*/
- constraint_pred = get_relation_constraints(root, rte->relid, rel, true);
+ constraint_pred = get_relation_constraints(root, relation, rel, true);
+
+ /*
+ * Append partition predicates, if any.
+ *
+ * If the partition is accessed indirectly via its parent table, partition
+ * pruning is performed with the parent table's partition bound descriptor,
+ * so there is no need to include the partition constraint in that case.
+ * We do need to include it if the partition is referenced directly in the
+ * query, because no partition pruning would have occurred in that case,
+ * except in the case where the partition is a target relation. (See
+ * inheritance_planner().)
+ */
+ if (rel->reloptkind == RELOPT_BASEREL &&
+ !(root->inhTargetKind == INHKIND_PARTITIONED &&
+ rel->relid == root->parse->resultRelation))
+ {
+ List *pcqual = RelationGetPartitionQual(relation);
+
+ if (pcqual)
+ {
+ Index varno = rel->relid;
+
+ /*
+ * Run the partition quals through const-simplification similar to
+ * check constraints. We skip canonicalize_qual, though, because
+ * partition quals should be in canonical form already; also,
+ * since the qual is in implicit-AND format, we'd have to
+ * explicitly convert it to explicit-AND format and back again.
+ */
+ pcqual = (List *) eval_const_expressions(root, (Node *) pcqual);
+
+ /* Fix Vars to have the desired varno */
+ if (varno != 1)
+ ChangeVarNodes((Node *) pcqual, 1, varno, 0);
+
+ constraint_pred = list_concat(constraint_pred, pcqual);
+ }
+ }
+
+ table_close(relation, NoLock);
/*
* We do not currently enforce that CHECK constraints contain only
diff --git a/src/test/regress/expected/partition_prune.out b/src/test/regress/expected/partition_prune.out
index 7806ba1d47..e957badf09 100644
--- a/src/test/regress/expected/partition_prune.out
+++ b/src/test/regress/expected/partition_prune.out
@@ -3643,4 +3643,52 @@ select * from listp where a = (select 2) and b <> 10;
-> Result (never executed)
(4 rows)
+--
+-- check that a partition directly accessed in a query is excluded with
+-- constraint_exclusion = on
+--
+-- turn off partition pruning, so that it doesn't interfere
+set enable_partition_pruning to off;
+-- constraint exclusion doesn't apply
+set constraint_exclusion to 'partition';
+explain (costs off) select * from listp1 where a = 2;
+ QUERY PLAN
+--------------------
+ Seq Scan on listp1
+ Filter: (a = 2)
+(2 rows)
+
+explain (costs off) select * from listp2 where a = 1;
+ QUERY PLAN
+-----------------------
+ Seq Scan on listp2_10
+ Filter: (a = 1)
+(2 rows)
+
+-- constraint exclusion applies
+set constraint_exclusion to 'on';
+explain (costs off) select * from listp1 where a = 2;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) select * from listp2 where a = 1;
+ QUERY PLAN
+--------------------------
+ Result
+ One-Time Filter: false
+(2 rows)
+
+explain (costs off) update listp set a = listp1.b from listp1 where listp.a = listp1.a and listp1.a = 2;
+ QUERY PLAN
+--------------------------------
+ Update on listp
+ -> Result
+ One-Time Filter: false
+(3 rows)
+
+reset constraint_exclusion;
+reset enable_partition_pruning;
drop table listp;
diff --git a/src/test/regress/sql/partition_prune.sql b/src/test/regress/sql/partition_prune.sql
index 2e4d2b483d..ff3312bb31 100644
--- a/src/test/regress/sql/partition_prune.sql
+++ b/src/test/regress/sql/partition_prune.sql
@@ -990,4 +990,23 @@ create table listp2_10 partition of listp2 for values in (10);
explain (analyze, costs off, summary off, timing off)
select * from listp where a = (select 2) and b <> 10;
+--
+-- check that a partition directly accessed in a query is excluded with
+-- constraint_exclusion = on
+--
+
+-- turn off partition pruning, so that it doesn't interfere
+set enable_partition_pruning to off;
+
+-- constraint exclusion doesn't apply
+set constraint_exclusion to 'partition';
+explain (costs off) select * from listp1 where a = 2;
+explain (costs off) select * from listp2 where a = 1;
+-- constraint exclusion applies
+set constraint_exclusion to 'on';
+explain (costs off) select * from listp1 where a = 2;
+explain (costs off) select * from listp2 where a = 1;
+explain (costs off) update listp set a = listp1.b from listp1 where listp.a = listp1.a and listp1.a = 2;
+reset constraint_exclusion;
+reset enable_partition_pruning;
drop table listp;