>From a136d87b072a40c045d16b74453ec30387d8c783 Mon Sep 17 00:00:00 2001
From: amit <amitlangote09@gmail.com>
Date: Tue, 9 Feb 2016 10:55:48 +0900
Subject: [PATCH 06/10] Add partition creation/management syntax.

This adds following ALTER TABLE commands for adding/dropping/attaching/
detaching partitions and sub-partitions to partitioned tables:

  ADD PARTITION name FOR VALUES .. [ WITH (..) ] [ TABLESPACE .. ]
  MODIFY PARTITION partname ADD SUBPARTITION FOR VALUES ..
    [ WITH (..) ] [ TABLESPACE .. ]
  DROP PARTITION partname
  DROP SUBPARTITION subpartname
  ATTACH PARTITION name FOR VALUES .. USING [ TABLE ] table_name
    [ WITH (..) ] [ TABLESPACE .. ]
  MODIFY PARTITION partname ATTACH SUBPARTITION name FOR VALUES ..
    USING [ TABLE ] table_name [ WITH (..) ] [ TABLESPACE .. ]
  DETACH PARTITION partname [ USING [ TABLE ] table_name ]
  DROP SUBPARTITION subpartname [ USING [ TABLE ] table_name ]

Two of these commands (ATTACH PARTITION/SUBPARTITION) will not be
implemented right away and will output an "feature not supproted"
error message.

The commands don't yet actually create partitions/sub-partitions; that
will have to wait the next commit which will add infrastructure to add
partition info to the catalog.

Adds LESS, THAN, MODIFY as unreserved keywords and changes PARTITION
and SUBPARTITION from unreserved to reserved keywords.
---
 doc/src/sgml/ref/alter_table.sgml  |  107 +++++++++++++++++++
 src/backend/commands/tablecmds.c   |   32 ++++++
 src/backend/nodes/copyfuncs.c      |   34 ++++++
 src/backend/nodes/equalfuncs.c     |   30 +++++
 src/backend/nodes/outfuncs.c       |    1 +
 src/backend/parser/gram.y          |  188 +++++++++++++++++++++++++++++++--
 src/backend/parser/parse_agg.c     |   16 +++-
 src/backend/parser/parse_expr.c    |   11 ++
 src/backend/parser/parse_utilcmd.c |  206 ++++++++++++++++++++++++++++++++++++
 src/include/nodes/nodes.h          |    2 +
 src/include/nodes/parsenodes.h     |   37 ++++++-
 src/include/parser/kwlist.h        |    9 ++-
 src/include/parser/parse_node.h    |    3 +-
 13 files changed, 661 insertions(+), 15 deletions(-)

diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml
index aca40f5..e8067cb 100644
--- a/doc/src/sgml/ref/alter_table.sgml
+++ b/doc/src/sgml/ref/alter_table.sgml
@@ -77,12 +77,25 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
     NOT OF
     OWNER TO { <replaceable class="PARAMETER">new_owner</replaceable> | CURRENT_USER | SESSION_USER }
     REPLICA IDENTITY { DEFAULT | USING INDEX <replaceable class="PARAMETER">index_name</replaceable> | FULL | NOTHING }
+    ADD PARTITION <replaceable class="PARAMETER">name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )] [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+    MODIFY PARTITION <replaceable class="PARAMETER">partition_name</replaceable> ADD SUBPARTITION <replaceable class="PARAMETER">name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )] [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+    DROP PARTITION <replaceable class="PARAMETER">name</replaceable>
+    DROP SUBPARTITION <replaceable class="PARAMETER">name</replaceable>
+    ATTACH PARTITION <replaceable class="PARAMETER">name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> USING [ TABLE ] <replaceable class="PARAMETER">table_name</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )] [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+    MODIFY PARTITION <replaceable class="PARAMETER">partition_name</replaceable> ATTACH SUBPARTITION <replaceable class="PARAMETER">name</replaceable> FOR VALUES <replaceable class="PARAMETER">partition_bound_spec</replaceable> USING [ TABLE ] <replaceable class="PARAMETER">table_name</replaceable> [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable> [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] )] [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ]
+    DETACH PARTITION <replaceable class="PARAMETER">name</replaceable> [ USING [ TABLE ] <replaceable class="PARAMETER">table_name</replaceable>]
+    DETACH SUBPARTITION <replaceable class="PARAMETER">name</replaceable> [ USING [ TABLE ] <replaceable class="PARAMETER">table_name</replaceable>]
 
 <phrase>and <replaceable class="PARAMETER">table_constraint_using_index</replaceable> is:</phrase>
 
     [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
     { UNIQUE | PRIMARY KEY } USING INDEX <replaceable class="PARAMETER">index_name</replaceable>
     [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
+
+<phrase>and <replaceable class="PARAMETER">partition_bound_spec</replaceable> is:</phrase>
+    FOR VALUES [ IN ] ( <replaceable class="PARAMETER">expression</replaceable> [, ...] )
+    FOR VALUES LESS THAN ( <replaceable class="PARAMETER">expression</replaceable> [, ...] )
+
 </synopsis>
  </refsynopsisdiv>
 
@@ -704,6 +717,100 @@ ALTER TABLE ALL IN TABLESPACE <replaceable class="PARAMETER">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><literal>ADD PARTITION</literal></term>
+    <listitem>
+     <para>
+      This form adds named partition to a partitioned table. The <literal>
+      FOR VALUES </literal> specification must match the partition key
+      of the partitioned table. 
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>ADD SUBPARTITION</literal></term>
+    <listitem>
+     <para>
+      This form adds named sub-partition to specified partition of a
+      partitioned table. The <literal> FOR VALUES </literal> specification
+      must match the corresponding sub-partition key of the partitioned table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP PARTITION</literal></term>
+    <listitem>
+     <para>
+      This form drops specified partition of a partitioned table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DROP SUBPARTITION</literal></term>
+    <listitem>
+     <para>
+      This form drops specified sub-partition of a partitioned table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>ATTACH PARTITION USING TABLE</literal></term>
+    <listitem>
+     <para>
+      This form attaches a specified non-partitioned table as named partition
+      of a partitioned table provided it is not sub-partitioned. The <literal>
+      FOR VALUES </literal> specifiation must match the partition key of the
+      partitioned table.
+     </para>
+     <para>
+      There are a number of restrictions on the composition of source table
+      for it to be a candidate for attaching as a partition.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>ATTACH SUBPARTITION USING TABLE</literal></term>
+    <listitem>
+     <para>
+      This form attaches a specified non-partitioned table as named sub-partition
+      of specified partition of a partitioned table provided it is sub-partitioned.
+      The <literal> FOR VALUES </literal> specifiation must match the partition
+      key of the partitioned table.
+     </para>
+     <para>
+      There are a number of restrictions on the composition of source table
+      for it to be a candidate for attaching as a partition.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DETACH PARTITION</literal></term>
+    <listitem>
+     <para>
+      This form detaches specified partition of a partitioned table.
+      Optionally, one can specifiy target_name to rename the no longer
+      partition to.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>DETACH SUBPARTITION</literal></term>
+    <listitem>
+     <para>
+      This form detaches specified sub-partition of a partitioned table.
+      Optionally, one can specifiy target_name to rename the no longer
+      sub-partition to.
+     </para>
+    </listitem>
+   </varlistentry>
+
   </variablelist>
   </para>
 
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 674fbe5..7bdbdf0 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -3117,6 +3117,17 @@ AlterTableGetLockLevel(List *cmds)
 				cmd_lockmode = AlterTableGetRelOptionsLockLevel((List *) cmd->def);
 				break;
 
+			case AT_AddPartition:
+			case AT_AddSubPartition:
+			case AT_DropPartition:
+			case AT_DropSubPartition:
+			case AT_AttachPartition:
+			case AT_AttachSubPartition:
+			case AT_DetachPartition:
+			case AT_DetachSubPartition:
+				cmd_lockmode = AccessExclusiveLock;
+				break;
+
 			default:			/* oops */
 				elog(ERROR, "unrecognized alter table type: %d",
 					 (int) cmd->subtype);
@@ -3434,6 +3445,18 @@ ATPrepCmd(List **wqueue, Relation rel, AlterTableCmd *cmd,
 			/* No command-specific prep needed */
 			pass = AT_PASS_MISC;
 			break;
+		case AT_AddPartition:
+		case AT_AddSubPartition:
+		case AT_DropPartition:
+		case AT_DropSubPartition:
+		case AT_AttachPartition:
+		case AT_AttachSubPartition:
+		case AT_DetachPartition:
+		case AT_DetachSubPartition:
+			ATSimplePermissions(rel, ATT_TABLE);
+			/* No command-specific prep needed */
+			pass = AT_PASS_MISC;
+			break;
 		default:				/* oops */
 			elog(ERROR, "unrecognized alter table type: %d",
 				 (int) cmd->subtype);
@@ -3757,6 +3780,15 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
 		case AT_GenericOptions:
 			ATExecGenericOptions(rel, (List *) cmd->def);
 			break;
+		case AT_AddPartition:
+		case AT_AddSubPartition:
+		case AT_DropPartition:
+		case AT_DropSubPartition:
+		case AT_AttachPartition:
+		case AT_AttachSubPartition:
+		case AT_DetachPartition:
+		case AT_DetachSubPartition:
+			break;
 		default:				/* oops */
 			elog(ERROR, "unrecognized alter table type: %d",
 				 (int) cmd->subtype);
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6b8eed8..aabe108 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -3002,6 +3002,7 @@ CopyCreateStmtFields(const CreateStmt *from, CreateStmt *newnode)
 	COPY_SCALAR_FIELD(oncommit);
 	COPY_STRING_FIELD(tablespacename);
 	COPY_SCALAR_FIELD(if_not_exists);
+	COPY_SCALAR_FIELD(is_internal_partition);
 }
 
 static CreateStmt *
@@ -4151,6 +4152,33 @@ _copyPartitionElem(const PartitionElem *from)
 	return newnode;
 }
 
+static PartitionStmt *
+_copyPartitionStmt(const PartitionStmt *from)
+{
+	PartitionStmt *newnode = makeNode(PartitionStmt);
+
+	COPY_NODE_FIELD(name);
+	COPY_NODE_FIELD(parent);
+	COPY_NODE_FIELD(using_table);
+	COPY_NODE_FIELD(values);
+	COPY_NODE_FIELD(options);
+	COPY_STRING_FIELD(tablespacename);
+
+	return newnode;
+}
+
+static PartitionValues *
+_copyPartitionValues(const PartitionValues *from)
+{
+	PartitionValues *newnode = makeNode(PartitionValues);
+
+	COPY_NODE_FIELD(listvalues);
+	COPY_NODE_FIELD(rangemaxs);
+	COPY_LOCATION_FIELD(location);
+
+	return newnode;
+}
+
 /* ****************************************************************
  *					pg_list.h copy functions
  * ****************************************************************
@@ -5044,6 +5072,12 @@ copyObject(const void *from)
 		case T_PartitionElem:
 			retval = _copyPartitionElem(from);
 			break;
+		case T_PartitionStmt:
+			retval = _copyPartitionStmt(from);
+			break;
+		case T_PartitionValues:
+			retval = _copyPartitionValues(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 befff43..cfb5db6 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -1160,6 +1160,7 @@ _equalCreateStmt(const CreateStmt *a, const CreateStmt *b)
 	COMPARE_SCALAR_FIELD(oncommit);
 	COMPARE_STRING_FIELD(tablespacename);
 	COMPARE_SCALAR_FIELD(if_not_exists);
+	COMPARE_SCALAR_FIELD(is_internal_partition);
 
 	return true;
 }
@@ -2616,6 +2617,29 @@ _equalPartitionElem(const PartitionElem *a, const PartitionElem *b)
 	return true;
 }
 
+static bool
+_equalPartitionStmt(const PartitionStmt *a, const PartitionStmt *b)
+{
+	COMPARE_NODE_FIELD(name);
+	COMPARE_NODE_FIELD(parent);
+	COMPARE_NODE_FIELD(using_table);
+	COMPARE_NODE_FIELD(values);
+	COMPARE_NODE_FIELD(options);
+	COMPARE_STRING_FIELD(tablespacename);
+
+	return true;
+}
+
+static bool
+_equalPartitionValues(const PartitionValues *a, const PartitionValues *b)
+{
+	COMPARE_NODE_FIELD(listvalues);
+	COMPARE_NODE_FIELD(rangemaxs);
+	COMPARE_LOCATION_FIELD(location);
+
+	return true;
+}
+
 /*
  * Stuff from pg_list.h
  */
@@ -3366,6 +3390,12 @@ equal(const void *a, const void *b)
 		case T_PartitionElem:
 			retval = _equalPartitionElem(a, b);
 			break;
+		case T_PartitionStmt:
+			retval = _equalPartitionStmt(a, b);
+			break;
+		case T_PartitionValues:
+			retval = _equalPartitionValues(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 9baf7cd..00b449d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2170,6 +2170,7 @@ _outCreateStmtInfo(StringInfo str, const CreateStmt *node)
 	WRITE_ENUM_FIELD(oncommit, OnCommitAction);
 	WRITE_STRING_FIELD(tablespacename);
 	WRITE_BOOL_FIELD(if_not_exists);
+	WRITE_BOOL_FIELD(is_internal_partition);
 }
 
 static void
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 6e3471d..902bd40 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -226,8 +226,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	struct ImportQual	*importqual;
 	InsertStmt			*istmt;
 	VariableSetStmt		*vsetstmt;
-	PartitionElem		*pelem;
+	PartitionElem		*partelem;
 	PartitionBy			*partby;
+	PartitionValues		*partvalues;
 }
 
 %type <node>	stmt schema_stmt
@@ -541,8 +542,11 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <boolean> opt_if_not_exists
 %type <list>	OptPartitionKey PartitionKey
 %type <partby>	PartitionBy SubPartitionBy
-%type <pelem>	part_elem
-%type <list>	part_params
+%type <partelem>	part_elem
+%type <list>		part_params
+%type <partvalues>	ForValues
+%type <list>		ValuesList
+%type <range>		using_table opt_using_table
 
 /*
  * Non-keyword token types.  These are hard-wired into the "flex" lexer.
@@ -568,7 +572,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 /* ordinary key words in alphabetical order */
 %token <keyword> ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER
 	AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC
-	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION
+	ASSERTION ASSIGNMENT ASYMMETRIC AT ATTACH ATTRIBUTE AUTHORIZATION
 
 	BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
 	BOOLEAN_P BOTH BY
@@ -583,7 +587,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
 
 	DATA_P DATABASE DAY_P DEALLOCATE DEC DECIMAL_P DECLARE DEFAULT DEFAULTS
-	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC
+	DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC DETACH
 	DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
 
 	EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT
@@ -607,10 +611,11 @@ 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 LIST LISTEN LOAD LOCAL
+	LEADING LEAKPROOF LEAST LEFT LESS 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
+	MAPPING MATCH MATERIALIZED MAXVALUE MINUTE_P MINVALUE MODIFY MODE MONTH_P
+	MOVE
 
 	NAME_P NAMES NATIONAL NATURAL NCHAR NEXT NO NONE
 	NOT NOTHING NOTIFY NOTNULL NOWAIT NULL_P NULLIF
