agora inbox for pgsql-hackers@postgresql.org
help / color / mirror / Atom feedFrom: amit <amitlangote09@gmail.com>
Subject: [PATCH 4/7] Add syntax to create partitions.
Date: Thu, 3 Mar 2016 18:29:15 +0900
As follows:
CREATE TABLE name PARTITION OF parent
[ ( { column_name WITH OPTION column_constraint | table_constraint } [, ...] ) ]
partition_bound_spec [ PARTITION BY partition_key_spec ]
ALTER TABLE parent ATTACH PARTITION name
partition_bound_spec [ VALIDATE | NO VALIDATE ]
ALTER TABLE parent DETACH PARTITION name
partition_bound_spec refers to either list partition bound of form:
FOR VALUES IN (list-of-expressions)
or range partition bound of form:
FOR VALUES range_spec
where range_spec is:
START lower-bound [ INCLUSIVE | EXCLUSIVE ] END upper-bound [ INCLUSIVE | EXCLUSIVE ]
lower-bound and upper-bound is:
{ ( expression [,...] ) | UNBOUNDED }
In the above, each of lower-bound and upper-bound is either a single
expression for a single column key or list of expressions for a multi-
column key or empty. Note that in the absence of explicit specification,
lower-bound is inclusive and upper-bound is exclusive.
A partition is created as inheritance child of the parent with certain
differences from regular inheritance behavior. For example, when a new
partition is created, it cannot contain its own column definitions or
constraints (unlike CREATE TABLE child(...) INHERITS(parent)). When
"attaching" a table as partition of parent, it cannot contain columns
that are not contained in the parent (unlike ALTER TABLE child INHERIT
parent). No dependency is created between a partition and the parent.
One cannot create regular inheritance children of a partitioned table.
Whereas this commit enables inheritance, it does not yet store partition
bound info anywhere, IOW, the fact that a given inheritance child is a
"partition" is not persisted.
---
doc/src/sgml/ref/alter_table.sgml | 57 +++++
doc/src/sgml/ref/create_table.sgml | 77 ++++++-
src/backend/commands/createas.c | 1 +
src/backend/commands/sequence.c | 1 +
src/backend/commands/tablecmds.c | 490 +++++++++++++++++++++++++++---------
src/backend/commands/view.c | 1 +
src/backend/nodes/copyfuncs.c | 48 ++++
src/backend/nodes/equalfuncs.c | 42 +++
src/backend/nodes/outfuncs.c | 27 ++
src/backend/parser/gram.y | 167 ++++++++++++-
src/backend/parser/parse_agg.c | 7 +-
src/backend/parser/parse_expr.c | 23 ++
src/backend/parser/parse_utilcmd.c | 318 +++++++++++++++++++++++
src/include/catalog/partition.h | 1 +
src/include/nodes/nodes.h | 3 +
src/include/nodes/parsenodes.h | 43 +++-
src/include/parser/kwlist.h | 3 +
src/include/parser/parse_node.h | 3 +-
18 files changed, 1182 insertions(+), 130 deletions(-)
diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index 5ca211e..97744c9 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -77,12 +77,15 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
NOT OF
OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
REPLICA IDENTITY { DEFAULT | USING INDEX <replaceable class="PARAMETER">index_name</replaceable> | FULL | NOTHING }
+ ATTACH PARTITION <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">partition_bound_spec</replaceable> [ VALIDATE | NO VALIDATE ]
+ DETACH PARTITION <replaceable class="PARAMETER">name</replaceable>
<phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
{ UNIQUE | PRIMARY KEY } USING INDEX <replaceable class="PARAMETER">index_name</replaceable>
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+
</synopsis>
</refsynopsisdiv>
@@ -704,6 +707,51 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><literal>ATTACH PARTITION</literal></term>
+ <listitem>
+ <para>
+ This form attaches an existing table as partition of the target table,
+ provided the target table is partitioned. The partition bound
+ specification must correspond with partition method and key of the
+ partitioned table. The table being attached must have all the columns
+ as the target table and no more. Also, it must have all the matching
+ constraints as the target table. That includes <literal>NOT NULL</>
+ and <literal>CHECK</> constraints. <literal>UNIQUE</literal>,
+ <literal>PRIMARY KEY</literal>, and <literal>FOREIGN KEY</literal>
+ constraints are not considered, but this might change in the future.
+ </para>
+
+ <para>
+ If <literal>NO VALIDATE</literal> clause is specified, existing rows in
+ source table are are not checked to see if they violate the partition
+ bound specification. This allows to quickly load a large amount of data
+ into the partitioned table. The default behavior is to perform the
+ check, as if <literal>VALIDATE</literal> clause were specified.
+ </para>
+
+ <para>
+ Note that once the table becomes a partition, it will be dropped or
+ truncated when the parent table is dropped or truncated. Dropping
+ it directly using <literal>DROP TABLE</literal> will fail; it must
+ first be detached from the parent. However, truncating a partition
+ directly works.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term><literal>DETACH PARTITION</literal></term>
+ <listitem>
+ <para>
+ This form detaches specified partition of the target table, provided the
+ target table is partitioned and the specified partition exists. The
+ resulting table continues to exist as a standalone table with no ties
+ remaining with the parent partitioned table.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</para>
@@ -936,6 +984,15 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="PARAMETER">partition_bound_spec</replaceable></term>
+ <listitem>
+ <para>
+ The partition bound specification for a new partition.
+ </para>
+ </listitem>
+ </varlistentry>
+
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 70a7579..79fb521 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -44,6 +44,17 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable>
+ PARTITION OF <replaceable class="PARAMETER">parent_table</replaceable> [ (
+ { <replaceable class="PARAMETER">column_name</replaceable> WITH OPTIONS [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+ | <replaceable>table_constraint</replaceable> }
+ [, ... ]
+) ] <replaceable class="PARAMETER">partition_bound_spec</replaceable>
+[ PARTITION BY {RANGE | LIST} ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [, ...] )
+[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) | WITH OIDS | WITHOUT OIDS ]
+[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
+[ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+
<phrase>where <replaceable class="PARAMETER">column_constraint</replaceable> is:</phrase>
[ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
@@ -72,6 +83,10 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
{ INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL }
+<phrase>and <replaceable class="PARAMETER">partition_bound_spec</replaceable> is:</phrase>
+
+FOR VALUES { <replaceable class="PARAMETER">list_spec</replaceable> | <replaceable class="PARAMETER">range_spec</replaceable> }
+
<phrase><replaceable class="PARAMETER">index_parameters</replaceable> in <literal>UNIQUE</literal>, <literal>PRIMARY KEY</literal>, and <literal>EXCLUDE</literal> constraints are:</phrase>
[ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
@@ -80,8 +95,20 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
<phrase><replaceable class="PARAMETER">exclude_element</replaceable> in an <literal>EXCLUDE</literal> constraint is:</phrase>
{ <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]
-</synopsis>
+<phrase><replaceable class="PARAMETER">list_spec</replaceable> in <literal>FOR VALUES</literal> is:</phrase>
+
+IN ( <replaceable class="PARAMETER">expression</replaceable> [, ...] )
+
+<phrase><replaceable class="PARAMETER">range_spec</replaceable> in <literal>FOR VALUES</literal> is:</phrase>
+
+START <replaceable class="PARAMETER">lower-bound</replaceable> [ INCLUSIVE | EXCLUSIVE ] END <replaceable class="PARAMETER">upper-bound</replaceable> [ INCLUSIVE | EXCLUSIVE ]
+
+<phrase>where <replaceable class="PARAMETER">lower-bound</replaceable> and <replaceable class="PARAMETER">upper-bound</replaceable> are:</phrase>
+
+{ ( <replaceable class="PARAMETER">expression</replaceable> [, ...] ) | UNBOUNDED }
+
+</synopsis>
</refsynopsisdiv>
<refsect1 id="SQL-CREATETABLE-description">
@@ -232,6 +259,49 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</varlistentry>
<varlistentry>
+ <term><literal>PARTITION OF <replaceable class="PARAMETER">parent_table</replaceable></literal></term>
+ <listitem>
+ <para>
+ Creates the table as <firstterm>partition</firstterm> of the specified
+ parent table (name optionally schema-qualified).
+ </para>
+
+ <para>
+ A partition bound specification must be present and must correspond with
+ partition method and key of the parent table. It is checked using the
+ specification that the new partition does not overlap with any existing
+ partitions of the parent.
+ </para>
+
+ <para>
+ A partition cannot have columns other than those inherited from the
+ parent. That includes the <structfield>oid</> column, which can be
+ specified using the <literal>WITH (OIDS)</literal> clause. On the other
+ hand, if parent has the <structfield>oid</> column, the partition
+ inherits the same, overriding the <literal>WITH (OIDS=FALSE)</literal>
+ clause, if any. Defaults and constraints can optionally be specified
+ for each of the inherited columns, which override those in the parent.
+ One can also specify table constraints, in addition to those inherited
+ from the parent. Note that all subsequent schema modifications to the
+ parent propagate to partition.
+ </para>
+
+ <para>
+ Any data row subsequently inserted into the parent table is mapped to
+ and stored in the partition, provided partition key of the row falls
+ within the partition bounds.
+ </para>
+
+ <para>
+ A partition is dropped or truncated when the parent table is dropped or
+ truncated. Dropping it directly using <literal>DROP TABLE</literal>
+ will fail; it must first be <firstterm>detached</> from the parent.
+ However, truncating a partition directly works.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><replaceable class="PARAMETER">column_name</replaceable></term>
<listitem>
<para>
@@ -340,6 +410,11 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</para>
<para>
+ When specifying for a table being created as partition, one needs to
+ use column names from the parent table as part of the key.
+ </para>
+
+ <para>
Currently, there are following limitations on definition of partitioned
tables: one cannot specify any UNIQUE, PRIMARY KEY, EXCLUDE and/or
FOREIGN KEY constraints.
diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c
index cb7a145..427470c 100644
--- a/src/backend/commands/createas.c
+++ b/src/backend/commands/createas.c
@@ -342,6 +342,7 @@ intorel_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
col->is_local = true;
col->is_not_null = false;
col->is_from_type = false;
+ col->is_for_partition = false;
col->storage = 0;
col->raw_default = NULL;
col->cooked_default = NULL;
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 5dcd4cf..21d9f96 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -159,6 +159,7 @@ DefineSequence(CreateSeqStmt *seq)
coldef->is_local = true;
coldef->is_not_null = true;
coldef->is_from_type = false;
+ coldef->is_for_partition = false;
coldef->storage = 0;
coldef->raw_default = NULL;
coldef->cooked_default = NULL;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 87bf466..192cae9 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -279,7 +279,8 @@ typedef struct
static void truncate_check_rel(Relation rel);
static List *MergeAttributes(List *schema, List *supers, char relpersistence,
- List **supOids, List **supconstr, int *supOidCount);
+ List **supOids, List **supconstr, int *supOidCount,
+ bool is_partition);
static bool MergeCheckConstraint(List *constraints, char *name, Node *expr);
static void MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel);
static void MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel);
@@ -448,6 +449,11 @@ static void ComputePartitionAttrs(Oid relid, List *partParams,
static bool find_attr_reference_walker(Node *node,
find_attr_reference_context *context);
static bool is_partition_attr(Relation rel, AttrNumber attnum, bool *is_expr);
+static void CreateInheritance(Relation child_rel, Relation parent_rel);
+static void RemoveInheritance(Relation child_rel, Relation parent_rel);
+static ObjectAddress ATExecAttachPartition(List **wqueue, Relation rel,
+ PartitionCmd *cmd);
+static ObjectAddress ATExecDetachPartition(Relation rel, RangeVar *name);
/* ----------------------------------------------------------------
@@ -590,10 +596,16 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
/*
* Look up inheritance ancestors and generate relation schema, including
* inherited attributes.
+ *
+ * The last parameter implicitly specifies that the table is being created
+ * as partition and schema consists of columns definitions corresponding
+ * to non-dropped columns of the parent constructed such that each
+ * attribute of the table is created as inherited and non-local.
*/
schema = MergeAttributes(schema, stmt->inhRelations,
stmt->relation->relpersistence,
- &inheritOids, &old_constraints, &parentOidCount);
+ &inheritOids, &old_constraints, &parentOidCount,
+ stmt->partbound != NULL);
/*
* Create a tuple descriptor from the relation schema. Note that this
@@ -1452,7 +1464,8 @@ storage_name(char c)
*/
static List *
MergeAttributes(List *schema, List *supers, char relpersistence,
- List **supOids, List **supconstr, int *supOidCount)
+ List **supOids, List **supconstr, int *supOidCount,
+ bool is_partition)
{
ListCell *entry;
List *inhSchema = NIL;
@@ -1497,8 +1510,8 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
/*
* Typed table column option that does not belong to a column from
- * the type. This works because the columns from the type come
- * first in the list.
+ * the type or the partition parent. This works because the columns
+ * from the type or the partition parent come first in the list.
*/
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
@@ -1525,6 +1538,18 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
coldef->is_from_type = false;
list_delete_cell(schema, rest, prev);
}
+ else if (coldef->is_for_partition)
+ {
+ /*
+ * merge the column options into the column from the parent
+ */
+ coldef->is_not_null = restdef->is_not_null;
+ coldef->raw_default = restdef->raw_default;
+ coldef->cooked_default = restdef->cooked_default;
+ coldef->constraints = restdef->constraints;
+ coldef->is_for_partition = false; /* job is done */
+ list_delete_cell(schema, rest, prev);
+ }
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
@@ -1563,8 +1588,12 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
*/
relation = heap_openrv(parent, ShareUpdateExclusiveLock);
- /* Cannot inherit from partitioned tables */
- if (relation->rd_rel->relkind == RELKIND_PARTITIONED_REL)
+ /*
+ * Cannot inherit from a partitioned table unless being created as
+ * partition.
+ */
+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_REL &&
+ !is_partition)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot inherit from partitioned table \"%s\"",
@@ -1710,6 +1739,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
def->is_local = false;
def->is_not_null = attribute->attnotnull;
def->is_from_type = false;
+ def->is_for_partition = false;
def->storage = attribute->attstorage;
def->raw_default = NULL;
def->cooked_default = NULL;
@@ -1838,6 +1868,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
* If we had no inherited attributes, the result schema is just the
* explicitly declared columns. Otherwise, we need to merge the declared
* columns into the inherited schema list.
+ *
+ * Note: In case of a partition, there are only inherited attributes that
+ * we copied from the parent as so, in transformPartitionOf().
*/
if (inhSchema != NIL)
{
@@ -1867,16 +1900,20 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
/*
* Yes, try to merge the two column definitions. They must
- * have the same type, typmod, and collation.
+ * have the same type, typmod, and collation. Don't output
+ * the notices, if partition, per note above.
*/
- if (exist_attno == schema_attno)
- ereport(NOTICE,
- (errmsg("merging column \"%s\" with inherited definition",
- attributeName)));
- else
- ereport(NOTICE,
- (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
- errdetail("User-specified column moved to the position of the inherited column.")));
+ if (!is_partition)
+ {
+ if (exist_attno == schema_attno)
+ ereport(NOTICE,
+ (errmsg("merging column \"%s\" with inherited definition",
+ attributeName)));
+ else
+ ereport(NOTICE,
+ (errmsg("moving and merging column \"%s\" with inherited definition", attributeName),
+ errdetail("User-specified column moved to the position of the inherited column.")));
+ }
def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
typenameTypeIdAndMod(NULL, def->typeName, &defTypeId, &deftypmod);
typenameTypeIdAndMod(NULL, newdef->typeName, &newTypeId, &newtypmod);
@@ -1913,8 +1950,9 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
storage_name(def->storage),
storage_name(newdef->storage))));
- /* Mark the column as locally defined */
- def->is_local = true;
+ /* Mark the column as locally defined, unless partition */
+ if (!is_partition)
+ def->is_local = true;
/* Merge of NOT NULL constraints = OR 'em together */
def->is_not_null |= newdef->is_not_null;
/* If new def has a default, override previous default */
@@ -2093,16 +2131,20 @@ StoreCatalogInheritance1(Oid relationId, Oid parentOid,
heap_freetuple(tuple);
/*
- * Store a dependency too
+ * Store a dependency too, iff a regular inheritance relationship, which
+ * it's not, if parent is a partitioned table.
*/
- parentobject.classId = RelationRelationId;
- parentobject.objectId = parentOid;
- parentobject.objectSubId = 0;
- childobject.classId = RelationRelationId;
- childobject.objectId = relationId;
- childobject.objectSubId = 0;
+ if (!relid_is_partitioned(parentOid))
+ {
+ parentobject.classId = RelationRelationId;
+ parentobject.objectId = parentOid;
+ parentobject.objectSubId = 0;
+ childobject.classId = RelationRelationId;
+ childobject.objectId = relationId;
+ childobject.objectSubId = 0;
- recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
+ recordDependencyOn(&childobject, &parentobject, DEPENDENCY_NORMAL);
+ }
/*
* Post creation hook of this inheritance. Since object_access_hook
@@ -3108,6 +3150,11 @@ AlterTableGetLockLevel(List *cmds)
cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
break;
+ case AT_AttachPartition:
+ case AT_DetachPartition:
+ cmd_lockmode = AccessExclusiveLock;
+ break;
+
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -3425,6 +3472,12 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
/* No command-specific prep needed */
pass = AT_PASS_MISC;
break;
+ case AT_AttachPartition:
+ case AT_DetachPartition:
+ ATSimplePermissions(rel, ATT_TABLE);
+ /* No command-specific prep needed */
+ pass = AT_PASS_MISC;
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -3744,6 +3797,12 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
case AT_GenericOptions:
ATExecGenericOptions(rel, (List *) cmd->def);
break;
+ case AT_AttachPartition:
+ ATExecAttachPartition(wqueue, rel, (PartitionCmd *) cmd->def);
+ break;
+ case AT_DetachPartition:
+ ATExecDetachPartition(rel, ((PartitionCmd *) cmd->def)->name);
+ break;
default: /* oops */
elog(ERROR, "unrecognized alter table type: %d",
(int) cmd->subtype);
@@ -10188,12 +10247,7 @@ ATPrepAddInherit(Relation child_rel)
static ObjectAddress
ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
{
- Relation parent_rel,
- catalogRelation;
- SysScanDesc scan;
- ScanKeyData key;
- HeapTuple inheritsTuple;
- int32 inhseqno;
+ Relation parent_rel;
List *children;
ObjectAddress address;
@@ -10238,38 +10292,6 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
errmsg("cannot inherit from partitioned table \"%s\"", parent->relname)));
/*
- * Check for duplicates in the list of parents, and determine the highest
- * inhseqno already present; we'll use the next one for the new parent.
- * (Note: get RowExclusiveLock because we will write pg_inherits below.)
- *
- * Note: we do not reject the case where the child already inherits from
- * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
- */
- catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
- ScanKeyInit(&key,
- Anum_pg_inherits_inhrelid,
- BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(child_rel)));
- scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
- true, NULL, 1, &key);
-
- /* inhseqno sequences start at 1 */
- inhseqno = 0;
- while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
- {
- Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
-
- if (inh->inhparent == RelationGetRelid(parent_rel))
- ereport(ERROR,
- (errcode(ERRCODE_DUPLICATE_TABLE),
- errmsg("relation \"%s\" would be inherited from more than once",
- RelationGetRelationName(parent_rel))));
- if (inh->inhseqno > inhseqno)
- inhseqno = inh->inhseqno;
- }
- systable_endscan(scan);
-
- /*
* Prevent circularity by seeing if proposed parent inherits from child.
* (In particular, this disallows making a rel inherit from itself.)
*
@@ -10302,6 +10324,69 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
RelationGetRelationName(child_rel),
RelationGetRelationName(parent_rel))));
+ /* OK to create inheritance */
+ CreateInheritance(child_rel, parent_rel);
+
+ ObjectAddressSet(address, RelationRelationId,
+ RelationGetRelid(parent_rel));
+
+ /* keep our lock on the parent relation until commit */
+ heap_close(parent_rel, NoLock);
+
+ return address;
+}
+
+/*
+ * CreateInheritance
+ * Catalog manipulation portion of creating inheritance between a child
+ * table and a parent table.
+ *
+ * Common to ATExecAddInherit() and ATExecAttachPartition().
+ */
+static void
+CreateInheritance(Relation child_rel, Relation parent_rel)
+{
+ Relation catalogRelation;
+ SysScanDesc scan;
+ ScanKeyData key;
+ HeapTuple inheritsTuple;
+ int32 inhseqno;
+
+ /* Note: get RowExclusiveLock because we will write pg_inherits below. */
+ catalogRelation = heap_open(InheritsRelationId, RowExclusiveLock);
+
+ /*
+ * Check for duplicates in the list of parents, and determine the highest
+ * inhseqno already present; we'll use the next one for the new parent.
+ * Also, if proposed child is a partition, it cannot already be inheriting.
+ *
+ * Note: we do not reject the case where the child already inherits from
+ * the parent indirectly; CREATE TABLE doesn't reject comparable cases.
+ */
+ ScanKeyInit(&key,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(child_rel)));
+ scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
+ true, NULL, 1, &key);
+
+ /* inhseqno sequences start at 1 */
+ inhseqno = 0;
+ while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
+ {
+ Form_pg_inherits inh = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
+
+ if (inh->inhparent == RelationGetRelid(parent_rel))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_TABLE),
+ errmsg("relation \"%s\" would be inherited from more than once",
+ RelationGetRelationName(parent_rel))));
+
+ if (inh->inhseqno > inhseqno)
+ inhseqno = inh->inhseqno;
+ }
+ systable_endscan(scan);
+
/* Match up the columns and bump attinhcount as needed */
MergeAttributesIntoExisting(child_rel, parent_rel);
@@ -10316,16 +10401,8 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
inhseqno + 1,
catalogRelation);
- ObjectAddressSet(address, RelationRelationId,
- RelationGetRelid(parent_rel));
-
/* Now we're done with pg_inherits */
heap_close(catalogRelation, RowExclusiveLock);
-
- /* keep our lock on the parent relation until commit */
- heap_close(parent_rel, NoLock);
-
- return address;
}
/*
@@ -10376,7 +10453,7 @@ constraints_equivalent(HeapTuple a, HeapTuple b, TupleDesc tupleDesc)
* Check columns in child table match up with columns in parent, and increment
* their attinhcount.
*
- * Called by ATExecAddInherit
+ * Called by CreateInheritance
*
* Currently all parent columns must be found in child. Missing columns are an
* error. One day we might consider creating new columns like CREATE TABLE
@@ -10394,11 +10471,13 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
int parent_natts;
TupleDesc tupleDesc;
HeapTuple tuple;
+ bool is_attach_partition;
attrrel = heap_open(AttributeRelationId, RowExclusiveLock);
tupleDesc = RelationGetDescr(parent_rel);
parent_natts = tupleDesc->natts;
+ is_attach_partition = relid_is_partitioned(RelationGetRelid(parent_rel));
for (parent_attno = 1; parent_attno <= parent_natts; parent_attno++)
{
@@ -10421,14 +10500,16 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
attribute->atttypmod != childatt->atttypmod)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("child table \"%s\" has different type for column \"%s\"",
+ errmsg("%s table \"%s\" has different type for column \"%s\"",
+ is_attach_partition ? "source" : "child",
RelationGetRelationName(child_rel),
attributeName)));
if (attribute->attcollation != childatt->attcollation)
ereport(ERROR,
(errcode(ERRCODE_COLLATION_MISMATCH),
- errmsg("child table \"%s\" has different collation for column \"%s\"",
+ errmsg("%s table \"%s\" has different collation for column \"%s\"",
+ is_attach_partition ? "source" : "child",
RelationGetRelationName(child_rel),
attributeName)));
@@ -10439,8 +10520,9 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
if (attribute->attnotnull && !childatt->attnotnull)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("column \"%s\" in child table must be marked NOT NULL",
- attributeName)));
+ errmsg("column \"%s\" in %s table must be marked NOT NULL",
+ attributeName,
+ is_attach_partition ? "source" : "child")));
/*
* OK, bump the child column's inheritance count. (If we fail
@@ -10455,7 +10537,8 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
{
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("child table is missing column \"%s\"",
+ errmsg("%s table is missing column \"%s\"",
+ is_attach_partition ? "source" : "child",
attributeName)));
}
}
@@ -10469,7 +10552,7 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel)
*
* Constraints that are marked ONLY in the parent are ignored.
*
- * Called by ATExecAddInherit
+ * Called by CreateInheritance
*
* Currently all constraints in parent must be present in the child. One day we
* may consider adding new constraints like CREATE TABLE does.
@@ -10488,9 +10571,11 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
SysScanDesc parent_scan;
ScanKeyData parent_key;
HeapTuple parent_tuple;
+ bool is_attach_partition;
catalog_relation = heap_open(ConstraintRelationId, RowExclusiveLock);
tuple_desc = RelationGetDescr(catalog_relation);
+ is_attach_partition = relid_is_partitioned(RelationGetRelid(parent_rel));
/* Outer loop scans through the parent's constraint definitions */
ScanKeyInit(&parent_key,
@@ -10538,7 +10623,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
if (!constraints_equivalent(parent_tuple, child_tuple, tuple_desc))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("child table \"%s\" has different definition for check constraint \"%s\"",
+ errmsg("%s table \"%s\" has different definition for check constraint \"%s\"",
+ is_attach_partition ? "source" : "child",
RelationGetRelationName(child_rel),
NameStr(parent_con->conname))));
@@ -10546,8 +10632,9 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
if (child_con->connoinherit)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
- errmsg("constraint \"%s\" conflicts with non-inherited constraint on child table \"%s\"",
+ errmsg("constraint \"%s\" conflicts with non-inherited constraint on %s table \"%s\"",
NameStr(child_con->conname),
+ is_attach_partition ? "source" : "child",
RelationGetRelationName(child_rel))));
/*
@@ -10570,7 +10657,8 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
if (!found)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("child table is missing constraint \"%s\"",
+ errmsg("%s table is missing constraint \"%s\"",
+ is_attach_partition ? "source" : "child",
NameStr(parent_con->conname))));
}
@@ -10581,6 +10669,41 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
/*
* ALTER TABLE NO INHERIT
*
+ * Return value is the address of the relation that is no longer parent.
+ */
+static ObjectAddress
+ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
+{
+ ObjectAddress address;
+ Relation parent_rel;
+
+ /*
+ * AccessShareLock on the parent is probably enough, seeing that DROP
+ * TABLE doesn't lock parent tables at all. We need some lock since we'll
+ * be inspecting the parent's schema.
+ */
+ parent_rel = heap_openrv(parent, AccessShareLock);
+
+ /*
+ * We don't bother to check ownership of the parent table --- ownership of
+ * the child is presumed enough rights.
+ */
+
+ /* Off to RemoveInheritance() where most of the work happens */
+ RemoveInheritance(rel, parent_rel);
+
+ /* keep our lock on the parent relation until commit */
+ heap_close(parent_rel, NoLock);
+
+ ObjectAddressSet(address, RelationRelationId,
+ RelationGetRelid(parent_rel));
+
+ return address;
+}
+
+/*
+ * RemoveInheritance
+ *
* Drop a parent from the child's parents. This just adjusts the attinhcount
* and attislocal of the columns and removes the pg_inherit and pg_depend
* entries.
@@ -10594,13 +10717,11 @@ MergeConstraintsIntoExisting(Relation child_rel, Relation parent_rel)
* coninhcount and conislocal for inherited constraints are adjusted in
* exactly the same way.
*
- * Return value is the address of the relation that is no longer parent.
+ * Common to ATExecDropInherit() and ATExecDetachPartition().
*/
-static ObjectAddress
-ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
+static void
+RemoveInheritance(Relation child_rel, Relation parent_rel)
{
- Relation parent_rel;
- Oid parent_oid;
Relation catalogRelation;
SysScanDesc scan;
ScanKeyData key[3];
@@ -10609,19 +10730,9 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
constraintTuple;
List *connames;
bool found = false;
- ObjectAddress address;
+ bool is_detach_partition;
- /*
- * AccessShareLock on the parent is probably enough, seeing that DROP
- * TABLE doesn't lock parent tables at all. We need some lock since we'll
- * be inspecting the parent's schema.
- */
- parent_rel = heap_openrv(parent, AccessShareLock);
-
- /*
- * We don't bother to check ownership of the parent table --- ownership of
- * the child is presumed enough rights.
- */
+ is_detach_partition = relid_is_partitioned(RelationGetRelid(parent_rel));
/*
* Find and destroy the pg_inherits entry linking the two, or error out if
@@ -10631,7 +10742,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
ScanKeyInit(&key[0],
Anum_pg_inherits_inhrelid,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
+ ObjectIdGetDatum(RelationGetRelid(child_rel)));
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
true, NULL, 1, key);
@@ -10652,11 +10763,20 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
heap_close(catalogRelation, RowExclusiveLock);
if (!found)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_TABLE),
- errmsg("relation \"%s\" is not a parent of relation \"%s\"",
- RelationGetRelationName(parent_rel),
- RelationGetRelationName(rel))));
+ {
+ if (!is_detach_partition)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" is not a parent of relation \"%s\"",
+ RelationGetRelationName(parent_rel),
+ RelationGetRelationName(child_rel))));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_TABLE),
+ errmsg("relation \"%s\" is not a partition of relation \"%s\"",
+ RelationGetRelationName(child_rel),
+ RelationGetRelationName(parent_rel))));
+ }
/*
* Search through child columns looking for ones matching parent rel
@@ -10665,7 +10785,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
ScanKeyInit(&key[0],
Anum_pg_attribute_attrelid,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
+ ObjectIdGetDatum(RelationGetRelid(child_rel)));
scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
true, NULL, 1, key);
while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
@@ -10727,7 +10847,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
ScanKeyInit(&key[0],
Anum_pg_constraint_conrelid,
BTEqualStrategyNumber, F_OIDEQ,
- ObjectIdGetDatum(RelationGetRelid(rel)));
+ ObjectIdGetDatum(RelationGetRelid(child_rel)));
scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
true, NULL, 1, key);
@@ -10758,7 +10878,7 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
if (copy_con->coninhcount <= 0) /* shouldn't happen */
elog(ERROR, "relation %u has non-inherited constraint \"%s\"",
- RelationGetRelid(rel), NameStr(copy_con->conname));
+ RelationGetRelid(child_rel), NameStr(copy_con->conname));
copy_con->coninhcount--;
if (copy_con->coninhcount == 0)
@@ -10770,30 +10890,22 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
}
}
- parent_oid = RelationGetRelid(parent_rel);
-
systable_endscan(scan);
heap_close(catalogRelation, RowExclusiveLock);
- drop_parent_dependency(RelationGetRelid(rel),
- RelationRelationId,
- RelationGetRelid(parent_rel));
-
+ /* Remember that dependency exists iff regular inheritance. */
+ if (!is_detach_partition)
+ drop_parent_dependency(RelationGetRelid(child_rel),
+ RelationRelationId,
+ RelationGetRelid(parent_rel));
/*
* Post alter hook of this inherits. Since object_access_hook doesn't take
* multiple object identifiers, we relay oid of parent relation using
* auxiliary_id argument.
*/
InvokeObjectPostAlterHookArg(InheritsRelationId,
- RelationGetRelid(rel), 0,
+ RelationGetRelid(child_rel), 0,
RelationGetRelid(parent_rel), false);
-
- /* keep our lock on the parent relation until commit */
- heap_close(parent_rel, NoLock);
-
- ObjectAddressSet(address, RelationRelationId, parent_oid);
-
- return address;
}
/*
@@ -12426,3 +12538,137 @@ ComputePartitionAttrs(Oid relid, List *partParams, AttrNumber *partattrs,
partopclass[attn++] = opclassOid;
}
}
+
+
+/*
+ * ALTER TABLE <name> ATTACH PARTITION <partition-name> FOR VALUES
+ *
+ * Return the address of the newly attached partition.
+ */
+static ObjectAddress
+ATExecAttachPartition(List **wqueue, Relation rel, PartitionCmd *cmd)
+{
+ Relation attachRel,
+ inheritsRelation;
+ SysScanDesc scan;
+ ScanKeyData key;
+ HeapTuple tuple;
+ AttrNumber attno;
+ int natts;
+ TupleDesc tupleDesc;
+ ObjectAddress address;
+
+ attachRel = heap_openrv(cmd->name, AccessShareLock);
+
+ /*
+ * Must be owner of both parent and source table -- parent was checked by
+ * ATSimplePermissions call in ATPrepCmd
+ */
+ ATSimplePermissions(attachRel, ATT_TABLE);
+
+ if (attachRel->rd_rel->reloftype)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach a typed table as partition")));
+
+ if (attachRel->rd_rel->relkind == RELKIND_PARTITIONED_REL)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach a partitioned table as partition")));
+
+ /* attachRel should not already be a inheritance child of some relation */
+ inheritsRelation = heap_open(InheritsRelationId, AccessShareLock);
+ ScanKeyInit(&key,
+ Anum_pg_inherits_inhrelid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationGetRelid(attachRel)));
+ scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
+ true, NULL, 1, &key);
+ if (HeapTupleIsValid(systable_getnext(scan)))
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach table that inherits as partition")));
+ systable_endscan(scan);
+ heap_close(inheritsRelation, AccessShareLock);
+
+ /* If attachRel is temp, it must belong to this session */
+ if (attachRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
+ !attachRel->rd_islocaltemp)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach a temporary relation of another session as partition ")));
+
+ /* If parent has OIDs, attachRel must have OIDs */
+ if (rel->rd_rel->relhasoids && !attachRel->rd_rel->relhasoids)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach table \"%s\" without OIDs as partition of"
+ " table \"%s\" with OIDs", RelationGetRelationName(attachRel),
+ RelationGetRelationName(rel))));
+
+ /* OTOH, if parent doesn't have them, do not allow in attachRel either */
+ if (attachRel->rd_rel->relhasoids && !rel->rd_rel->relhasoids)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot attach table \"%s\" with OIDs as partition of table"
+ " \"%s\" without OIDs", RelationGetRelationName(attachRel),
+ RelationGetRelationName(rel))));
+
+ /* Check if there are any columns in attachRel that aren't in the parent */
+ tupleDesc = RelationGetDescr(attachRel);
+ natts = tupleDesc->natts;
+ for (attno = 1; attno <= natts; attno++)
+ {
+ Form_pg_attribute attribute = tupleDesc->attrs[attno - 1];
+ char *attributeName = NameStr(attribute->attname);
+
+ /* Ignore dropped */
+ if (attribute->attisdropped)
+ continue;
+
+ /* Find same column in parent (matching on column name). */
+ tuple = SearchSysCacheCopyAttName(RelationGetRelid(rel), attributeName);
+ if (!HeapTupleIsValid(tuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("table \"%s\" contains column \"%s\" not found in parent \"%s\"",
+ RelationGetRelationName(attachRel), attributeName,
+ RelationGetRelationName(rel)),
+ errdetail("Table being attached should contain only the columns"
+ " present in parent.")));
+ }
+
+ /* OK to create inheritance. Rest of the checks performed there */
+ CreateInheritance(attachRel, rel);
+
+ ObjectAddressSet(address, RelationRelationId, RelationGetRelid(attachRel));
+
+ /* keep our lock until commit */
+ heap_close(attachRel, NoLock);
+
+ return address;
+}
+
+/*
+ * ALTER TABLE DETACH PARTITION
+ *
+ * Return the address of the relation that is no longer a partition of rel.
+ */
+static ObjectAddress
+ATExecDetachPartition(Relation rel, RangeVar *name)
+{
+ Relation partRel;
+ ObjectAddress address;
+
+ partRel = heap_openrv(name, AccessShareLock);
+
+ /* All inheritance related checks are performed within the function */
+ RemoveInheritance(partRel, rel);
+
+ ObjectAddressSet(address, RelationRelationId, RelationGetRelid(partRel));
+
+ /* keep our lock until commit */
+ heap_close(partRel, NoLock);
+
+ return address;
+}
diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c
index e9d9ba2..c9c72d9 100644
--- a/src/backend/commands/view.c
+++ b/src/backend/commands/view.c
@@ -95,6 +95,7 @@ DefineVirtualRelation(RangeVar *relation, List *tlist, bool replace,
def->is_local = true;
def->is_not_null = false;
def->is_from_type = false;
+ def->is_for_partition = false;
def->storage = 0;
def->raw_default = NULL;
def->cooked_default = NULL;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 968a3f4..111fcf5 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2606,6 +2606,7 @@ _copyColumnDef(const ColumnDef *from)
COPY_SCALAR_FIELD(is_local);
COPY_SCALAR_FIELD(is_not_null);
COPY_SCALAR_FIELD(is_from_type);
+ COPY_SCALAR_FIELD(is_for_partition);
COPY_SCALAR_FIELD(storage);
COPY_NODE_FIELD(raw_default);
COPY_NODE_FIELD(cooked_default);
@@ -3001,6 +3002,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(tableElts);
COPY_NODE_FIELD(inhRelations);
+ COPY_NODE_FIELD(partbound);
COPY_NODE_FIELD(partby);
COPY_NODE_FIELD(ofTypename);
COPY_NODE_FIELD(constraints);
@@ -4183,6 +4185,43 @@ _copyPartitionElem(const PartitionElem *from)
return newnode;
}
+static PartitionListSpec *
+_copyPartitionListSpec(const PartitionListSpec *from)
+{
+ PartitionListSpec *newnode = makeNode(PartitionListSpec);
+
+ COPY_NODE_FIELD(values);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
+static PartitionRangeSpec *
+_copyPartitionRangeSpec(const PartitionRangeSpec *from)
+{
+ PartitionRangeSpec *newnode = makeNode(PartitionRangeSpec);
+
+ COPY_SCALAR_FIELD(lowerinc);
+ COPY_NODE_FIELD(lower);
+ COPY_SCALAR_FIELD(upperinc);
+ COPY_NODE_FIELD(upper);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
+static PartitionCmd *
+_copyPartitionCmd(const PartitionCmd *from)
+{
+ PartitionCmd *newnode = makeNode(PartitionCmd);
+
+ COPY_NODE_FIELD(name);
+ COPY_NODE_FIELD(bound);
+ COPY_SCALAR_FIELD(skip_validate);
+
+ return newnode;
+}
+
/* ****************************************************************
* pg_list.h copy functions
* ****************************************************************
@@ -5082,6 +5121,15 @@ copyObject(const void *from)
case T_PartitionElem:
retval = _copyPartitionElem(from);
break;
+ case T_PartitionListSpec:
+ retval = _copyPartitionListSpec(from);
+ break;
+ case T_PartitionRangeSpec:
+ retval = _copyPartitionRangeSpec(from);
+ break;
+ case T_PartitionCmd:
+ retval = _copyPartitionCmd(from);
+ break;
default:
elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 56325fe..1a25acf 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1156,6 +1156,7 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(tableElts);
COMPARE_NODE_FIELD(inhRelations);
+ COMPARE_NODE_FIELD(partbound);
COMPARE_NODE_FIELD(partby);
COMPARE_NODE_FIELD(ofTypename);
COMPARE_NODE_FIELD(constraints);
@@ -2363,6 +2364,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
COMPARE_SCALAR_FIELD(is_local);
COMPARE_SCALAR_FIELD(is_not_null);
COMPARE_SCALAR_FIELD(is_from_type);
+ COMPARE_SCALAR_FIELD(is_for_partition);
COMPARE_SCALAR_FIELD(storage);
COMPARE_NODE_FIELD(raw_default);
COMPARE_NODE_FIELD(cooked_default);
@@ -2641,6 +2643,37 @@ _equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
return true;
}
+static bool
+_equalPartitionListSpec(const PartitionListSpec *a, const PartitionListSpec *b)
+{
+ COMPARE_NODE_FIELD(values);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalPartitionRangeSpec(const PartitionRangeSpec *a, const PartitionRangeSpec *b)
+{
+ COMPARE_SCALAR_FIELD(lowerinc);
+ COMPARE_NODE_FIELD(lower);
+ COMPARE_SCALAR_FIELD(upperinc);
+ COMPARE_NODE_FIELD(upper);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalPartitionCmd(const PartitionCmd *a, const PartitionCmd *b)
+{
+ COMPARE_NODE_FIELD(name);
+ COMPARE_NODE_FIELD(bound);
+ COMPARE_SCALAR_FIELD(skip_validate);
+
+ return true;
+}
+
/*
* Stuff from pg_list.h
*/
@@ -3397,6 +3430,15 @@ equal(const void *a, const void *b)
case T_PartitionElem:
retval = _equalPartitionElem(a, b);
break;
+ case T_PartitionListSpec:
+ retval = _equalPartitionListSpec(a, b);
+ break;
+ case T_PartitionRangeSpec:
+ retval = _equalPartitionRangeSpec(a, b);
+ break;
+ case T_PartitionCmd:
+ retval = _equalPartitionCmd(a, b);
+ break;
default:
elog(ERROR, "unrecognized node type: %d",
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 5623471..7537473 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2369,6 +2369,7 @@ _outCreateStmtInfo(StringInfo str, const CreateStmt *node)
WRITE_NODE_FIELD(relation);
WRITE_NODE_FIELD(tableElts);
WRITE_NODE_FIELD(inhRelations);
+ WRITE_NODE_FIELD(partbound);
WRITE_NODE_FIELD(partby);
WRITE_NODE_FIELD(ofTypename);
WRITE_NODE_FIELD(constraints);
@@ -2549,6 +2550,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
WRITE_BOOL_FIELD(is_local);
WRITE_BOOL_FIELD(is_not_null);
WRITE_BOOL_FIELD(is_from_type);
+ WRITE_BOOL_FIELD(is_for_partition);
WRITE_CHAR_FIELD(storage);
WRITE_NODE_FIELD(raw_default);
WRITE_NODE_FIELD(cooked_default);
@@ -3239,6 +3241,25 @@ _outPartitionElem(StringInfo str, const PartitionElem *node)
WRITE_LOCATION_FIELD(location);
}
+static void
+_outPartitionListSpec(StringInfo str, const PartitionListSpec *node)
+{
+ WRITE_NODE_TYPE("PARTITIONLISTVALUES");
+
+ WRITE_NODE_FIELD(values);
+}
+
+static void
+_outPartitionRangeSpec(StringInfo str, const PartitionRangeSpec *node)
+{
+ WRITE_NODE_TYPE("PARTITIONRANGE");
+
+ WRITE_BOOL_FIELD(lowerinc);
+ WRITE_NODE_FIELD(lower);
+ WRITE_BOOL_FIELD(upperinc);
+ WRITE_NODE_FIELD(upper);
+}
+
/*
* outNode -
* converts a Node into ascii string and append it to 'str'
@@ -3823,6 +3844,12 @@ outNode(StringInfo str, const void *obj)
case T_PartitionElem:
_outPartitionElem(str, obj);
break;
+ case T_PartitionListSpec:
+ _outPartitionListSpec(str, obj);
+ break;
+ case T_PartitionRangeSpec:
+ _outPartitionRangeSpec(str, obj);
+ break;
default:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 0607439..79fbb32 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -229,6 +229,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
VariableSetStmt *vsetstmt;
PartitionElem *partelem;
PartitionBy *partby;
+ PartitionRangeSpec *partrange;
}
%type <node> stmt schema_stmt
@@ -544,6 +545,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <partby> PartitionBy OptPartitionBy
%type <partelem> part_elem
%type <list> part_params
+%type <list> OptPartitionElementList PartitionElementList
+%type <node> PartitionElement
+%type <node> ForValues
+%type <node> partvalue
+%type <list> partvalue_list
+%type <boolean> lb_inc ub_inc
+%type <list> RangeBound
+%type <boolean> opt_validate_spec
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -569,7 +578,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
/* ordinary key words in alphabetical order */
%token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
- ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
+ ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
BOOLEAN_P BOTH BY
@@ -585,7 +594,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DEPENDS DESC
- DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
+ DETACH DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P
+ DOUBLE_P DROP
EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN
@@ -599,7 +609,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
HANDLER HAVING HEADER_P HOLD HOUR_P
IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IMPORT_P IN_P
- INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
+ INCLUDING INCLUSIVE INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY INLINE_P
INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
@@ -2373,6 +2383,38 @@ alter_table_cmd:
n->def = (Node *)$1;
$$ = (Node *) n;
}
+ /* ALTER TABLE <name> ATTACH PARTITION <table_name> FOR VALUES */
+ | ATTACH PARTITION qualified_name ForValues opt_validate_spec
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ PartitionCmd *cmd = makeNode(PartitionCmd);
+
+ n->subtype = AT_AttachPartition;
+ cmd->name = $3;
+ cmd->bound = (Node *) $4;
+ cmd->skip_validate = $5;
+ n->def = (Node *) cmd;
+
+ $$ = (Node *) n;
+ }
+ /* ALTER TABLE <name> DETACH PARTITION <partition_name> */
+ | DETACH PARTITION qualified_name
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ PartitionCmd *cmd = makeNode(PartitionCmd);
+
+ n->subtype = AT_DetachPartition;
+ cmd->name = $3;
+ n->def = (Node *) cmd;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+opt_validate_spec:
+ NO VALIDATE { $$ = true; }
+ | VALIDATE { $$ = false; }
+ | /* EMPTY */ { $$ = false; }
;
alter_column_default:
@@ -2468,6 +2510,60 @@ reloption_elem:
}
;
+ForValues:
+ /* a LIST partition */
+ FOR VALUES IN_P '(' partvalue_list ')'
+ {
+ PartitionListSpec *n = makeNode(PartitionListSpec);
+
+ n->values = $5;
+ n->location = @1;
+
+ $$ = (Node *) n;
+ }
+
+ /* a RANGE partition */
+ | FOR VALUES START RangeBound lb_inc END_P RangeBound ub_inc
+ {
+ PartitionRangeSpec *n = makeNode(PartitionRangeSpec);
+
+ n->lowerinc = $5;
+ n->lower = $4;
+ n->upperinc = $8;
+ n->upper = $7;
+ n->location = @1;
+
+ $$ = (Node *) n;
+ }
+ ;
+
+RangeBound:
+ UNBOUNDED { $$ = NIL; }
+ | '(' partvalue_list ')' { $$ = $2; }
+ ;
+
+lb_inc:
+ EXCLUSIVE { $$ = false;}
+ | INCLUSIVE { $$ = true; }
+ | /* EMPTY */ { $$ = true; }
+ ;
+
+ub_inc:
+ INCLUSIVE { $$ = true; }
+ | EXCLUSIVE { $$ = false; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
+partvalue:
+ Sconst { $$ = makeStringConst($1, @1); }
+ | NumericOnly { $$ = makeAConst($1, @1); }
+ | NULL_P { $$ = makeNullAConst(@1); }
+ ;
+
+partvalue_list:
+ partvalue { $$ = list_make1($1); }
+ | partvalue_list ',' partvalue { $$ = lappend($1, $3); }
+ ;
/*****************************************************************************
*
@@ -2885,6 +2981,44 @@ CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
n->if_not_exists = true;
$$ = (Node *)n;
}
+ | CREATE OptTemp TABLE qualified_name PARTITION OF qualified_name
+ OptPartitionElementList ForValues OptPartitionBy OptWith
+ OnCommitOption OptTableSpace
+ {
+ CreateStmt *n = makeNode(CreateStmt);
+ $4->relpersistence = $2;
+ n->relation = $4;
+ n->tableElts = $8;
+ n->inhRelations = list_make1($7);
+ n->partbound = (Node *) $9;
+ n->partby = $10;
+ n->ofTypename = NULL;
+ n->constraints = NIL;
+ n->options = $11;
+ n->oncommit = $12;
+ n->tablespacename = $13;
+ n->if_not_exists = false;
+ $$ = (Node *)n;
+ }
+ | CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name PARTITION OF
+ qualified_name OptPartitionElementList ForValues OptPartitionBy
+ OptWith OnCommitOption OptTableSpace
+ {
+ CreateStmt *n = makeNode(CreateStmt);
+ $7->relpersistence = $2;
+ n->relation = $7;
+ n->tableElts = $11;
+ n->inhRelations = list_make1($10);
+ n->partbound = (Node *) $12;
+ n->partby = $13;
+ n->ofTypename = NULL;
+ n->constraints = NIL;
+ n->options = $14;
+ n->oncommit = $15;
+ n->tablespacename = $16;
+ n->if_not_exists = true;
+ $$ = (Node *)n;
+ }
;
/*
@@ -2930,6 +3064,11 @@ OptTypedTableElementList:
| /*EMPTY*/ { $$ = NIL; }
;
+OptPartitionElementList:
+ '(' PartitionElementList ')' { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
TableElementList:
TableElement
{
@@ -2952,6 +3091,17 @@ TypedTableElementList:
}
;
+PartitionElementList:
+ PartitionElement
+ {
+ $$ = list_make1($1);
+ }
+ | PartitionElementList ',' PartitionElement
+ {
+ $$ = lappend($1, $3);
+ }
+ ;
+
TableElement:
columnDef { $$ = $1; }
| TableLikeClause { $$ = $1; }
@@ -2963,6 +3113,11 @@ TypedTableElement:
| TableConstraint { $$ = $1; }
;
+PartitionElement:
+ columnOptions { $$ = $1; }
+ | TableConstraint { $$ = $1; }
+ ;
+
columnDef: ColId Typename create_generic_options ColQualList
{
ColumnDef *n = makeNode(ColumnDef);
@@ -2972,6 +3127,7 @@ columnDef: ColId Typename create_generic_options ColQualList
n->is_local = true;
n->is_not_null = false;
n->is_from_type = false;
+ n->is_for_partition = false;
n->storage = 0;
n->raw_default = NULL;
n->cooked_default = NULL;
@@ -2993,6 +3149,7 @@ columnOptions: ColId WITH OPTIONS ColQualList
n->is_local = true;
n->is_not_null = false;
n->is_from_type = false;
+ n->is_for_partition = false;
n->storage = 0;
n->raw_default = NULL;
n->cooked_default = NULL;
@@ -11122,6 +11279,7 @@ TableFuncElement: ColId Typename opt_collate_clause
n->is_local = true;
n->is_not_null = false;
n->is_from_type = false;
+ n->is_for_partition = false;
n->storage = 0;
n->raw_default = NULL;
n->cooked_default = NULL;
@@ -13805,6 +13963,7 @@ unreserved_keyword:
| ASSERTION
| ASSIGNMENT
| AT
+ | ATTACH
| ATTRIBUTE
| BACKWARD
| BEFORE
@@ -13851,6 +14010,7 @@ unreserved_keyword:
| DELIMITER
| DELIMITERS
| DEPENDS
+ | DETACH
| DICTIONARY
| DISABLE_P
| DISCARD
@@ -13893,6 +14053,7 @@ unreserved_keyword:
| IMPLICIT_P
| IMPORT_P
| INCLUDING
+ | INCLUSIVE
| INCREMENT
| INDEX
| INDEXES
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 77ffd84..d71fdfe 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -493,7 +493,9 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
err = _("grouping operations are not allowed in partition key expression");
break;
-
+ case EXPR_KIND_PARTITION_FOR_VALUES:
+ errkind = true;
+ break;
/*
* There is intentionally no default: case here, so that the
@@ -854,6 +856,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_PARTITION_KEY:
err = _("window functions are not allowed in partition key expression");
break;
+ case EXPR_KIND_PARTITION_FOR_VALUES:
+ errkind = true;
+ break;
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 147c6da..f14d776 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -352,6 +352,26 @@ transformExprRecurse(ParseState *pstate, Node *expr)
result = (Node *) expr;
break;
}
+ case T_PartitionListSpec:
+ {
+ PartitionListSpec *list = (PartitionListSpec *) expr;
+
+ list->values = transformExpressionList(pstate, list->values,
+ pstate->p_expr_kind);
+ result = expr;
+ break;
+ }
+ case T_PartitionRangeSpec:
+ {
+ PartitionRangeSpec *range = (PartitionRangeSpec *) expr;
+
+ range->lower = transformExpressionList(pstate, range->lower,
+ pstate->p_expr_kind);
+ range->upper = transformExpressionList(pstate, range->upper,
+ pstate->p_expr_kind);
+ result = expr;
+ break;
+ }
default:
/* should not reach here */
@@ -1698,6 +1718,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_OFFSET:
case EXPR_KIND_RETURNING:
case EXPR_KIND_VALUES:
+ case EXPR_KIND_PARTITION_FOR_VALUES:
/* okay */
break;
case EXPR_KIND_CHECK_CONSTRAINT:
@@ -3254,6 +3275,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "WHEN";
case EXPR_KIND_PARTITION_KEY:
return "partition key expression";
+ case EXPR_KIND_PARTITION_FOR_VALUES:
+ return "FOR VALUES";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 465597d..e69cbfc 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -48,8 +48,10 @@
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
+#include "optimizer/planner.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
+#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
#include "parser/parse_expr.h"
#include "parser/parse_relation.h"
@@ -89,6 +91,7 @@ typedef struct
* the table */
IndexStmt *pkey; /* PRIMARY KEY index, if any */
bool ispartitioned; /* true if table is partitioned */
+ Node *partbound; /* transformed FOR VALUES */
} CreateStmtContext;
/* State shared by transformCreateSchemaStmt and its subroutines */
@@ -131,6 +134,10 @@ static void transformConstraintAttrs(CreateStmtContext *cxt,
List *constraintList);
static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
static void setSchemaName(char *context_schema, char **stmt_schema_name);
+static void transformPartitionOf(CreateStmtContext *cxt, Node *bound);
+static void transformAttachPartition(CreateStmtContext *cxt, PartitionCmd *cmd);
+static Node *transformPartitionBound(CreateStmtContext *cxt, Relation parent,
+ Node *bound);
/*
@@ -232,6 +239,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.alist = NIL;
cxt.pkey = NULL;
cxt.ispartitioned = stmt->partby != NULL;
+ cxt.partbound = NULL;
/*
* Notice that we allow OIDs here only for plain tables, even though
@@ -250,6 +258,9 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
if (stmt->ofTypename)
transformOfType(&cxt, stmt->ofTypename);
+ if (stmt->partbound)
+ transformPartitionOf(&cxt, stmt->partbound);
+
if (stmt->partby)
{
int partnatts = list_length(stmt->partby->partParams);
@@ -355,6 +366,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
*/
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
+ stmt->partbound = cxt.partbound;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -894,6 +906,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->is_local = true;
def->is_not_null = attribute->attnotnull;
def->is_from_type = false;
+ def->is_for_partition = false;
def->storage = 0;
def->raw_default = NULL;
def->cooked_default = NULL;
@@ -1113,6 +1126,7 @@ transformOfType(CreateStmtContext *cxt, TypeName *ofTypename)
n->is_local = true;
n->is_not_null = false;
n->is_from_type = true;
+ n->is_for_partition = false;
n->storage = 0;
n->raw_default = NULL;
n->cooked_default = NULL;
@@ -2580,6 +2594,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.alist = NIL;
cxt.pkey = NULL;
cxt.ispartitioned = relid_is_partitioned(relid);
+ cxt.partbound = NULL;
/*
* The only subtypes that currently require parse transformation handling
@@ -2668,6 +2683,22 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
break;
}
+ case AT_AttachPartition:
+ {
+ PartitionCmd *partcmd = (PartitionCmd *) cmd->def;
+
+ transformAttachPartition(&cxt, partcmd);
+
+ /* assign transformed values */
+ partcmd->bound = cxt.partbound;
+ }
+
+ newcmds = lappend(newcmds, cmd);
+ break;
+ case AT_DetachPartition:
+ newcmds = lappend(newcmds, cmd);
+ break;
+
default:
newcmds = lappend(newcmds, cmd);
break;
@@ -3032,3 +3063,290 @@ setSchemaName(char *context_schema, char **stmt_schema_name)
"different from the one being created (%s)",
*stmt_schema_name, context_schema)));
}
+
+/*
+ * transformPartitionOf
+ * Analyze PARTITION OF ... FOR VALUES ...
+ */
+static void
+transformPartitionOf(CreateStmtContext *cxt, Node *bound)
+{
+ TupleDesc tupdesc;
+ int i;
+ Relation parentRel;
+ RangeVar *partof = linitial(cxt->inhRelations);
+
+ parentRel = heap_openrv(partof, AccessShareLock);
+
+ /* Check if the target table is partitioned at all */
+ if (parentRel->rd_rel->relkind != RELKIND_PARTITIONED_REL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("\"%s\" is not partitioned",
+ RelationGetRelationName(parentRel))));
+ Assert(RelationGetPartitionKey(parentRel) != NULL);
+
+ /* If parent rel is temp, it must belong to this session */
+ if (parentRel->rd_rel->relpersistence == RELPERSISTENCE_TEMP &&
+ !parentRel->rd_islocaltemp)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create as partition of temporary relation of another session")));
+
+ /*
+ * Do not allow OIDs in a partition, if not present in the parent. But
+ * force them in partition, if they are present in the parent.
+ */
+ if (cxt->hasoids && !parentRel->rd_rel->relhasoids)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create table with OIDs as partition of table without OIDs")));
+
+ if (parentRel->rd_rel->relhasoids)
+ cxt->hasoids = true;
+
+ tupdesc = RelationGetDescr(parentRel);
+ for (i = 0; i < tupdesc->natts; i++)
+ {
+ Form_pg_attribute attribute = tupdesc->attrs[i];
+ ColumnDef *def;
+
+ if (attribute->attisdropped)
+ continue;
+
+ def = makeNode(ColumnDef);
+ def->colname = pstrdup(NameStr(attribute->attname));
+ def->typeName = makeTypeNameFromOid(attribute->atttypid,
+ attribute->atttypmod);
+ def->inhcount = 1;
+ def->is_local = false;
+ def->is_not_null = attribute->attnotnull;
+ def->is_from_type = false;
+ def->is_for_partition = true;
+ def->storage = attribute->attstorage;
+ def->raw_default = NULL;
+ def->cooked_default = NULL;
+ def->collClause = NULL;
+ def->collOid = attribute->attcollation;
+ def->constraints = NIL;
+ def->location = -1;
+
+ cxt->columns = lappend(cxt->columns, def);
+ }
+
+ /* tranform the values */
+ cxt->partbound = transformPartitionBound(cxt, parentRel, bound);
+
+ heap_close(parentRel, AccessShareLock);
+}
+
+/*
+ * transformAttachPartition
+ * Analyze ATTACH PARTITION ... FOR VALUES ...
+ */
+static void
+transformAttachPartition(CreateStmtContext *cxt, PartitionCmd *cmd)
+{
+ Relation parentRel = cxt->rel;
+
+ /* Check if the target table is partitioned at all */
+ if (parentRel->rd_rel->relkind != RELKIND_PARTITIONED_REL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("\"%s\" is not partitioned", cxt->relation->relname)));
+ Assert(RelationGetPartitionKey(parentRel) != NULL);
+
+ /* tranform the values */
+ cxt->partbound = transformPartitionBound(cxt, parentRel, cmd->bound);
+}
+
+/*
+ * transformPartitionBound
+ *
+ * Transform partition bound per the partition key
+ */
+static Node *
+transformPartitionBound(CreateStmtContext *cxt, Relation parent, Node *bound)
+{
+ int i;
+ ListCell *cell;
+ Oid parentId = RelationGetRelid(parent);
+ PartitionKey key = RelationGetPartitionKey(parent);
+ PartitionListSpec *list, *result_list;
+ PartitionRangeSpec *range, *result_range;
+
+ switch (key->strategy)
+ {
+ case PARTITION_STRAT_LIST:
+ if (!IsA(bound, PartitionListSpec))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a list partition"),
+ parser_errposition(cxt->pstate, exprLocation(bound))));
+
+ list = (PartitionListSpec *) transformExpr(cxt->pstate, bound,
+ EXPR_KIND_PARTITION_FOR_VALUES);
+
+ result_list = makeNode(PartitionListSpec);
+
+ foreach(cell, list->values)
+ {
+ Node *value = (Node *) lfirst(cell);
+ Node *orig_value = value;
+ Oid valuetype;
+
+ valuetype = exprType(value);
+ value = coerce_to_target_type(cxt->pstate,
+ value, valuetype,
+ key->tcinfo->typid[0],
+ key->tcinfo->typmod[0],
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (value == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("specified value cannot be cast to type \"%s\""
+ " of key column \"%s\"",
+ format_type_be(key->tcinfo->typid[0]),
+ get_relid_attribute_name(parentId, key->partattrs[0])),
+ parser_errposition(cxt->pstate, exprLocation(orig_value))));
+
+ /* Simplify the expression */
+ value = (Node *) expression_planner((Expr *) value);
+
+ result_list->values = lappend(result_list->values, value);
+ }
+ return (Node *) result_list;
+
+ case PARTITION_STRAT_RANGE:
+ if (!IsA(bound, PartitionRangeSpec))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("invalid bound specification for a range partition"),
+ parser_errposition(cxt->pstate, exprLocation(bound))));
+
+ range = (PartitionRangeSpec *) transformExpr(cxt->pstate, bound,
+ EXPR_KIND_PARTITION_FOR_VALUES);
+
+ if (!range->lower && !range->upper)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("both START and END cannot be UNBOUNDED"),
+ parser_errposition(cxt->pstate, range->location)));
+
+ result_range = makeNode(PartitionRangeSpec);
+ result_range->lowerinc = range->lowerinc;
+ result_range->upperinc = range->upperinc;
+
+ if (range->lower)
+ {
+ if (list_length(range->lower) > key->partnatts)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("START has more values specified than number"
+ " of columns in the partition key"),
+ parser_errposition(cxt->pstate,
+ exprLocation(list_nth(range->lower,
+ list_length(range->lower) - 1)))));
+ else if (list_length(range->lower) < key->partnatts)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("START has fewer values specified than number"
+ " of columns in the partition key"),
+ parser_errposition(cxt->pstate,
+ exprLocation(list_nth(range->lower,
+ list_length(range->lower) - 1)))));
+
+ i = 0;
+ foreach (cell, range->lower)
+ {
+ Node *value = (Node *) lfirst(cell);
+ Node *orig_value = value;
+ Oid valuetype;
+
+ valuetype = exprType(value);
+ value = coerce_to_target_type(cxt->pstate,
+ value, valuetype,
+ key->tcinfo->typid[i],
+ key->tcinfo->typmod[i],
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (value == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("specified value cannot be cast to type"
+ " \"%s\" of key column \"%s\"",
+ format_type_be(key->tcinfo->typid[i]),
+ get_relid_attribute_name(parentId, key->partattrs[i])),
+ parser_errposition(cxt->pstate, exprLocation(orig_value))));
+
+ /* Simplify the expression */
+ value = (Node *) expression_planner((Expr *) value);
+
+ result_range->lower = lappend(result_range->lower, value);
+ ++i;
+ }
+ }
+ else
+ result_range->lowerinc = false;
+
+ if (range->upper)
+ {
+ if (list_length(range->upper) > key->partnatts)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("END has more values specified than number"
+ " of columns in the partition key"),
+ parser_errposition(cxt->pstate,
+ exprLocation(list_nth(range->upper,
+ list_length(range->upper) - 1)))));
+ else if (list_length(range->upper) < key->partnatts)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("END has fewer values specified than number"
+ " of columns in the partition key"),
+ parser_errposition(cxt->pstate,
+ exprLocation(list_nth(range->upper,
+ list_length(range->upper) - 1)))));
+
+ i = 0;
+ foreach (cell, range->upper)
+ {
+ Node *value = (Node *) lfirst(cell);
+ Node *orig_value = value;
+ Oid valuetype;
+
+ valuetype = exprType(value);
+ value = coerce_to_target_type(cxt->pstate,
+ value, valuetype,
+ key->tcinfo->typid[i],
+ key->tcinfo->typmod[i],
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (value == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("specified value cannot be cast to type"
+ " \"%s\" of key column \"%s\"",
+ format_type_be(key->tcinfo->typid[i]),
+ get_relid_attribute_name(parentId, key->partattrs[i])),
+ parser_errposition(cxt->pstate, exprLocation(orig_value))));
+
+ /* Simplify the expression */
+ value = (Node *) expression_planner((Expr *) value);
+
+ result_range->upper = lappend(result_range->upper, value);
+ ++i;
+ }
+ }
+ else
+ result_range->upperinc = false;
+
+ return (Node *) result_range;
+ }
+
+ return NULL;
+}
diff --git a/src/include/catalog/partition.h b/src/include/catalog/partition.h
index 2c876c4..31dbe17 100644
--- a/src/include/catalog/partition.h
+++ b/src/include/catalog/partition.h
@@ -14,6 +14,7 @@
#define PARTITION_H
#include "fmgr.h"
+#include "parser/parse_node.h"
#include "utils/relcache.h"
/* Type and collation information for partition key columns */
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 9e582a5..56bbe43 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -405,6 +405,7 @@ typedef enum NodeTag
T_AlterPolicyStmt,
T_CreateTransformStmt,
T_CreateAmStmt,
+ T_PartitionCmd,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
@@ -454,6 +455,8 @@ typedef enum NodeTag
T_RoleSpec,
T_PartitionElem,
T_PartitionBy,
+ T_PartitionListSpec,
+ T_PartitionRangeSpec,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 4f697b8..d9dff96 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -591,6 +591,7 @@ typedef struct ColumnDef
bool is_local; /* column has local (non-inherited) def'n */
bool is_not_null; /* NOT NULL constraint specified? */
bool is_from_type; /* column definition came from table type */
+ bool is_for_partition; /* column definition is for a partition */
char storage; /* attstorage setting, or 0 for default */
Node *raw_default; /* default value (untransformed parse tree) */
Node *cooked_default; /* default value (transformed expr tree) */
@@ -731,6 +732,40 @@ typedef struct PartitionBy
int location; /* token location, or -1 if unknown */
} PartitionBy;
+/*
+ * PartitionRangeSpec - range type syntax for range partition bound
+ */
+typedef struct PartitionListSpec
+{
+ NodeTag type;
+ List *values;
+ int location;
+} PartitionListSpec;
+
+/*
+ * PartitionRangeSpec - range type syntax for range partition bound
+ */
+typedef struct PartitionRangeSpec
+{
+ NodeTag type;
+ bool lowerinc;
+ List *lower;
+ bool upperinc;
+ List *upper;
+ int location; /* token location, or -1 if unknown */
+} PartitionRangeSpec;
+
+/*
+ * PartitionCmd - ALTER TABLE partition commands
+ */
+typedef struct PartitionCmd
+{
+ NodeTag type;
+ RangeVar *name;
+ Node *bound;
+ bool skip_validate;
+} PartitionCmd;
+
/****************************************************************************
* Nodes for a Query tree
****************************************************************************/
@@ -1558,7 +1593,9 @@ typedef enum AlterTableType
AT_DisableRowSecurity, /* DISABLE ROW SECURITY */
AT_ForceRowSecurity, /* FORCE ROW SECURITY */
AT_NoForceRowSecurity, /* NO FORCE ROW SECURITY */
- AT_GenericOptions /* OPTIONS (...) */
+ AT_GenericOptions, /* OPTIONS (...) */
+ AT_AttachPartition, /* ATTACH PARTITION */
+ AT_DetachPartition /* DETACH PARTITION */
} AlterTableType;
typedef struct ReplicaIdentityStmt
@@ -1783,7 +1820,9 @@ typedef struct CreateStmt
RangeVar *relation; /* relation to create */
List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelations; /* relations to inherit from (list of
- * inhRelation) */
+ * inhRelation); (ab)used also as partition
+ * parent */
+ Node *partbound; /* FOR VALUES clause */
PartitionBy *partby; /* PARTITION BY clause */
TypeName *ofTypename; /* OF typename */
List *constraints; /* constraints (list of Constraint nodes) */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 40da67a..70c264c 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -49,6 +49,7 @@ PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD)
PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
+PG_KEYWORD("attach", ATTACH, UNRESERVED_KEYWORD)
PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
@@ -127,6 +128,7 @@ PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD)
PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD)
PG_KEYWORD("depends", DEPENDS, UNRESERVED_KEYWORD)
PG_KEYWORD("desc", DESC, RESERVED_KEYWORD)
+PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD)
PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD)
PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD)
@@ -191,6 +193,7 @@ PG_KEYWORD("implicit", IMPLICIT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("import", IMPORT_P, UNRESERVED_KEYWORD)
PG_KEYWORD("in", IN_P, RESERVED_KEYWORD)
PG_KEYWORD("including", INCLUDING, UNRESERVED_KEYWORD)
+PG_KEYWORD("inclusive", INCLUSIVE, UNRESERVED_KEYWORD)
PG_KEYWORD("increment", INCREMENT, UNRESERVED_KEYWORD)
PG_KEYWORD("index", INDEX, UNRESERVED_KEYWORD)
PG_KEYWORD("indexes", INDEXES, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index a13c6fb..3d45663 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -65,7 +65,8 @@ typedef enum ParseExprKind
EXPR_KIND_EXECUTE_PARAMETER, /* parameter value in EXECUTE */
EXPR_KIND_TRIGGER_WHEN, /* WHEN condition in CREATE TRIGGER */
EXPR_KIND_POLICY, /* USING or WITH CHECK expr in policy */
- EXPR_KIND_PARTITION_KEY /* partition key expression */
+ EXPR_KIND_PARTITION_KEY, /* partition key expression */
+ EXPR_KIND_PARTITION_FOR_VALUES /* partition bound */
} ParseExprKind;
--
1.7.1
--------------050808050504080802030900
Content-Type: text/x-diff;
name="0005-Infrastructure-for-partition-metadata-storage-and-ma-v5.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
filename*0="0005-Infrastructure-for-partition-metadata-storage-and-ma-v5";
filename*1=".patch"
view thread (7+ messages) latest in thread
Message-ID: <no-message-id-818904@localhost>
Permalink: ../../no-message-id-818904@localhost/
Also on: postgresql.org/message-id/no-message-id-818904@localhost
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: pgsql-hackers@postgresql.org
Cc: amitlangote09@gmail.com
Subject: Re: [PATCH 4/7] Add syntax to create partitions.
In-Reply-To: <no-message-id-818904@localhost>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox