]> granicus.if.org Git - postgresql/commitdiff
Teach \d+ to show partitioning constraints.
authorRobert Haas <rhaas@postgresql.org>
Sat, 13 May 2017 16:04:53 +0000 (12:04 -0400)
committerRobert Haas <rhaas@postgresql.org>
Sat, 13 May 2017 16:04:53 +0000 (12:04 -0400)
The fact that we didn't have this in the first place is likely why
the problem fixed by f8bffe9e6d700fd34759a92e47930ce9ba7dcbd5
escaped detection.

Patch by Amit Langote, reviewed and slightly adjusted by me.

Discussion: http://postgr.es/m/CA+TgmoYWnV2GMnYLG-Czsix-E1WGAbo4D+0tx7t9NdfYBDMFsA@mail.gmail.com

src/backend/catalog/partition.c
src/backend/utils/adt/ruleutils.c
src/bin/psql/describe.c
src/include/catalog/catversion.h
src/include/catalog/partition.h
src/include/catalog/pg_proc.h
src/test/regress/expected/create_table.out
src/test/regress/expected/foreign_data.out
src/test/regress/sql/create_table.sql

index 9ef966607f8de4f974379d94b12bb01c60904e13..885c533280fdda540b1eeba8d260318b4cefa00e 100644 (file)
@@ -976,6 +976,35 @@ RelationGetPartitionQual(Relation rel)
        return generate_partition_qual(rel);
 }
 
+/*
+ * get_partition_qual_relid
+ *
+ * Returns an expression tree describing the passed-in relation's partition
+ * constraint.
+ */
+Expr *
+get_partition_qual_relid(Oid relid)
+{
+       Relation        rel = heap_open(relid, AccessShareLock);
+       Expr       *result = NULL;
+       List       *and_args;
+
+       /* Do the work only if this relation is a partition. */
+       if (rel->rd_rel->relispartition)
+       {
+               and_args = generate_partition_qual(rel);
+               if (list_length(and_args) > 1)
+                       result = makeBoolExpr(AND_EXPR, and_args, -1);
+               else
+                       result = linitial(and_args);
+       }
+
+       /* Keep the lock. */
+       heap_close(rel, NoLock);
+
+       return result;
+}
+
 /*
  * Append OIDs of rel's partitions to the list 'partoids' and for each OID,
  * append pointer rel to the list 'parents'.
index 983b9800ccd4c90f422b51a1982fca22d40e8062..c9bded082ece6c0116cb3c64ea68f9c2327288d8 100644 (file)
@@ -24,6 +24,7 @@
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/partition.h"
 #include "catalog/pg_aggregate.h"
 #include "catalog/pg_am.h"
 #include "catalog/pg_authid.h"
@@ -1728,6 +1729,37 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
        return buf.data;
 }
 
+/*
+ * pg_get_partition_constraintdef
+ *
+ * Returns partition constraint expression as a string for the input relation
+ */
+Datum
+pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
+{
+       Oid             relationId = PG_GETARG_OID(0);
+       Expr   *constr_expr;
+       int             prettyFlags;
+       List   *context;
+       char   *consrc;
+
+       constr_expr = get_partition_qual_relid(relationId);
+
+       /* Quick exit if not a partition */
+       if (constr_expr == NULL)
+               PG_RETURN_NULL();
+
+       /*
+        * Deparse and return the constraint expression.
+        */
+       prettyFlags = PRETTYFLAG_INDENT;
+       context = deparse_context_for(get_relation_name(relationId), relationId);
+       consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
+                                                                          false, prettyFlags, 0);
+
+       PG_RETURN_TEXT_P(string_to_text(consrc));
+}
+
 /*
  * pg_get_constraintdef
  *
index 386af6168fec0c1618ebcdc568a63ba53bb48034..cb174070367e9adfe501ed63df79b3d5afe6a485 100644 (file)
@@ -1858,13 +1858,26 @@ describeOneTableDetails(const char *schemaname,
                PGresult   *result;
                char       *parent_name;
                char       *partdef;
+               char       *partconstraintdef = NULL;
 
-               printfPQExpBuffer(&buf,
-                        "SELECT inhparent::pg_catalog.regclass, pg_get_expr(c.relpartbound, inhrelid)"
-                        " FROM pg_catalog.pg_class c"
-                        " JOIN pg_catalog.pg_inherits"
-                        " ON c.oid = inhrelid"
-                        " WHERE c.oid = '%s' AND c.relispartition;", oid);
+               /* If verbose, also request the partition constraint definition */
+               if (verbose)
+                       printfPQExpBuffer(&buf,
+                               "SELECT inhparent::pg_catalog.regclass,"
+                               "               pg_get_expr(c.relpartbound, inhrelid),"
+                               "               pg_get_partition_constraintdef(inhrelid)"
+                               " FROM pg_catalog.pg_class c"
+                               " JOIN pg_catalog.pg_inherits"
+                               " ON c.oid = inhrelid"
+                               " WHERE c.oid = '%s' AND c.relispartition;", oid);
+               else
+                       printfPQExpBuffer(&buf,
+                               "SELECT inhparent::pg_catalog.regclass,"
+                               "               pg_get_expr(c.relpartbound, inhrelid)"
+                               " FROM pg_catalog.pg_class c"
+                               " JOIN pg_catalog.pg_inherits"
+                               " ON c.oid = inhrelid"
+                               " WHERE c.oid = '%s' AND c.relispartition;", oid);
                result = PSQLexec(buf.data);
                if (!result)
                        goto error_return;