@@ -636,7 +641,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 	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
+	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THAN THEN
 	TIME TIMESTAMP TO TRAILING TRANSACTION TRANSFORM TREAT TRIGGER TRIM TRUE_P
 	TRUNCATE TRUSTED TYPE_P TYPES_P
 
@@ -2370,6 +2375,124 @@ alter_table_cmd:
 					n->def = (Node *)$1;
 					$$ = (Node *) n;
 				}
+			/* ALTER TABLE <name> ADD PARTITION */
+			| ADD_P PARTITION qualified_name ForValues OptWithPartition
+			  OptTableSpace
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_AddPartition;
+					stmt->name = $3;
+					stmt->values = $4;
+					stmt->options = $5;
+					stmt->tablespacename = $6;
+					n->def = (Node *) stmt;
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> MODIFY PARTITION ADD SUBPARTITION */
+			| MODIFY PARTITION qualified_name ADD_P SUBPARTITION qualified_name
+			  ForValues OptWithPartition OptTableSpace
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_AddSubPartition;
+					stmt->name = $6;
+					stmt->parent = $3;
+					stmt->values = $7;
+					stmt->options = $8;
+					stmt->tablespacename = $9;
+					n->def = (Node *) stmt;
+
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> DROP PARTITION */
+			| DROP PARTITION qualified_name
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropPartition;
+					n->def = (Node *) $3;
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> DROP SUBPARTITION */
+			| DROP SUBPARTITION qualified_name
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					n->subtype = AT_DropSubPartition;
+					n->def = (Node *) $3;
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> ATTACH PARTITION USING TABLE */
+			| ATTACH PARTITION qualified_name ForValues using_table OptWithPartition
+			  OptTableSpace
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_AttachPartition;
+					stmt->name = $3;
+					stmt->values = $4;
+					stmt->using_table = $5;
+					stmt->options = $6;
+					stmt->tablespacename = $7;
+					n->def = (Node *) stmt;
+
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> MODIFY PARTITION ATTACH SUBPARTITION USING TABLE */
+			| MODIFY PARTITION qualified_name ATTACH SUBPARTITION qualified_name
+			  ForValues using_table OptWithPartition OptTableSpace
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_AttachSubPartition;
+					stmt->name = $6;
+					stmt->parent = $3;
+					stmt->values = $7;
+					stmt->using_table = $8;
+					stmt->options = $9;
+					stmt->tablespacename = $10;
+					n->def = (Node *) stmt;
+
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> DETACH PARTITION [ USING TABLE ] */
+			| DETACH PARTITION qualified_name opt_using_table
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_DetachPartition;
+					stmt->name = $3;
+					stmt->using_table = $4;
+					n->def = (Node *) stmt;
+
+					$$ = (Node *) n;
+				}
+			/* ALTER TABLE <name> DETACH SUBPARTITION [ USING TABLE ] */
+			| DETACH SUBPARTITION qualified_name opt_using_table
+				{
+					AlterTableCmd *n = makeNode(AlterTableCmd);
+					PartitionStmt *stmt = makeNode(PartitionStmt);
+
+					n->subtype = AT_DetachSubPartition;
+					stmt->name = $3;
+					stmt->using_table = $4;
+					n->def = (Node *) stmt;
+
+					$$ = (Node *) n;
+				}
+		;
+
+opt_using_table:
+			using_table							{ $$ = $1; }
+			| /* EMPTY */						{ $$ = NULL; }
+		;
+using_table:
+			/* USING [ TABLE ] qualified_name */
+			USING opt_table qualified_name		{ $$ = $3; }
 		;
 
 alter_column_default:
@@ -2465,6 +2588,40 @@ reloption_elem:
 				}
 		;
 
+ForValues:
+			/* LIST partition */
+			FOR VALUES opt_in '(' ValuesList ')'
+				{
+					PartitionValues *n = makeNode(PartitionValues);
+
+					n->listvalues = $5;
+					n->rangemaxs = NIL;
+					n->location = @1;
+
+					$$ = n;
+				}
+
+			/* RANGE partition */
+			| FOR VALUES LESS THAN '(' ValuesList ')'
+				{
+					PartitionValues *n = makeNode(PartitionValues);
+
+					n->listvalues = NIL;
+					n->rangemaxs = $6;
+					n->location = @1;
+
+					$$ = n;
+				}
+		;
+
+opt_in:		IN_P				{}
+			|   /* EMPTY */		{}
+		;
+
+ValuesList:
+			a_expr						{ $$ = list_make1($1); }
+			| ValuesList ',' a_expr		{ $$ = lappend($1, $3); }
+		;
 
 /*****************************************************************************
  *
@@ -3525,6 +3682,12 @@ OptWith:
 			| /*EMPTY*/					{ $$ = NIL; }
 		;
 
