0001-Catalog-and-DDL-for-partitioned-tables-10.patch
text/x-diff
Filename: 0001-Catalog-and-DDL-for-partitioned-tables-10.patch
Type: text/x-diff
Part: 0
Patch
Format: unified
Series: patch 0001
| File | + | − |
|---|---|---|
| doc/src/sgml/catalogs.sgml | 111 | 1 |
| doc/src/sgml/ref/create_table.sgml | 57 | 0 |
| src/backend/access/common/reloptions.c | 2 | 0 |
| src/backend/catalog/aclchk.c | 2 | 0 |
| src/backend/catalog/dependency.c | 6 | 4 |
| src/backend/catalog/heap.c | 156 | 4 |
| src/backend/catalog/index.c | 2 | 2 |
| src/backend/catalog/Makefile | 1 | 1 |
| src/backend/catalog/objectaddress.c | 4 | 1 |
| src/backend/catalog/pg_constraint.c | 1 | 1 |
| src/backend/commands/analyze.c | 2 | 0 |
| src/backend/commands/copy.c | 6 | 0 |
| src/backend/commands/indexcmds.c | 6 | 3 |
| src/backend/commands/lockcmds.c | 1 | 1 |
| src/backend/commands/policy.c | 1 | 1 |
| src/backend/commands/seclabel.c | 1 | 0 |
| src/backend/commands/sequence.c | 1 | 0 |
| src/backend/commands/tablecmds.c | 439 | 7 |
| src/backend/commands/trigger.c | 12 | 2 |
| src/backend/commands/vacuum.c | 1 | 0 |
| src/backend/executor/execMain.c | 2 | 0 |
| src/backend/executor/nodeModifyTable.c | 1 | 0 |
| src/backend/nodes/copyfuncs.c | 34 | 0 |
| src/backend/nodes/equalfuncs.c | 29 | 0 |
| src/backend/nodes/outfuncs.c | 28 | 0 |
| src/backend/parser/gram.y | 88 | 17 |
| src/backend/parser/parse_agg.c | 11 | 0 |
| src/backend/parser/parse_expr.c | 5 | 0 |
| src/backend/parser/parse_func.c | 3 | 0 |
| src/backend/parser/parse_utilcmd.c | 68 | 0 |
| src/backend/rewrite/rewriteDefine.c | 1 | 0 |
| src/backend/rewrite/rewriteHandler.c | 1 | 0 |
| src/backend/utils/cache/relcache.c | 256 | 1 |
| src/backend/utils/cache/syscache.c | 12 | 0 |
| src/include/catalog/dependency.h | 2 | 1 |
| src/include/catalog/heap.h | 10 | 0 |
| src/include/catalog/indexing.h | 3 | 0 |
| src/include/catalog/pg_class.h | 1 | 0 |
| src/include/catalog/pg_partitioned_table.h | 69 | 0 |
| src/include/commands/defrem.h | 2 | 0 |
| src/include/nodes/nodes.h | 2 | 0 |
| src/include/nodes/parsenodes.h | 36 | 0 |
| src/include/parser/parse_node.h | 2 | 1 |
| src/include/pg_config_manual.h | 5 | 0 |
| src/include/utils/rel.h | 66 | 0 |
| src/include/utils/syscache.h | 1 | 0 |
| src/test/regress/expected/alter_table.out | 46 | 0 |
| src/test/regress/expected/create_table.out | 163 | 0 |
| src/test/regress/expected/sanity_check.out | 1 | 0 |
| src/test/regress/sql/alter_table.sql | 34 | 0 |
| src/test/regress/sql/create_table.sql | 142 | 0 |
>From a9e30554e62a978db973364ef8e515bbc1578b30 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Thu, 14 Jul 2016 09:59:15 +0900
Subject: [PATCH 1/9] Catalog and DDL for partitioned tables.
In addition to a catalog for storing the partitioning information, this
commit also adds a new relkind to pg_class.h.
PARTITION BY clause is added to CREATE TABLE. The tables so created are
RELKIND_PARTITIONED_TABLE relations which are special in number of ways,
especially their interactions with table inheritance features.
---
doc/src/sgml/catalogs.sgml | 112 +++++++-
doc/src/sgml/ref/create_table.sgml | 57 ++++
src/backend/access/common/reloptions.c | 2 +
src/backend/catalog/Makefile | 2 +-
src/backend/catalog/aclchk.c | 2 +
src/backend/catalog/dependency.c | 10 +-
src/backend/catalog/heap.c | 160 ++++++++++-
src/backend/catalog/index.c | 4 +-
src/backend/catalog/objectaddress.c | 5 +-
src/backend/catalog/pg_constraint.c | 2 +-
src/backend/commands/analyze.c | 2 +
src/backend/commands/copy.c | 6 +
src/backend/commands/indexcmds.c | 9 +-
src/backend/commands/lockcmds.c | 2 +-
src/backend/commands/policy.c | 2 +-
src/backend/commands/seclabel.c | 1 +
src/backend/commands/sequence.c | 1 +
src/backend/commands/tablecmds.c | 446 +++++++++++++++++++++++++++-
src/backend/commands/trigger.c | 14 +-
src/backend/commands/vacuum.c | 1 +
src/backend/executor/execMain.c | 2 +
src/backend/executor/nodeModifyTable.c | 1 +
src/backend/nodes/copyfuncs.c | 34 +++
src/backend/nodes/equalfuncs.c | 29 ++
src/backend/nodes/outfuncs.c | 28 ++
src/backend/parser/gram.y | 105 ++++++-
src/backend/parser/parse_agg.c | 11 +
src/backend/parser/parse_expr.c | 5 +
src/backend/parser/parse_func.c | 3 +
src/backend/parser/parse_utilcmd.c | 68 +++++
src/backend/rewrite/rewriteDefine.c | 1 +
src/backend/rewrite/rewriteHandler.c | 1 +
src/backend/utils/cache/relcache.c | 257 ++++++++++++++++-
src/backend/utils/cache/syscache.c | 12 +
src/include/catalog/dependency.h | 3 +-
src/include/catalog/heap.h | 10 +
src/include/catalog/indexing.h | 3 +
src/include/catalog/pg_class.h | 1 +
src/include/catalog/pg_partitioned_table.h | 69 +++++
src/include/commands/defrem.h | 2 +
src/include/nodes/nodes.h | 2 +
src/include/nodes/parsenodes.h | 36 +++
src/include/parser/parse_node.h | 3 +-
src/include/pg_config_manual.h | 5 +
src/include/utils/rel.h | 66 ++++
src/include/utils/syscache.h | 1 +
src/test/regress/expected/alter_table.out | 46 +++
src/test/regress/expected/create_table.out | 163 ++++++++++
src/test/regress/expected/sanity_check.out | 1 +
src/test/regress/sql/alter_table.sql | 34 +++
src/test/regress/sql/create_table.sql | 142 +++++++++
51 files changed, 1936 insertions(+), 48 deletions(-)
create mode 100644 src/include/catalog/pg_partitioned_table.h
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 29738b0..ccc2b6b 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -226,6 +226,11 @@
</row>
<row>
+ <entry><link linkend="catalog-pg-partitioned-table"><structname>pg_partitioned_table</structname></link></entry>
+ <entry>information about partition key of tables</entry>
+ </row>
+
+ <row>
<entry><link linkend="catalog-pg-policy"><structname>pg_policy</structname></link></entry>
<entry>row-security policies</entry>
</row>
@@ -1723,7 +1728,8 @@
<entry><type>char</type></entry>
<entry></entry>
<entry>
- <literal>r</> = ordinary table, <literal>i</> = index,
+ <literal>r</> = ordinary table, <literal>P</> = partitioned table,
+ <literal>i</> = index
<literal>S</> = sequence, <literal>v</> = view,
<literal>m</> = materialized view,
<literal>c</> = composite type, <literal>t</> = TOAST table,
@@ -4689,6 +4695,110 @@
</sect1>
+ <sect1 id="catalog-pg-partitioned-table">
+ <title><structname>pg_partitioned_table</structname></title>
+
+ <indexterm zone="catalog-pg-partitioned-table">
+ <primary>pg_partitioned_table</primary>
+ </indexterm>
+
+ <para>
+ The catalog <structname>pg_partitioned_table</structname> stores
+ information about how tables are partitioned.
+ </para>
+
+ <table>
+ <title><structname>pg_partitioned_table</> Columns</title>
+
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+
+ <row>
+ <entry><structfield>partrelid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-class"><structname>pg_class</structname></link>.oid</literal></entry>
+ <entry>The OID of the <structname>pg_class</> entry for this partitioned table</entry>
+ </row>
+
+ <row>
+ <entry><structfield>partstrat</structfield></entry>
+ <entry><type>char</type></entry>
+ <entry></entry>
+ <entry>
+ Partitioning strategy; <literal>l</> = list partitioned table,
+ <literal>r</> = range partitioned table
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>partnatts</structfield></entry>
+ <entry><type>int2</type></entry>
+ <entry></entry>
+ <entry>The number of columns in partition key</entry>
+ </row>
+
+ <row>
+ <entry><structfield>partattrs</structfield></entry>
+ <entry><type>int2vector</type></entry>
+ <entry><literal><link linkend="catalog-pg-attribute"><structname>pg_attribute</structname></link>.attnum</literal></entry>
+ <entry>
+ This is an array of <structfield>partnatts</structfield> values that
+ indicate which table columns are used as partition key. For example,
+ a value of <literal>1 3</literal> would mean that the first and the
+ third table columns make up the partition key. A zero in this array
+ indicates that the corresponding partition key column is an expression
+ over the table columns, rather than a simple column reference.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>partclass</structfield></entry>
+ <entry><type>oidvector</type></entry>
+ <entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
+ <entry>
+ For each column in the partition key, this contains the OID of
+ the operator class to use. See
+ <link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link> for details.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>partcollation</structfield></entry>
+ <entry><type>oidvector</type></entry>
+ <entry><literal><link linkend="catalog-pg-opclass"><structname>pg_opclass</structname></link>.oid</literal></entry>
+ <entry>
+ For each column in the partition key, this contains the OID of
+ the collation to use for partitioning.
+ </entry>
+ </row>
+
+ <row>
+ <entry><structfield>partexprs</structfield></entry>
+ <entry><type>pg_node_tree</type></entry>
+ <entry></entry>
+ <entry>
+ Expression trees (in <function>nodeToString()</function>
+ representation) for partition key columns that are not simple column
+ references. This is a list with one element for each zero
+ entry in <structfield>partattrs</>. Null if all partition key columns
+ are simple references.
+ </entry>
+ </row>
+
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="catalog-pg-policy">
<title><structname>pg_policy</structname></title>
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index bf2ad64..893c899 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -28,6 +28,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
[, ... ]
] )
[ INHERITS ( <replaceable>parent_table</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> ]
@@ -38,6 +39,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
| <replaceable>table_constraint</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> ]
@@ -314,6 +316,41 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
</varlistentry>
<varlistentry>
+ <term><literal>PARTITION BY { RANGE | LIST } ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [, ...] ) </literal></term>
+ <listitem>
+ <para>
+ The optional <literal>PARTITION BY</literal> clause specifies a method
+ of partitioning the table. The table thus created is called a
+ <firstterm>partitioned</firstterm> table. The parenthesized list of
+ columns or expressions forms the <firstterm>partitioning key</firstterm>
+ for the table. When using range partitioning, the partitioning key can
+ include multiple columns or expressions, but for list partitioning, the
+ partitioning key must consist of a single column or expression. If no
+ btree operator class is specified when creating a partitioned table,
+ the default btree operator class for the datatype will be used. If
+ there is none, an error will be reported.
+ </para>
+
+ <para>
+ A partitioned table is divided into sub-tables (called partitions), which
+ are created using separate <literal>CREATE TABLE</> commands.
+ The table itself is empty. A data row inserted into the table is routed
+ to a partition based on the value of columns or expressions in the
+ partition key. If no existing partition matches the values in the new
+ row, an error will be reported.
+ </para>
+
+ <para>
+ Partitioned tables do not support <literal>UNIQUE</literal>,
+ <literal>PRIMARY KEY</literal>, <literal>EXCLUDE</literal>, or
+ <literal>FOREIGN KEY</literal> constraints; however, you can define
+ these constraints on individual partitions.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><literal>LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
<listitem>
<para>
@@ -1369,6 +1406,26 @@ CREATE TABLE employees OF employee_type (
salary WITH OPTIONS DEFAULT 1000
);
</programlisting></para>
+
+ <para>
+ Create a range partitioned table:
+<programlisting>
+CREATE TABLE measurement (
+ city_id int not null,
+ logdate date not null,
+ peaktemp int,
+ unitsales int
+) PARTITION BY RANGE (logdate);
+</programlisting></para>
+
+ <para>
+ Create a list partitioned table:
+<programlisting>
+CREATE TABLE cities (
+ name text not null,
+ population int,
+) PARTITION BY LIST (name);
+</programlisting></para>
</refsect1>
<refsect1 id="SQL-CREATETABLE-compatibility">
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 83a97b0..34018ca 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -930,6 +930,7 @@ extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
case RELKIND_RELATION:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
options = heap_reloptions(classForm->relkind, datum, false);
break;
case RELKIND_VIEW:
@@ -1381,6 +1382,7 @@ heap_reloptions(char relkind, Datum reloptions, bool validate)
return (bytea *) rdopts;
case RELKIND_RELATION:
case RELKIND_MATVIEW:
+ case RELKIND_PARTITIONED_TABLE:
return default_reloptions(reloptions, validate, RELOPT_KIND_HEAP);
default:
/* other relkinds are not supported */
diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile
index 1ce7610..362deca 100644
--- a/src/backend/catalog/Makefile
+++ b/src/backend/catalog/Makefile
@@ -41,7 +41,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
pg_foreign_data_wrapper.h pg_foreign_server.h pg_user_mapping.h \
pg_foreign_table.h pg_policy.h pg_replication_origin.h \
pg_default_acl.h pg_init_privs.h pg_seclabel.h pg_shseclabel.h \
- pg_collation.h pg_range.h pg_transform.h \
+ pg_collation.h pg_partitioned_table.h pg_range.h pg_transform.h \
toasting.h indexing.h \
)
diff --git a/src/backend/catalog/aclchk.c b/src/backend/catalog/aclchk.c
index c0df671..8a4ac7e 100644
--- a/src/backend/catalog/aclchk.c
+++ b/src/backend/catalog/aclchk.c
@@ -762,6 +762,8 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
case ACL_OBJECT_RELATION:
objs = getRelationsInNamespace(namespaceId, RELKIND_RELATION);
objects = list_concat(objects, objs);
+ objs = getRelationsInNamespace(namespaceId, RELKIND_PARTITIONED_TABLE);
+ objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_VIEW);
objects = list_concat(objects, objs);
objs = getRelationsInNamespace(namespaceId, RELKIND_MATVIEW);
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 04d7840..9746f24 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -1393,7 +1393,8 @@ void
recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
- DependencyType self_behavior)
+ DependencyType self_behavior,
+ bool ignore_self)
{
find_expr_references_context context;
RangeTblEntry rte;
@@ -1448,9 +1449,10 @@ recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
context.addrs->numrefs = outrefs;
/* Record the self-dependencies */
- recordMultipleDependencies(depender,
- self_addrs->refs, self_addrs->numrefs,
- self_behavior);
+ if (!ignore_self)
+ recordMultipleDependencies(depender,
+ self_addrs->refs, self_addrs->numrefs,
+ self_behavior);
free_object_addresses(self_addrs);
}
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0cf7b9e..31a7bb4 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -48,6 +48,8 @@
#include "catalog/pg_foreign_table.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_namespace.h"
+#include "catalog/pg_opclass.h"
+#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_type.h"
@@ -1102,9 +1104,10 @@ heap_create_with_catalog(const char *relname,
{
/* Use binary-upgrade override for pg_class.oid/relfilenode? */
if (IsBinaryUpgrade &&
- (relkind == RELKIND_RELATION || relkind == RELKIND_SEQUENCE ||
- relkind == RELKIND_VIEW || relkind == RELKIND_MATVIEW ||
- relkind == RELKIND_COMPOSITE_TYPE || relkind == RELKIND_FOREIGN_TABLE))
+ (relkind == RELKIND_RELATION || relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_SEQUENCE || relkind == RELKIND_VIEW ||
+ relkind == RELKIND_MATVIEW || relkind == RELKIND_COMPOSITE_TYPE ||
+ relkind == RELKIND_FOREIGN_TABLE))
{
if (!OidIsValid(binary_upgrade_next_heap_pg_class_oid))
ereport(ERROR,
@@ -1135,6 +1138,7 @@ heap_create_with_catalog(const char *relname,
switch (relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
case RELKIND_FOREIGN_TABLE:
@@ -1179,6 +1183,7 @@ heap_create_with_catalog(const char *relname,
* such is an implementation detail: toast tables, sequences and indexes.
*/
if (IsUnderPostmaster && (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_PARTITIONED_TABLE ||
relkind == RELKIND_VIEW ||
relkind == RELKIND_MATVIEW ||
relkind == RELKIND_FOREIGN_TABLE ||
@@ -1354,7 +1359,8 @@ heap_create_with_catalog(const char *relname,
if (relpersistence == RELPERSISTENCE_UNLOGGED)
{
Assert(relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW ||
- relkind == RELKIND_TOASTVALUE);
+ relkind == RELKIND_TOASTVALUE || relkind == RELKIND_PARTITIONED_TABLE);
+
heap_create_init_fork(new_rel_desc);
}
@@ -1801,6 +1807,12 @@ heap_drop_with_catalog(Oid relid)
}
/*
+ * If a partitioned table, delete the pg_partitioned_table tuple.
+ */
+ if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ RemovePartitionKeyByRelId(relid);
+
+ /*
* Schedule unlinking of the relation's physical files at commit.
*/
if (rel->rd_rel->relkind != RELKIND_VIEW &&
@@ -2033,6 +2045,17 @@ StoreRelCheck(Relation rel, char *ccname, Node *expr,
attNos = NULL;
/*
+ * Partitioned tables do not contain any rows themselves, so a NO INHERIT
+ * constraint makes no sense.
+ */
+ if (is_no_inherit &&
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot add NO INHERIT constraint to partitioned table \"%s\"",
+ RelationGetRelationName(rel))));
+
+ /*
* Create the Check Constraint
*/
constrOid =
@@ -3018,3 +3041,132 @@ insert_ordered_unique_oid(List *list, Oid datum)
lappend_cell_oid(list, prev, datum);
return list;
}
+
+/*
+ * StorePartitionKey
+ * Store the partition key information of rel into the catalog
+ */
+void
+StorePartitionKey(Relation rel,
+ char strategy,
+ int16 partnatts,
+ AttrNumber *partattrs,
+ List *partexprs,
+ Oid *partopclass,
+ Oid *partcollation)
+{
+ int i;
+ int2vector *partattrs_vec;
+ oidvector *partopclass_vec;
+ oidvector *partcollation_vec;
+ Datum partexprDatum;
+ Relation pg_partitioned_table;
+ HeapTuple tuple;
+ Datum values[Natts_pg_partitioned_table];
+ bool nulls[Natts_pg_partitioned_table];
+ ObjectAddress myself;
+ ObjectAddress referenced;
+
+ Assert(rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
+
+ tuple = SearchSysCache1(PARTRELID,
+ ObjectIdGetDatum(RelationGetRelid(rel)));
+
+ /* Copy the partition key, opclass info into arrays */
+ partattrs_vec = buildint2vector(partattrs, partnatts);
+ partopclass_vec = buildoidvector(partopclass, partnatts);
+ partcollation_vec = buildoidvector(partcollation, partnatts);
+
+ /* Convert the partition key expressions (if any) to a text datum */
+ if (partexprs)
+ {
+ char *exprString;
+
+ exprString = nodeToString(partexprs);
+ partexprDatum = CStringGetTextDatum(exprString);
+ pfree(exprString);
+ }
+ else
+ partexprDatum = (Datum) 0;
+
+ pg_partitioned_table = heap_open(PartitionedRelationId, RowExclusiveLock);
+
+ MemSet(nulls, false, sizeof(nulls));
+
+ /* Only this can ever be NULL */
+ if (!partexprDatum)
+ nulls[Anum_pg_partitioned_table_partexprs - 1] = true;
+
+ values[Anum_pg_partitioned_table_partrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel));
+ values[Anum_pg_partitioned_table_partstrat - 1] = CharGetDatum(strategy);
+ values[Anum_pg_partitioned_table_partnatts - 1] = Int16GetDatum(partnatts);
+ values[Anum_pg_partitioned_table_partattrs - 1] = PointerGetDatum(partattrs_vec);
+ values[Anum_pg_partitioned_table_partclass - 1] = PointerGetDatum(partopclass_vec);
+ values[Anum_pg_partitioned_table_partcollation - 1] = PointerGetDatum(partcollation_vec);
+ values[Anum_pg_partitioned_table_partexprs - 1] = partexprDatum;
+
+ tuple = heap_form_tuple(RelationGetDescr(pg_partitioned_table), values, nulls);
+
+ simple_heap_insert(pg_partitioned_table, tuple);
+
+ /* Update the indexes on pg_partitioned_table */
+ CatalogUpdateIndexes(pg_partitioned_table, tuple);
+
+ /* Mark this relation as dependent on a few things as follows */
+ myself.classId = RelationRelationId;
+ myself.objectId = RelationGetRelid(rel);;
+ myself.objectSubId = 0;
+
+ /* Operator class and collation per key column */
+ for (i = 0; i < partnatts; i++)
+ {
+ referenced.classId = OperatorClassRelationId;
+ referenced.objectId = partopclass[i];
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+
+ referenced.classId = CollationRelationId;
+ referenced.objectId = partcollation[i];
+ referenced.objectSubId = 0;
+
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
+
+ /*
+ * Anything mentioned in the expressions. We must ignore the column
+ * references which will count as self-dependency items; in this case,
+ * the depender is the table itself (there is no such thing as partition
+ * key object).
+ */
+ if (partexprs)
+ recordDependencyOnSingleRelExpr(&myself,
+ (Node *) partexprs,
+ RelationGetRelid(rel),
+ DEPENDENCY_NORMAL,
+ DEPENDENCY_AUTO, true);
+
+ heap_close(pg_partitioned_table, RowExclusiveLock);
+}
+
+/*
+ * RemovePartitionKeyByRelId
+ * Remove pg_partitioned_table entry for a relation
+ */
+void
+RemovePartitionKeyByRelId(Oid relid)
+{
+ Relation rel;
+ HeapTuple tuple;
+
+ rel = heap_open(PartitionedRelationId, RowExclusiveLock);
+
+ tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for partition key of relation %u", relid);
+
+ simple_heap_delete(rel, &tuple->t_self);
+
+ ReleaseSysCache(tuple);
+ heap_close(rel, RowExclusiveLock);
+}
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 08b646d..08b0989 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -1043,7 +1043,7 @@ index_create(Relation heapRelation,
(Node *) indexInfo->ii_Expressions,
heapRelationId,
DEPENDENCY_NORMAL,
- DEPENDENCY_AUTO);
+ DEPENDENCY_AUTO, false);
}
/* Store dependencies on anything mentioned in predicate */
@@ -1053,7 +1053,7 @@ index_create(Relation heapRelation,
(Node *) indexInfo->ii_Predicate,
heapRelationId,
DEPENDENCY_NORMAL,
- DEPENDENCY_AUTO);
+ DEPENDENCY_AUTO, false);
}
}
else
diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c
index d531d17..bb4b080 100644
--- a/src/backend/catalog/objectaddress.c
+++ b/src/backend/catalog/objectaddress.c
@@ -1204,7 +1204,8 @@ get_relation_by_qualified_name(ObjectType objtype, List *objname,
RelationGetRelationName(relation))));
break;
case OBJECT_TABLE:
- if (relation->rd_rel->relkind != RELKIND_RELATION)
+ if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
@@ -3244,6 +3245,7 @@ getRelationDescription(StringInfo buffer, Oid relid)
switch (relForm->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
appendStringInfo(buffer, _("table %s"),
relname);
break;
@@ -3701,6 +3703,7 @@ getRelationTypeDescription(StringInfo buffer, Oid relid, int32 objectSubId)
switch (relForm->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
appendStringInfoString(buffer, "table");
break;
case RELKIND_INDEX:
diff --git a/src/backend/catalog/pg_constraint.c b/src/backend/catalog/pg_constraint.c
index 8fabe68..724b41e 100644
--- a/src/backend/catalog/pg_constraint.c
+++ b/src/backend/catalog/pg_constraint.c
@@ -368,7 +368,7 @@ CreateConstraintEntry(const char *constraintName,
*/
recordDependencyOnSingleRelExpr(&conobject, conExpr, relId,
DEPENDENCY_NORMAL,
- DEPENDENCY_NORMAL);
+ DEPENDENCY_NORMAL, false);
}
/* Post creation hook for new constraint */
diff --git a/src/backend/commands/analyze.c b/src/backend/commands/analyze.c
index c617abb..c4db6f7 100644
--- a/src/backend/commands/analyze.c
+++ b/src/backend/commands/analyze.c
@@ -201,6 +201,7 @@ analyze_rel(Oid relid, RangeVar *relation, int options,
* locked the relation.
*/
if (onerel->rd_rel->relkind == RELKIND_RELATION ||
+ onerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
onerel->rd_rel->relkind == RELKIND_MATVIEW)
{
/* Regular table, so we'll use the regular row acquisition function */
@@ -1317,6 +1318,7 @@ acquire_inherited_sample_rows(Relation onerel, int elevel,
/* Check table type (MATVIEW can't happen, but might as well allow) */
if (childrel->rd_rel->relkind == RELKIND_RELATION ||
+ childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
childrel->rd_rel->relkind == RELKIND_MATVIEW)
{
/* Regular table, so use the regular row acquisition function */
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index b4140eb..0ef590a 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -1751,6 +1751,12 @@ BeginCopyTo(ParseState *pstate,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot copy from sequence \"%s\"",
RelationGetRelationName(rel))));
+ else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot copy from partitioned table \"%s\"",
+ RelationGetRelationName(rel)),
+ errhint("Try the COPY (SELECT ...) TO variant.")));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 85817c6..9f90e62 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -69,8 +69,6 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
char *accessMethodName, Oid accessMethodId,
bool amcanorder,
bool isconstraint);
-static Oid GetIndexOpClass(List *opclass, Oid attrType,
- char *accessMethodName, Oid accessMethodId);
static char *ChooseIndexName(const char *tabname, Oid namespaceId,
List *colnames, List *exclusionOpNames,
bool primary, bool isconstraint);
@@ -383,6 +381,11 @@ DefineIndex(Oid relationId,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot create index on foreign table \"%s\"",
RelationGetRelationName(rel))));
+ else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot create index on partitioned table \"%s\"",
+ RelationGetRelationName(rel))));
else
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -1256,7 +1259,7 @@ ComputeIndexAttrs(IndexInfo *indexInfo,
/*
* Resolve possibly-defaulted operator class specification
*/
-static Oid
+Oid
GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId)
{
diff --git a/src/backend/commands/lockcmds.c b/src/backend/commands/lockcmds.c
index a0c0d75..9e62e00 100644
--- a/src/backend/commands/lockcmds.c
+++ b/src/backend/commands/lockcmds.c
@@ -87,7 +87,7 @@ RangeVarCallbackForLockTable(const RangeVar *rv, Oid relid, Oid oldrelid,
* check */
/* Currently, we only allow plain tables to be locked */
- if (relkind != RELKIND_RELATION)
+ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
diff --git a/src/backend/commands/policy.c b/src/backend/commands/policy.c
index d694cf8..e5bcb89 100644
--- a/src/backend/commands/policy.c
+++ b/src/backend/commands/policy.c
@@ -88,7 +88,7 @@ RangeVarCallbackForPolicy(const RangeVar *rv, Oid relid, Oid oldrelid,
rv->relname)));
/* Relation type MUST be a table. */
- if (relkind != RELKIND_RELATION)
+ if (relkind != RELKIND_RELATION && relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table", rv->relname)));
diff --git a/src/backend/commands/seclabel.c b/src/backend/commands/seclabel.c
index 5bd7e12..10268be 100644
--- a/src/backend/commands/seclabel.c
+++ b/src/backend/commands/seclabel.c
@@ -107,6 +107,7 @@ ExecSecLabelStmt(SecLabelStmt *stmt)
* are the only relkinds for which pg_dump will dump labels).
*/
if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_MATVIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index fc3a8ee..e08fd5d 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -1475,6 +1475,7 @@ process_owned_by(Relation seqrel, List *owned_by)
/* Must be a regular or foreign table */
if (!(tablerel->rd_rel->relkind == RELKIND_RELATION ||
+ tablerel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
tablerel->rd_rel->relkind == RELKIND_FOREIGN_TABLE))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2137372..c2ae3f8 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -65,6 +65,7 @@
#include "nodes/parsenodes.h"
#include "optimizer/clauses.h"
#include "optimizer/planner.h"
+#include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parse_coerce.h"
#include "parser/parse_collate.h"
@@ -216,6 +217,12 @@ static const struct dropmsgstrings dropmsgstringarray[] = {
gettext_noop("table \"%s\" does not exist, skipping"),
gettext_noop("\"%s\" is not a table"),
gettext_noop("Use DROP TABLE to remove a table.")},
+ {RELKIND_PARTITIONED_TABLE,
+ ERRCODE_UNDEFINED_TABLE,
+ gettext_noop("table \"%s\" does not exist"),
+ gettext_noop("table \"%s\" does not exist, skipping"),
+ gettext_noop("\"%s\" is not a table"),
+ gettext_noop("Use DROP TABLE to remove a table.")},
{RELKIND_SEQUENCE,
ERRCODE_UNDEFINED_TABLE,
gettext_noop("sequence \"%s\" does not exist"),
@@ -433,6 +440,10 @@ static void RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid,
Oid oldRelOid, void *arg);
static void RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid,
Oid oldrelid, void *arg);
+static bool is_partition_attr(Relation rel, AttrNumber attnum, bool *is_expr);
+static PartitionSpec *transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy);
+static void ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
+ List **partexprs, Oid *partopclass, Oid *partcollation);
/* ----------------------------------------------------------------
@@ -492,6 +503,14 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
errmsg("ON COMMIT can only be used on temporary tables")));
+ if (stmt->partspec != NULL)
+ {
+ if (relkind != RELKIND_RELATION)
+ elog(ERROR, "unexpected relkind: %d", (int) relkind);
+
+ relkind = RELKIND_PARTITIONED_TABLE;
+ }
+
/*
* Look up the namespace in which we are supposed to create the relation,
* check we have permission to create there, lock it against concurrent
@@ -596,7 +615,8 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
* affect other relkinds, but it would complicate interpretOidsOption().
*/
localHasOids = interpretOidsOption(stmt->options,
- (relkind == RELKIND_RELATION));
+ (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_PARTITIONED_TABLE));
descriptor->tdhasoid = (localHasOids || parentOidCount > 0);
/*
@@ -710,6 +730,33 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid ownerId,
AddRelationNewConstraints(rel, rawDefaults, stmt->constraints,
true, true, false);
+ /* Process and store partition key information, if any */
+ if (stmt->partspec)
+ {
+ char strategy;
+ int partnatts;
+ AttrNumber partattrs[PARTITION_MAX_KEYS];
+ Oid partopclass[PARTITION_MAX_KEYS];
+ Oid partcollation[PARTITION_MAX_KEYS];
+ List *partexprs = NIL;
+
+ /*
+ * We need to transform the raw parsetrees corresponding to partition
+ * expressions into executable expression trees. Like column defaults
+ * and CHECK constraints, we could not have done the transformation
+ * earlier.
+ */
+ stmt->partspec = transformPartitionSpec(rel, stmt->partspec,
+ &strategy);
+ ComputePartitionAttrs(rel, stmt->partspec->partParams,
+ partattrs, &partexprs, partopclass,
+ partcollation);
+
+ partnatts = list_length(stmt->partspec->partParams);
+ StorePartitionKey(rel, strategy, partnatts, partattrs, partexprs,
+ partopclass, partcollation);
+ }
+
ObjectAddressSet(address, RelationRelationId, relationId);
/*
@@ -926,7 +973,8 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
{
HeapTuple tuple;
struct DropRelationCallbackState *state;
- char relkind;
+ char relkind,
+ expected_relkind;
Form_pg_class classform;
LOCKMODE heap_lockmode;
@@ -955,7 +1003,19 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid,
return; /* concurrently dropped, so nothing to do */
classform = (Form_pg_class) GETSTRUCT(tuple);
- if (classform->relkind != relkind)
+ /*
+ * Both RELKIND_RELATION and RELKIND_PARTITIONED_TABLE are OBJECT_TABLE,
+ * but RemoveRelations() can only pass one relkind for a given relation.
+ * It chooses RELKIND_RELATION for both regular and partitioned tables.
+ * That means we must be careful before giving the wrong type error when
+ * the relation is RELKIND_PARTITIONED_TABLE.
+ */
+ if (classform->relkind == RELKIND_PARTITIONED_TABLE)
+ expected_relkind = RELKIND_RELATION;
+ else
+ expected_relkind = classform->relkind;
+
+ if (relkind != expected_relkind)
DropErrorMsgWrongType(rel->relname, classform->relkind, relkind);
/* Allow DROP to either table owner or schema owner */
@@ -1293,7 +1353,8 @@ truncate_check_rel(Relation rel)
AclResult aclresult;
/* Only allow truncate on regular tables */
- if (rel->rd_rel->relkind != RELKIND_RELATION)
+ if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table",
@@ -1521,6 +1582,13 @@ MergeAttributes(List *schema, List *supers, char relpersistence,
*/
relation = heap_openrv(parent, ShareUpdateExclusiveLock);
+ /* Cannot inherit from partitioned tables */
+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot inherit from table \"%s\"", parent->relname),
+ errdetail("Table \"%s\" is partitioned.", parent->relname)));
+
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
@@ -2162,6 +2230,7 @@ renameatt_check(Oid myrelid, Form_pg_class classform, bool recursing)
* restriction.
*/
if (relkind != RELKIND_RELATION &&
+ relkind != RELKIND_PARTITIONED_TABLE &&
relkind != RELKIND_VIEW &&
relkind != RELKIND_MATVIEW &&
relkind != RELKIND_COMPOSITE_TYPE &&
@@ -4291,6 +4360,7 @@ ATSimplePermissions(Relation rel, int allowed_targets)
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
actual_target = ATT_TABLE;
break;
case RELKIND_VIEW:
@@ -4527,6 +4597,7 @@ find_composite_type_dependencies(Oid typeOid, Relation origRelation,
att = rel->rd_att->attrs[pg_depend->objsubid - 1];
if (rel->rd_rel->relkind == RELKIND_RELATION ||
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
rel->rd_rel->relkind == RELKIND_MATVIEW)
{
if (origTypeName)
@@ -5417,6 +5488,7 @@ ATPrepSetStatistics(Relation rel, const char *colName, Node *newValue, LOCKMODE
* allowSystemTableMods to be turned on.
*/
if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
rel->rd_rel->relkind != RELKIND_MATVIEW &&
rel->rd_rel->relkind != RELKIND_INDEX &&
rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
@@ -5692,6 +5764,69 @@ ATPrepDropColumn(List **wqueue, Relation rel, bool recurse, bool recursing,
}
/*
+ * Checks if attnum is a partition attribute for rel
+ *
+ * Sets *is_expr if attnum is found to be referenced in some partition key
+ * expression.
+ */
+static bool
+is_partition_attr(Relation rel, AttrNumber attnum, bool *is_expr)
+{
+ PartitionKey key;
+ int partnatts;
+ List *partexprs;
+ ListCell *partexprs_item;
+ int i;
+
+ if (rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ return false;
+
+ key = RelationGetPartitionKey(rel);
+ partnatts = get_partition_natts(key);
+ partexprs = get_partition_exprs(key);
+
+ partexprs_item = list_head(partexprs);
+ for (i = 0; i < partnatts; i++)
+ {
+ AttrNumber partattno = get_partition_col_attnum(key, i);
+
+ if (partattno != 0)
+ {
+ if (is_expr)
+ *is_expr = false;
+ if (attnum == partattno)
+ return true;
+ }
+ else
+ {
+ /* Arbitrary expression */
+ Node *expr = (Node *) lfirst(partexprs_item);
+ Bitmapset *expr_attrs = NULL;
+ int index;
+
+ if (is_expr)
+ *is_expr = true;
+
+ /* Find all attributes referenced */
+ pull_varattnos(expr, 1, &expr_attrs);
+ partexprs_item = lnext(partexprs_item);
+
+ index = -1;
+ while ((index = bms_next_member(expr_attrs, index)) > 0)
+ {
+ AttrNumber attno = index + FirstLowInvalidHeapAttributeNumber;
+
+ if (attno == attnum)
+ return true;
+ }
+ partexprs_item = lnext(partexprs_item);
+ }
+ }
+
+ return false;
+}
+
+/*
* Return value is the address of the dropped column.
*/
static ObjectAddress
@@ -5705,6 +5840,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
AttrNumber attnum;
List *children;
ObjectAddress object;
+ bool is_expr;
/* At top level, permission check was done in ATPrepCmd, else do it */
if (recursing)
@@ -5749,6 +5885,19 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
errmsg("cannot drop inherited column \"%s\"",
colName)));
+ /* Don't drop columns used in partition key */
+ if (is_partition_attr(rel, attnum, &is_expr))
+ {
+ if (!is_expr)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot drop column named in partition key")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot drop column referenced in partition key expression")));
+ }
+
ReleaseSysCache(tuple);
/*
@@ -6267,6 +6416,12 @@ ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
* Validity checks (permission checks wait till we have the column
* numbers)
*/
+ if (pkrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot reference relation \"%s\"", RelationGetRelationName(pkrel)),
+ errdetail("Referencing partitioned tables in foreign key constraints is not supported.")));
+
if (pkrel->rd_rel->relkind != RELKIND_RELATION)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -7883,6 +8038,7 @@ ATPrepAlterColumnType(List **wqueue,
NewColumnValue *newval;
ParseState *pstate = make_parsestate(NULL);
AclResult aclresult;
+ bool is_expr;
if (rel->rd_rel->reloftype && !recursing)
ereport(ERROR,
@@ -7913,6 +8069,19 @@ ATPrepAlterColumnType(List **wqueue,
errmsg("cannot alter inherited column \"%s\"",
colName)));
+ /* Don't alter columns used in partition key */
+ if (is_partition_attr(rel, attnum, &is_expr))
+ {
+ if (!is_expr)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot alter type of column named in partition key")));
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+ errmsg("cannot alter type of column referenced in partition key expression")));
+ }
+
/* Look up the target type */
typenameTypeIdAndMod(NULL, typeName, &targettype, &targettypmod);
@@ -7928,7 +8097,8 @@ ATPrepAlterColumnType(List **wqueue,
list_make1_oid(rel->rd_rel->reltype),
false);
- if (tab->relkind == RELKIND_RELATION)
+ if (tab->relkind == RELKIND_RELATION ||
+ tab->relkind == RELKIND_PARTITIONED_TABLE)
{
/*
* Set up an expression to transform the old data value to the new
@@ -8955,6 +9125,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
switch (tuple_class->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
case RELKIND_VIEW:
case RELKIND_MATVIEW:
case RELKIND_FOREIGN_TABLE:
@@ -9417,6 +9588,7 @@ ATExecSetRelOptions(Relation rel, List *defList, AlterTableType operation,
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
case RELKIND_TOASTVALUE:
case RELKIND_MATVIEW:
(void) heap_reloptions(rel->rd_rel->relkind, newOptions, true);
@@ -9839,7 +10011,8 @@ AlterTableMoveAll(AlterTableMoveAllStmt *stmt)
/* Only move the object type requested */
if ((stmt->objtype == OBJECT_TABLE &&
- relForm->relkind != RELKIND_RELATION) ||
+ relForm->relkind != RELKIND_RELATION &&
+ relForm->relkind != RELKIND_PARTITIONED_TABLE) ||
(stmt->objtype == OBJECT_INDEX &&
relForm->relkind != RELKIND_INDEX) ||
(stmt->objtype == OBJECT_MATVIEW &&
@@ -10038,6 +10211,11 @@ ATPrepAddInherit(Relation child_rel)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot change inheritance of typed table")));
+
+ if (child_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot change inheritance of partitioned table")));
}
/*
@@ -10089,6 +10267,13 @@ ATExecAddInherit(Relation child_rel, RangeVar *parent, LOCKMODE lockmode)
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cannot inherit to temporary relation of another session")));
+ /* Prevent partitioned tables from becoming inheritance parents */
+ if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("cannot inherit from \"%s\"", parent->relname),
+ errdetail("Table \"%s\" is partitioned.", 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.
@@ -11478,6 +11663,7 @@ AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
/* Fix other dependent stuff */
if (rel->rd_rel->relkind == RELKIND_RELATION ||
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
rel->rd_rel->relkind == RELKIND_MATVIEW)
{
AlterIndexNamespaces(classRel, rel, oldNspOid, nspOid, objsMoved);
@@ -11927,7 +12113,7 @@ RangeVarCallbackOwnsTable(const RangeVar *relation,
if (!relkind)
return;
if (relkind != RELKIND_RELATION && relkind != RELKIND_TOASTVALUE &&
- relkind != RELKIND_MATVIEW)
+ relkind != RELKIND_MATVIEW && relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table or materialized view", relation->relname)));
@@ -12081,6 +12267,7 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
*/
if (IsA(stmt, AlterObjectSchemaStmt) &&
relkind != RELKIND_RELATION &&
+ relkind != RELKIND_PARTITIONED_TABLE &&
relkind != RELKIND_VIEW &&
relkind != RELKIND_MATVIEW &&
relkind != RELKIND_SEQUENCE &&
@@ -12092,3 +12279,248 @@ RangeVarCallbackForAlterRelation(const RangeVar *rv, Oid relid, Oid oldrelid,
ReleaseSysCache(tuple);
}
+
+/*
+ * Transform any expressions present in the partition key
+ */
+static PartitionSpec *
+transformPartitionSpec(Relation rel, PartitionSpec *partspec, char *strategy)
+{
+ PartitionSpec *newspec;
+ ParseState *pstate;
+ RangeTblEntry *rte;
+ ListCell *l;
+
+ newspec = (PartitionSpec *) makeNode(PartitionSpec);
+
+ newspec->strategy = partspec->strategy;
+ newspec->location = partspec->location;
+ newspec->partParams = NIL;
+
+ /* Parse partitioning strategy name */
+ if (!pg_strcasecmp(partspec->strategy, "list"))
+ *strategy = PARTITION_STRATEGY_LIST;
+ else if (!pg_strcasecmp(partspec->strategy, "range"))
+ *strategy = PARTITION_STRATEGY_RANGE;
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("unrecognized partition strategy \"%s\"",
+ partspec->strategy)));
+
+ /*
+ * Create a dummy ParseState and insert the target relation as its sole
+ * rangetable entry. We need a ParseState for transformExpr.
+ */
+ pstate = make_parsestate(NULL);
+ rte = addRangeTableEntryForRelation(pstate, rel, NULL, false, true);
+ addRTEtoQuery(pstate, rte, true, true, true);
+
+ /* take care of any partition expressions */
+ foreach(l, partspec->partParams)
+ {
+ ListCell *lc;
+ PartitionElem *pelem = (PartitionElem *) lfirst(l);
+
+ /* Check for PARTITION BY ... (foo, foo) */
+ foreach(lc, newspec->partParams)
+ {
+ PartitionElem *pparam = (PartitionElem *) lfirst(lc);
+
+ if (pelem->name && pparam->name &&
+ !strcmp(pelem->name, pparam->name))
+ ereport(ERROR,
+ (errcode(ERRCODE_DUPLICATE_COLUMN),
+ errmsg("column \"%s\" appears twice in partition key", pelem->name),
+ parser_errposition(pstate, pelem->location)));
+ }
+
+ if (pelem->expr)
+ {
+ /* Now do parse transformation of the expression */
+ pelem->expr = transformExpr(pstate, pelem->expr,
+ EXPR_KIND_PARTITION_EXPRESSION);
+
+ /* we have to fix its collations too */
+ assign_expr_collations(pstate, pelem->expr);
+ }
+
+ newspec->partParams = lappend(newspec->partParams, pelem);
+ }
+
+ return newspec;
+}
+
+/*
+ * Compute per-partition-column information from a list of PartitionElem's
+ */
+static void
+ComputePartitionAttrs(Relation rel, List *partParams, AttrNumber *partattrs,
+ List **partexprs, Oid *partopclass, Oid *partcollation)
+{
+ int attn;
+ ListCell *lc;
+
+ attn = 0;
+ foreach(lc, partParams)
+ {
+ PartitionElem *pelem = (PartitionElem *) lfirst(lc);
+ Oid atttype;
+ Oid attcollation;
+
+ if (pelem->name != NULL)
+ {
+ HeapTuple atttuple;
+ Form_pg_attribute attform;
+
+ atttuple = SearchSysCacheAttName(RelationGetRelid(rel), pelem->name);
+ if (!HeapTupleIsValid(atttuple))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("column \"%s\" named in partition key does not exist",
+ pelem->name)));
+ attform = (Form_pg_attribute) GETSTRUCT(atttuple);
+
+ if (attform->attnum <= 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("cannot use system column \"%s\" in partition key",
+ pelem->name)));
+
+ partattrs[attn] = attform->attnum;
+ atttype = attform->atttypid;
+ attcollation = attform->attcollation;
+ ReleaseSysCache(atttuple);
+
+ /* Note that whole-row references can't happen here; see below */
+ }
+ else
+ {
+ /* Partition key expression */
+ Node *expr = pelem->expr;
+
+ Assert(expr != NULL);
+ atttype = exprType(expr);
+ attcollation = exprCollation(expr);
+
+ /*
+ * Strip any top-level COLLATE clause. This ensures that we treat
+ * "x COLLATE y" and "(x COLLATE y)" alike.
+ */
+ while (IsA(expr, CollateExpr))
+ expr = (Node *) ((CollateExpr *) expr)->arg;
+
+ if (IsA(expr, Var) &&
+ ((Var *) expr)->varattno != InvalidAttrNumber)
+ {
+ /*
+ * User wrote "(column)" or "(column COLLATE something)".
+ * Treat it like simple attribute anyway.
+ */
+ partattrs[attn] = ((Var *) expr)->varattno;
+ }
+ else
+ {
+ Bitmapset *expr_attrs = NULL;
+
+ partattrs[attn] = 0; /* marks the column as expression */
+ *partexprs = lappend(*partexprs, expr);
+
+ /*
+ * Note that expression_planner does not change the passed in
+ * expression destructively and we have already saved the
+ * expression to be stored into the catalog above.
+ */
+ expr = (Node *) expression_planner((Expr *) expr);
+
+ /*
+ * Partition expression cannot contain mutable functions,
+ * because a given row must always map to the same partition
+ * as long as there is no change in the partition boundary
+ * structure.
+ */
+ if (contain_mutable_functions(expr))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("functions in partition key expression must be marked IMMUTABLE")));
+
+ /*
+ * While it is not exactly *wrong* for an expression to be
+ * a constant value, it seems better to prevent such input.
+ */
+ if (IsA(expr, Const))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot use constant expression as partition key")));
+
+ /*
+ * transformPartitionSpec() should have already rejected subqueries,
+ * aggregates, window functions, and SRFs, based on the EXPR_KIND_
+ * for partition expressions.
+ */
+
+ /* Cannot have expressions containing whole-row references */
+ pull_varattnos(expr, 1, &expr_attrs);
+ if (bms_is_member(0 - FirstLowInvalidHeapAttributeNumber,
+ expr_attrs))
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("partition key expressions cannot contain whole-row references")));
+ }
+ }
+
+ /*
+ * Apply collation override if any
+ */
+ if (pelem->collation)
+ attcollation = get_collation_oid(pelem->collation, false);
+
+ /*
+ * Check we have a collation iff it's a collatable type. The only
+ * expected failures here are (1) COLLATE applied to a noncollatable
+ * type, or (2) partition expression had an unresolved collation.
+ * But we might as well code this to be a complete consistency check.
+ */
+ if (type_is_collatable(atttype))
+ {
+ if (!OidIsValid(attcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_INDETERMINATE_COLLATION),
+ errmsg("could not determine which collation to use for partition expression"),
+ errhint("Use the COLLATE clause to set the collation explicitly.")));
+ }
+ else
+ {
+ if (OidIsValid(attcollation))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("collations are not supported by type %s",
+ format_type_be(atttype))));
+ }
+
+ partcollation[attn] = attcollation;
+
+ /*
+ * Identify a btree opclass to use. Currently, we use only btree
+ * operators, which seems enough for list and range partitioning.
+ */
+ if (!pelem->opclass)
+ {
+ partopclass[attn] = GetDefaultOpClass(atttype, BTREE_AM_OID);
+
+ if (!OidIsValid(partopclass[attn]))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("data type %s has no default btree operator class",
+ format_type_be(atttype)),
+ errhint("You must specify a btree operator class or define a default btree operator class for the data type.")));
+ }
+ else
+ partopclass[attn] = GetIndexOpClass(pelem->opclass,
+ atttype,
+ "btree",
+ BTREE_AM_OID);
+
+ attn++;
+ }
+}
diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c
index 9de22a1..133776d 100644
--- a/src/backend/commands/trigger.c
+++ b/src/backend/commands/trigger.c
@@ -174,7 +174,8 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
* Triggers must be on tables or views, and there are additional
* relation-type-specific restrictions.
*/
- if (rel->rd_rel->relkind == RELKIND_RELATION)
+ if (rel->rd_rel->relkind == RELKIND_RELATION ||
+ rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
{
/* Tables can't have INSTEAD OF triggers */
if (stmt->timing != TRIGGER_TYPE_BEFORE &&
@@ -184,6 +185,13 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
errmsg("\"%s\" is a table",
RelationGetRelationName(rel)),
errdetail("Tables cannot have INSTEAD OF triggers.")));
+ /* Disallow ROW triggers on partitioned tables */
+ if (stmt->row && rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ ereport(ERROR,
+ (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+ errmsg("\"%s\" is a partitioned table",
+ RelationGetRelationName(rel)),
+ errdetail("Partitioned tables cannot have ROW triggers.")));
}
else if (rel->rd_rel->relkind == RELKIND_VIEW)
{
@@ -1112,6 +1120,7 @@ RemoveTriggerById(Oid trigOid)
rel = heap_open(relid, AccessExclusiveLock);
if (rel->rd_rel->relkind != RELKIND_RELATION &&
+ rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
rel->rd_rel->relkind != RELKIND_VIEW &&
rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE)
ereport(ERROR,
@@ -1218,7 +1227,8 @@ RangeVarCallbackForRenameTrigger(const RangeVar *rv, Oid relid, Oid oldrelid,
/* only tables and views can have triggers */
if (form->relkind != RELKIND_RELATION && form->relkind != RELKIND_VIEW &&
- form->relkind != RELKIND_FOREIGN_TABLE)
+ form->relkind != RELKIND_FOREIGN_TABLE &&
+ form->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is not a table, view, or foreign table",
diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c
index 58bbf55..efa5200 100644
--- a/src/backend/commands/vacuum.c
+++ b/src/backend/commands/vacuum.c
@@ -1313,6 +1313,7 @@ vacuum_rel(Oid relid, RangeVar *relation, int options, VacuumParams *params)
* relation.
*/
if (onerel->rd_rel->relkind != RELKIND_RELATION &&
+ onerel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
onerel->rd_rel->relkind != RELKIND_MATVIEW &&
onerel->rd_rel->relkind != RELKIND_TOASTVALUE)
{
diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c
index 32bb3f9..9773272 100644
--- a/src/backend/executor/execMain.c
+++ b/src/backend/executor/execMain.c
@@ -1019,6 +1019,7 @@ CheckValidResultRel(Relation resultRel, CmdType operation)
switch (resultRel->rd_rel->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
/* OK */
break;
case RELKIND_SEQUENCE:
@@ -1152,6 +1153,7 @@ CheckValidRowMarkRel(Relation rel, RowMarkType markType)
switch (rel->rd_rel->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
/* OK */
break;
case RELKIND_SEQUENCE:
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index efb0c5e..0668462 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -1886,6 +1886,7 @@ ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags)
relkind = resultRelInfo->ri_RelationDesc->rd_rel->relkind;
if (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_PARTITIONED_TABLE ||
relkind == RELKIND_MATVIEW)
{
j->jf_junkAttNo = ExecFindJunkAttribute(j, "ctid");
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 71714bc..f283a97 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3018,6 +3018,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(tableElts);
COPY_NODE_FIELD(inhRelations);
+ COPY_NODE_FIELD(partspec);
COPY_NODE_FIELD(ofTypename);
COPY_NODE_FIELD(constraints);
COPY_NODE_FIELD(options);
@@ -4174,6 +4175,33 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
return newnode;
}
+static PartitionSpec *
+_copyPartitionSpec(const PartitionSpec *from)
+{
+
+ PartitionSpec *newnode = makeNode(PartitionSpec);
+
+ COPY_STRING_FIELD(strategy);
+ COPY_NODE_FIELD(partParams);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
+static PartitionElem *
+_copyPartitionElem(const PartitionElem *from)
+{
+ PartitionElem *newnode = makeNode(PartitionElem);
+
+ COPY_STRING_FIELD(name);
+ COPY_NODE_FIELD(expr);
+ COPY_NODE_FIELD(collation);
+ COPY_NODE_FIELD(opclass);
+ COPY_LOCATION_FIELD(location);
+
+ return newnode;
+}
+
/* ****************************************************************
* pg_list.h copy functions
* ****************************************************************
@@ -5088,6 +5116,12 @@ copyObject(const void *from)
case T_RoleSpec:
retval = _copyRoleSpec(from);
break;
+ case T_PartitionSpec:
+ retval = _copyPartitionSpec(from);
+ break;
+ case T_PartitionElem:
+ retval = _copyPartitionElem(from);
+ break;
/*
* MISCELLANEOUS NODES
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 29a090f..a6421d2 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1168,6 +1168,7 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(tableElts);
COMPARE_NODE_FIELD(inhRelations);
+ COMPARE_NODE_FIELD(partspec);
COMPARE_NODE_FIELD(ofTypename);
COMPARE_NODE_FIELD(constraints);
COMPARE_NODE_FIELD(options);
@@ -2634,6 +2635,28 @@ _equalRoleSpec(const RoleSpec *a, const RoleSpec *b)
return true;
}
+static bool
+_equalPartitionSpec(const PartitionSpec *a, const PartitionSpec *b)
+{
+ COMPARE_STRING_FIELD(strategy);
+ COMPARE_NODE_FIELD(partParams);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
+static bool
+_equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
+{
+ COMPARE_STRING_FIELD(name);
+ COMPARE_NODE_FIELD(expr);
+ COMPARE_NODE_FIELD(collation);
+ COMPARE_NODE_FIELD(opclass);
+ COMPARE_LOCATION_FIELD(location);
+
+ return true;
+}
+
/*
* Stuff from pg_list.h
*/
@@ -3387,6 +3410,12 @@ equal(const void *a, const void *b)
case T_RoleSpec:
retval = _equalRoleSpec(a, b);
break;
+ case T_PartitionSpec:
+ retval = _equalPartitionSpec(a, b);
+ break;
+ case T_PartitionElem:
+ retval = _equalPartitionElem(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 ae86954..417e20a 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2392,6 +2392,7 @@ _outCreateStmtInfo(StringInfo str, const CreateStmt *node)
WRITE_NODE_FIELD(relation);
WRITE_NODE_FIELD(tableElts);
WRITE_NODE_FIELD(inhRelations);
+ WRITE_NODE_FIELD(partspec);
WRITE_NODE_FIELD(ofTypename);
WRITE_NODE_FIELD(constraints);
WRITE_NODE_FIELD(options);
@@ -3267,6 +3268,27 @@ _outForeignKeyCacheInfo(StringInfo str, const ForeignKeyCacheInfo *node)
appendStringInfo(str, " %u", node->conpfeqop[i]);
}
+static void
+_outPartitionSpec(StringInfo str, const PartitionSpec *node)
+{
+ WRITE_NODE_TYPE("PARTITIONBY");
+
+ WRITE_STRING_FIELD(strategy);
+ WRITE_NODE_FIELD(partParams);
+ WRITE_LOCATION_FIELD(location);
+}
+
+static void
+_outPartitionElem(StringInfo str, const PartitionElem *node)
+{
+ WRITE_NODE_TYPE("PARTITIONELEM");
+
+ WRITE_STRING_FIELD(name);
+ WRITE_NODE_FIELD(expr);
+ WRITE_NODE_FIELD(collation);
+ WRITE_NODE_FIELD(opclass);
+ WRITE_LOCATION_FIELD(location);
+}
/*
* outNode -
@@ -3852,6 +3874,12 @@ outNode(StringInfo str, const void *obj)
case T_ForeignKeyCacheInfo:
_outForeignKeyCacheInfo(str, obj);
break;
+ case T_PartitionSpec:
+ _outPartitionSpec(str, obj);
+ break;
+ case T_PartitionElem:
+ _outPartitionElem(str, obj);
+ break;
default:
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5547fc8..9d32a20 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -229,6 +229,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
struct ImportQual *importqual;
InsertStmt *istmt;
VariableSetStmt *vsetstmt;
+ PartitionElem *partelem;
+ PartitionSpec *partspec;
}
%type <node> stmt schema_stmt
@@ -541,6 +543,10 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
+%type <partspec> PartitionSpec OptPartitionSpec
+%type <str> part_strategy
+%type <partelem> part_elem
+%type <list> part_params
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
@@ -2808,69 +2814,75 @@ copy_generic_opt_arg_list_item:
*****************************************************************************/
CreateStmt: CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
- OptInherit OptWith OnCommitOption OptTableSpace
+ OptInherit OptPartitionSpec OptWith OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->relpersistence = $2;
n->relation = $4;
n->tableElts = $6;
n->inhRelations = $8;
+ n->partspec = $9;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $9;
- n->oncommit = $10;
- n->tablespacename = $11;
+ n->options = $10;
+ n->oncommit = $11;
+ n->tablespacename = $12;
n->if_not_exists = false;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name '('
- OptTableElementList ')' OptInherit OptWith OnCommitOption
- OptTableSpace
+ OptTableElementList ')' OptInherit OptPartitionSpec OptWith
+ OnCommitOption OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$7->relpersistence = $2;
n->relation = $7;
n->tableElts = $9;
n->inhRelations = $11;
+ n->partspec = $12;
n->ofTypename = NULL;
n->constraints = NIL;
- n->options = $12;
- n->oncommit = $13;
- n->tablespacename = $14;
+ n->options = $13;
+ n->oncommit = $14;
+ n->tablespacename = $15;
n->if_not_exists = true;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE qualified_name OF any_name
- OptTypedTableElementList OptWith OnCommitOption OptTableSpace
+ OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption
+ OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$4->relpersistence = $2;
n->relation = $4;
n->tableElts = $7;
n->inhRelations = NIL;
+ n->partspec = $8;
n->ofTypename = makeTypeNameFromNameList($6);
n->ofTypename->location = @6;
n->constraints = NIL;
- n->options = $8;
- n->oncommit = $9;
- n->tablespacename = $10;
+ n->options = $9;
+ n->oncommit = $10;
+ n->tablespacename = $11;
n->if_not_exists = false;
$$ = (Node *)n;
}
| CREATE OptTemp TABLE IF_P NOT EXISTS qualified_name OF any_name
- OptTypedTableElementList OptWith OnCommitOption OptTableSpace
+ OptTypedTableElementList OptPartitionSpec OptWith OnCommitOption
+ OptTableSpace
{
CreateStmt *n = makeNode(CreateStmt);
$7->relpersistence = $2;
n->relation = $7;
n->tableElts = $10;
n->inhRelations = NIL;
+ n->partspec = $11;
n->ofTypename = makeTypeNameFromNameList($9);
n->ofTypename->location = @9;
n->constraints = NIL;
- n->options = $11;
- n->oncommit = $12;
- n->tablespacename = $13;
+ n->options = $12;
+ n->oncommit = $13;
+ n->tablespacename = $14;
n->if_not_exists = true;
$$ = (Node *)n;
}
@@ -3415,6 +3427,65 @@ OptInherit: INHERITS '(' qualified_name_list ')' { $$ = $3; }
| /*EMPTY*/ { $$ = NIL; }
;
+/* Optional partition key definition */
+OptPartitionSpec: PartitionSpec { $$ = $1; }
+ | /*EMPTY*/ { $$ = NULL; }
+ ;
+
+PartitionSpec: PARTITION BY part_strategy '(' part_params ')'
+ {
+ PartitionSpec *n = makeNode(PartitionSpec);
+
+ n->strategy = $3;
+ n->partParams = $5;
+ n->location = @1;
+
+ $$ = n;
+ }
+ ;
+
+part_strategy: IDENT { $$ = $1; }
+ | unreserved_keyword { $$ = pstrdup($1); }
+ ;
+
+part_params: part_elem { $$ = list_make1($1); }
+ | part_params ',' part_elem { $$ = lappend($1, $3); }
+ ;
+
+part_elem: ColId opt_collate opt_class
+ {
+ PartitionElem *n = makeNode(PartitionElem);
+
+ n->name = $1;
+ n->expr = NULL;
+ n->collation = $2;
+ n->opclass = $3;
+ n->location = @1;
+ $$ = n;
+ }
+ | func_expr_windowless opt_collate opt_class
+ {
+ PartitionElem *n = makeNode(PartitionElem);
+
+ n->name = NULL;
+ n->expr = $1;
+ n->collation = $2;
+ n->opclass = $3;
+ n->location = @1;
+ $$ = n;
+ }
+ | '(' a_expr ')' opt_collate opt_class
+ {
+ PartitionElem *n = makeNode(PartitionElem);
+
+ n->name = NULL;
+ n->expr = $2;
+ n->collation = $4;
+ n->opclass = $5;
+ n->location = @1;
+ $$ = n;
+ }
+ ;
/* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */
OptWith:
WITH reloptions { $$ = $2; }
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 481a4dd..9cb9222 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -501,6 +501,14 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
err = _("grouping operations are not allowed in trigger WHEN conditions");
break;
+ case EXPR_KIND_PARTITION_EXPRESSION:
+ if (isAgg)
+ err = _("aggregate functions are not allowed in partition key expression");
+ else
+ err = _("grouping operations are not allowed in partition key expression");
+
+ break;
+
/*
* There is intentionally no default: case here, so that the
@@ -858,6 +866,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
case EXPR_KIND_TRIGGER_WHEN:
err = _("window functions are not allowed in trigger WHEN conditions");
break;
+ case EXPR_KIND_PARTITION_EXPRESSION:
+ err = _("window functions are not allowed in partition key expression");
+ 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 63f7965..031d827 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -1757,6 +1757,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
case EXPR_KIND_TRIGGER_WHEN:
err = _("cannot use subquery in trigger WHEN condition");
break;
+ case EXPR_KIND_PARTITION_EXPRESSION:
+ err = _("cannot use subquery in partition key expression");
+ break;
/*
* There is intentionally no default: case here, so that the
@@ -3359,6 +3362,8 @@ ParseExprKindName(ParseExprKind exprKind)
return "EXECUTE";
case EXPR_KIND_TRIGGER_WHEN:
return "WHEN";
+ case EXPR_KIND_PARTITION_EXPRESSION:
+ return "PARTITION BY";
/*
* There is intentionally no default: case here, so that the
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 56c9a42..7d9b415 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -2166,6 +2166,9 @@ check_srf_call_placement(ParseState *pstate, int location)
case EXPR_KIND_TRIGGER_WHEN:
err = _("set-returning functions are not allowed in trigger WHEN conditions");
break;
+ case EXPR_KIND_PARTITION_EXPRESSION:
+ err = _("set-returning functions are not allowed in partition key expression");
+ break;
/*
* 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 0670bc2..666cc1f 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -87,6 +87,7 @@ typedef struct
List *alist; /* "after list" of things to do after creating
* the table */
IndexStmt *pkey; /* PRIMARY KEY index, if any */
+ bool ispartitioned; /* true if table is partitioned */
} CreateStmtContext;
/* State shared by transformCreateSchemaStmt and its subroutines */
@@ -229,6 +230,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
+ cxt.ispartitioned = stmt->partspec != NULL;
/*
* Notice that we allow OIDs here only for plain tables, even though
@@ -247,6 +249,28 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
if (stmt->ofTypename)
transformOfType(&cxt, stmt->ofTypename);
+ if (stmt->partspec)
+ {
+ int partnatts = list_length(stmt->partspec->partParams);
+
+ if (stmt->inhRelations)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot create partitioned table as inheritance child")));
+
+ if (partnatts > PARTITION_MAX_KEYS)
+ ereport(ERROR,
+ (errcode(ERRCODE_TOO_MANY_COLUMNS),
+ errmsg("cannot partition using more than %d columns",
+ PARTITION_MAX_KEYS)));
+
+ if (!pg_strcasecmp(stmt->partspec->strategy, "list") &&
+ partnatts > 1)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("cannot list partition using more than one column")));
+ }
+
/*
* Run through each primary element in the table creation clause. Separate
* column defs from constraints, and do preliminary analysis. We have to
@@ -583,6 +607,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
errmsg("primary key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("primary key constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
/* FALL THRU */
case CONSTR_UNIQUE:
@@ -592,6 +622,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
errmsg("unique constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unique constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
if (constraint->keys == NIL)
constraint->keys = list_make1(makeString(column->colname));
cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
@@ -609,6 +645,12 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
errmsg("foreign key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign key constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
/*
* Fill in the current attribute's name and throw it into the
@@ -674,6 +716,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
errmsg("primary key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("primary key constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
break;
@@ -684,6 +732,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
errmsg("unique constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unique constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
break;
@@ -694,6 +748,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
errmsg("exclusion constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("exclusion constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
cxt->ixconstraints = lappend(cxt->ixconstraints, constraint);
break;
@@ -708,6 +768,12 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
errmsg("foreign key constraints are not supported on foreign tables"),
parser_errposition(cxt->pstate,
constraint->location)));
+ if (cxt->ispartitioned)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("foreign key constraints are not supported on partitioned tables"),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
cxt->fkconstraints = lappend(cxt->fkconstraints, constraint);
break;
@@ -760,6 +826,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
relation = relation_openrv(table_like_clause->relation, AccessShareLock);
if (relation->rd_rel->relkind != RELKIND_RELATION &&
+ relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_MATVIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
@@ -2512,6 +2579,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
+ cxt.ispartitioned = rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE;
/*
* The only subtypes that currently require parse transformation handling
diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c
index f82d891..8d28634 100644
--- a/src/backend/rewrite/rewriteDefine.c
+++ b/src/backend/rewrite/rewriteDefine.c
@@ -260,6 +260,7 @@ DefineQueryRewrite(char *rulename,
* blocks them for users. Don't mention them in the error message.
*/
if (event_relation->rd_rel->relkind != RELKIND_RELATION &&
+ event_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
event_relation->rd_rel->relkind != RELKIND_MATVIEW &&
event_relation->rd_rel->relkind != RELKIND_VIEW)
ereport(ERROR,
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b828e3c..a766835 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -1222,6 +1222,7 @@ rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
TargetEntry *tle;
if (target_relation->rd_rel->relkind == RELKIND_RELATION ||
+ target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE ||
target_relation->rd_rel->relkind == RELKIND_MATVIEW)
{
/*
diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c
index 79e0b1f..e46f879 100644
--- a/src/backend/utils/cache/relcache.c
+++ b/src/backend/utils/cache/relcache.c
@@ -32,6 +32,7 @@
#include "access/htup_details.h"
#include "access/multixact.h"
+#include "access/nbtree.h"
#include "access/reloptions.h"
#include "access/sysattr.h"
#include "access/xact.h"
@@ -49,6 +50,7 @@
#include "catalog/pg_database.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
+#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_shseclabel.h"
@@ -258,6 +260,8 @@ static HeapTuple ScanPgRelation(Oid targetRelId, bool indexOK, bool force_non_hi
static Relation AllocateRelationDesc(Form_pg_class relp);
static void RelationParseRelOptions(Relation relation, HeapTuple tuple);
static void RelationBuildTupleDesc(Relation relation);
+static void RelationBuildPartitionKey(Relation relation);
+static PartitionKey copy_partition_key(PartitionKey fromkey);
static Relation RelationBuildDesc(Oid targetRelId, bool insertIt);
static void RelationInitPhysicalAddr(Relation relation);
static void load_critical_index(Oid indexoid, Oid heapoid);
@@ -431,6 +435,7 @@ RelationParseRelOptions(Relation relation, HeapTuple tuple)
switch (relation->rd_rel->relkind)
{
case RELKIND_RELATION:
+ case RELKIND_PARTITIONED_TABLE:
case RELKIND_TOASTVALUE:
case RELKIND_INDEX:
case RELKIND_VIEW:
@@ -796,6 +801,239 @@ RelationBuildRuleLock(Relation relation)
}
/*
+ * RelationBuildPartitionKey
+ * Build and attach to relcache partition key data of relation
+ *
+ * Partition key data is stored in CacheMemoryContext to ensure it survives
+ * as long as the relcache. To avoid leaking memory in that context in case
+ * of an error partway through this function, we build the structure in the
+ * working context (which must be short-lived) and copy the completed
+ * structure into the cache memory.
+ *
+ * Also, since the structure being created here is sufficiently complex, we
+ * make a private child context of CacheMemoryContext for each relation that
+ * has associated partition key information. That means no complicated logic
+ * to free individual elements whenever the relcache entry is flushed - just
+ * delete the context.
+ */
+static void
+RelationBuildPartitionKey(Relation relation)
+{
+ Form_pg_partitioned_table form;
+ Relation catalog;
+ HeapTuple tuple;
+ bool isnull;
+ int i;
+ PartitionKey key;
+ AttrNumber *attrs;
+ oidvector *opclass;
+ oidvector *collation;
+ ListCell *partexprs_item;
+ Datum datum;
+ MemoryContext partkeycxt,
+ oldcxt;
+
+ tuple = SearchSysCache1(PARTRELID,
+ ObjectIdGetDatum(RelationGetRelid(relation)));
+ /*
+ * The following happens when we have created our pg_class entry but not
+ * the pg_partitioned_table entry yet.
+ */
+ if (!HeapTupleIsValid(tuple))
+ return;
+
+ key = (PartitionKey) palloc0(sizeof(PartitionKeyData));
+
+ /* Fixed-length attributes */
+ form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
+ key->strategy = form->partstrat;
+ key->partnatts = form->partnatts;
+ attrs = form->partattrs.values;
+
+ /*
+ * To retrieve further variable-length attributes, we'd need the catalog's
+ * tuple descriptor
+ */
+ catalog = heap_open(PartitionedRelationId, AccessShareLock);
+
+ /* Operator class */
+ datum = fastgetattr(tuple, Anum_pg_partitioned_table_partclass,
+ RelationGetDescr(catalog),
+ &isnull);
+ Assert(!isnull);
+ opclass = (oidvector *) DatumGetPointer(datum);
+
+ /* Collation */
+ datum = fastgetattr(tuple, Anum_pg_partitioned_table_partcollation,
+ RelationGetDescr(catalog),
+ &isnull);
+ Assert(!isnull);
+ collation = (oidvector *) DatumGetPointer(datum);
+
+ /* Expressions */
+ datum = heap_getattr(tuple,
+ Anum_pg_partitioned_table_partexprs,
+ RelationGetDescr(catalog),
+ &isnull);
+ if (!isnull)
+ {
+ char *exprString;
+ Node *expr;
+
+ exprString = TextDatumGetCString(datum);
+ expr = stringToNode(exprString);
+ pfree(exprString);
+
+ /*
+ * Run the expressions through const-simplification since the planner
+ * will be comparing them to similarly-processed qual clause operands,
+ * and may fail to detect valid matches without this step. We don't
+ * need to bother with canonicalize_qual() though, because partition
+ * expressions are not full-fledged qualification clauses.
+ */
+ expr = eval_const_expressions(NULL, (Node *) expr);
+
+ /* May as well fix opfuncids too */
+ fix_opfuncids((Node *) expr);
+ key->partexprs = (List *) expr;
+ }
+
+ key->partattrs = (AttrNumber *) palloc0(key->partnatts * sizeof(AttrNumber));
+ key->partopfamily = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+ key->partopcintype = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+ key->partsupfunc = (FmgrInfo *) palloc0(key->partnatts * sizeof(FmgrInfo));
+
+ key->partcollation = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+
+ /* Gather type and collation info as well */
+ key->parttypid = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+ key->parttypmod = (int32 *) palloc0(key->partnatts * sizeof(int32));
+ key->parttyplen = (int16 *) palloc0(key->partnatts * sizeof(int16));
+ key->parttypbyval = (bool *) palloc0(key->partnatts * sizeof(bool));
+ key->parttypalign = (char *) palloc0(key->partnatts * sizeof(char));
+ key->parttypcoll = (Oid *) palloc0(key->partnatts * sizeof(Oid));
+
+ /* Copy partattrs and fill other per-attribute info */
+ memcpy(key->partattrs, attrs, key->partnatts * sizeof(int16));
+ partexprs_item = list_head(key->partexprs);
+ for (i = 0; i < key->partnatts; i++)
+ {
+ AttrNumber attno = key->partattrs[i];
+ HeapTuple tuple;
+ Form_pg_opclass form;
+ Oid funcid;
+
+ /* Collect opfamily information */
+ tuple = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass->values[i]));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for opclass %u", opclass->values[i]);
+
+ form = (Form_pg_opclass) GETSTRUCT(tuple);
+ key->partopfamily[i] = form->opcfamily;
+ key->partopcintype[i] = form->opcintype;
+
+ /*
+ * A btree support function covers the cases of list and range methods
+ * currently supported.
+ */
+ funcid = get_opfamily_proc(form->opcfamily,
+ form->opcintype, form->opcintype,
+ BTORDER_PROC);
+
+ fmgr_info(funcid, &key->partsupfunc[i]);
+
+ /* Collation */
+ key->partcollation[i] = collation->values[i];
+
+ /* Collect type information */
+ if (attno != 0)
+ {
+ key->parttypid[i] = relation->rd_att->attrs[attno - 1]->atttypid;
+ key->parttypmod[i] = relation->rd_att->attrs[attno - 1]->atttypmod;
+ key->parttypcoll[i] = relation->rd_att->attrs[attno - 1]->attcollation;
+ }
+ else
+ {
+ key->parttypid[i] = exprType(lfirst(partexprs_item));
+ key->parttypmod[i] = exprTypmod(lfirst(partexprs_item));
+ key->parttypcoll[i] = exprCollation(lfirst(partexprs_item));
+ }
+ get_typlenbyvalalign(key->parttypid[i],
+ &key->parttyplen[i],
+ &key->parttypbyval[i],
+ &key->parttypalign[i]);
+
+ ReleaseSysCache(tuple);
+ }
+
+ ReleaseSysCache(tuple);
+ heap_close(catalog, AccessShareLock);
+
+ /* Success --- now copy to the cache memory */
+ partkeycxt = AllocSetContextCreate(CacheMemoryContext,
+ RelationGetRelationName(relation),
+ ALLOCSET_SMALL_SIZES);
+ relation->rd_partkeycxt = partkeycxt;
+ oldcxt = MemoryContextSwitchTo(relation->rd_partkeycxt);
+ relation->rd_partkey = copy_partition_key(key);
+ MemoryContextSwitchTo(oldcxt);
+}
+
+/*
+ * copy_partition_key
+ *
+ * The copy is allocated in the current memory context.
+ */
+static PartitionKey
+copy_partition_key(PartitionKey fromkey)
+{
+ PartitionKey newkey;
+ int n;
+
+ newkey = (PartitionKey) palloc(sizeof(PartitionKeyData));
+
+ newkey->strategy = fromkey->strategy;
+ newkey->partnatts = n = fromkey->partnatts;
+
+ newkey->partattrs = (AttrNumber *) palloc(n * sizeof(AttrNumber));
+ memcpy(newkey->partattrs, fromkey->partattrs, n * sizeof(AttrNumber));
+
+ newkey->partexprs = copyObject(fromkey->partexprs);
+
+ newkey->partopfamily = (Oid *) palloc(n * sizeof(Oid));
+ memcpy(newkey->partopfamily, fromkey->partopfamily, n * sizeof(Oid));
+
+ newkey->partopcintype = (Oid *) palloc(n * sizeof(Oid));
+ memcpy(newkey->partopcintype, fromkey->partopcintype, n * sizeof(Oid));
+
+ newkey->partsupfunc = (FmgrInfo *) palloc(n * sizeof(FmgrInfo));
+ memcpy(newkey->partsupfunc, fromkey->partsupfunc, n * sizeof(FmgrInfo));
+
+ newkey->partcollation = (Oid *) palloc(n * sizeof(Oid));
+ memcpy(newkey->partcollation, fromkey->partcollation, n * sizeof(Oid));
+
+ newkey->parttypid = (Oid *) palloc(n * sizeof(Oid));
+ memcpy(newkey->parttypid, fromkey->parttypid, n * sizeof(Oid));
+
+ newkey->parttypmod = (int32 *) palloc(n * sizeof(int32));
+ memcpy(newkey->parttypmod, fromkey->parttypmod, n * sizeof(int32));
+
+ newkey->parttyplen = (int16 *) palloc(n * sizeof(int16));
+ memcpy(newkey->parttyplen, fromkey->parttyplen, n * sizeof(int16));
+
+ newkey->parttypbyval = (bool *) palloc(n * sizeof(bool));
+ memcpy(newkey->parttypbyval, fromkey->parttypbyval, n * sizeof(bool));
+
+ newkey->parttypalign = (char *) palloc(n * sizeof(bool));
+ memcpy(newkey->parttypalign, fromkey->parttypalign, n * sizeof(char));
+
+ newkey->parttypcoll = (Oid *) palloc(n * sizeof(Oid));
+ memcpy(newkey->parttypcoll, fromkey->parttypcoll, n * sizeof(Oid));
+
+ return newkey;
+}
+
+/*
* equalRuleLocks
*
* Determine whether two RuleLocks are equivalent
@@ -1050,6 +1288,15 @@ RelationBuildDesc(Oid targetRelId, bool insertIt)
relation->rd_fkeylist = NIL;
relation->rd_fkeyvalid = false;
+ /* if it's a partitioned table, initialize key info */
+ if (relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ RelationBuildPartitionKey(relation);
+ else
+ {
+ relation->rd_partkeycxt = NULL;
+ relation->rd_partkey = NULL;
+ }
+
/*
* if it's an index, initialize index-related information
*/
@@ -2042,6 +2289,8 @@ RelationDestroyRelation(Relation relation, bool remember_tupdesc)
MemoryContextDelete(relation->rd_rulescxt);
if (relation->rd_rsdesc)
MemoryContextDelete(relation->rd_rsdesc->rscxt);
+ if (relation->rd_partkeycxt)
+ MemoryContextDelete(relation->rd_partkeycxt);
if (relation->rd_fdwroutine)
pfree(relation->rd_fdwroutine);
pfree(relation);
@@ -2983,7 +3232,9 @@ RelationBuildLocalRelation(const char *relname,
/* system relations and non-table objects don't have one */
if (!IsSystemNamespace(relnamespace) &&
- (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW))
+ (relkind == RELKIND_RELATION ||
+ relkind == RELKIND_PARTITIONED_TABLE ||
+ relkind == RELKIND_MATVIEW))
rel->rd_rel->relreplident = REPLICA_IDENTITY_DEFAULT;
else
rel->rd_rel->relreplident = REPLICA_IDENTITY_NOTHING;
@@ -4267,6 +4518,8 @@ RelationGetIndexExpressions(Relation relation)
*/
result = (List *) eval_const_expressions(NULL, (Node *) result);
+ result = (List *) canonicalize_qual((Expr *) result);
+
/* May as well fix opfuncids too */
fix_opfuncids((Node *) result);
@@ -5035,6 +5288,8 @@ load_relcache_init_file(bool shared)
rel->rd_rulescxt = NULL;
rel->trigdesc = NULL;
rel->rd_rsdesc = NULL;
+ rel->rd_partkeycxt = NULL;
+ rel->rd_partkey = NULL;
rel->rd_indexprs = NIL;
rel->rd_indpred = NIL;
rel->rd_exclops = NULL;
diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c
index 65ffe84..a3e0517 100644
--- a/src/backend/utils/cache/syscache.c
+++ b/src/backend/utils/cache/syscache.c
@@ -48,6 +48,7 @@
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
+#include "catalog/pg_partitioned_table.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
@@ -568,6 +569,17 @@ static const struct cachedesc cacheinfo[] = {
},
8
},
+ {PartitionedRelationId, /* PARTRELID */
+ PartitionedRelidIndexId,
+ 1,
+ {
+ Anum_pg_partitioned_table_partrelid,
+ 0,
+ 0,
+ 0
+ },
+ 32
+ },
{ProcedureRelationId, /* PROCNAMEARGSNSP */
ProcedureNameArgsNspIndexId,
3,
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index 09b36c5..960a697 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -188,7 +188,8 @@ extern void recordDependencyOnExpr(const ObjectAddress *depender,
extern void recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
- DependencyType self_behavior);
+ DependencyType self_behavior,
+ bool ignore_self);
extern ObjectClass getObjectClass(const ObjectAddress *object);
diff --git a/src/include/catalog/heap.h b/src/include/catalog/heap.h
index b80d8d8..11b16a9 100644
--- a/src/include/catalog/heap.h
+++ b/src/include/catalog/heap.h
@@ -134,4 +134,14 @@ extern void CheckAttributeType(const char *attname,
List *containing_rowtypes,
bool allow_system_table_mods);
+/* pg_partitioned_table catalog manipulation functions */
+extern void StorePartitionKey(Relation rel,
+ char strategy,
+ int16 partnatts,
+ AttrNumber *partattrs,
+ List *partexprs,
+ Oid *partopclass,
+ Oid *partcollation);
+extern void RemovePartitionKeyByRelId(Oid relid);
+
#endif /* HEAP_H */
diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h
index ca5eb3d..40f7576 100644
--- a/src/include/catalog/indexing.h
+++ b/src/include/catalog/indexing.h
@@ -319,6 +319,9 @@ DECLARE_UNIQUE_INDEX(pg_replication_origin_roiident_index, 6001, on pg_replicati
DECLARE_UNIQUE_INDEX(pg_replication_origin_roname_index, 6002, on pg_replication_origin using btree(roname text_pattern_ops));
#define ReplicationOriginNameIndex 6002
+DECLARE_UNIQUE_INDEX(pg_partitioned_table_partrelid_index, 3351, on pg_partitioned_table using btree(partrelid oid_ops));
+#define PartitionedRelidIndexId 3351
+
/* last step of initialization script: build the indexes declared above */
BUILD_INDICES
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..ba0f745 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -154,6 +154,7 @@ DESCR("");
#define RELKIND_RELATION 'r' /* ordinary table */
+#define RELKIND_PARTITIONED_TABLE 'P' /* partitioned table */
#define RELKIND_INDEX 'i' /* secondary index */
#define RELKIND_SEQUENCE 'S' /* sequence object */
#define RELKIND_TOASTVALUE 't' /* for out-of-line values */
diff --git a/src/include/catalog/pg_partitioned_table.h b/src/include/catalog/pg_partitioned_table.h
new file mode 100644
index 0000000..9f9ee5e
--- /dev/null
+++ b/src/include/catalog/pg_partitioned_table.h
@@ -0,0 +1,69 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_partitioned_table.h
+ * definition of the system "partitioned table" relation
+ * along with the relation's initial contents.
+ *
+ *
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_partitioned_table.h $
+ *
+ * NOTES
+ * the genbki.sh script reads this file and generates .bki
+ * information from the DATA() statements.
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_PARTITIONED_TABLE_H
+#define PG_PARTITIONED_TABLE_H
+
+#include "catalog/genbki.h"
+
+/* ----------------
+ * pg_partitioned_table definition. cpp turns this into
+ * typedef struct FormData_pg_partitioned_table
+ * ----------------
+ */
+#define PartitionedRelationId 3350
+
+CATALOG(pg_partitioned_table,3350) BKI_WITHOUT_OIDS
+{
+ Oid partrelid; /* partitioned table oid */
+ char partstrat; /* partition key strategy */
+ int16 partnatts; /* number of partition key columns */
+
+ /* variable-length fields start here, but we allow direct access to partattrs */
+ int2vector partattrs; /* attribute numbers of partition key
+ * columns */
+
+#ifdef CATALOG_VARLEN
+ oidvector partclass; /* operator class to compare keys */
+ oidvector partcollation; /* user-specified collation for keys */
+ pg_node_tree partexprs; /* expression trees for partition key members
+ * that are not simple column references; one
+ * for each zero entry in partattrs[] */
+#endif
+} FormData_pg_partitioned_table;
+
+/* ----------------
+ * Form_pg_partitioned_table corresponds to a pointer to a tuple with
+ * the format of pg_partitioned_table relation.
+ * ----------------
+ */
+typedef FormData_pg_partitioned_table *Form_pg_partitioned_table;
+
+/* ----------------
+ * compiler constants for pg_partitioned_table
+ * ----------------
+ */
+#define Natts_pg_partitioned_table 7
+#define Anum_pg_partitioned_table_partrelid 1
+#define Anum_pg_partitioned_table_partstrat 2
+#define Anum_pg_partitioned_table_partnatts 3
+#define Anum_pg_partitioned_table_partattrs 4
+#define Anum_pg_partitioned_table_partclass 5
+#define Anum_pg_partitioned_table_partcollation 6
+#define Anum_pg_partitioned_table_partexprs 7
+
+#endif /* PG_PARTITIONED_TABLE_H */
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 2b894ff..c7b0af3 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -42,6 +42,8 @@ extern bool CheckIndexCompatible(Oid oldId,
List *attributeList,
List *exclusionOpNames);
extern Oid GetDefaultOpClass(Oid type_id, Oid am_id);
+extern Oid GetIndexOpClass(List *opclass, Oid attrType,
+ char *accessMethodName, Oid accessMethodId);
/* commands/functioncmds.c */
extern ObjectAddress CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt);
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 88297bb..65d0009 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -453,6 +453,8 @@ typedef enum NodeTag
T_OnConflictClause,
T_CommonTableExpr,
T_RoleSpec,
+ T_PartitionElem,
+ T_PartitionSpec,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 6de2cab..8cc41cf 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -699,6 +699,41 @@ typedef struct XmlSerialize
int location; /* token location, or -1 if unknown */
} XmlSerialize;
+/* Partitioning related definitions */
+
+/*
+ * PartitionElem - a partition key column
+ *
+ * 'name' Name of the table column included in the key
+ * 'expr' Expression node tree of expressional key column
+ * 'opclass' Operator class name associated with the column
+ */
+typedef struct PartitionElem
+{
+ NodeTag type;
+ char *name; /* name of column to partition on, or NULL */
+ Node *expr; /* expression to partition on, or NULL */
+ List *collation; /* name of collation; NIL = default */
+ List *opclass; /* name of desired opclass; NIL = default */
+ int location; /* token location, or -1 if unknown */
+} PartitionElem;
+
+/*
+ * PartitionSpec - partition key definition including the strategy
+ *
+ * 'strategy' partition strategy name (ie, 'list' and 'range')
+ * 'partParams' List of PartitionElems, one for each key column
+ */
+typedef struct PartitionSpec
+{
+ NodeTag type;
+ char *strategy;
+ List *partParams;
+ int location; /* token location, or -1 if unknown */
+} PartitionSpec;
+
+#define PARTITION_STRATEGY_LIST 'l'
+#define PARTITION_STRATEGY_RANGE 'r'
/****************************************************************************
* Nodes for a Query tree
@@ -1753,6 +1788,7 @@ typedef struct CreateStmt
List *tableElts; /* column definitions (list of ColumnDef) */
List *inhRelations; /* relations to inherit from (list of
* inhRelation) */
+ PartitionSpec *partspec; /* PARTITION BY clause */
TypeName *ofTypename; /* OF typename */
List *constraints; /* constraints (list of Constraint nodes) */
List *options; /* options from WITH clause */
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 6633586..bd6dc02 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -64,7 +64,8 @@ typedef enum ParseExprKind
EXPR_KIND_ALTER_COL_TRANSFORM, /* transform expr in ALTER COLUMN TYPE */
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_POLICY, /* USING or WITH CHECK expr in policy */
+ EXPR_KIND_PARTITION_EXPRESSION /* PARTITION BY expression */
} ParseExprKind;
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index a2b2b61..01c6c09 100644
--- a/src/include/pg_config_manual.h
+++ b/src/include/pg_config_manual.h
@@ -46,6 +46,11 @@
#define INDEX_MAX_KEYS 32
/*
+ * Maximum number of columns in a partition key
+ */
+#define PARTITION_MAX_KEYS 32
+
+/*
* Set the upper and lower bounds of sequence values.
*/
#define SEQ_MAXVALUE PG_INT64_MAX
diff --git a/src/include/utils/rel.h b/src/include/utils/rel.h
index ed14442..f7c0ab0 100644
--- a/src/include/utils/rel.h
+++ b/src/include/utils/rel.h
@@ -45,6 +45,33 @@ typedef struct LockInfoData
typedef LockInfoData *LockInfo;
+/*
+ * Partition key information
+ */
+typedef struct PartitionKeyData
+{
+ char strategy; /* partition strategy */
+ int16 partnatts; /* number of partition attributes */
+ AttrNumber *partattrs; /* partition attnums */
+ List *partexprs; /* partition key expressions, if any */
+
+ Oid *partopfamily; /* OIDs of operator families */
+ Oid *partopcintype; /* OIDs of opclass declared input data types */
+ FmgrInfo *partsupfunc; /* lookup info for support funcs */
+
+ /* Partitioning collation */
+ Oid *partcollation;
+
+ /* Type information of partition attributes */
+ Oid *parttypid;
+ int32 *parttypmod;
+ int16 *parttyplen;
+ bool *parttypbyval;
+ char *parttypalign;
+ Oid *parttypcoll;
+} PartitionKeyData;
+
+typedef struct PartitionKeyData *PartitionKey;
/*
* Here are the contents of a relation cache entry.
@@ -94,6 +121,9 @@ typedef struct RelationData
List *rd_fkeylist; /* list of ForeignKeyCacheInfo (see below) */
bool rd_fkeyvalid; /* true if list has been computed */
+ MemoryContext rd_partkeycxt; /* private memory cxt for the below */
+ struct PartitionKeyData *rd_partkey; /* partition key, or NULL */
+
/* data managed by RelationGetIndexList: */
List *rd_indexlist; /* list of OIDs of indexes on relation */
Oid rd_oidindex; /* OID of unique index on OID, if any */
@@ -532,6 +562,42 @@ typedef struct ViewOptions
RelationNeedsWAL(relation) && \
!IsCatalogRelation(relation))
+/*
+ * RelationGetPartitionKey
+ * Returns partition key for a relation.
+ */
+#define RelationGetPartitionKey(relation) ((relation)->rd_partkey)
+
+/*
+ * Partition key information inquiry functions
+ */
+static inline int
+get_partition_strategy(PartitionKey key)
+{
+ return key->strategy;
+}
+
+static inline int
+get_partition_natts(PartitionKey key)
+{
+ return key->partnatts;
+}
+
+static inline List *
+get_partition_exprs(PartitionKey key)
+{
+ return key->partexprs;
+}
+
+/*
+ * Partition key information inquiry functions - one column
+ */
+static inline int16
+get_partition_col_attnum(PartitionKey key, int col)
+{
+ return key->partattrs[col];
+}
+
/* routines in utils/cache/relcache.c */
extern void RelationIncrementReferenceCount(Relation rel);
extern void RelationDecrementReferenceCount(Relation rel);
diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h
index 256615b..39fe947 100644
--- a/src/include/utils/syscache.h
+++ b/src/include/utils/syscache.h
@@ -72,6 +72,7 @@ enum SysCacheIdentifier
OPEROID,
OPFAMILYAMNAMENSP,
OPFAMILYOID,
+ PARTRELID,
PROCNAMEARGSNSP,
PROCOID,
RANGETYPE,
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index a1ab823..2c913e5 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2974,3 +2974,49 @@ Table "public.test_add_column"
c4 | integer |
DROP TABLE test_add_column;
+-- PRIMARY KEY, FOREIGN KEY, UNIQUE, EXCLUSION constraints not supported
+CREATE TABLE partitioned (
+ a int
+) PARTITION BY LIST (a);
+ALTER TABLE partitioned ADD UNIQUE (a);
+ERROR: unique constraints are not supported on partitioned tables
+LINE 1: ALTER TABLE partitioned ADD UNIQUE (a);
+ ^
+ALTER TABLE partitioned ADD PRIMARY KEY (a);
+ERROR: primary key constraints are not supported on partitioned tables
+LINE 1: ALTER TABLE partitioned ADD PRIMARY KEY (a);
+ ^
+ALTER TABLE partitioned ADD FOREIGN KEY (a) REFERENCES blah;
+ERROR: foreign key constraints are not supported on partitioned tables
+LINE 1: ALTER TABLE partitioned ADD FOREIGN KEY (a) REFERENCES blah;
+ ^
+ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&);
+ERROR: exclusion constraints are not supported on partitioned tables
+LINE 1: ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&);
+ ^
+-- cannot drop column that is part of the partition key
+CREATE TABLE no_drop_or_alter_partcol (
+ a int
+) PARTITION BY RANGE (a);
+ALTER TABLE no_drop_or_alter_partcol DROP COLUMN a;
+ERROR: cannot drop column named in partition key
+ALTER TABLE no_drop_or_alter_partcol ALTER COLUMN a TYPE char(5);
+ERROR: cannot alter type of column named in partition key
+CREATE TABLE no_drop_or_alter_partexpr (
+ a text
+) PARTITION BY RANGE ((substring(a from 1 for 1)));
+ALTER TABLE no_drop_alter_partexpr DROP COLUMN a;
+ERROR: relation "no_drop_alter_partexpr" does not exist
+ALTER TABLE no_drop_alter_partcol ALTER COLUMN a TYPE char(5);
+ERROR: relation "no_drop_alter_partcol" does not exist
+-- partitioned table cannot partiticipate in regular inheritance
+CREATE TABLE no_inh_child (
+ a int
+) PARTITION BY RANGE (a);
+CREATE TABLE inh_parent(a int);
+ALTER TABLE no_inh_child INHERIT inh_parent;
+ERROR: cannot change inheritance of partitioned table
+-- cannot add NO INHERIT constraint to partitioned tables
+ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT;
+ERROR: cannot add NO INHERIT constraint to partitioned table "partitioned"
+DROP TABLE partitioned, no_drop_or_alter_partcol, no_drop_or_alter_partexpr, no_inh_child, inh_parent;
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 41ceb87..c6ed153 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -253,3 +253,166 @@ DROP TABLE as_select1;
-- check that the oid column is added before the primary key is checked
CREATE TABLE oid_pk (f1 INT, PRIMARY KEY(oid)) WITH OIDS;
DROP TABLE oid_pk;
+--
+-- CREATE TABLE PARTITION BY
+--
+-- cannot combine INHERITS and PARTITION BY (although grammar allows)
+CREATE TABLE fail_inh_partition_by (
+ a int
+) INHERITS (some_table) PARTITION BY LIST (a);
+ERROR: cannot create partitioned table as inheritance child
+-- cannot use more than 1 column as partition key for list partitioned table
+CREATE TABLE fail_two_col_list_key (
+ a1 int,
+ a2 int
+) PARTITION BY LIST (a1, a2); -- fail
+ERROR: cannot list partition using more than one column
+-- PRIMARY KEY, FOREIGN KEY, UNIQUE, EXCLUSION constraints not supported
+CREATE TABLE fail_pk (
+ a int PRIMARY KEY
+) PARTITION BY RANGE (a);
+ERROR: primary key constraints are not supported on partitioned tables
+LINE 2: a int PRIMARY KEY
+ ^
+CREATE TABLE pkrel (
+ a int PRIMARY KEY
+);
+CREATE TABLE fail_fk (
+ a int REFERENCES pkrel(a)
+) PARTITION BY RANGE (a);
+ERROR: foreign key constraints are not supported on partitioned tables
+LINE 2: a int REFERENCES pkrel(a)
+ ^
+DROP TABLE pkrel;
+CREATE TABLE fail_unique (
+ a int UNIQUE
+) PARTITION BY RANGE (a);
+ERROR: unique constraints are not supported on partitioned tables
+LINE 2: a int UNIQUE
+ ^
+CREATE TABLE fail_exclusion (
+ a int,
+ EXCLUDE USING gist (a WITH &&)
+) PARTITION BY RANGE (a);
+ERROR: exclusion constraints are not supported on partitioned tables
+LINE 3: EXCLUDE USING gist (a WITH &&)
+ ^
+-- prevent column from being used twice in the partition key
+CREATE TABLE fail_col_used_twice (
+ a int
+) PARTIION BY RANGE (a, a);
+ERROR: syntax error at or near "PARTIION"
+LINE 3: ) PARTIION BY RANGE (a, a);
+ ^
+-- prevent using prohibited expressions in the key
+CREATE FUNCTION retset (a int) RETURNS SETOF int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE;
+CREATE TABLE fail_set_returning_expr_in_key (
+ a int
+) PARTITION BY RANGE (retset(a));
+ERROR: set-returning functions are not allowed in partition key expression
+DROP FUNCTION retset(int);
+CREATE TABLE fail_agg_in_key (
+ a int
+) PARTITION BY RANGE ((avg(a)));
+ERROR: aggregate functions are not allowed in partition key expression
+CREATE TABLE fail_window_fun_in_key (
+ a int,
+ b int
+) PARTITION BY RANGE ((avg(a) OVER (PARTITION BY b)));
+ERROR: window functions are not allowed in partition key expression
+CREATE TABLE fail_subquery_in_key (
+ a int
+) PARTITION BY LIST ((a LIKE (SELECT 1)));
+ERROR: cannot use subquery in partition key expression
+CREATE TABLE fail_const_key (
+ a int
+) PARTITION BY RANGE (('a'));
+ERROR: cannot use constant expression as partition key
+CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE;
+CREATE TABLE fail_const_key (
+ a int
+) PARTITION BY RANGE (const_func());
+ERROR: cannot use constant expression as partition key
+DROP FUNCTION const_func();
+-- only accept "list" and "range" as partitioning strategy
+CREATE TABLE wrong_strategy_name (
+ a int
+) PARTITION BY HASH (a);
+ERROR: unrecognized partition strategy "hash"
+-- specified column must be present in the table
+CREATE TABLE fail_nonexistant_col (
+ a int
+) PARTITION BY RANGE (b);
+ERROR: column "b" named in partition key does not exist
+-- cannot use system columns in partition key
+CREATE TABLE fail_system_col_key (
+ a int
+) PARTITION BY RANGE (xmin);
+ERROR: cannot use system column "xmin" in partition key
+-- functions in key must be immutable
+CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL;
+CREATE TABLE fail_immut_func_key (
+ a int
+) PARTITION BY RANGE (immut_func(a));
+ERROR: functions in partition key expression must be marked IMMUTABLE
+DROP FUNCTION immut_func(int);
+-- cannot contain whole-row references
+CREATE TABLE fail_wholerow_ref (
+ a int
+) PARTITION BY RANGE ((fail_wholerow_ref));
+ERROR: partition key expressions cannot contain whole-row references
+-- prevent using columns of unsupported types in key (type must have a btree operator class)
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY LIST (a);
+ERROR: data type point has no default btree operator class
+HINT: You must specify a btree operator class or define a default btree operator class for the data type.
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY LIST (a point_ops);
+ERROR: operator class "point_ops" does not exist for access method "btree"
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY RANGE (a);
+ERROR: data type point has no default btree operator class
+HINT: You must specify a btree operator class or define a default btree operator class for the data type.
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY RANGE (a point_ops);
+ERROR: operator class "point_ops" does not exist for access method "btree"
+-- check relkind
+CREATE TABLE check_relkind (
+ a int
+) PARTITION BY RANGE (a);
+SELECT relkind FROM pg_class WHERE relname = 'check_relkind';
+ relkind
+---------
+ P
+(1 row)
+
+DROP TABLE check_relkind;
+-- prevent a function referenced in partition key from being dropped
+CREATE FUNCTION plusone(a int) RETURNS INT AS $$ SELECT a+1; $$ LANGUAGE SQL;
+CREATE TABLE dependency_matters (
+ a int
+) PARTITION BY RANGE (plusone(a));
+DROP FUNCTION plusone(int);
+ERROR: cannot drop function plusone(integer) because other objects depend on it
+DETAIL: table dependency_matters depends on function plusone(integer)
+HINT: Use DROP ... CASCADE to drop the dependent objects too.
+DROP TABLE dependency_matters;
+DROP FUNCTION plusone(int);
+-- partitioned table cannot partiticipate in regular inheritance
+CREATE TABLE no_inh_parted (
+ a int
+) PARTITION BY RANGE (a);
+CREATE TABLE fail () INHERITS (no_inh_parted);
+ERROR: cannot inherit from table "no_inh_parted"
+DETAIL: Table "no_inh_parted" is partitioned.
+DROP TABLE no_inh_parted;
+-- cannot add NO INHERIT constraints to partitioned tables
+CREATE TABLE no_inh_con_parted (
+ a int,
+ CONSTRAINT check_a CHECK (a > 0) NO INHERIT
+) PARTITION BY RANGE (a);
+ERROR: cannot add NO INHERIT constraint to partitioned table "no_inh_con_parted"
diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out
index b1ebcf6..8fa929a 100644
--- a/src/test/regress/expected/sanity_check.out
+++ b/src/test/regress/expected/sanity_check.out
@@ -120,6 +120,7 @@ pg_namespace|t
pg_opclass|t
pg_operator|t
pg_opfamily|t
+pg_partitioned_table|t
pg_pltemplate|t
pg_policy|t
pg_proc|t
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index c8eed3e..3dbeb48 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1875,3 +1875,37 @@ ALTER TABLE test_add_column
ADD COLUMN c4 integer;
\d test_add_column
DROP TABLE test_add_column;
+
+-- PRIMARY KEY, FOREIGN KEY, UNIQUE, EXCLUSION constraints not supported
+CREATE TABLE partitioned (
+ a int
+) PARTITION BY LIST (a);
+ALTER TABLE partitioned ADD UNIQUE (a);
+ALTER TABLE partitioned ADD PRIMARY KEY (a);
+ALTER TABLE partitioned ADD FOREIGN KEY (a) REFERENCES blah;
+ALTER TABLE partitioned ADD EXCLUDE USING gist (a WITH &&);
+
+-- cannot drop column that is part of the partition key
+CREATE TABLE no_drop_or_alter_partcol (
+ a int
+) PARTITION BY RANGE (a);
+ALTER TABLE no_drop_or_alter_partcol DROP COLUMN a;
+ALTER TABLE no_drop_or_alter_partcol ALTER COLUMN a TYPE char(5);
+
+CREATE TABLE no_drop_or_alter_partexpr (
+ a text
+) PARTITION BY RANGE ((substring(a from 1 for 1)));
+ALTER TABLE no_drop_alter_partexpr DROP COLUMN a;
+ALTER TABLE no_drop_alter_partcol ALTER COLUMN a TYPE char(5);
+
+-- partitioned table cannot partiticipate in regular inheritance
+CREATE TABLE no_inh_child (
+ a int
+) PARTITION BY RANGE (a);
+CREATE TABLE inh_parent(a int);
+ALTER TABLE no_inh_child INHERIT inh_parent;
+
+-- cannot add NO INHERIT constraint to partitioned tables
+ALTER TABLE partitioned ADD CONSTRAINT chk_a CHECK (a > 0) NO INHERIT;
+
+DROP TABLE partitioned, no_drop_or_alter_partcol, no_drop_or_alter_partexpr, no_inh_child, inh_parent;
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index 78bdc8b..6a2f8f6 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -269,3 +269,145 @@ DROP TABLE as_select1;
-- check that the oid column is added before the primary key is checked
CREATE TABLE oid_pk (f1 INT, PRIMARY KEY(oid)) WITH OIDS;
DROP TABLE oid_pk;
+
+--
+-- CREATE TABLE PARTITION BY
+--
+
+-- cannot combine INHERITS and PARTITION BY (although grammar allows)
+CREATE TABLE fail_inh_partition_by (
+ a int
+) INHERITS (some_table) PARTITION BY LIST (a);
+
+-- cannot use more than 1 column as partition key for list partitioned table
+CREATE TABLE fail_two_col_list_key (
+ a1 int,
+ a2 int
+) PARTITION BY LIST (a1, a2); -- fail
+
+-- PRIMARY KEY, FOREIGN KEY, UNIQUE, EXCLUSION constraints not supported
+CREATE TABLE fail_pk (
+ a int PRIMARY KEY
+) PARTITION BY RANGE (a);
+CREATE TABLE pkrel (
+ a int PRIMARY KEY
+);
+
+CREATE TABLE fail_fk (
+ a int REFERENCES pkrel(a)
+) PARTITION BY RANGE (a);
+DROP TABLE pkrel;
+
+CREATE TABLE fail_unique (
+ a int UNIQUE
+) PARTITION BY RANGE (a);
+
+CREATE TABLE fail_exclusion (
+ a int,
+ EXCLUDE USING gist (a WITH &&)
+) PARTITION BY RANGE (a);
+
+-- prevent column from being used twice in the partition key
+CREATE TABLE fail_col_used_twice (
+ a int
+) PARTIION BY RANGE (a, a);
+
+-- prevent using prohibited expressions in the key
+CREATE FUNCTION retset (a int) RETURNS SETOF int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE;
+CREATE TABLE fail_set_returning_expr_in_key (
+ a int
+) PARTITION BY RANGE (retset(a));
+DROP FUNCTION retset(int);
+
+CREATE TABLE fail_agg_in_key (
+ a int
+) PARTITION BY RANGE ((avg(a)));
+
+CREATE TABLE fail_window_fun_in_key (
+ a int,
+ b int
+) PARTITION BY RANGE ((avg(a) OVER (PARTITION BY b)));
+
+CREATE TABLE fail_subquery_in_key (
+ a int
+) PARTITION BY LIST ((a LIKE (SELECT 1)));
+
+CREATE TABLE fail_const_key (
+ a int
+) PARTITION BY RANGE (('a'));
+
+CREATE FUNCTION const_func () RETURNS int AS $$ SELECT 1; $$ LANGUAGE SQL IMMUTABLE;
+CREATE TABLE fail_const_key (
+ a int
+) PARTITION BY RANGE (const_func());
+DROP FUNCTION const_func();
+
+-- only accept "list" and "range" as partitioning strategy
+CREATE TABLE wrong_strategy_name (
+ a int
+) PARTITION BY HASH (a);
+
+-- specified column must be present in the table
+CREATE TABLE fail_nonexistant_col (
+ a int
+) PARTITION BY RANGE (b);
+
+-- cannot use system columns in partition key
+CREATE TABLE fail_system_col_key (
+ a int
+) PARTITION BY RANGE (xmin);
+
+-- functions in key must be immutable
+CREATE FUNCTION immut_func (a int) RETURNS int AS $$ SELECT a + random()::int; $$ LANGUAGE SQL;
+CREATE TABLE fail_immut_func_key (
+ a int
+) PARTITION BY RANGE (immut_func(a));
+DROP FUNCTION immut_func(int);
+
+-- cannot contain whole-row references
+CREATE TABLE fail_wholerow_ref (
+ a int
+) PARTITION BY RANGE ((fail_wholerow_ref));
+
+-- prevent using columns of unsupported types in key (type must have a btree operator class)
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY LIST (a);
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY LIST (a point_ops);
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY RANGE (a);
+CREATE TABLE fail_point_type_key (
+ a point
+) PARTITION BY RANGE (a point_ops);
+
+-- check relkind
+CREATE TABLE check_relkind (
+ a int
+) PARTITION BY RANGE (a);
+SELECT relkind FROM pg_class WHERE relname = 'check_relkind';
+DROP TABLE check_relkind;
+
+-- prevent a function referenced in partition key from being dropped
+CREATE FUNCTION plusone(a int) RETURNS INT AS $$ SELECT a+1; $$ LANGUAGE SQL;
+CREATE TABLE dependency_matters (
+ a int
+) PARTITION BY RANGE (plusone(a));
+DROP FUNCTION plusone(int);
+DROP TABLE dependency_matters;
+DROP FUNCTION plusone(int);
+
+-- partitioned table cannot partiticipate in regular inheritance
+CREATE TABLE no_inh_parted (
+ a int
+) PARTITION BY RANGE (a);
+CREATE TABLE fail () INHERITS (no_inh_parted);
+DROP TABLE no_inh_parted;
+
+-- cannot add NO INHERIT constraints to partitioned tables
+CREATE TABLE no_inh_con_parted (
+ a int,
+ CONSTRAINT check_a CHECK (a > 0) NO INHERIT
+) PARTITION BY RANGE (a);
--
1.7.1