@@ -1873,9 +1886,21 @@ describeOneTableDetails(const char *schemaname,
                {
                        parent_name = PQgetvalue(result, 0, 0);
                        partdef = PQgetvalue(result, 0, 1);
+
+                       if (PQnfields(result) == 3)
+                               partconstraintdef = PQgetvalue(result, 0, 2);
+
                        printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name,
                                                  partdef);
                        printTableAddFooter(&cont, tmpbuf.data);
+
+                       if (partconstraintdef)
+                       {
+                               printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
+                                                                 partconstraintdef);
+                               printTableAddFooter(&cont, tmpbuf.data);
+                       }
+
                        PQclear(result);
                }
        }
index 8e5b95a5eea19cbbc48b6f8e6d3efcc79495981e..7adfd328f67a1db56aaf16d0e81f6224ba8e8852 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     201705122
+#define CATALOG_VERSION_NO     201705131
 
 #endif
index 421644ca775dd4f3eb0f060f53bee241f0390a49..25fb0a044018756aa196d6b49138ab74d0f861f5 100644 (file)
@@ -80,6 +80,7 @@ extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound)
 extern List *map_partition_varattnos(List *expr, int target_varno,
                                                Relation partrel, Relation parent);
 extern List *RelationGetPartitionQual(Relation rel);
+extern Expr *get_partition_qual_relid(Oid relid);
 
 /* For tuple routing */
 extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
index 8d84d9a8908ce9a79ee6f3848ef8a2043a2f0b24..74346963d9018c5bef86857854847ab81e0162c2 100644 (file)
@@ -1992,6 +1992,8 @@ DATA(insert OID = 3415 (  pg_get_statisticsextdef    PGNSP PGUID 12 1 0 0 0 f f
 DESCR("index description");
 DATA(insert OID = 3352 (  pg_get_partkeydef    PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partkeydef _null_ _null_ _null_ ));
 DESCR("partition key description");
+DATA(insert OID = 3408 (  pg_get_partition_constraintdef    PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_partition_constraintdef _null_ _null_ _null_ ));
+DESCR("partition constraint description");
 DATA(insert OID = 1662 (  pg_get_triggerdef    PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_triggerdef _null_ _null_ _null_ ));
 DESCR("trigger description");
 DATA(insert OID = 1387 (  pg_get_constraintdef PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 25 "26" _null_ _null_ _null_ _null_ _null_ pg_get_constraintdef _null_ _null_ _null_ ));
index 15d4ce591c7f567214580cd1471ddb7ced27b4d5..bbf039ccadaf8450b7f913c8143b6e3e6f9b8cc9 100644 (file)
@@ -546,6 +546,7 @@ CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (1
 --------+---------+-----------+----------+---------+---------+--------------+-------------
  a      | integer |           | not null |         | plain   |              | 
 Partition of: oids_parted FOR VALUES FROM (1) TO (10)
+Partition constraint: ((a >= 1) AND (a < 10))
 Has OIDs: yes
 
 DROP TABLE oids_parted, part_forced_oids;
@@ -643,29 +644,43 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
 -- create a level-2 partition
 CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
 -- Partition bound in describe output
-\d part_b
-               Table "public.part_b"
- Column |  Type   | Collation | Nullable | Default 
---------+---------+-----------+----------+---------
- a      | text    |           |          | 
- b      | integer |           | not null | 1
+\d+ part_b
+                                   Table "public.part_b"
+ Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ a      | text    |           |          |         | extended |              | 
+ b      | integer |           | not null | 1       | plain    |              | 
 Partition of: parted FOR VALUES IN ('b')
+Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
 Check constraints:
     "check_a" CHECK (length(a) > 0)
     "part_b_b_check" CHECK (b >= 0)
 
 -- Both partition bound and partition key in describe output
-\d part_c
-               Table "public.part_c"
- Column |  Type   | Collation | Nullable | Default 
---------+---------+-----------+----------+---------
- a      | text    |           |          | 
- b      | integer |           | not null | 0
+\d+ part_c
+                                   Table "public.part_c"
+ Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ a      | text    |           |          |         | extended |              | 
+ b      | integer |           | not null | 0       | plain    |              | 
 Partition of: parted FOR VALUES IN ('c')
+Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])))
 Partition key: RANGE (b)
 Check constraints:
     "check_a" CHECK (length(a) > 0)