+/* WITH (options) only for partitions */
+OptWithPartition:
+			WITH reloptions				{ $$ = $2; }
+			| /*EMPTY*/					{ $$ = NIL; }
+		;
+
 OnCommitOption:  ON COMMIT DROP				{ $$ = ONCOMMIT_DROP; }
 			| ON COMMIT DELETE_P ROWS		{ $$ = ONCOMMIT_DELETE_ROWS; }
 			| ON COMMIT PRESERVE ROWS		{ $$ = ONCOMMIT_PRESERVE_ROWS; }
@@ -13767,6 +13930,7 @@ unreserved_keyword:
 			| ASSERTION
 			| ASSIGNMENT
 			| AT
+			| ATTACH
 			| ATTRIBUTE
 			| BACKWARD
 			| BEFORE
@@ -13812,6 +13976,7 @@ unreserved_keyword:
 			| DELETE_P
 			| DELIMITER
 			| DELIMITERS
+			| DETACH
 			| DICTIONARY
 			| DISABLE_P
 			| DISCARD
@@ -13872,6 +14037,7 @@ unreserved_keyword:
 			| LARGE_P
 			| LAST_P
 			| LEAKPROOF
+			| LESS
 			| LEVEL
 			| LIST
 			| LISTEN
@@ -13888,6 +14054,7 @@ unreserved_keyword:
 			| MINUTE_P
 			| MINVALUE
 			| MODE
+			| MODIFY
 			| MONTH_P
 			| MOVE
 			| NAME_P
@@ -13912,7 +14079,6 @@ unreserved_keyword:
 			| PARALLEL
 			| PARSER
 			| PARTIAL
-			| PARTITION
 			| PASSING
 			| PASSWORD
 			| PLANS
@@ -13980,7 +14146,6 @@ unreserved_keyword:
 			| STORAGE
 			| STRICT_P
 			| STRIP_P
-			| SUBPARTITION
 			| SYSID
 			| SYSTEM_P
 			| TABLES
@@ -13989,6 +14154,7 @@ unreserved_keyword:
 			| TEMPLATE
 			| TEMPORARY
 			| TEXT_P
+			| THAN
 			| TRANSACTION
 			| TRANSFORM
 			| TRIGGER
@@ -14185,6 +14351,7 @@ reserved_keyword:
 			| ONLY
 			| OR
 			| ORDER
+			| PARTITION
 			| PLACING
 			| PRIMARY
 			| REFERENCES
@@ -14192,6 +14359,7 @@ reserved_keyword:
 			| SELECT
 			| SESSION_USER
 			| SOME
+			| SUBPARTITION
 			| SYMMETRIC
 			| TABLE
 			| THEN
diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c
index 3d465c0..5420b20 100644
--- a/src/backend/parser/parse_agg.c
+++ b/src/backend/parser/parse_agg.c
@@ -458,7 +458,11 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
 
 			break;
 		case EXPR_KIND_PARTKEY_EXPRESSION:
-			err = _("aggregate functions are not allowed in partition key expressions");
+			if (isAgg)
+				err = _("aggregate functions are not allowed in partition key expressions");
+			else
+				err = _("grouping operations are not allowed in partition key expressions");
+
 			break;
 		case EXPR_KIND_INDEX_PREDICATE:
 			if (isAgg)
@@ -488,6 +492,13 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
 				err = _("grouping operations are not allowed in trigger WHEN conditions");
 
 			break;
