From ad90ca32803d6f9b23e0262e4c558526c5574735 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Mon, 8 Feb 2016 15:21:20 +0900
Subject: [PATCH 03/10] Introduce partition key clause in CREATE TABLE.

This adds the following clause:

  PARTITION BY method ON (col [USING opclass] [, ...])
  SUBPARTITION BY method ON (col [USING opclass] [, ...])

where 'method' is either RANGE or LIST. 'col' could be either a simple
column reference or a parenthesized expression . 'opclass' is name
of btree operator class to use with the column (compatible with the
type of the column).

Syntax allows specifycing multiple SUBPARTITION BY clauses each of which
specifies the partition key for successively lower level of partition
hierarchy. However, only 2 levels of partitioning will be supported
in upcoming commits that will add support for creating and managing
partitions. So for now, higher level code emits an error if more than one
SUBPARTITION BY clauses are specified. Note that if we find an aggreable
syntax for creating and managing partitions at arbitrary number of levels,
we can remove the aforementioned restriction.

This adds 2 parser nodes viz. PartitionElem and PartitionBy. The former
is similar to IndexElem and the latter encapsulates details of partition
key such as the method and list of PartitionElems. The new member
partitionBy of CreateStmt is simply a list of PartitionBy nodes. The
first member always corresponds to PARTITION BY clause and subsequent
members (if any) to SUBPARTITION BY clause(s).
---
 doc/src/sgml/ref/create_table.sgml |   58 ++++++++++++++++
 src/backend/nodes/copyfuncs.c      |   33 +++++++++
 src/backend/nodes/equalfuncs.c     |   28 ++++++++
 src/backend/nodes/outfuncs.c       |   27 +++++++
 src/backend/parser/gram.y          |  132 ++++++++++++++++++++++++++++++++---
 src/backend/parser/parse_utilcmd.c |   35 ++++++++++
 src/include/nodes/nodes.h          |    2 +
 src/include/nodes/parsenodes.h     |   35 ++++++++++
 src/include/parser/kwlist.h        |    2 +
 src/include/pg_config_manual.h     |    5 ++
 10 files changed, 345 insertions(+), 12 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index cd234db..43725e0 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -28,6 +28,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     [, ... ]
 ] )
 [ INHERITS ( <replaceable>parent_table</replaceable> [, ... ] ) ]