-Number of partitions: 1 (Use \d+ to list them.)
+Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
+
+-- a level-2 partition's constraint will include the parent's expressions
+\d+ part_c_1_10
+                                Table "public.part_c_1_10"
+ Column |  Type   | Collation | Nullable | Default | Storage  | Stats target | Description 
+--------+---------+-----------+----------+---------+----------+--------------+-------------
+ a      | text    |           |          |         | extended |              | 
+ b      | integer |           | not null | 0       | plain    |              | 
+Partition of: part_c FOR VALUES FROM (1) TO (10)
+Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])) AND (b >= 1) AND (b < 10))
+Check constraints:
+    "check_a" CHECK (length(a) > 0)
 
 -- Show partition count in the parent's describe output
 -- Tempted to include \d+ output listing partitions with bound info but
@@ -682,6 +697,54 @@ Check constraints:
     "check_a" CHECK (length(a) > 0)
 Number of partitions: 3 (Use \d+ to list them.)
 
+-- check that we get the expected partition constraints
+CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
+CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED);
+\d+ unbounded_range_part
+                           Table "public.unbounded_range_part"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+ c      | integer |           | not null |         | plain   |              | 
+Partition of: range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED)
+Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL))
+
+DROP TABLE unbounded_range_part;
+CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED);
+\d+ range_parted4_1
+                              Table "public.range_parted4_1"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+ c      | integer |           | not null |         | plain   |              | 
+Partition of: range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED)
+Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND (abs(a) <= 1))
+
+CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED);
+\d+ range_parted4_2
+                              Table "public.range_parted4_2"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+ c      | integer |           | not null |         | plain   |              | 
+Partition of: range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED)
+Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND ((abs(a) > 3) OR ((abs(a) = 3) AND (abs(b) > 4)) OR ((abs(a) = 3) AND (abs(b) = 4) AND (c >= 5))) AND ((abs(a) < 6) OR ((abs(a) = 6) AND (abs(b) <= 7))))
+
+CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED);
+\d+ range_parted4_3
+                              Table "public.range_parted4_3"
+ Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a      | integer |           |          |         | plain   |              | 
+ b      | integer |           |          |         | plain   |              | 
+ c      | integer |           | not null |         | plain   |              | 
+Partition of: range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED)
+Partition constraint: ((abs(a) IS NOT NULL) AND (abs(b) IS NOT NULL) AND ((abs(a) > 6) OR ((abs(a) = 6) AND (abs(b) >= 8))) AND (abs(a) <= 9))
+
+DROP TABLE range_parted4;
 -- cleanup
 DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
 -- comments on partitioned tables columns
index 6a1f22ebeba10f35c0cf679cbfdfe61a868aa3a4..699309b3b2db0dcf2e14ed6570a92e86ed681050 100644 (file)
@@ -1844,6 +1844,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           |          |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
 Server: s0
 FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
 
@@ -1914,6 +1915,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           |          |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
 Server: s0
 FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
 
@@ -1941,6 +1943,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
  c2     | text    |           |          |         |             | extended |              | 
  c3     | date    |           | not null |         |             | plain    |              | 
 Partition of: pt2 FOR VALUES IN (1)
+Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
 Check constraints:
     "p21chk" CHECK (c2 <> ''::text)
 Server: s0
index 95035c5947c801a857b8c1ca127721640341a2e7..766f35a3edb259b13f01e0dc32c38703b383ed0e 100644 (file)
@@ -598,10 +598,13 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR
 CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
 
 -- Partition bound in describe output
-\d part_b
+\d+ part_b
 
 -- Both partition bound and partition key in describe output
-\d part_c
+\d+ part_c
+
+-- a level-2 partition's constraint will include the parent's expressions
+\d+ part_c_1_10
 
 -- Show partition count in the parent's describe output
 -- Tempted to include \d+ output listing partitions with bound info but
@@ -609,6 +612,19 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
 -- returned.
 \d parted
 
+-- check that we get the expected partition constraints
+CREATE TABLE range_parted4 (a int, b int, c int) PARTITION BY RANGE (abs(a), abs(b), c);
+CREATE TABLE unbounded_range_part PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (UNBOUNDED, UNBOUNDED, UNBOUNDED);
+\d+ unbounded_range_part
+DROP TABLE unbounded_range_part;
+CREATE TABLE range_parted4_1 PARTITION OF range_parted4 FOR VALUES FROM (UNBOUNDED, UNBOUNDED, UNBOUNDED) TO (1, UNBOUNDED, UNBOUNDED);
+\d+ range_parted4_1
+CREATE TABLE range_parted4_2 PARTITION OF range_parted4 FOR VALUES FROM (3, 4, 5) TO (6, 7, UNBOUNDED);
+\d+ range_parted4_2
+CREATE TABLE range_parted4_3 PARTITION OF range_parted4 FOR VALUES FROM (6, 8, UNBOUNDED) TO (9, UNBOUNDED, UNBOUNDED);
+\d+ range_parted4_3
+DROP TABLE range_parted4;
+
 -- cleanup
 DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;