+		case EXPR_KIND_PARTITION_VALUE:
+			if (isAgg)
+				err = _("aggregate functions are not allowed in partition value expressions");
+			else
+				err = _("grouping operations are not allowed in partition value expressions");
+
+			break;
 
 			/*
 			 * There is intentionally no default: case here, so that the
@@ -848,6 +859,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_VALUE:
+			err = _("window functions are not allowed in partition value expressions");
+			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 b115f66..49fca98 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -514,6 +514,12 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
 			return node;
 	}
 
+	if (pstate->p_expr_kind == EXPR_KIND_PARTITION_VALUE)
+		ereport(ERROR,
+				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+				 errmsg("cannot use column reference in partition value expression"),
+				 parser_errposition(pstate, cref->location)));
+
 	/*----------
 	 * The allowed syntaxes are:
 	 *
@@ -1718,6 +1724,9 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
 		case EXPR_KIND_TRIGGER_WHEN:
 			err = _("cannot use subquery in trigger WHEN condition");
 			break;
+		case EXPR_KIND_PARTITION_VALUE:
+			err = _("cannot use subquery in partition value expressions");
+			break;
 
 			/*
 			 * There is intentionally no default: case here, so that the
@@ -3230,6 +3239,8 @@ ParseExprKindName(ParseExprKind exprKind)
 			return "WHEN";
 		case EXPR_KIND_PARTKEY_EXPRESSION:
 			return "partition key expression";
+		case EXPR_KIND_PARTITION_VALUE:
+			return "partition value expression";
 
 			/*
 			 * 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 02655da..ce8ed33 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -49,6 +49,7 @@
 #include "nodes/nodeFuncs.h"
 #include "parser/analyze.h"
 #include "parser/parse_clause.h"
+#include "parser/parse_coerce.h"
 #include "parser/parse_collate.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
@@ -130,6 +131,10 @@ static void transformConstraintAttrs(CreateStmtContext *cxt,
 						 List *constraintList);
 static void transformColumnType(CreateStmtContext *cxt, ColumnDef *column);
 static void setSchemaName(char *context_schema, char **stmt_schema_name);
+static void transformAddPartition(CreateStmtContext *cxt, PartitionStmt *stmt);
+static PartitionValues *transformPartitionValues(CreateStmtContext *cxt,
+						PartitionKey key,
+						PartitionValues *values);
 
 
 /*
@@ -2660,6 +2665,41 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
 					break;
 				}
 
+			case AT_AddPartition:
+			case AT_AddSubPartition:
+				transformAddPartition(&cxt, (PartitionStmt *) cmd->def);
+				newcmds = lappend(newcmds, cmd);
+				break;
+			case AT_DropPartition:
+			case AT_DropSubPartition:
+				break;
+			case AT_AttachPartition:
+			case AT_AttachSubPartition:
+				ereport(ERROR,
+						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+						 errmsg("ATTACH PARTITION/SUBPARTITION USING TABLE not implemented")));
+				break;
+			case AT_DetachPartition:
+			case AT_DetachSubPartition:
+				{
+					PartitionStmt *partstmt = (PartitionStmt *) cmd->def;
+
+					if (partstmt->using_table)
+					{
+						RenameStmt *stmt = makeNode(RenameStmt);
+
+						stmt->renameType = OBJECT_TABLE;
+						stmt->relation = partstmt->name;
+						stmt->newname = partstmt->using_table->relname;
+						stmt->missing_ok = false;
+
+						cxt.alist = lappend(cxt.alist, stmt);
+					}
+
+					newcmds = lappend(newcmds, cmd);
+					break;
+				}
+
 			default:
 				newcmds = lappend(newcmds, cmd);
 				break;
@@ -3024,3 +3064,169 @@ setSchemaName(char *context_schema, char **stmt_schema_name)
 						"different from the one being created (%s)",
 						*stmt_schema_name, context_schema)));
 }
+
+/*
+ * transformAddPartition
+ *		Transforms ADD PARTITION / SUBPARTITION
+ *
+ * Note that cxt->relation corresponds to the root partitioned table.
+ */
+static void
+transformAddPartition(CreateStmtContext *cxt, PartitionStmt *stmt)
+{
+	Relation		parentRel;
+	bool			is_subpartition = stmt->parent != NULL;
+	PartitionKey	key;
+
+	/* Check if the target table is partitioned at all */
+	if (cxt->rel->rd_partkeys == NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("\"%s\" is not partitioned", cxt->relation->relname)));
+
+	/* If adding a sub-partition, check if we can do so */
+	if (is_subpartition && list_length(cxt->rel->rd_partkeys) < 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("cannot add sub-partition"),
+				 errdetail("\"%s\" is not sub-partitioned", cxt->relation->relname)));
+
+	if (stmt->parent)
+	{
+		parentRel = heap_openrv_extended(stmt->parent, NoLock, true);
+
+		if (parentRel == NULL)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("partition \"%s\" of \"%s\" does not exist",
+							stmt->parent->relname, cxt->relation->relname),
+						parser_errposition(cxt->pstate, stmt->parent->location)));
+	}
+	else
+		parentRel = heap_openrv(cxt->relation, NoLock);
+
+	/* tranform FOR VALUES clause */
+	key = !is_subpartition ? linitial(cxt->rel->rd_partkeys)
+									: lsecond(cxt->rel->rd_partkeys);
+	stmt->values = transformPartitionValues(cxt, key, stmt->values);
+
+	heap_close(parentRel, NoLock);
+}
+
+/*
+ * transformPartitionValues
+ *
+ * Transform partition value as returned by the grammar into something
+ * we can evaluate to store into pg_partition.
+ */
+static PartitionValues *
+transformPartitionValues(CreateStmtContext *cxt, PartitionKey key,
+						 PartitionValues *values)
+{
+	int			i;
+	ListCell   *cell;
+	PartitionKeyTypeInfo *typinfo;
+	PartitionValues		*result = (PartitionValues *)
+											makeNode(PartitionValues);
+
+	typinfo = get_partition_key_type_info(cxt->rel, key);
+
+	switch (key->strategy)
+	{
+		case PARTITION_STRAT_LIST:
+			if (!values->listvalues)
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+						 errmsg("invalid FOR VALUES specification for a list partition"),
+						 parser_errposition(cxt->pstate, values->location)));
+
+			foreach(cell, values->listvalues)
+			{
+				Node   *value = (Node *) lfirst(cell);
+				Node   *orig_value = value;
+				Oid		valuetype;
+
+				value = transformExpr(cxt->pstate, value,
+										EXPR_KIND_PARTITION_VALUE);
+				valuetype = exprType(value);
+				value = coerce_to_target_type(cxt->pstate,
+											value, valuetype,
+											typinfo->typid[0],
+											typinfo->typmod[0],
+											COERCION_ASSIGNMENT,
+											COERCE_IMPLICIT_CAST,
+											-1);
+				if (value == NULL)
+					ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("partition key column is of type %s"
+								" but expression is of type %s",
+									format_type_be(typinfo->typid[0]),
+									format_type_be(valuetype)),
+						 errhint("You will need to rewrite or cast the expression."),
+						 parser_errposition(cxt->pstate, exprLocation(orig_value))));
+
+				result->listvalues = lappend(result->listvalues, value);
+			}
+			break;
+
+		case PARTITION_STRAT_RANGE:
+			if (!values->rangemaxs)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("invalid FOR VALUES specification for a range partition"),
+					 parser_errposition(cxt->pstate, values->location)));
+
+			if (list_length(values->rangemaxs) > key->partnatts)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("range partition FOR VALUES specification has more"
+							" expressions than columns in the partition key"),
+							parser_errposition(cxt->pstate,
+									exprLocation(list_nth(values->rangemaxs,
+												 key->partnatts)))));
+			else if (list_length(values->rangemaxs) < key->partnatts)
+				ereport(ERROR,
+					(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+					 errmsg("range partition FOR VALUES specification has fewer"
+							" expressions than columns in the partition key"),
+							parser_errposition(cxt->pstate,
+									exprLocation(list_nth(values->rangemaxs,
+												 list_length(values->rangemaxs))))));
+			i = 0;
+			foreach(cell, values->rangemaxs)
+			{
+				Node   *value = (Node *) lfirst(cell);
+				Node   *orig_value = value;
+				Oid		valuetype;
+
+				value = transformExpr(cxt->pstate, value,
+										EXPR_KIND_PARTITION_VALUE);
+				valuetype = exprType(value);
+				value = coerce_to_target_type(NULL,
+											value, valuetype,
+											typinfo->typid[i],
+											typinfo->typmod[i],
+											COERCION_ASSIGNMENT,
+											COERCE_IMPLICIT_CAST,
+											-1);
+				if (value == NULL)
+					ereport(ERROR,
+						(errcode(ERRCODE_DATATYPE_MISMATCH),
+						 errmsg("partition key column is of type %s"
+								" but expression is of type %s",
+									format_type_be(typinfo->typid[0]),
+									format_type_be(valuetype)),
+						 errhint("You will need to rewrite or cast the expression."),
+						 parser_errposition(cxt->pstate, exprLocation(orig_value))));
+
+				result->rangemaxs = lappend(result->rangemaxs, value);
+				++i;
+			}
+			break;
+	}
+
+	result->location = values->location;
+
+	return result;
+}
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 825f7d9..8032f0d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -435,6 +435,8 @@ typedef enum NodeTag
 	T_RoleSpec,
 	T_PartitionElem,
 	T_PartitionBy,
