]> granicus.if.org Git - postgresql/commitdiff
Add GUC enable_partition_pruning
authorAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 23 Apr 2018 20:57:43 +0000 (17:57 -0300)
committerAlvaro Herrera <alvherre@alvh.no-ip.org>
Mon, 23 Apr 2018 20:57:43 +0000 (17:57 -0300)
This controls both plan-time and execution-time new-style partition
pruning.  While finer-grain control is possible (maybe using an enum GUC
instead of boolean), there doesn't seem to be much need for that.

This new parameter controls partition pruning for all queries:
trivially, SELECT queries that affect partitioned tables are naturally
under its control since they are using the new technology.  However,
while UPDATE/DELETE queries do not use the new code, we make the new GUC
control their behavior also (stealing control from
constraint_exclusion), because it is more natural, and it leads to a
more natural transition to the future in which those queries will also
use the new pruning code.

Constraint exclusion still controls pruning for regular inheritance
situations (those not involving partitioned tables).

Author: David Rowley
Review: Amit Langote, Ashutosh Bapat, Justin Pryzby, David G. Johnston
Discussion: https://postgr.es/m/CAKJS1f_0HwsxJG9m+nzU+CizxSdGtfe6iF_ykPYBiYft302DCw@mail.gmail.com

16 files changed:
doc/src/sgml/config.sgml
doc/src/sgml/ddl.sgml
src/backend/nodes/outfuncs.c
src/backend/optimizer/path/allpaths.c
src/backend/optimizer/path/costsize.c
src/backend/optimizer/plan/createplan.c
src/backend/optimizer/plan/planner.c
src/backend/optimizer/prep/prepjointree.c
src/backend/optimizer/util/plancat.c
src/backend/utils/misc/guc.c
src/backend/utils/misc/postgresql.conf.sample
src/include/nodes/relation.h
src/include/optimizer/cost.h
src/test/regress/expected/partition_prune.out
src/test/regress/expected/sysviews.out
src/test/regress/sql/partition_prune.sql

index 5d5f2d23c4f27de69b7159cd1f7f6690c6b6d7a8..eabe2a9235280dcc889fd00c19b9dade24c0715b 100644 (file)
@@ -3826,6 +3826,23 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
       </listitem>
      </varlistentry>
 
+     <varlistentry id="guc-enable-partition-pruning" xreflabel="enable_partition_pruning">
+      <term><varname>enable_partition_pruning</varname> (<type>boolean</type>)
+       <indexterm>
+        <primary><varname>enable_partition_pruning</varname> configuration parameter</primary>
+       </indexterm>
+      </term>
+      <listitem>
+       <para>
+        Enables or disables the query planner's ability to eliminate a
+        partitioned table's partitions from query plans.  This also controls
+        the planner's ability to generate query plans which allow the query
+        executor to remove (ignore) partitions during query execution.  The
+        default is <literal>on</literal>.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-enable-partitionwise-join" xreflabel="enable_partitionwise_join">
       <term><varname>enable_partitionwise_join</varname> (<type>boolean</type>)
       <indexterm>
