From 3602e8ba81b5e457af0487de33d2a7fc5357947f Mon Sep 17 00:00:00 2001
From: Alvaro Herrera <alvherre@alvh.no-ip.org>
Date: Tue, 21 Apr 2015 15:28:51 -0300
Subject: [PATCH 08/24] Add COLUMN STORE clause to CREATE TABLE

This allows two ways to specify column stores when creating a table.
The first is using column constraint syntax:

  CREATE TABLE test (
     a INT COLUMN STORE x USING y [WITH (...)] [TABLESPACE t],
     b INT COLUMN STORE y USING z [WITH (...)] [TABLESPACE t]
  );

which maps each column into its own column store.  Note the USING
clause, which specifies the column store access method used for that
particular store.

The alternative syntax piggy-backs on table-level constraints, and
allows column stores comprising more than one column:

  CREATE TABLE test (
     a INT,
     b INT,
     COLUMN STORE y USING z (a,b) WITH (...) [TABLESPACE t]
  );

Implementation notes:

* Adds "STORE" as an unreserved keyword.

* Adds a new node type, ColumnStoreClause.

* The two forms of column stores are represented separately in the
  CreateStmt parse node.  The column-level form lives inside ColumnDef;
  the table-level form lives in the list of constraints.

* We allow for a "WITH (options)", which is supposed to be passed to the
  column store AM.  Currently it is not yet used for anything.
---
 doc/src/sgml/ref/create_table.sgml | 46 +++++++++++++++++++++++++++++--
 src/backend/nodes/copyfuncs.c      | 19 +++++++++++++
 src/backend/nodes/equalfuncs.c     | 17 ++++++++++++
 src/backend/nodes/outfuncs.c       | 17 ++++++++++++
 src/backend/parser/gram.y          | 55 ++++++++++++++++++++++++++++++++++++--
 src/backend/parser/parse_utilcmd.c |  4 +++
 src/include/nodes/nodes.h          |  1 +
 src/include/nodes/parsenodes.h     | 36 +++++++++++++++++++++----
 src/include/parser/kwlist.h        |  1 +
 9 files changed, 187 insertions(+), 9 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index fac7e1e..116ba82 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -22,8 +22,9 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] <replaceable class="PARAMETER">table_name</replaceable> ( [
-  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
+  { <replaceable class="PARAMETER">column_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [ COLLATE <replaceable>collation</replaceable> ] [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ] [ column_storage ]
     | <replaceable>table_constraint</replaceable>
+    | <replaceable>table_level_column_storage</replaceable>
     | LIKE <replaceable>source_table</replaceable> [ <replaceable>like_option</replaceable> ... ] }
     [, ... ]
 ] )
@@ -55,6 +56,14 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
 [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
+<phrase>and <replaceable class="PARAMETER">column_store</replaceable> is:</phrase>
+
+[ COLUMN STORE <replaceable class="PARAMETER">column_store_name</replaceable>
+  USING <replaceable class="PARAMETER">column_store_access_method</replaceable>
+  [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable>
+            [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
+  [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ] ]
+
 <phrase>and <replaceable class="PARAMETER">table_constraint</replaceable> is:</phrase>
 
 [ CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> ]
@@ -66,6 +75,15 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
     [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE <replaceable class="parameter">action</replaceable> ] [ ON UPDATE <replaceable class="parameter">action</replaceable> ] }
 [ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ]
 
+<phrase>and <replaceable class="PARAMETER">table_level_column_store</replaceable> is:</phrase>
+
+[ COLUMN STORE <replaceable class="PARAMETER">column_store_name</replaceable>
+  USING <replaceable class="PARAMETER">column_store_access_method</replaceable>
+  (<replaceable class="PARAMETER">column_name</replaceable> [, ...] )
+  [ WITH ( <replaceable class="PARAMETER">storage_parameter</replaceable>
+            [= <replaceable class="PARAMETER">value</replaceable>] [, ... ] ) ]
+  [ TABLESPACE <replaceable class="PARAMETER">tablespace_name</replaceable> ] ]
+
 <phrase>and <replaceable class="PARAMETER">like_option</replaceable> is:</phrase>
 
 { INCLUDING | EXCLUDING } { DEFAULTS | CONSTRAINTS | INDEXES | STORAGE | COMMENTS | ALL }
@@ -262,6 +280,28 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
    </varlistentry>
 
    <varlistentry>
+    <term><literal>COLUMN STORE <replaceable>column store name</replaceable></literal></term>
+    <listitem>
+     <para>
+      The <literal>COLUMN STORE</> clause assigns a column to a named
+      column store with a column store access method defined by the
+      compulsory <literal>USING</> clause. Optional parameters may be set
+      in the <literal>WITH</> clause. A tablespace can be defined for each
+      column store by using the <literal>TABLESPACE</> clause, otherwise
+      the column store will use the same tablespace as the main table.
+     </para>
+     <para>
+      Column stores may contain multiple columns. Multi-column column stores
+      must be defined as table-level clauses.
+     </para>
+     <para>
+      Columns may be assigned to only one column store, otherwise they will
+      be stored as part of the main table.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
     <term><literal>INHERITS ( <replaceable>parent_table</replaceable> [, ... ] )</literal></term>
     <listitem>
      <para>
@@ -307,7 +347,8 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
      </para>
 
      <para>
-      Column <literal>STORAGE</> settings are also copied from parent tables.
+      Column <literal>STORAGE</> settings are also copied from parent tables,
+      but not column storage definitions.
      </para>
 
     </listitem>
@@ -320,6 +361,7 @@ CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXI
       The <literal>LIKE</literal> clause specifies a table from which
       the new table automatically copies all column names, their data types,
       and their not-null constraints.
+      Column storage parameters are not copied by the LIKE clause.
      </para>
      <para>
       Unlike <literal>INHERITS</literal>, the new table and original table
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index bd2e80e..ae7ca30 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -2554,6 +2554,21 @@ _copyCollateClause(const CollateClause *from)
 	return newnode;
 }
 
+static ColumnStoreClause *
+_copyColumnStoreClause(const ColumnStoreClause *from)
+{
+	ColumnStoreClause *newnode = makeNode(ColumnStoreClause);
+
+	COPY_STRING_FIELD(name);
+	COPY_STRING_FIELD(storetype);
+	COPY_NODE_FIELD(columns);
+	COPY_NODE_FIELD(options);
+	COPY_LOCATION_FIELD(location);
+	COPY_STRING_FIELD(tablespacename);
+
+	return newnode;
+}
+
 static IndexElem *
 _copyIndexElem(const IndexElem *from)
 {
@@ -2586,6 +2601,7 @@ _copyColumnDef(const ColumnDef *from)
 	COPY_NODE_FIELD(cooked_default);
 	COPY_NODE_FIELD(collClause);
 	COPY_SCALAR_FIELD(collOid);
+	COPY_NODE_FIELD(cstoreClause);
 	COPY_NODE_FIELD(constraints);
 	COPY_NODE_FIELD(fdwoptions);
 	COPY_LOCATION_FIELD(location);
@@ -4883,6 +4899,9 @@ copyObject(const void *from)
 		case T_CollateClause:
 			retval = _copyCollateClause(from);
 			break;
+		case T_ColumnStoreClause:
+			retval = _copyColumnStoreClause(from);
+			break;
 		case T_SortBy:
 			retval = _copySortBy(from);
 			break;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 19412fe..18c2119 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -2241,6 +2241,19 @@ _equalCollateClause(const CollateClause *a, const CollateClause *b)
 }
 
 static bool
+_equalColumnStoreClause(const ColumnStoreClause *a, const ColumnStoreClause *b)
+{
+	COMPARE_STRING_FIELD(name);
+	COMPARE_STRING_FIELD(storetype);
+	COMPARE_NODE_FIELD(columns);
+	COMPARE_NODE_FIELD(options);
+	COMPARE_LOCATION_FIELD(location);
+	COMPARE_STRING_FIELD(tablespacename);
+
+	return true;
+}
+
+static bool
 _equalSortBy(const SortBy *a, const SortBy *b)
 {
 	COMPARE_NODE_FIELD(node);
@@ -2330,6 +2343,7 @@ _equalColumnDef(const ColumnDef *a, const ColumnDef *b)
 	COMPARE_NODE_FIELD(cooked_default);
 	COMPARE_NODE_FIELD(collClause);
 	COMPARE_SCALAR_FIELD(collOid);
+	COMPARE_NODE_FIELD(cstoreClause);
 	COMPARE_NODE_FIELD(constraints);
 	COMPARE_NODE_FIELD(fdwoptions);
 	COMPARE_LOCATION_FIELD(location);
@@ -3240,6 +3254,9 @@ equal(const void *a, const void *b)
 		case T_CollateClause:
 			retval = _equalCollateClause(a, b);
 			break;
+		case T_ColumnStoreClause:
+			retval = _equalColumnStoreClause(a, b);
+			break;
 		case T_SortBy:
 			retval = _equalSortBy(a, b);
 			break;
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index a878498..6399d30 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -2279,6 +2279,7 @@ _outColumnDef(StringInfo str, const ColumnDef *node)
 	WRITE_NODE_FIELD(collClause);
 	WRITE_OID_FIELD(collOid);
 	WRITE_NODE_FIELD(constraints);
+	WRITE_NODE_FIELD(cstoreClause);
 	WRITE_NODE_FIELD(fdwoptions);
 	WRITE_LOCATION_FIELD(location);
 }
@@ -2319,6 +2320,19 @@ _outCollateClause(StringInfo str, const CollateClause *node)
 }
 
 static void
+_outColumnStoreClause(StringInfo str, const ColumnStoreClause *node)
+{
+	WRITE_NODE_TYPE("COLUMNSTORECLAUSE");
+
+	WRITE_STRING_FIELD(name);
+	WRITE_STRING_FIELD(storetype);
+	WRITE_NODE_FIELD(columns);
+	WRITE_NODE_FIELD(options);
+	WRITE_LOCATION_FIELD(location);
+	WRITE_STRING_FIELD(tablespacename);
+}
+
+static void
 _outIndexElem(StringInfo str, const IndexElem *node)
 {
 	WRITE_NODE_TYPE("INDEXELEM");
@@ -3363,6 +3377,9 @@ _outNode(StringInfo str, const void *obj)
 			case T_CollateClause:
 				_outCollateClause(str, obj);
 				break;
+			case T_ColumnStoreClause:
+				_outColumnStoreClause(str, obj);
+				break;
 			case T_IndexElem:
 				_outIndexElem(str, obj);
 				break;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 1efc6d6..1ec1426 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -171,6 +171,7 @@ static TypeName *TableFuncTypeName(List *columns);
 static RangeVar *makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner);
 static void SplitColQualList(List *qualList,
 							 List **constraintList, CollateClause **collClause,
+							 ColumnStoreClause **cstoreClause,
 							 core_yyscan_t yyscanner);
 static void processCASbits(int cas_bits, int location, const char *constrType,
 			   bool *deferrable, bool *initdeferred, bool *not_valid,
@@ -501,7 +502,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 %type <keyword> unreserved_keyword type_func_name_keyword
 %type <keyword> col_name_keyword reserved_keyword
 
-%type <node>	TableConstraint TableLikeClause
+%type <node>	TableConstraint TableLikeClause ColStoreClause
 %type <ival>	TableLikeOptionList TableLikeOption
 %type <list>	ColQualList
 %type <node>	ColConstraint ColConstraintElem ConstraintAttr
@@ -627,7 +628,7 @@ 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
+	STATEMENT STATISTICS STDIN STDOUT STORAGE STORE STRICT_P STRIP_P SUBSTRING
 	SYMMETRIC SYSID SYSTEM_P
 
 	TABLE TABLES TABLESAMPLE TABLESPACE TEMP TEMPLATE TEMPORARY TEXT_P THEN
@@ -2934,11 +2935,13 @@ TableElement:
 			columnDef							{ $$ = $1; }
 			| TableLikeClause					{ $$ = $1; }
 			| TableConstraint					{ $$ = $1; }
+			| ColStoreClause					{ $$ = $1; }
 		;
 
 TypedTableElement:
 			columnOptions						{ $$ = $1; }
 			| TableConstraint					{ $$ = $1; }
+			| ColStoreClause					{ $$ = $1; }
 		;
 
 columnDef:	ColId Typename create_generic_options ColQualList
@@ -2956,6 +2959,7 @@ columnDef:	ColId Typename create_generic_options ColQualList
 					n->collOid = InvalidOid;
 					n->fdwoptions = $3;
 					SplitColQualList($4, &n->constraints, &n->collClause,
+									 &n->cstoreClause,
 									 yyscanner);
 					n->location = @1;
 					$$ = (Node *)n;
@@ -2976,6 +2980,7 @@ columnOptions:	ColId WITH OPTIONS ColQualList
 					n->cooked_default = NULL;
 					n->collOid = InvalidOid;
 					SplitColQualList($4, &n->constraints, &n->collClause,
+									 &n->cstoreClause,
 									 yyscanner);
 					n->location = @1;
 					$$ = (Node *)n;
@@ -3011,6 +3016,20 @@ ColConstraint:
 					n->location = @1;
 					$$ = (Node *) n;
 				}
+			| COLUMN STORE ColId USING ColId opt_reloptions OptTableSpace
+				{
+					/*
+					 * Note: as with COLLATE, this is here only temporarily.
+					 */
+					ColumnStoreClause *n = makeNode(ColumnStoreClause);
+					n->name = $3;
+					n->storetype = $5;
+					n->columns = NIL;
+					n->options = $6;
+					n->tablespacename = $7;
+					n->location = @1;
+					$$ = (Node *) n;
+				}
 		;
 
 /* DEFAULT NULL is already the default for Postgres.
@@ -3190,6 +3209,19 @@ TableConstraint:
 			| ConstraintElem						{ $$ = $1; }
 		;
 
+ColStoreClause:
+			COLUMN STORE ColId USING ColId '(' columnList ')' opt_reloptions OptTableSpace
+				{
+					ColumnStoreClause *n = makeNode(ColumnStoreClause);
+					n->name = $3;
+					n->storetype = $5;
+					n->columns = $7;
+					n->options = $9;
+					n->tablespacename = $10;
+					$$ = (Node *) n;
+				}
+		;
+
 ConstraintElem:
 			CHECK '(' a_expr ')' ConstraintAttributeSpec
 				{
@@ -8972,6 +9004,7 @@ CreateDomainStmt:
 					n->domainname = $3;
 					n->typeName = $5;
 					SplitColQualList($6, &n->constraints, &n->collClause,
+									 NULL,
 									 yyscanner);
 					$$ = (Node *)n;
 				}
@@ -13846,6 +13879,7 @@ unreserved_keyword:
 			| STDIN
 			| STDOUT
 			| STORAGE
+			| STORE
 			| STRICT_P
 			| STRIP_P
 			| SYSID
@@ -14706,6 +14740,7 @@ makeRangeVarFromAnyName(List *names, int position, core_yyscan_t yyscanner)
 static void
 SplitColQualList(List *qualList,
 				 List **constraintList, CollateClause **collClause,
+				 ColumnStoreClause **cstoreClause,
 				 core_yyscan_t yyscanner)
 {
 	ListCell   *cell;
@@ -14736,6 +14771,22 @@ SplitColQualList(List *qualList,
 						 parser_errposition(c->location)));
 			*collClause = c;
 		}
+		else if (IsA(n, ColumnStoreClause))
+		{
+			ColumnStoreClause *c = (ColumnStoreClause *) n;
+
+			if (cstoreClause == NULL)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("COLUMN STORE clause not allowed here"),
+						 parser_errposition(c->location)));
+			if (*cstoreClause)
+				ereport(ERROR,
+						(errcode(ERRCODE_SYNTAX_ERROR),
+						 errmsg("multiple COLUMN STORE clauses not allowed"),
+						 parser_errposition(c->location)));
+			*cstoreClause = c;
+		}
 		else
 			elog(ERROR, "unexpected node type %d", (int) n->type);
 		/* remove non-Constraint nodes from qualList */
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 16d40c7..562eff9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -262,6 +262,10 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
 				transformTableLikeClause(&cxt, (TableLikeClause *) element);
 				break;
 
+			case T_ColumnStoreClause:
+				stmt->colstores = lappend(stmt->colstores, element);
+				break;
+
 			default:
 				elog(ERROR, "unrecognized node type: %d",
 					 (int) nodeTag(element));
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 748e434..27b38b1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -396,6 +396,7 @@ typedef enum NodeTag
 	T_MultiAssignRef,
 	T_TypeCast,
 	T_CollateClause,
+	T_ColumnStoreClause,
 	T_SortBy,
 	T_WindowDef,
 	T_RangeSubselect,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index f0dcd2f..302ba1f 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -558,6 +558,24 @@ typedef struct RangeTableSample
 } RangeTableSample;
 
 /*
+ * ColumnStoreClause - a COLUMN STORE declaration
+ *
+ * When this appears as a column constraint, the store includes only that
+ * column, and the column list is NIL.  When it appears as a table constraint,
+ * the column list contains one or more column names.
+ */
+typedef struct ColumnStoreClause
+{
+	NodeTag	type;
+	char   *name;			/* column store name */
+	char   *storetype;		/* column store type */
+	List   *columns;		/* list of String column names, or NIL */
+	List   *options;		/* list of DefElem options */
+	char   *tablespacename; /* table space to use, or NULL */
+	int		location;
+} ColumnStoreClause;
+
+/*
  * ColumnDef - column definition (used in various creates)
  *
  * If the column has a default value, we may have the value expression
@@ -570,6 +588,10 @@ typedef struct RangeTableSample
  * (represented as a CollateClause with arg==NULL) or cooked form
  * (the collation's OID).
  *
+ * Similarly, a COLUMN STORE specification may come in raw form (a
+ * ColumnStoreClause) or cooked (the OID of the corresponding store in the
+ * parent relation.  Note we only reuse its name, not the store itself.)
+ *
  * The constraints list may contain a CONSTR_DEFAULT item in a raw
  * parsetree produced by gram.y, but transformCreateStmt will remove
  * the item and set raw_default instead.  CONSTR_DEFAULT items
@@ -589,6 +611,7 @@ typedef struct ColumnDef
 	Node	   *cooked_default; /* default value (transformed expr tree) */
 	CollateClause *collClause;	/* untransformed COLLATE spec, if any */
 	Oid			collOid;		/* collation OID (InvalidOid if not set) */
+	ColumnStoreClause *cstoreClause; /* untransformed COL STORE, if any */
 	List	   *constraints;	/* other constraints on column */
 	List	   *fdwoptions;		/* per-column FDW options */
 	int			location;		/* parse location, or -1 if none/unknown */
@@ -1723,11 +1746,13 @@ typedef struct VariableShowStmt
 /* ----------------------
  *		Create Table Statement
  *
- * NOTE: in the raw gram.y output, ColumnDef and Constraint nodes are
- * intermixed in tableElts, and constraints is NIL.  After parse analysis,
- * tableElts contains just ColumnDefs, and constraints contains just
- * Constraint nodes (in fact, only CONSTR_CHECK nodes, in the present
- * implementation).
+ * NOTE: in the raw gram.y output, ColumnDef, ColumnStoreClause and Constraint
+ * nodes are intermixed in tableElts, and constraints and colstores are NIL.
+ * After parse analysis, tableElts contains just ColumnDefs, constraints
+ * contains just Constraint nodes (in fact, only CONSTR_CHECK nodes, in the
+ * present implementation), and colstores contains only ColumnStoreClause
+ * nodes (further note that ColumnDef nodes might have ColumnStoreClause nodes
+ * within.  Those are left alone during parse analysis.)
  * ----------------------
  */
 
@@ -1740,6 +1765,7 @@ typedef struct CreateStmt
 								 * inhRelation) */
 	TypeName   *ofTypename;		/* OF typename */
 	List	   *constraints;	/* constraints (list of Constraint nodes) */
+	List	   *colstores;		/* list of ColumnStoreClause nodes */
 	List	   *options;		/* options from WITH clause */
 	OnCommitAction oncommit;	/* what do we do at COMMIT? */
 	char	   *tablespacename; /* table space to use, or NULL */
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 2414069..3d499c1 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -364,6 +364,7 @@ PG_KEYWORD("statistics", STATISTICS, UNRESERVED_KEYWORD)
 PG_KEYWORD("stdin", STDIN, UNRESERVED_KEYWORD)
 PG_KEYWORD("stdout", STDOUT, UNRESERVED_KEYWORD)
 PG_KEYWORD("storage", STORAGE, UNRESERVED_KEYWORD)
+PG_KEYWORD("store", STORE, UNRESERVED_KEYWORD)
 PG_KEYWORD("strict", STRICT_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("strip", STRIP_P, UNRESERVED_KEYWORD)
 PG_KEYWORD("substring", SUBSTRING, COL_NAME_KEYWORD)
-- 
2.1.4