+[ PARTITION BY {RANGE | LIST} ON ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [, ...] )
+    [ SUBPARTITION BY {RANGE | LIST} ON ( { <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> ]
@@ -126,6 +128,21 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
   </para>
 
   <para>
+   The optional partition clause specifies the partition key and sub-partition
+   keys (if any). If specified, an empty partitioned table is created. Data
+   rows added to the table are mapped to and stored in partitions (or sub-
+   partitions thereof) which are created using separate commands. The rows
+   are mapped based on values of the columns specified in the key and partition
+   rules associated with individual partitions. Partitions can be specified
+   to be further divided into sub-partitions by specifying the sub-partition
+   key. In fact, there can be multiple sub-partition keys each of which
+   corresponds to division of partitions on successive levels. When partitions
+   are so sub-partitioned, any rows mapped to a given partition are further
+   mapped to one of its own partitions based on values of the columns in
+   the sub-partition key corrsponding to the partition's level.
+  </para>
+
+  <para>
    To be able to create a table, you must have <literal>USAGE</literal>
    privilege on all column types or the type in the <literal>OF</literal>
    clause, respectively.
@@ -314,6 +331,47 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
    </varlistentry>
 
    <varlistentry>
+    <term><literal>PARTITION BY {RANGE | LIST} ON ( { <replaceable class="parameter">column_name</replaceable> | ( <replaceable class="parameter">expression</replaceable> ) } [ <replaceable class="parameter">opclass</replaceable> ] [, ...] )
+    [ SUBPARTITION BY {RANGE | LIST} ON ( { <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</> clause specifies the partition key
+      of the table. Table thus created is called a partitioned table. The key
+      consists of an ordered list of column names and/or parenthesized
+      expressions when using the <literal>RANGE</> method, whereas only a
+      single column or parenthesized expression can be specified when using
+      the <literal>LIST</> method. The type of a key column or an expresion
+      must have an associated btree operator class or one must be specified
+      along with the column or the expression.
+     </para>
+
+     <para>
+      Partitioned table is divided into sub-tables (called partitions), which
+      in turn, are created using separate commands. The table itself is empty
+      and does not have any storage files. A data row inserted into the table
+      is mapped to and stored in one of its partitions based on the values of
+      columns and/or expressions in the partition key and partition rules
+      associated with the partitions.
+     </para>
+
+     <para>
+      If <literal>SUBPARTITION BY</> clause(s) are specified, any partitions
+      of the table are themselves further divided into partitions using
+      similar rules and so on. Thus a multi-level partition hierarchy can
+      be created. In such a multi-level partitioned table, data is stored
+      in the leaf-level partitions.
+     </para>
+
+     <para>
+      Currently, there are following limitations on definition of partitioned
+      tables: one cannot specify any UNIQUE, PRIMARY KEY, EXCLUDE and/or
+      FOREIGN KEY constraints.
+     </para>
+
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ]</literal></term>
     <listitem>
      <para>
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index a9e9cc3..6b8eed8 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2995,6 +2995,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
 	COPY_NODE_FIELD(relation);
 	COPY_NODE_FIELD(tableElts);
 	COPY_NODE_FIELD(inhRelations);
+	COPY_NODE_FIELD(partitionBy);
 	COPY_NODE_FIELD(ofTypename);
 	COPY_NODE_FIELD(constraints);
 	COPY_NODE_FIELD(options);
@@ -4124,6 +4125,32 @@ _copyAlterPolicyStmt(const AlterPolicyStmt *from)
 	return newnode;
 }
 
+static PartitionBy *
+_copyPartitionBy(const PartitionBy *from)
+{
+
+	PartitionBy *newnode = makeNode(PartitionBy);
+
+	COPY_SCALAR_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(opclass);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *					pg_list.h copy functions
  * ****************************************************************
@@ -5011,6 +5038,12 @@ copyObject(const void *from)
 		case T_RoleSpec:
 			retval = _copyRoleSpec(from);
 			break;
+		case T_PartitionBy:
+			retval = _copyPartitionBy(from);
+			break;
+		case T_PartitionElem:
+			retval = _copyPartitionElem(from);
+			break;
 
 		default:
 			elog(ERROR, "unrecognized node type: %d", (int) nodeTag(from));
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index b9c3959..befff43 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1153,6 +1153,7 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
 	COMPARE_NODE_FIELD(relation);
 	COMPARE_NODE_FIELD(tableElts);
 	COMPARE_NODE_FIELD(inhRelations);
+	COMPARE_NODE_FIELD(partitionBy);
 	COMPARE_NODE_FIELD(ofTypename);
 	COMPARE_NODE_FIELD(constraints);
 	COMPARE_NODE_FIELD(options);
@@ -2594,6 +2595,27 @@ _equalRoleSpec(const RoleSpec *a, const RoleSpec *b)
 	return true;
 }
 
+static bool
+_equalPartitionBy(const PartitionBy *a, const PartitionBy *b)
+{
+	COMPARE_SCALAR_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(opclass);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -3338,6 +3360,12 @@ equal(const void *a, const void *b)
 		case T_RoleSpec:
 			retval = _equalRoleSpec(a, b);
 			break;
+		case T_PartitionBy:
+			retval = _equalPartitionBy(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 28d983c..9baf7cd 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2163,6 +2163,7 @@ _outCreateStmtInfo(StringInfo str, const CreateStmt *node)
 	WRITE_NODE_FIELD(relation);
 	WRITE_NODE_FIELD(tableElts);
 	WRITE_NODE_FIELD(inhRelations);
+	WRITE_NODE_FIELD(partitionBy);
 	WRITE_NODE_FIELD(ofTypename);
 	WRITE_NODE_FIELD(constraints);
 	WRITE_NODE_FIELD(options);
@@ -3011,6 +3012,26 @@ _outConstraint(StringInfo str, const Constraint *node)
 	}
 }
 
+static void
+_outPartitionBy(StringInfo str, const PartitionBy *node)
+{
+	WRITE_NODE_TYPE("PARTITIONBY");
+
+	WRITE_CHAR_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(opclass);
+	WRITE_LOCATION_FIELD(location);
+}
 
 /*
  * _outNode -
@@ -3542,6 +3563,12 @@ _outNode(StringInfo str, const void *obj)
 			case T_XmlSerialize:
 				_outXmlSerialize(str, obj);
 				break;
+			case T_PartitionBy:
+				_outPartitionBy(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 b307b48..6e3471d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -226,6 +226,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	struct ImportQual	*importqual;
 	InsertStmt			*istmt;
 	VariableSetStmt		*vsetstmt;
+	PartitionElem		*pelem;
+	PartitionBy			*partby;
 }
 
 %type <node>	stmt schema_stmt
@@ -537,6 +539,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 <list>	OptPartitionKey PartitionKey
+%type <partby>	PartitionBy SubPartitionBy
+%type <pelem>	part_elem
+%type <list>	part_params
 
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
@@ -601,7 +607,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	KEY
 
 	LABEL LANGUAGE LARGE_P LAST_P LATERAL_P
-	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LISTEN LOAD LOCAL
+	LEADING LEAKPROOF LEAST LEFT LEVEL LIKE LIMIT LIST LISTEN LOAD LOCAL
 	LOCALTIME LOCALTIMESTAMP LOCATION LOCK_P LOCKED LOGGED
 
 	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODE MONTH_P MOVE
@@ -627,8 +633,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
 	SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
 	SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START
-	STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING
-	SYMMETRIC SYSID SYSTEM_P
+	STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBPARTITION
+	SUBSTRING SYMMETRIC SYSID SYSTEM_P
 
 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
 	TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P
@@ -2804,35 +2810,37 @@ copy_generic_opt_arg_list_item:
  *****************************************************************************/
 
 CreateStmt:	CREATE OptTemp TABLE qualified_name '(' OptTableElementList ')'
-			OptInherit OptWith OnCommitOption OptTableSpace
+			OptInherit OptPartitionKey OptWith OnCommitOption OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 					$4->relpersistence = $2;
 					n->relation = $4;
 					n->tableElts = $6;
 					n->inhRelations = $8;
+					n->partitionBy = $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 OptPartitionKey OptWith
+			OnCommitOption OptTableSpace
 				{
 					CreateStmt *n = makeNode(CreateStmt);
 					$7->relpersistence = $2;
 					n->relation = $7;
 					n->tableElts = $9;
 					n->inhRelations = $11;
+					n->partitionBy = $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;
 				}
@@ -3411,6 +3419,104 @@ OptInherit: INHERITS '(' qualified_name_list ')'	{ $$ = $3; }
 			| /*EMPTY*/								{ $$ = NIL; }
 		;
 
+/* Optional partition key definition */
+OptPartitionKey: PartitionKey	{ $$ = $1; }
+			| /*EMPTY*/			{ $$ = NULL; }
+		;
+
+PartitionKey: PartitionBy
+				{
+					List *n = list_make1($1);
+
+					$$ = n;
+				}
+			| PartitionKey SubPartitionBy
+				{
+					List *n = lappend($1, $2);
+
+					$$ = n;
+				}
+		;
+
+PartitionBy: PARTITION BY RANGE ON '(' part_params ')'
+				{
+					PartitionBy *n = makeNode(PartitionBy);
+
+					n->strategy = PARTITION_STRAT_RANGE;
+					n->partParams = $6;
+					n->location = @1;
+
+					$$ = n;
+				}
+			| PARTITION BY LIST ON '(' part_params ')'
+				{
+					PartitionBy *n = makeNode(PartitionBy);
+
+					n->strategy = PARTITION_STRAT_LIST;
+					n->partParams = $6;
+					n->location = @1;
+
+					$$ = n;
+				}
+		;
+
+SubPartitionBy: SUBPARTITION BY RANGE ON '(' part_params ')'
+				{
+					PartitionBy *n = makeNode(PartitionBy);
+
+					n->strategy = PARTITION_STRAT_RANGE;
+					n->partParams = $6;
+					n->location = @1;
+
+					$$ = n;
+				}
+			| SUBPARTITION BY LIST ON '(' part_params ')'
+				{
+					PartitionBy *n = makeNode(PartitionBy);
+
+					n->strategy = PARTITION_STRAT_LIST;
+					n->partParams = $6;
+					n->location = @1;
+
+					$$ = n;
+				}
+		;
+
+part_params:	part_elem						{ $$ = list_make1($1); }
+			| part_params ',' part_elem			{ $$ = lappend($1, $3); }
+		;
+
+part_elem: ColId opt_class
+				{
+					PartitionElem *n = makeNode(PartitionElem);
+
+					n->name = $1;
+					n->expr = NULL;
+					n->opclass = $2;
+					n->location = @1;
+					$$ = n;
+				}
+			| func_expr_windowless opt_class
+				{
+					PartitionElem *n = makeNode(PartitionElem);
+
+					n->name = NULL;
+					n->expr = $1;
+					n->opclass = $2;
+					n->location = @1;
+					$$ = n;
+				}
+			| '(' a_expr ')' opt_class
+				{
+					PartitionElem *n = makeNode(PartitionElem);
+
+					n->name = NULL;
+					n->expr = $2;
+					n->opclass = $4;
+					n->location = @1;
+					$$ = n;
+				}
+		;
 /* WITH (options) is preferred, WITH OIDS and WITHOUT OIDS are legacy forms */
 OptWith:
 			WITH reloptions				{ $$ = $2; }
@@ -13767,6 +13873,7 @@ unreserved_keyword:
 			| LAST_P
 			| LEAKPROOF
 			| LEVEL
+			| LIST
 			| LISTEN
 			| LOAD
 			| LOCAL
@@ -13873,6 +13980,7 @@ unreserved_keyword:
 			| STORAGE
 			| STRICT_P
 			| STRIP_P
+			| SUBPARTITION
 			| SYSID
 			| SYSTEM_P
 			| TABLES
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index dc431c7..54efa35 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -205,6 +205,41 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 		&& stmt->relation->relpersistence != RELPERSISTENCE_TEMP)
 		stmt->relation->schemaname = get_namespace_name(namespaceid);
 
+	if (stmt->partitionBy)
+	{
+		ListCell   *cell;
+		int			keyidx = 0;
+
+		if (list_length(stmt->partitionBy) > 2)
+			ereport(ERROR,
+					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+					 errmsg("specifying more than one SUBPARTITION BY clause"
+							" is not supported")));
+
+		foreach(cell, stmt->partitionBy)
+		{
+			PartitionBy *partby = (PartitionBy *) lfirst(cell);
+			int		partnatts = list_length(partby->partParams);
+
+			if (partnatts > PARTITION_MAX_KEYS)
+				ereport(ERROR,
+					(errcode(ERRCODE_TOO_MANY_COLUMNS),
+					 errmsg("cannot use more than %d columns in the %s key",
+							PARTITION_MAX_KEYS,
+							keyidx > 0 ? "sub-partition" : "partition")));
+
+			if (partby->strategy == PARTITION_STRAT_LIST &&
+				partnatts > 1)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("cannot use more than one column in the %s key",
+							keyidx > 0 ? "sub-partition" : "partition"),
+					 errdetail("Only one column allowed with list partitioning.")));
+
+			keyidx++;
+		}
+	}
+
 	/* Set up CreateStmtContext */
 	cxt.pstate = pstate;
 	if (IsA(stmt, CreateForeignTableStmt))
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c407fa2..825f7d9 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -433,6 +433,8 @@ typedef enum NodeTag
 	T_OnConflictClause,
 	T_CommonTableExpr,
 	T_RoleSpec,