@@ -4417,8 +4434,7 @@ ANY <replaceable class="parameter">num_sync</replaceable> ( <replaceable class="
         <literal>partition</literal> (examine constraints only for inheritance child
         tables and <literal>UNION ALL</literal> subqueries).
         <literal>partition</literal> is the default setting.
-        It is often used with inheritance and partitioned tables to
-        improve performance.
+        It is often used with inheritance tables to improve performance.
       </para>
 
        <para>
@@ -4441,11 +4457,11 @@ SELECT * FROM parent WHERE key = 2400;
 
        <para>
         Currently, constraint exclusion is enabled by default
-        only for cases that are often used to implement table partitioning.
-        Turning it on for all tables imposes extra planning overhead that is
-        quite noticeable on simple queries, and most often will yield no
-        benefit for simple queries.  If you have no partitioned tables
-        you might prefer to turn it off entirely.
+        only for cases that are often used to implement table partitioning via
+        inheritance tables.  Turning it on for all tables imposes extra
+        planning overhead that is quite noticeable on simple queries, and most
+        often will yield no benefit for simple queries.  If you have no
+        inheritance partitioned tables you might prefer to turn it off entirely.
        </para>
 
        <para>
index feb2ab77920b551f1b2289d9ea6e270a09141981..34da0d8d5737641a765a5aa43b2b3d055c37476f 100644 (file)
@@ -3196,7 +3196,7 @@ CREATE INDEX ON measurement (logdate);
 
       <listitem>
        <para>
-        Ensure that the <xref linkend="guc-constraint-exclusion"/>
+        Ensure that the <xref linkend="guc-enable-partition-pruning"/>
         configuration parameter is not disabled in <filename>postgresql.conf</filename>.
         If it is, queries will not be optimized as desired.
        </para>
index 3991a0ce83ac17e8d480a9fa2778a91c1a21e1e9..f2d00c5e3123cf4ea32d48353927984517b4ec13 100644 (file)
@@ -2291,7 +2291,7 @@ _outPlannerInfo(StringInfo str, const PlannerInfo *node)
        WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
        WRITE_FLOAT_FIELD(limit_tuples, "%.0f");
        WRITE_UINT_FIELD(qual_security_level);
-       WRITE_BOOL_FIELD(hasInheritedTarget);
+       WRITE_ENUM_FIELD(inhTargetKind, InheritanceKind);
        WRITE_BOOL_FIELD(hasJoinRTEs);
        WRITE_BOOL_FIELD(hasLateralRTEs);
        WRITE_BOOL_FIELD(hasDeletedRTEs);
index 3ba3f87eb7c3a520cdb154ce5506ec9807b499b1..9ed73da0f79c6d3885921502b3aed802e2beba35 100644 (file)
@@ -901,7 +901,8 @@ set_append_rel_size(PlannerInfo *root, RelOptInfo *rel,
         * store the relids of all partitions which could possibly contain a
         * matching tuple, and skip anything else in the loop below.
         */
-       if (rte->relkind == RELKIND_PARTITIONED_TABLE &&
+       if (enable_partition_pruning &&
+               rte->relkind == RELKIND_PARTITIONED_TABLE &&
                rel->baserestrictinfo != NIL)
        {
                live_children = prune_append_rel_partitions(rel);
index 957f751bd4889248d58012cc43c2990259026467..a2a7e0c520228b7840fb2d1507179b6aec84bbc5 100644 (file)
@@ -138,6 +138,7 @@ bool                enable_partitionwise_join = false;
 bool           enable_partitionwise_aggregate = false;
 bool           enable_parallel_append = true;
 bool           enable_parallel_hash = true;
+bool           enable_partition_pruning = true;
 
 typedef struct
 {
index 280f21cd45c37fe8d1a69f0c72a3e3b8fd9a057d..0317763f43c7a10313176c3d063ea620cbe855ae 100644 (file)
@@ -1077,7 +1077,8 @@ create_append_plan(PlannerInfo *root, AppendPath *best_path)
                subplans = lappend(subplans, subplan);
        }
 
-       if (rel->reloptkind == RELOPT_BASEREL &&
+       if (enable_partition_pruning &&
+               rel->reloptkind == RELOPT_BASEREL &&
                best_path->partitioned_rels != NIL)
        {
                List       *prunequal;
@@ -1979,7 +1980,7 @@ create_groupingsets_plan(PlannerInfo *root, GroupingSetsPath *best_path)
         * create_modifytable_plan).  Fortunately we can't be because there would
         * never be grouping in an UPDATE/DELETE; but let's Assert that.
         */
-       Assert(!root->hasInheritedTarget);
+       Assert(root->inhTargetKind == INHKIND_NONE);
        Assert(root->grouping_map == NULL);
        root->grouping_map = grouping_map;
 
@@ -2141,7 +2142,7 @@ create_minmaxagg_plan(PlannerInfo *root, MinMaxAggPath *best_path)
         * create_modifytable_plan).  Fortunately we can't be because there would
         * never be aggregates in an UPDATE/DELETE; but let's Assert that.
         */
-       Assert(!root->hasInheritedTarget);
+       Assert(root->inhTargetKind == INHKIND_NONE);
        Assert(root->minmax_aggs == NIL);
        root->minmax_aggs = best_path->mmaggregates;
 
index 2e298f8357377e2e66a630f297fe399d9b93e71a..24e6ee026e15fcaa1df0212132292214bfe8373a 100644 (file)
@@ -623,7 +623,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
        root->grouping_map = NULL;
        root->minmax_aggs = NIL;
        root->qual_security_level = 0;
-       root->hasInheritedTarget = false;
+       root->inhTargetKind = INHKIND_NONE;
        root->hasRecursion = hasRecursion;
        if (hasRecursion)
                root->wt_param_id = SS_assign_special_param(root);
@@ -1424,8 +1424,13 @@ inheritance_planner(PlannerInfo *root)
                Assert(subroot->join_info_list == NIL);
                /* and we haven't created PlaceHolderInfos, either */
                Assert(subroot->placeholder_list == NIL);
-               /* hack to mark target relation as an inheritance partition */
-               subroot->hasInheritedTarget = true;
+
+               /*
+                * Mark if we're planning a query to a partitioned table or an
+                * inheritance parent.
+                */
+               subroot->inhTargetKind =
+                       partitioned_relids ? INHKIND_PARTITIONED : INHKIND_INHERITED;
 
                /*
                 * If the child is further partitioned, remember it as a parent. Since
index 45d82da4591a2132f55dcb428613d9d43fa598c2..c3f46a26c3aa7e576a685dfd0d5abf2ad6c0590b 100644 (file)
@@ -914,7 +914,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte,
        subroot->grouping_map = NULL;
        subroot->minmax_aggs = NIL;
        subroot->qual_security_level = 0;
-       subroot->hasInheritedTarget = false;
+       subroot->inhTargetKind = INHKIND_NONE;
        subroot->hasRecursion = false;
        subroot->wt_param_id = -1;
        subroot->non_recursive_path = NULL;
index 1ff0ef4866b69e0a8fa2e31ee27931bd0374664b..6973fe34586cd5b598422d6c0619518ddf42bfe7 100644 (file)
@@ -1272,7 +1272,7 @@ get_relation_constraints(PlannerInfo *root,
         * descriptor, instead of constraint exclusion which is driven by the
         * individual partition's partition constraint.
         */
-       if (root->parse->commandType != CMD_SELECT)
+       if (enable_partition_pruning && root->parse->commandType != CMD_SELECT)
        {
                List       *pcqual = RelationGetPartitionQual(relation);
 
@@ -1415,14 +1415,41 @@ relation_excluded_by_constraints(PlannerInfo *root,
                        return true;
        }
 
-       /* Skip further tests if constraint exclusion is disabled for the rel */
-       if (constraint_exclusion == CONSTRAINT_EXCLUSION_OFF ||
-               (constraint_exclusion == CONSTRAINT_EXCLUSION_PARTITION &&
-                !(rel->reloptkind == RELOPT_OTHER_MEMBER_REL ||
-                  (root->hasInheritedTarget &&
-                       rel->reloptkind == RELOPT_BASEREL &&
-                       rel->relid == root->parse->resultRelation))))
-               return false;
+       /*
+        * Skip further tests, depending on constraint_exclusion.
+        */
+       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.
+                        */
+                       if ((rel->reloptkind != RELOPT_OTHER_MEMBER_REL) &&
+                               !(rel->reloptkind == RELOPT_BASEREL &&
+                                 root->inhTargetKind != INHKIND_NONE &&
+                                 rel->relid == root->parse->resultRelation))
+                                 return false;
+                       break;
+
+               case CONSTRAINT_EXCLUSION_ON:
+                       break;          /* always try to exclude */
+       }
 
        /*
         * Check for self-contradictory restriction clauses.  We dare not make
index fa92ce2e68315485eec51940608464d93e4861a4..44dfa92722ce62fcb9979fa2881b7424ac8f196d 100644 (file)
@@ -951,6 +951,17 @@ static struct config_bool ConfigureNamesBool[] =
                true,
                NULL, NULL, NULL
        },
+       {
+               {"enable_partition_pruning", PGC_USERSET, QUERY_TUNING_METHOD,
+                       gettext_noop("Enable plan-time and run-time partition pruning."),
+                       gettext_noop("Allows the query planner and executor to compare partition "
+                                                "bounds to conditions in the query to determine which "
+                                                "partitions must be scanned.")
+               },
+               &enable_partition_pruning,
+               true,
+               NULL, NULL, NULL
+       },
        {
                {"geqo", PGC_USERSET, QUERY_TUNING_GEQO,
                        gettext_noop("Enables genetic query optimization."),
index 66d09388278fd70044cf5ca0158131317754411f..3d88e80a2042cf7d8bf33cba8282004acc5f74ce 100644 (file)
 #enable_partitionwise_join = off
 #enable_partitionwise_aggregate = off
 #enable_parallel_hash = on
+#enable_partition_pruning = on
 
 # - Planner Cost Constants -
 
index 2108b6ab1dff2edab39e37c6e2c7c39736a2a406..8b153a9d4e840d4a9d52df20c6a4b3f4ee67680e 100644 (file)
@@ -82,6 +82,17 @@ typedef enum UpperRelationKind
        /* NB: UPPERREL_FINAL must be last enum entry; it's used to size arrays */
 } UpperRelationKind;
 
+/*
+ * This enum identifies which type of relation is being planned through the
+ * inheritance planner.  INHKIND_NONE indicates the inheritance planner
+ * was not used.
+ */
+typedef enum InheritanceKind
+{
+       INHKIND_NONE,
+       INHKIND_INHERITED,
+       INHKIND_PARTITIONED
+} InheritanceKind;
 
 /*----------
  * PlannerGlobal
@@ -298,8 +309,9 @@ typedef struct PlannerInfo
        Index           qual_security_level;    /* minimum security_level for quals */
        /* Note: qual_security_level is zero if there are no securityQuals */
 
-       bool            hasInheritedTarget; /* true if parse->resultRelation is an
-                                                                        * inheritance child rel */
+       InheritanceKind inhTargetKind; /* indicates if the target relation is an
+                                                                       * inheritance child or partition or a
+                                                                       * partitioned table */
        bool            hasJoinRTEs;    /* true if any RTEs are RTE_JOIN kind */
        bool            hasLateralRTEs; /* true if any RTEs are marked LATERAL */
        bool            hasDeletedRTEs; /* true if any RTE was deleted from jointree */
index 6e6d0d3c7960d847742267663cf9434e7dae33ba..55e6a8488fe7b4e536bc87b9069f22aab6beaabe 100644 (file)
@@ -71,6 +71,7 @@ extern PGDLLIMPORT bool enable_partitionwise_join;
 extern PGDLLIMPORT bool enable_partitionwise_aggregate;
 extern PGDLLIMPORT bool enable_parallel_append;
 extern PGDLLIMPORT bool enable_parallel_hash;
+extern PGDLLIMPORT bool enable_partition_pruning;
 extern PGDLLIMPORT int constraint_exclusion;
 
 extern double clamp_row_est(double nrows);
index 3e818edd33948014376cab499d3bd901cf8d776b..e0cc5f3393a8db5bc0d1e023117e9a9fed1cc50f 100644 (file)
@@ -2760,3 +2760,167 @@ explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range;
 (2 rows)
 
 drop table pp_intrangepart;
+--
+-- Ensure the enable_partition_prune GUC properly disables partition pruning.
+--
+create table pp_lp (a int, value int) partition by list (a);
+create table pp_lp1 partition of pp_lp for values in(1);
+create table pp_lp2 partition of pp_lp for values in(2);
+explain (costs off) select * from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Append
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+(3 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+        QUERY PLAN        
+--------------------------
+ Update on pp_lp
+   Update on pp_lp1
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+(4 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Delete on pp_lp
+   Delete on pp_lp1
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+(4 rows)
+
+set enable_partition_pruning = off;
+set constraint_exclusion = 'partition'; -- this should not affect the result.
+explain (costs off) select * from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Append
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+        QUERY PLAN        
+--------------------------
+ Update on pp_lp
+   Update on pp_lp1
+   Update on pp_lp2
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(7 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Delete on pp_lp
+   Delete on pp_lp1
+   Delete on pp_lp2
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(7 rows)
+
+set constraint_exclusion = 'off'; -- this should not affect the result.
+explain (costs off) select * from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Append
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update pp_lp set value = 10 where a = 1;
+        QUERY PLAN        
+--------------------------
+ Update on pp_lp
+   Update on pp_lp1
+   Update on pp_lp2
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(7 rows)
+
+explain (costs off) delete from pp_lp where a = 1;
+        QUERY PLAN        
+--------------------------
+ Delete on pp_lp
+   Delete on pp_lp1
+   Delete on pp_lp2
+   ->  Seq Scan on pp_lp1
+         Filter: (a = 1)
+   ->  Seq Scan on pp_lp2
+         Filter: (a = 1)
+(7 rows)
+
+drop table pp_lp;
+-- Ensure enable_partition_prune does not affect non-partitioned tables.
+create table inh_lp (a int, value int);
+create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp);
+NOTICE:  merging column "a" with inherited definition
+NOTICE:  merging column "value" with inherited definition
+create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp);
+NOTICE:  merging column "a" with inherited definition
+NOTICE:  merging column "value" with inherited definition
+set constraint_exclusion = 'partition';
+-- inh_lp2 should be removed in the following 3 cases.
+explain (costs off) select * from inh_lp where a = 1;
+        QUERY PLAN         
+---------------------------
+ Append
+   ->  Seq Scan on inh_lp
+         Filter: (a = 1)
+   ->  Seq Scan on inh_lp1
+         Filter: (a = 1)
+(5 rows)
+
+explain (costs off) update inh_lp set value = 10 where a = 1;
+        QUERY PLAN         
+---------------------------
+ Update on inh_lp
+   Update on inh_lp
+   Update on inh_lp1
+   ->  Seq Scan on inh_lp
+         Filter: (a = 1)
+   ->  Seq Scan on inh_lp1
+         Filter: (a = 1)
+(7 rows)
+
+explain (costs off) delete from inh_lp where a = 1;
+        QUERY PLAN         
+---------------------------
+ Delete on inh_lp
+   Delete on inh_lp
+   Delete on inh_lp1
+   ->  Seq Scan on inh_lp
+         Filter: (a = 1)
+   ->  Seq Scan on inh_lp1
+         Filter: (a = 1)
+(7 rows)
+
+-- Ensure we don't exclude normal relations when we only expect to exclude
+-- inheritance children
+explain (costs off) update inh_lp1 set value = 10 where a = 2;
+        QUERY PLAN         
+---------------------------
+ Update on inh_lp1
+   ->  Seq Scan on inh_lp1
+         Filter: (a = 2)
+(3 rows)
+
+\set VERBOSITY terse   \\ -- suppress cascade details
+drop table inh_lp cascade;
+NOTICE:  drop cascades to 2 other objects
+\set VERBOSITY default
+reset enable_partition_pruning;
+reset constraint_exclusion;
index a19ee08749b51916022cd6e86896b2248cff686a..a1c90eb9057550c6862bb76a195eb0aff068c69a 100644 (file)
@@ -83,12 +83,13 @@ select name, setting from pg_settings where name like 'enable%';
  enable_nestloop                | on
  enable_parallel_append         | on
  enable_parallel_hash           | on
+ enable_partition_pruning       | on
  enable_partitionwise_aggregate | off
  enable_partitionwise_join      | off
  enable_seqscan                 | on
  enable_sort                    | on
  enable_tidscan                 | on
-(16 rows)
+(17 rows)
 
 -- Test that the pg_timezone_names and pg_timezone_abbrevs views are
 -- more-or-less working.  We can't test their contents in any great detail
index d8d3e3c47db92c16366a2f2f05821a9a33b2c01a..6b7f57ab41380941e9c449b7e8361f3172de0d59 100644 (file)
@@ -723,3 +723,55 @@ create table pp_intrangepart2inf partition of pp_intrangepart for values in ('[2
 explain (costs off) select * from pp_intrangepart where a = '[1,2]'::int4range;
 explain (costs off) select * from pp_intrangepart where a = '(1,2)'::int4range;
 drop table pp_intrangepart;
+
+--
+-- Ensure the enable_partition_prune GUC properly disables partition pruning.
+--
+
+create table pp_lp (a int, value int) partition by list (a);
+create table pp_lp1 partition of pp_lp for values in(1);
+create table pp_lp2 partition of pp_lp for values in(2);
+
+explain (costs off) select * from pp_lp where a = 1;
+explain (costs off) update pp_lp set value = 10 where a = 1;
+explain (costs off) delete from pp_lp where a = 1;
+
+set enable_partition_pruning = off;
+
+set constraint_exclusion = 'partition'; -- this should not affect the result.
+
+explain (costs off) select * from pp_lp where a = 1;
+explain (costs off) update pp_lp set value = 10 where a = 1;
+explain (costs off) delete from pp_lp where a = 1;
+
+set constraint_exclusion = 'off'; -- this should not affect the result.
+
+explain (costs off) select * from pp_lp where a = 1;
+explain (costs off) update pp_lp set value = 10 where a = 1;
+explain (costs off) delete from pp_lp where a = 1;
+
+drop table pp_lp;
+
+-- Ensure enable_partition_prune does not affect non-partitioned tables.
+
+create table inh_lp (a int, value int);
+create table inh_lp1 (a int, value int, check(a = 1)) inherits (inh_lp);
+create table inh_lp2 (a int, value int, check(a = 2)) inherits (inh_lp);
+
+set constraint_exclusion = 'partition';
+
+-- inh_lp2 should be removed in the following 3 cases.
+explain (costs off) select * from inh_lp where a = 1;
+explain (costs off) update inh_lp set value = 10 where a = 1;
+explain (costs off) delete from inh_lp where a = 1;
+
+-- Ensure we don't exclude normal relations when we only expect to exclude
+-- inheritance children
+explain (costs off) update inh_lp1 set value = 10 where a = 2;
+
+\set VERBOSITY terse   \\ -- suppress cascade details
+drop table inh_lp cascade;
+\set VERBOSITY default
+
+reset enable_partition_pruning;
+reset constraint_exclusion;