aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2008-05-16 23:36:05 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2008-05-16 23:36:05 +0000
commit10a3471bed7b57fb986a5be8afdee5f0dda419de (patch)
tree32de8db702827c67c5cb85479d9bbff22c7b6e94 /src/backend
parent8a2f5d221b0d6e41dc66b7e7389668bd208e3529 (diff)
downloadpostgresql-10a3471bed7b57fb986a5be8afdee5f0dda419de.tar.gz
postgresql-10a3471bed7b57fb986a5be8afdee5f0dda419de.zip
Add a RESTART (without parameter) option to ALTER SEQUENCE, allowing a
sequence to be reset to its original starting value. This requires adding the original start value to the set of parameters (columns) of a sequence object, which is a user-visible change with potential compatibility implications; it also forces initdb. Also add hopefully-SQL-compatible RESTART/CONTINUE IDENTITY options to TRUNCATE TABLE. RESTART IDENTITY executes ALTER SEQUENCE RESTART for all sequences "owned by" any of the truncated relations. CONTINUE IDENTITY is a no-op option. Zoltan Boszormenyi
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/pg_depend.c54
-rw-r--r--src/backend/commands/sequence.c129
-rw-r--r--src/backend/commands/tablecmds.c57
-rw-r--r--src/backend/nodes/copyfuncs.c3
-rw-r--r--src/backend/nodes/equalfuncs.c3
-rw-r--r--src/backend/parser/gram.y31
-rw-r--r--src/backend/parser/keywords.c4
7 files changed, 238 insertions, 43 deletions
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 32d8d07044b..d1fd8f5200f 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.27 2008/03/26 21:10:37 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.28 2008/05/16 23:36:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -418,6 +418,58 @@ markSequenceUnowned(Oid seqId)
heap_close(depRel, RowExclusiveLock);
}
+/*
+ * Collect a list of OIDs of all sequences owned by the specified relation.
+ */
+List *
+getOwnedSequences(Oid relid)
+{
+ List *result = NIL;
+ Relation depRel;
+ ScanKeyData key[2];
+ SysScanDesc scan;
+ HeapTuple tup;
+
+ depRel = heap_open(DependRelationId, AccessShareLock);
+
+ ScanKeyInit(&key[0],
+ Anum_pg_depend_refclassid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(RelationRelationId));
+ ScanKeyInit(&key[1],
+ Anum_pg_depend_refobjid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(relid));
+
+ scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+ SnapshotNow, 2, key);
+
+ while (HeapTupleIsValid(tup = systable_getnext(scan)))
+ {
+ Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+ /*
+ * We assume any auto dependency of a sequence on a column must be
+ * what we are looking for. (We need the relkind test because indexes
+ * can also have auto dependencies on columns.)
+ */
+ if (deprec->classid == RelationRelationId &&
+ deprec->objsubid == 0 &&
+ deprec->refobjsubid != 0 &&
+ deprec->deptype == DEPENDENCY_AUTO &&
+ get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
+ {
+ result = lappend_oid(result, deprec->objid);
+ }
+ }
+
+ systable_endscan(scan);
+
+ heap_close(depRel, AccessShareLock);
+
+ return result;
+}
+
/*
* get_constraint_index
diff --git a/src/backend/commands/sequence.c b/src/backend/commands/sequence.c
index 670fcbcfbdb..748413ebed3 100644
--- a/src/backend/commands/sequence.c
+++ b/src/backend/commands/sequence.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.150 2008/05/12 00:00:47 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/sequence.c,v 1.151 2008/05/16 23:36:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -91,7 +91,7 @@ static Relation open_share_lock(SeqTable seq);
static void init_sequence(Oid relid, SeqTable *p_elm, Relation *p_rel);
static Form_pg_sequence read_info(SeqTable elm, Relation rel, Buffer *buf);
static void init_params(List *options, bool isInit,
- Form_pg_sequence new, List **owned_by);
+ Form_pg_sequence new, Form_pg_sequence old, List **owned_by);
static void do_setval(Oid relid, int64 next, bool iscalled);
static void process_owned_by(Relation seqrel, List *owned_by);
@@ -119,10 +119,10 @@ DefineSequence(CreateSeqStmt *seq)
NameData name;
/* Check and set all option values */
- init_params(seq->options, true, &new, &owned_by);
+ init_params(seq->options, true, &new, NULL, &owned_by);
/*
- * Create relation (and fill *null & *value)
+ * Create relation (and fill value[] and null[] for the tuple)
*/
stmt->tableElts = NIL;
for (i = SEQ_COL_FIRSTCOL; i <= SEQ_COL_LASTCOL; i++)
@@ -151,6 +151,11 @@ DefineSequence(CreateSeqStmt *seq)
coldef->colname = "last_value";
value[i - 1] = Int64GetDatumFast(new.last_value);
break;
+ case SEQ_COL_STARTVAL:
+ coldef->typename = makeTypeNameFromOid(INT8OID, -1);
+ coldef->colname = "start_value";
+ value[i - 1] = Int64GetDatumFast(new.start_value);
+ break;
case SEQ_COL_INCBY:
coldef->typename = makeTypeNameFromOid(INT8OID, -1);
coldef->colname = "increment_by";
@@ -314,6 +319,29 @@ void
AlterSequence(AlterSeqStmt *stmt)
{
Oid relid;
+
+ /* find sequence */
+ relid = RangeVarGetRelid(stmt->sequence, false);
+
+ /* allow ALTER to sequence owner only */
+ /* if you change this, see also callers of AlterSequenceInternal! */
+ if (!pg_class_ownercheck(relid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ stmt->sequence->relname);
+
+ /* do the work */
+ AlterSequenceInternal(relid, stmt->options);
+}
+
+/*
+ * AlterSequenceInternal
+ *
+ * Same as AlterSequence except that the sequence is specified by OID
+ * and we assume the caller already checked permissions.
+ */
+void
+AlterSequenceInternal(Oid relid, List *options)
+{
SeqTable elm;
Relation seqrel;
Buffer buf;
@@ -323,23 +351,14 @@ AlterSequence(AlterSeqStmt *stmt)
List *owned_by;
/* open and AccessShareLock sequence */
- relid = RangeVarGetRelid(stmt->sequence, false);
init_sequence(relid, &elm, &seqrel);
- /* allow ALTER to sequence owner only */
- if (!pg_class_ownercheck(elm->relid, GetUserId()))
- aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
- stmt->sequence->relname);
-
/* lock page' buffer and read tuple into new sequence structure */
seq = read_info(elm, seqrel, &buf);
page = BufferGetPage(buf);
- /* Copy old values of options into workspace */
- memcpy(&new, seq, sizeof(FormData_pg_sequence));
-
- /* Check and set new values */
- init_params(stmt->options, false, &new, &owned_by);
+ /* Fill workspace with appropriate new info */
+ init_params(options, false, &new, seq, &owned_by);
/* Clear local cache so that we don't think we have cached numbers */
/* Note that we do not change the currval() state */
@@ -970,7 +989,7 @@ read_info(SeqTable elm, Relation rel, Buffer *buf)
*/
static void
init_params(List *options, bool isInit,
- Form_pg_sequence new, List **owned_by)
+ Form_pg_sequence new, Form_pg_sequence old, List **owned_by)
{
DefElem *last_value = NULL;
DefElem *increment_by = NULL;
@@ -982,6 +1001,12 @@ init_params(List *options, bool isInit,
*owned_by = NIL;
+ /* Copy old values of options into workspace */
+ if (old != NULL)
+ memcpy(new, old, sizeof(FormData_pg_sequence));
+ else
+ memset(new, 0, sizeof(FormData_pg_sequence));
+
foreach(option, options)
{
DefElem *defel = (DefElem *) lfirst(option);
@@ -994,13 +1019,24 @@ init_params(List *options, bool isInit,
errmsg("conflicting or redundant options")));
increment_by = defel;
}
-
- /*
- * start is for a new sequence restart is for alter
- */
- else if (strcmp(defel->defname, "start") == 0 ||
- strcmp(defel->defname, "restart") == 0)
+ else if (strcmp(defel->defname, "start") == 0)
+ {
+ if (!isInit)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("use RESTART not START in ALTER SEQUENCE")));
+ if (last_value)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ last_value = defel;
+ }
+ else if (strcmp(defel->defname, "restart") == 0)
{
+ if (isInit)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("use START not RESTART in CREATE SEQUENCE")));
if (last_value)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
@@ -1109,24 +1145,59 @@ init_params(List *options, bool isInit,
bufm, bufx)));
}
- /* START WITH */
+ /* START/RESTART [WITH] */
if (last_value != NULL)
{
- new->last_value = defGetInt64(last_value);
+ if (last_value->arg != NULL)
+ new->last_value = defGetInt64(last_value);
+ else
+ {
+ Assert(old != NULL);
+ new->last_value = old->start_value;
+ }
+ if (isInit)
+ new->start_value = new->last_value;
new->is_called = false;
new->log_cnt = 1;
}
else if (isInit)
{
if (new->increment_by > 0)
- new->last_value = new->min_value; /* ascending seq */
+ new->start_value = new->min_value; /* ascending seq */
else
- new->last_value = new->max_value; /* descending seq */
+ new->start_value = new->max_value; /* descending seq */
+ new->last_value = new->start_value;
new->is_called = false;
new->log_cnt = 1;
}
- /* crosscheck */
+ /* crosscheck START */
+ if (new->start_value < new->min_value)
+ {
+ char bufs[100],
+ bufm[100];
+
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("START value (%s) cannot be less than MINVALUE (%s)",
+ bufs, bufm)));
+ }
+ if (new->start_value > new->max_value)
+ {
+ char bufs[100],
+ bufm[100];
+
+ snprintf(bufs, sizeof(bufs), INT64_FORMAT, new->start_value);
+ snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+ errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
+ bufs, bufm)));
+ }
+
+ /* must crosscheck RESTART separately */
if (new->last_value < new->min_value)
{
char bufs[100],
@@ -1136,7 +1207,7 @@ init_params(List *options, bool isInit,
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->min_value);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("START value (%s) cannot be less than MINVALUE (%s)",
+ errmsg("RESTART value (%s) cannot be less than MINVALUE (%s)",
bufs, bufm)));
}
if (new->last_value > new->max_value)
@@ -1148,7 +1219,7 @@ init_params(List *options, bool isInit,
snprintf(bufm, sizeof(bufm), INT64_FORMAT, new->max_value);
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
- errmsg("START value (%s) cannot be greater than MAXVALUE (%s)",
+ errmsg("RESTART value (%s) cannot be greater than MAXVALUE (%s)",
bufs, bufm)));
}
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 48e229e0fb5..0bef48e07bf 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.253 2008/05/12 00:00:48 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.254 2008/05/16 23:36:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -37,6 +37,7 @@
#include "catalog/toasting.h"
#include "commands/cluster.h"
#include "commands/defrem.h"
+#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/trigger.h"
@@ -531,6 +532,7 @@ ExecuteTruncate(TruncateStmt *stmt)
{
List *rels = NIL;
List *relids = NIL;
+ List *seq_relids = NIL;
EState *estate;
ResultRelInfo *resultRelInfos;
ResultRelInfo *resultRelInfo;
@@ -596,6 +598,40 @@ ExecuteTruncate(TruncateStmt *stmt)
heap_truncate_check_FKs(rels, false);
#endif
+ /*
+ * If we are asked to restart sequences, find all the sequences,
+ * lock them (we only need AccessShareLock because that's all that
+ * ALTER SEQUENCE takes), and check permissions. We want to do this
+ * early since it's pointless to do all the truncation work only to fail
+ * on sequence permissions.
+ */
+ if (stmt->restart_seqs)
+ {
+ foreach(cell, rels)
+ {
+ Relation rel = (Relation) lfirst(cell);
+ List *seqlist = getOwnedSequences(RelationGetRelid(rel));
+ ListCell *seqcell;
+
+ foreach(seqcell, seqlist)
+ {
+ Oid seq_relid = lfirst_oid(seqcell);
+ Relation seq_rel;
+
+ seq_rel = relation_open(seq_relid, AccessShareLock);
+
+ /* This check must match AlterSequence! */
+ if (!pg_class_ownercheck(seq_relid, GetUserId()))
+ aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_CLASS,
+ RelationGetRelationName(seq_rel));
+
+ seq_relids = lappend_oid(seq_relids, seq_relid);
+
+ relation_close(seq_rel, NoLock);
+ }
+ }
+ }
+
/* Prepare to catch AFTER triggers. */
AfterTriggerBeginQuery();
@@ -694,6 +730,25 @@ ExecuteTruncate(TruncateStmt *stmt)
heap_close(rel, NoLock);
}
+
+ /*
+ * Lastly, restart any owned sequences if we were asked to. This is done
+ * last because it's nontransactional: restarts will not roll back if
+ * we abort later. Hence it's important to postpone them as long as
+ * possible. (This is also a big reason why we locked and
+ * permission-checked the sequences beforehand.)
+ */
+ if (stmt->restart_seqs)
+ {
+ List *options = list_make1(makeDefElem("restart", NULL));
+
+ foreach(cell, seq_relids)
+ {
+ Oid seq_relid = lfirst_oid(cell);
+
+ AlterSequenceInternal(seq_relid, options);
+ }
+ }
}
/*
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 67dcf9a2182..a42c40327f2 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.393 2008/04/29 14:59:16 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.394 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -2156,6 +2156,7 @@ _copyTruncateStmt(TruncateStmt *from)
TruncateStmt *newnode = makeNode(TruncateStmt);
COPY_NODE_FIELD(relations);
+ COPY_SCALAR_FIELD(restart_seqs);
COPY_SCALAR_FIELD(behavior);
return newnode;
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 0c444901322..435ee6a6afb 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.322 2008/04/29 14:59:16 alvherre Exp $
+ * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.323 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1010,6 +1010,7 @@ static bool
_equalTruncateStmt(TruncateStmt *a, TruncateStmt *b)
{
COMPARE_NODE_FIELD(relations);
+ COMPARE_SCALAR_FIELD(restart_seqs);
COMPARE_SCALAR_FIELD(behavior);
return true;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 56a56627e7f..591920d6f68 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.614 2008/04/29 20:44:49 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.615 2008/05/16 23:36:05 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@@ -206,7 +206,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
%type <str> OptSchemaName
%type <list> OptSchemaEltList
-%type <boolean> TriggerActionTime TriggerForSpec opt_trusted
+%type <boolean> TriggerActionTime TriggerForSpec opt_trusted opt_restart_seqs
%type <str> opt_lancompiler
%type <str> TriggerEvents
@@ -381,7 +381,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
CLUSTER COALESCE COLLATE COLUMN COMMENT COMMIT
COMMITTED CONCURRENTLY CONFIGURATION CONNECTION CONSTRAINT CONSTRAINTS
- CONTENT_P CONVERSION_P COPY COST CREATE CREATEDB
+ CONTENT_P CONTINUE_P CONVERSION_P COPY COST CREATE CREATEDB
CREATEROLE CREATEUSER CROSS CSV CURRENT_P CURRENT_DATE CURRENT_ROLE
CURRENT_TIME CURRENT_TIMESTAMP CURRENT_USER CURSOR CYCLE
@@ -399,10 +399,10 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
HANDLER HAVING HEADER_P HOLD HOUR_P
- IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P INCLUDING INCREMENT
- INDEX INDEXES INHERIT INHERITS INITIALLY INNER_P INOUT INPUT_P
- INSENSITIVE INSERT INSTEAD INT_P INTEGER INTERSECT
- INTERVAL INTO INVOKER IS ISNULL ISOLATION
+ IDENTITY_P IF_P ILIKE IMMEDIATE IMMUTABLE IMPLICIT_P IN_P
+ INCLUDING INCREMENT INDEX INDEXES INHERIT INHERITS INITIALLY
+ INNER_P INOUT INPUT_P INSENSITIVE INSERT INSTEAD INT_P INTEGER
+ INTERSECT INTERVAL INTO INVOKER IS ISNULL ISOLATION
JOIN
@@ -2489,6 +2489,10 @@ OptSeqElem: CACHE NumericOnly
{
$$ = makeDefElem("start", (Node *)$3);
}
+ | RESTART
+ {
+ $$ = makeDefElem("restart", NULL);
+ }
| RESTART opt_with NumericOnly
{
$$ = makeDefElem("restart", (Node *)$3);
@@ -3364,15 +3368,22 @@ attrs: '.' attr_name
*****************************************************************************/
TruncateStmt:
- TRUNCATE opt_table qualified_name_list opt_drop_behavior
+ TRUNCATE opt_table qualified_name_list opt_restart_seqs opt_drop_behavior
{
TruncateStmt *n = makeNode(TruncateStmt);
n->relations = $3;
- n->behavior = $4;
+ n->restart_seqs = $4;
+ n->behavior = $5;
$$ = (Node *)n;
}
;
+opt_restart_seqs:
+ CONTINUE_P IDENTITY_P { $$ = false; }
+ | RESTART IDENTITY_P { $$ = true; }
+ | /* EMPTY */ { $$ = false; }
+ ;
+
/*****************************************************************************
*
* The COMMENT ON statement can take different forms based upon the type of
@@ -8964,6 +8975,7 @@ unreserved_keyword:
| CONNECTION
| CONSTRAINTS
| CONTENT_P
+ | CONTINUE_P
| CONVERSION_P
| COPY
| COST
@@ -9014,6 +9026,7 @@ unreserved_keyword:
| HEADER_P
| HOLD
| HOUR_P
+ | IDENTITY_P
| IF_P
| IMMEDIATE
| IMMUTABLE
diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c
index b5312a487fd..46b306b98d1 100644
--- a/src/backend/parser/keywords.c
+++ b/src/backend/parser/keywords.c
@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.195 2008/03/27 03:57:33 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.196 2008/05/16 23:36:05 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -101,6 +101,7 @@ static const ScanKeyword ScanKeywords[] = {
{"constraint", CONSTRAINT, RESERVED_KEYWORD},
{"constraints", CONSTRAINTS, UNRESERVED_KEYWORD},
{"content", CONTENT_P, UNRESERVED_KEYWORD},
+ {"continue", CONTINUE_P, UNRESERVED_KEYWORD},
{"conversion", CONVERSION_P, UNRESERVED_KEYWORD},
{"copy", COPY, UNRESERVED_KEYWORD},
{"cost", COST, UNRESERVED_KEYWORD},
@@ -181,6 +182,7 @@ static const ScanKeyword ScanKeywords[] = {
{"header", HEADER_P, UNRESERVED_KEYWORD},
{"hold", HOLD, UNRESERVED_KEYWORD},
{"hour", HOUR_P, UNRESERVED_KEYWORD},
+ {"identity", IDENTITY_P, UNRESERVED_KEYWORD},
{"if", IF_P, UNRESERVED_KEYWORD},
{"ilike", ILIKE, TYPE_FUNC_NAME_KEYWORD},
{"immediate", IMMEDIATE, UNRESERVED_KEYWORD},