+	T_PartitionElem,
+	T_PartitionBy,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 2fd0629..130410c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -696,6 +696,40 @@ 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	   *opclass;	/* name of desired opclass; NIL = default */
+	int			location;	/* token location, or -1 if unknown */
+} PartitionElem;
+
+/*
+ * PartitionBy - partition key definition including the strategy
+ *
+ *	'strategy'		partition strategy to use (one of the below defined)
+ *	'partParams'	List of PartitionElems, one for each key column
+ */
+#define PARTITION_STRAT_LIST	'l'
+#define PARTITION_STRAT_RANGE	'r'
+
+typedef struct PartitionBy
+{
+	NodeTag		type;
+	char		strategy;
+	List	   *partParams;
+	int			location;	/* token location, or -1 if unknown */
+} PartitionBy;
 
 /****************************************************************************
  *	Nodes for a Query tree
@@ -1749,6 +1783,7 @@ typedef struct CreateStmt
 	List	   *tableElts;		/* column definitions (list of ColumnDef) */
 	List	   *inhRelations;	/* relations to inherit from (list of
 								 * inhRelation) */
+	List	   *partitionBy;
 	TypeName   *ofTypename;		/* OF typename */
 	List	   *constraints;	/* constraints (list of Constraint nodes) */
 	List	   *options;		/* options from WITH clause */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 6e1e820..38c7afe 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -226,6 +226,7 @@ PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
 PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
+PG_KEYWORD("list", LIST, UNRESERVED_KEYWORD)
 PG_KEYWORD("listen", LISTEN, UNRESERVED_KEYWORD)
 PG_KEYWORD("load", LOAD, UNRESERVED_KEYWORD)
 PG_KEYWORD("local", LOCAL, UNRESERVED_KEYWORD)
@@ -367,6 +368,7 @@ PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD)
 PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD)
 PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("subpartition", SUBPARTITION, UNRESERVED_KEYWORD)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
 PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD)
 PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD)
diff --git a/src/include/pg_config_manual.h b/src/include/pg_config_manual.h
index ef89521..ac40571 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 (XXX - arbitrary)
+ */
+#define PARTITION_MAX_KEYS	32
+
+/*
  * Set the upper and lower bounds of sequence values.
  */
 #define SEQ_MAXVALUE	PG_INT64_MAX
-- 
1.7.1