+	T_PartitionValues,
+	T_PartitionStmt,
 
 	/*
 	 * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 130410c..8128226 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -731,6 +731,31 @@ typedef struct PartitionBy
 	int			location;	/* token location, or -1 if unknown */
 } PartitionBy;
 
+/*
+ * PartitionValues - partition FOR VALUES descriptor
+ */
+typedef struct PartitionValues
+{
+    NodeTag     type;
+    List       *listvalues;
+    List       *rangemaxs;
+    int         location;   /* token location, or -1 if unknown */
+} PartitionValues;
+
+/*
+ * PartitionStmt -	create new partition
+ */
+typedef struct PartitionStmt
+{
+    NodeTag          type;
+    RangeVar        *name;				/* partition name */
+    RangeVar		*parent;     		/* RangeVar node for the parent */
+	RangeVar		*using_table;		/* USING TABLE <name> */
+    PartitionValues *values;            /* FOR VALUES clause descriptor */
+    List            *options;           /* options from WITH clause */
+    char            *tablespacename;    /* table space to use, or NULL */
+} PartitionStmt;
+
 /****************************************************************************
  *	Nodes for a Query tree
  ****************************************************************************/
@@ -1557,7 +1582,16 @@ typedef enum AlterTableType
 	AT_DisableRowSecurity,		/* DISABLE ROW SECURITY */
 	AT_ForceRowSecurity,		/* FORCE ROW SECURITY */
 	AT_NoForceRowSecurity,		/* NO FORCE ROW SECURITY */
-	AT_GenericOptions			/* OPTIONS (...) */
+	AT_GenericOptions,			/* OPTIONS (...) */
+	AT_AddPartition,			/* ADD PARTITION ... */
+	AT_AddSubPartition,			/* MODIFY PARTITION ... ADD SUBPARTITION */
+	AT_DropPartition,			/* DROP PARTITION */
+	AT_DropSubPartition,		/* DROP SUBPARTITION */
+	AT_AttachPartition,			/* ATTACH PARTITION .. USING TABLE .. */
+	AT_AttachSubPartition,		/* MODIFY PARTITION ..
+								 * ATTACH SUBPARTITION .. USING TABLE .. */
+	AT_DetachPartition,			/* DETACH PARTITION .. USING TABLE .. */
+	AT_DetachSubPartition		/* DETACH SUBPARTITION .. USING TABLE .. */
 } AlterTableType;
 
 typedef struct ReplicaIdentityStmt
@@ -1790,6 +1824,7 @@ typedef struct CreateStmt
 	OnCommitAction oncommit;	/* what do we do at COMMIT? */
 	char	   *tablespacename; /* table space to use, or NULL */
 	bool		if_not_exists;	/* just do nothing if it already exists? */
+	bool		is_internal_partition;	/* relation is internal partition? */
 } CreateStmt;
 
 /* ----------
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 38c7afe..f56eaee 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -49,6 +49,7 @@ PG_KEYWORD("assertion", ASSERTION, UNRESERVED_KEYWORD)
 PG_KEYWORD("assignment", ASSIGNMENT, UNRESERVED_KEYWORD)
 PG_KEYWORD("asymmetric", ASYMMETRIC, RESERVED_KEYWORD)
 PG_KEYWORD("at", AT, UNRESERVED_KEYWORD)
+PG_KEYWORD("attach", ATTACH, UNRESERVED_KEYWORD)
 PG_KEYWORD("attribute", ATTRIBUTE, UNRESERVED_KEYWORD)
 PG_KEYWORD("authorization", AUTHORIZATION, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("backward", BACKWARD, UNRESERVED_KEYWORD)
@@ -126,6 +127,7 @@ PG_KEYWORD("delete", DELETE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("delimiter", DELIMITER, UNRESERVED_KEYWORD)
 PG_KEYWORD("delimiters", DELIMITERS, UNRESERVED_KEYWORD)
 PG_KEYWORD("desc", DESC, RESERVED_KEYWORD)
+PG_KEYWORD("detach", DETACH, UNRESERVED_KEYWORD)
 PG_KEYWORD("dictionary", DICTIONARY, UNRESERVED_KEYWORD)
 PG_KEYWORD("disable", DISABLE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("discard", DISCARD, UNRESERVED_KEYWORD)
@@ -223,6 +225,7 @@ PG_KEYWORD("leading", LEADING, RESERVED_KEYWORD)
 PG_KEYWORD("leakproof", LEAKPROOF, UNRESERVED_KEYWORD)
 PG_KEYWORD("least", LEAST, COL_NAME_KEYWORD)
 PG_KEYWORD("left", LEFT, TYPE_FUNC_NAME_KEYWORD)
+PG_KEYWORD("less", LESS, UNRESERVED_KEYWORD)
 PG_KEYWORD("level", LEVEL, UNRESERVED_KEYWORD)
 PG_KEYWORD("like", LIKE, TYPE_FUNC_NAME_KEYWORD)
 PG_KEYWORD("limit", LIMIT, RESERVED_KEYWORD)
@@ -243,6 +246,7 @@ PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
 PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
 PG_KEYWORD("mode", MODE, UNRESERVED_KEYWORD)
+PG_KEYWORD("modify", MODIFY, UNRESERVED_KEYWORD)
 PG_KEYWORD("month", MONTH_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("move", MOVE, UNRESERVED_KEYWORD)
 PG_KEYWORD("name", NAME_P, UNRESERVED_KEYWORD)
@@ -285,7 +289,7 @@ PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD)
 PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD)
 PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD)
 PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD)
-PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD)
+PG_KEYWORD("partition", PARTITION, RESERVED_KEYWORD)
 PG_KEYWORD("passing", PASSING, UNRESERVED_KEYWORD)
 PG_KEYWORD("password", PASSWORD, UNRESERVED_KEYWORD)
 PG_KEYWORD("placing", PLACING, RESERVED_KEYWORD)
@@ -368,7 +372,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("subpartition", SUBPARTITION, RESERVED_KEYWORD)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
 PG_KEYWORD("symmetric", SYMMETRIC, RESERVED_KEYWORD)
 PG_KEYWORD("sysid", SYSID, UNRESERVED_KEYWORD)
@@ -381,6 +385,7 @@ PG_KEYWORD("temp", TEMP, UNRESERVED_KEYWORD)
 PG_KEYWORD("template", TEMPLATE, UNRESERVED_KEYWORD)
 PG_KEYWORD("temporary", TEMPORARY, UNRESERVED_KEYWORD)
 PG_KEYWORD("text", TEXT_P, UNRESERVED_KEYWORD)
+PG_KEYWORD("than", THAN, UNRESERVED_KEYWORD)
 PG_KEYWORD("then", THEN, RESERVED_KEYWORD)
 PG_KEYWORD("time", TIME, COL_NAME_KEYWORD)
 PG_KEYWORD("timestamp", TIMESTAMP, COL_NAME_KEYWORD)
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index f6f3f52..054cd37 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -65,7 +65,8 @@ typedef enum ParseExprKind
 	EXPR_KIND_EXECUTE_PARAMETER,	/* parameter value in EXECUTE */
 	EXPR_KIND_TRIGGER_WHEN,		/* WHEN condition in CREATE TRIGGER */
 	EXPR_KIND_POLICY,			/* USING or WITH CHECK expr in policy */
-	EXPR_KIND_PARTKEY_EXPRESSION	/* partition key expression */
+	EXPR_KIND_PARTKEY_EXPRESSION,	/* partition key expression */
+	EXPR_KIND_PARTITION_VALUE
 } ParseExprKind;
 
 
-- 
1.7.1

