Commit 1848b73d authored by Robert Haas's avatar Robert Haas

Teach \d+ to show partitioning constraints.

The fact that we didn't have this in the first place is likely why
the problem fixed by f8bffe9e
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
parent f8bffe9e
...@@ -976,6 +976,35 @@ RelationGetPartitionQual(Relation rel) ...@@ -976,6 +976,35 @@ RelationGetPartitionQual(Relation rel)
return generate_partition_qual(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 OIDs of rel's partitions to the list 'partoids' and for each OID,
* append pointer rel to the list 'parents'. * append pointer rel to the list 'parents'.
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "access/sysattr.h" #include "access/sysattr.h"
#include "catalog/dependency.h" #include "catalog/dependency.h"
#include "catalog/indexing.h" #include "catalog/indexing.h"
#include "catalog/partition.h"
#include "catalog/pg_aggregate.h" #include "catalog/pg_aggregate.h"
#include "catalog/pg_am.h" #include "catalog/pg_am.h"
#include "catalog/pg_authid.h" #include "catalog/pg_authid.h"
...@@ -1728,6 +1729,37 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags, ...@@ -1728,6 +1729,37 @@ pg_get_partkeydef_worker(Oid relid, int prettyFlags,
return buf.data; 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 * pg_get_constraintdef
* *
......
...@@ -1858,9 +1858,22 @@ describeOneTableDetails(const char *schemaname, ...@@ -1858,9 +1858,22 @@ describeOneTableDetails(const char *schemaname,
PGresult *result; PGresult *result;
char *parent_name; char *parent_name;
char *partdef; char *partdef;
char *partconstraintdef = NULL;
/* 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, printfPQExpBuffer(&buf,
"SELECT inhparent::pg_catalog.regclass, pg_get_expr(c.relpartbound, inhrelid)" "SELECT inhparent::pg_catalog.regclass,"
" pg_get_expr(c.relpartbound, inhrelid)"
" FROM pg_catalog.pg_class c" " FROM pg_catalog.pg_class c"
" JOIN pg_catalog.pg_inherits" " JOIN pg_catalog.pg_inherits"
" ON c.oid = inhrelid" " ON c.oid = inhrelid"
...@@ -1873,9 +1886,21 @@ describeOneTableDetails(const char *schemaname, ...@@ -1873,9 +1886,21 @@ describeOneTableDetails(const char *schemaname,
{ {
parent_name = PQgetvalue(result, 0, 0); parent_name = PQgetvalue(result, 0, 0);
partdef = PQgetvalue(result, 0, 1); partdef = PQgetvalue(result, 0, 1);
if (PQnfields(result) == 3)
partconstraintdef = PQgetvalue(result, 0, 2);
printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name, printfPQExpBuffer(&tmpbuf, _("Partition of: %s %s"), parent_name,
partdef); partdef);
printTableAddFooter(&cont, tmpbuf.data); printTableAddFooter(&cont, tmpbuf.data);
if (partconstraintdef)
{
printfPQExpBuffer(&tmpbuf, _("Partition constraint: %s"),
partconstraintdef);
printTableAddFooter(&cont, tmpbuf.data);
}
PQclear(result); PQclear(result);
} }
} }
......
...@@ -53,6 +53,6 @@ ...@@ -53,6 +53,6 @@
*/ */
/* yyyymmddN */ /* yyyymmddN */
#define CATALOG_VERSION_NO 201705122 #define CATALOG_VERSION_NO 201705131
#endif #endif
...@@ -80,6 +80,7 @@ extern List *get_qual_from_partbound(Relation rel, Relation parent, Node *bound) ...@@ -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, extern List *map_partition_varattnos(List *expr, int target_varno,
Relation partrel, Relation parent); Relation partrel, Relation parent);
extern List *RelationGetPartitionQual(Relation rel); extern List *RelationGetPartitionQual(Relation rel);
extern Expr *get_partition_qual_relid(Oid relid);
/* For tuple routing */ /* For tuple routing */
extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel, extern PartitionDispatch *RelationGetPartitionDispatchInfo(Relation rel,
......
...@@ -1992,6 +1992,8 @@ DATA(insert OID = 3415 ( pg_get_statisticsextdef PGNSP PGUID 12 1 0 0 0 f f ...@@ -1992,6 +1992,8 @@ DATA(insert OID = 3415 ( pg_get_statisticsextdef PGNSP PGUID 12 1 0 0 0 f f
DESCR("index description"); 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_ )); 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"); 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_ )); 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"); 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_ )); 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_ ));
......
...@@ -546,6 +546,7 @@ CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (1 ...@@ -546,6 +546,7 @@ CREATE TABLE part_forced_oids PARTITION OF oids_parted FOR VALUES FROM (1) TO (1
--------+---------+-----------+----------+---------+---------+--------------+------------- --------+---------+-----------+----------+---------+---------+--------------+-------------
a | integer | | not null | | plain | | a | integer | | not null | | plain | |
Partition of: oids_parted FOR VALUES FROM (1) TO (10) Partition of: oids_parted FOR VALUES FROM (1) TO (10)
Partition constraint: ((a >= 1) AND (a < 10))
Has OIDs: yes Has OIDs: yes
DROP TABLE oids_parted, part_forced_oids; 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 ...@@ -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 a level-2 partition
CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10); CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output -- Partition bound in describe output
\d part_b \d+ part_b
Table "public.part_b" Table "public.part_b"
Column | Type | Collation | Nullable | Default Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+--------- --------+---------+-----------+----------+---------+----------+--------------+-------------
a | text | | | a | text | | | | extended | |
b | integer | | not null | 1 b | integer | | not null | 1 | plain | |
Partition of: parted FOR VALUES IN ('b') Partition of: parted FOR VALUES IN ('b')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['b'::text])))
Check constraints: Check constraints:
"check_a" CHECK (length(a) > 0) "check_a" CHECK (length(a) > 0)
"part_b_b_check" CHECK (b >= 0) "part_b_b_check" CHECK (b >= 0)
-- Both partition bound and partition key in describe output -- Both partition bound and partition key in describe output
\d part_c \d+ part_c
Table "public.part_c" Table "public.part_c"
Column | Type | Collation | Nullable | Default Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+---------+-----------+----------+--------- --------+---------+-----------+----------+---------+----------+--------------+-------------
a | text | | | a | text | | | | extended | |
b | integer | | not null | 0 b | integer | | not null | 0 | plain | |
Partition of: parted FOR VALUES IN ('c') Partition of: parted FOR VALUES IN ('c')
Partition constraint: ((a IS NOT NULL) AND (a = ANY (ARRAY['c'::text])))
Partition key: RANGE (b) Partition key: RANGE (b)
Check constraints: Check constraints:
"check_a" CHECK (length(a) > 0) "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 -- Show partition count in the parent's describe output
-- Tempted to include \d+ output listing partitions with bound info but -- Tempted to include \d+ output listing partitions with bound info but
...@@ -682,6 +697,54 @@ Check constraints: ...@@ -682,6 +697,54 @@ Check constraints:
"check_a" CHECK (length(a) > 0) "check_a" CHECK (length(a) > 0)
Number of partitions: 3 (Use \d+ to list them.) 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 -- cleanup
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
-- comments on partitioned tables columns -- comments on partitioned tables columns
......
...@@ -1844,6 +1844,7 @@ Partitions: pt2_1 FOR VALUES IN (1) ...@@ -1844,6 +1844,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
c2 | text | | | | | extended | | c2 | text | | | | | extended | |
c3 | date | | | | | plain | | c3 | date | | | | | plain | |
Partition of: pt2 FOR VALUES IN (1) Partition of: pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
Server: s0 Server: s0
FDW Options: (delimiter ',', quote '"', "be quoted" 'value') FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
...@@ -1914,6 +1915,7 @@ Partitions: pt2_1 FOR VALUES IN (1) ...@@ -1914,6 +1915,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
c2 | text | | | | | extended | | c2 | text | | | | | extended | |
c3 | date | | | | | plain | | c3 | date | | | | | plain | |
Partition of: pt2 FOR VALUES IN (1) Partition of: pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
Server: s0 Server: s0
FDW Options: (delimiter ',', quote '"', "be quoted" 'value') FDW Options: (delimiter ',', quote '"', "be quoted" 'value')
...@@ -1941,6 +1943,7 @@ Partitions: pt2_1 FOR VALUES IN (1) ...@@ -1941,6 +1943,7 @@ Partitions: pt2_1 FOR VALUES IN (1)
c2 | text | | | | | extended | | c2 | text | | | | | extended | |
c3 | date | | not null | | | plain | | c3 | date | | not null | | | plain | |
Partition of: pt2 FOR VALUES IN (1) Partition of: pt2 FOR VALUES IN (1)
Partition constraint: ((c1 IS NOT NULL) AND (c1 = ANY (ARRAY[1])))
Check constraints: Check constraints:
"p21chk" CHECK (c2 <> ''::text) "p21chk" CHECK (c2 <> ''::text)
Server: s0 Server: s0
......
...@@ -598,10 +598,13 @@ CREATE TABLE part_c PARTITION OF parted (b WITH OPTIONS NOT NULL DEFAULT 0) FOR ...@@ -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); CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- Partition bound in describe output -- Partition bound in describe output
\d part_b \d+ part_b
-- Both partition bound and partition key in describe output -- 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 -- Show partition count in the parent's describe output
-- Tempted to include \d+ output listing partitions with bound info but -- 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); ...@@ -609,6 +612,19 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
-- returned. -- returned.
\d parted \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 -- cleanup
DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3; DROP TABLE parted, list_parted, range_parted, list_parted2, range_parted2, range_parted3;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment