aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser
diff options
context:
space:
mode:
authorPeter Eisentraut <peter_e@gmx.net>2017-04-06 08:33:16 -0400
committerPeter Eisentraut <peter_e@gmx.net>2017-04-06 08:41:37 -0400
commit3217327053638085d24dd4d276e7c1f7ac2c4c6b (patch)
tree513d1264a2935b05e28b0d8322d73a0411a3d02f /src/backend/parser
parent6bad580d9e678a0b604883e14d8401d469b06566 (diff)
downloadpostgresql-3217327053638085d24dd4d276e7c1f7ac2c4c6b.tar.gz
postgresql-3217327053638085d24dd4d276e7c1f7ac2c4c6b.zip
Identity columns
This is the SQL standard-conforming variant of PostgreSQL's serial columns. It fixes a few usability issues that serial columns have: - CREATE TABLE / LIKE copies default but refers to same sequence - cannot add/drop serialness with ALTER TABLE - dropping default does not drop sequence - need to grant separate privileges to sequence - other slight weirdnesses because serial is some kind of special macro Reviewed-by: Vitaly Burovoy <vitaly.burovoy@gmail.com>
Diffstat (limited to 'src/backend/parser')
-rw-r--r--src/backend/parser/analyze.c2
-rw-r--r--src/backend/parser/gram.y134
-rw-r--r--src/backend/parser/parse_utilcmd.c360
3 files changed, 420 insertions, 76 deletions
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 811fccaec97..c4140a65d22 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -487,6 +487,8 @@ transformInsertStmt(ParseState *pstate, InsertStmt *stmt)
qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
}
+ qry->override = stmt->override;
+
isOnConflictUpdate = (stmt->onConflictClause &&
stmt->onConflictClause->action == ONCONFLICT_UPDATE);
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 5ecb6997b3c..29ca5f13ea8 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -292,6 +292,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
%type <node> alter_table_cmd alter_type_cmd opt_collate_clause
replica_identity partition_cmd
%type <list> alter_table_cmds alter_type_cmds
+%type <list> alter_identity_column_option_list
+%type <defelt> alter_identity_column_option
%type <dbehavior> opt_drop_behavior
@@ -449,7 +451,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
select_offset_value2 opt_select_fetch_first_value
%type <ival> row_or_rows first_or_next
-%type <list> OptSeqOptList SeqOptList
+%type <list> OptSeqOptList SeqOptList OptParenthesizedSeqOptList
%type <defelt> SeqOptElem
%type <istmt> insert_rest
@@ -569,6 +571,7 @@ 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 <ival> generated_when override_kind
%type <partspec> PartitionSpec OptPartitionSpec
%type <str> part_strategy
%type <partelem> part_elem
@@ -631,7 +634,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
FALSE_P FAMILY FETCH FILTER FIRST_P FLOAT_P FOLLOWING FOR
FORCE FOREIGN FORWARD FREEZE FROM FULL FUNCTION FUNCTIONS
- GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
+ GENERATED GLOBAL GRANT GRANTED GREATEST GROUP_P GROUPING
HANDLER HAVING HEADER_P HOLD HOUR_P
@@ -655,7 +658,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
NULLS_P NUMERIC
OBJECT_P OF OFF OFFSET OIDS OLD ON ONLY OPERATOR OPTION OPTIONS OR
- ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OWNED OWNER
+ ORDER ORDINALITY OUT_P OUTER_P OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
@@ -726,6 +729,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* same as if they weren't keywords). We need to do this for PARTITION,
* RANGE, ROWS to support opt_existing_window_name; and for RANGE, ROWS
* so that they can follow a_expr without creating postfix-operator problems;
+ * for GENERATED so that it can follow b_expr;
* and for NULL so that it can follow b_expr in ColQualList without creating
* postfix-operator problems.
*
@@ -744,7 +748,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
* blame any funny behavior of UNBOUNDED on the SQL standard, though.
*/
%nonassoc UNBOUNDED /* ideally should have same precedence as IDENT */
-%nonassoc IDENT NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
+%nonassoc IDENT GENERATED NULL_P PARTITION RANGE ROWS PRECEDING FOLLOWING CUBE ROLLUP
%left Op OPERATOR /* multi-character ops and user-defined operators */
%left '+' '-'
%left '*' '/' '%'
@@ -2128,6 +2132,50 @@ alter_table_cmd:
n->def = (Node *) makeString($6);
$$ = (Node *)n;
}
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> ADD GENERATED ... AS IDENTITY ... */
+ | ALTER opt_column ColId ADD_P GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ Constraint *c = makeNode(Constraint);
+
+ c->contype = CONSTR_IDENTITY;
+ c->generated_when = $6;
+ c->options = $9;
+ c->location = @5;
+
+ n->subtype = AT_AddIdentity;
+ n->name = $3;
+ n->def = (Node *) c;
+
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> SET <sequence options>/RESET */
+ | ALTER opt_column ColId alter_identity_column_option_list
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_SetIdentity;
+ n->name = $3;
+ n->def = (Node *) $4;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY */
+ | ALTER opt_column ColId DROP IDENTITY_P
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropIdentity;
+ n->name = $3;
+ n->missing_ok = false;
+ $$ = (Node *)n;
+ }
+ /* ALTER TABLE <name> ALTER [COLUMN] <colname> DROP IDENTITY IF EXISTS */
+ | ALTER opt_column ColId DROP IDENTITY_P IF_P EXISTS
+ {
+ AlterTableCmd *n = makeNode(AlterTableCmd);
+ n->subtype = AT_DropIdentity;
+ n->name = $3;
+ n->missing_ok = true;
+ $$ = (Node *)n;
+ }
/* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
| DROP opt_column IF_P EXISTS ColId opt_drop_behavior
{
@@ -2565,6 +2613,39 @@ reloption_elem:
}
;
+alter_identity_column_option_list:
+ alter_identity_column_option
+ { $$ = list_make1($1); }
+ | alter_identity_column_option_list alter_identity_column_option
+ { $$ = lappend($1, $2); }
+ ;
+
+alter_identity_column_option:
+ RESTART
+ {
+ $$ = makeDefElem("restart", NULL, @1);
+ }
+ | RESTART opt_with NumericOnly
+ {
+ $$ = makeDefElem("restart", (Node *)$3, @1);
+ }
+ | SET SeqOptElem
+ {
+ if (strcmp($2->defname, "as") == 0 ||
+ strcmp($2->defname, "restart") == 0 ||
+ strcmp($2->defname, "owned_by") == 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("sequence option \"%s\" not supported here", $2->defname),
+ parser_errposition(@2)));
+ $$ = $2;
+ }
+ | SET GENERATED generated_when
+ {
+ $$ = makeDefElem("generated", (Node *) makeInteger($3), @1);
+ }
+ ;
+
ForValues:
/* a LIST partition */
FOR VALUES IN_P '(' partbound_datum_list ')'
@@ -3347,6 +3428,15 @@ ColConstraintElem:
n->cooked_expr = NULL;
$$ = (Node *)n;
}
+ | GENERATED generated_when AS IDENTITY_P OptParenthesizedSeqOptList
+ {
+ Constraint *n = makeNode(Constraint);
+ n->contype = CONSTR_IDENTITY;
+ n->generated_when = $2;
+ n->options = $5;
+ n->location = @1;
+ $$ = (Node *)n;
+ }
| REFERENCES qualified_name opt_column_list key_match key_actions
{
Constraint *n = makeNode(Constraint);
@@ -3364,6 +3454,11 @@ ColConstraintElem:
}
;
+generated_when:
+ ALWAYS { $$ = ATTRIBUTE_IDENTITY_ALWAYS; }
+ | BY DEFAULT { $$ = ATTRIBUTE_IDENTITY_BY_DEFAULT; }
+ ;
+
/*
* ConstraintAttr represents constraint attributes, which we parse as if
* they were independent constraint clauses, in order to avoid shift/reduce
@@ -3430,6 +3525,7 @@ TableLikeOptionList:
TableLikeOption:
DEFAULTS { $$ = CREATE_TABLE_LIKE_DEFAULTS; }
| CONSTRAINTS { $$ = CREATE_TABLE_LIKE_CONSTRAINTS; }
+ | IDENTITY_P { $$ = CREATE_TABLE_LIKE_IDENTITY; }
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
| COMMENTS { $$ = CREATE_TABLE_LIKE_COMMENTS; }
@@ -3967,6 +4063,10 @@ OptSeqOptList: SeqOptList { $$ = $1; }
| /*EMPTY*/ { $$ = NIL; }
;
+OptParenthesizedSeqOptList: '(' SeqOptList ')' { $$ = $2; }
+ | /*EMPTY*/ { $$ = NIL; }
+ ;
+
SeqOptList: SeqOptElem { $$ = list_make1($1); }
| SeqOptList SeqOptElem { $$ = lappend($1, $2); }
;
@@ -4011,6 +4111,11 @@ SeqOptElem: AS SimpleTypename
{
$$ = makeDefElem("owned_by", (Node *)$3, @1);
}
+ | SEQUENCE NAME_P any_name
+ {
+ /* not documented, only used by pg_dump */
+ $$ = makeDefElem("sequence_name", (Node *)$3, @1);
+ }
| START opt_with NumericOnly
{
$$ = makeDefElem("start", (Node *)$3, @1);
@@ -10412,12 +10517,26 @@ insert_rest:
$$->cols = NIL;
$$->selectStmt = $1;
}
+ | OVERRIDING override_kind VALUE_P SelectStmt
+ {
+ $$ = makeNode(InsertStmt);
+ $$->cols = NIL;
+ $$->override = $2;
+ $$->selectStmt = $4;
+ }
| '(' insert_column_list ')' SelectStmt
{
$$ = makeNode(InsertStmt);
$$->cols = $2;
$$->selectStmt = $4;
}
+ | '(' insert_column_list ')' OVERRIDING override_kind VALUE_P SelectStmt
+ {
+ $$ = makeNode(InsertStmt);
+ $$->cols = $2;
+ $$->override = $5;
+ $$->selectStmt = $7;
+ }
| DEFAULT VALUES
{
$$ = makeNode(InsertStmt);
@@ -10426,6 +10545,11 @@ insert_rest:
}
;
+override_kind:
+ USER { $$ = OVERRIDING_USER_VALUE; }
+ | SYSTEM_P { $$ = OVERRIDING_SYSTEM_VALUE; }
+ ;
+
insert_column_list:
insert_column_item
{ $$ = list_make1($1); }
@@ -14597,6 +14721,7 @@ unreserved_keyword:
| FORWARD
| FUNCTION
| FUNCTIONS
+ | GENERATED
| GLOBAL
| GRANTED
| HANDLER
@@ -14666,6 +14791,7 @@ unreserved_keyword:
| OPTIONS
| ORDINALITY
| OVER
+ | OVERRIDING
| OWNED
| OWNER
| PARALLEL
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 1ae43dc25dc..926699608be 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -42,6 +42,7 @@
#include "catalog/pg_type.h"
#include "commands/comment.h"
#include "commands/defrem.h"
+#include "commands/sequence.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "miscadmin.h"
@@ -356,6 +357,132 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
return result;
}
+static void
+generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column,
+ Oid seqtypid, List *seqoptions, bool for_identity,
+ char **snamespace_p, char **sname_p)
+{
+ ListCell *option;
+ DefElem *nameEl = NULL;
+ Oid snamespaceid;
+ char *snamespace;
+ char *sname;
+ CreateSeqStmt *seqstmt;
+ AlterSeqStmt *altseqstmt;
+ List *attnamelist;
+
+ /*
+ * Determine namespace and name to use for the sequence.
+ *
+ * First, check if a sequence name was passed in as an option. This is
+ * used by pg_dump. Else, generate a name.
+ *
+ * Although we use ChooseRelationName, it's not guaranteed that the
+ * selected sequence name won't conflict; given sufficiently long
+ * field names, two different serial columns in the same table could
+ * be assigned the same sequence name, and we'd not notice since we
+ * aren't creating the sequence quite yet. In practice this seems
+ * quite unlikely to be a problem, especially since few people would
+ * need two serial columns in one table.
+ */
+
+ foreach(option, seqoptions)
+ {
+ DefElem *defel = castNode(DefElem, lfirst(option));
+
+ if (strcmp(defel->defname, "sequence_name") == 0)
+ {
+ if (nameEl)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("conflicting or redundant options")));
+ nameEl = defel;
+ }
+ }
+
+ if (nameEl)
+ {
+ RangeVar *rv = makeRangeVarFromNameList(castNode(List, nameEl->arg));
+ snamespace = rv->schemaname;
+ sname = rv->relname;
+ seqoptions = list_delete_ptr(seqoptions, nameEl);
+ }
+ else
+ {
+ if (cxt->rel)
+ snamespaceid = RelationGetNamespace(cxt->rel);
+ else
+ {
+ snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
+ RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
+ }
+ snamespace = get_namespace_name(snamespaceid);
+ sname = ChooseRelationName(cxt->relation->relname,
+ column->colname,
+ "seq",
+ snamespaceid);
+ }
+
+ ereport(DEBUG1,
+ (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
+ cxt->stmtType, sname,
+ cxt->relation->relname, column->colname)));
+
+ /*
+ * Build a CREATE SEQUENCE command to create the sequence object, and
+ * add it to the list of things to be done before this CREATE/ALTER
+ * TABLE.
+ */
+ seqstmt = makeNode(CreateSeqStmt);
+ seqstmt->for_identity = for_identity;
+ seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
+ seqstmt->options = seqoptions;
+ /*
+ * If a sequence data type was specified, add it to the options. Prepend
+ * to the list rather than append; in case a user supplied their own AS
+ * clause, the "redundant options" error will point to their occurrence,
+ * not our synthetic one.
+ */
+ if (seqtypid)
+ seqstmt->options = lcons(makeDefElem("as", (Node *) makeTypeNameFromOid(seqtypid, -1), -1),
+ seqstmt->options);
+
+ /*
+ * If this is ALTER ADD COLUMN, make sure the sequence will be owned
+ * by the table's owner. The current user might be someone else
+ * (perhaps a superuser, or someone who's only a member of the owning
+ * role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
+ * and sequence have exactly the same owning role.
+ */
+ if (cxt->rel)
+ seqstmt->ownerId = cxt->rel->rd_rel->relowner;
+ else
+ seqstmt->ownerId = InvalidOid;
+
+ cxt->blist = lappend(cxt->blist, seqstmt);
+
+ /*
+ * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
+ * as owned by this column, and add it to the list of things to be
+ * done after this CREATE/ALTER TABLE.
+ */
+ altseqstmt = makeNode(AlterSeqStmt);
+ altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
+ attnamelist = list_make3(makeString(snamespace),
+ makeString(cxt->relation->relname),
+ makeString(column->colname));
+ altseqstmt->options = list_make1(makeDefElem("owned_by",
+ (Node *) attnamelist, -1));
+ altseqstmt->for_identity = for_identity;
+
+ cxt->alist = lappend(cxt->alist, altseqstmt);
+
+ if (snamespace_p)
+ *snamespace_p = snamespace;
+ if (sname_p)
+ *sname_p = sname;
+}
+
/*
* transformColumnDefinition -
* transform a single ColumnDef within CREATE TABLE
@@ -367,7 +494,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
bool is_serial;
bool saw_nullable;
bool saw_default;
- Constraint *constraint;
+ bool saw_identity;
ListCell *clist;
cxt->columns = lappend(cxt->columns, column);
@@ -422,83 +549,17 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
/* Special actions for SERIAL pseudo-types */
if (is_serial)
{
- Oid snamespaceid;
char *snamespace;
char *sname;
char *qstring;
A_Const *snamenode;
TypeCast *castnode;
FuncCall *funccallnode;
- CreateSeqStmt *seqstmt;
- AlterSeqStmt *altseqstmt;
- List *attnamelist;
+ Constraint *constraint;
- /*
- * Determine namespace and name to use for the sequence.
- *
- * Although we use ChooseRelationName, it's not guaranteed that the
- * selected sequence name won't conflict; given sufficiently long
- * field names, two different serial columns in the same table could
- * be assigned the same sequence name, and we'd not notice since we
- * aren't creating the sequence quite yet. In practice this seems
- * quite unlikely to be a problem, especially since few people would
- * need two serial columns in one table.
- */
- if (cxt->rel)
- snamespaceid = RelationGetNamespace(cxt->rel);
- else
- {
- snamespaceid = RangeVarGetCreationNamespace(cxt->relation);
- RangeVarAdjustRelationPersistence(cxt->relation, snamespaceid);
- }
- snamespace = get_namespace_name(snamespaceid);
- sname = ChooseRelationName(cxt->relation->relname,
- column->colname,
- "seq",
- snamespaceid);
-
- ereport(DEBUG1,
- (errmsg("%s will create implicit sequence \"%s\" for serial column \"%s.%s\"",
- cxt->stmtType, sname,
- cxt->relation->relname, column->colname)));
-
- /*
- * Build a CREATE SEQUENCE command to create the sequence object, and
- * add it to the list of things to be done before this CREATE/ALTER
- * TABLE.
- */
- seqstmt = makeNode(CreateSeqStmt);
- seqstmt->sequence = makeRangeVar(snamespace, sname, -1);
- seqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(column->typeName->typeOid, -1), -1));
-
- /*
- * If this is ALTER ADD COLUMN, make sure the sequence will be owned
- * by the table's owner. The current user might be someone else
- * (perhaps a superuser, or someone who's only a member of the owning
- * role), but the SEQUENCE OWNED BY mechanisms will bleat unless table
- * and sequence have exactly the same owning role.
- */
- if (cxt->rel)
- seqstmt->ownerId = cxt->rel->rd_rel->relowner;
- else
- seqstmt->ownerId = InvalidOid;
-
- cxt->blist = lappend(cxt->blist, seqstmt);
-
- /*
- * Build an ALTER SEQUENCE ... OWNED BY command to mark the sequence
- * as owned by this column, and add it to the list of things to be
- * done after this CREATE/ALTER TABLE.
- */
- altseqstmt = makeNode(AlterSeqStmt);
- altseqstmt->sequence = makeRangeVar(snamespace, sname, -1);
- attnamelist = list_make3(makeString(snamespace),
- makeString(cxt->relation->relname),
- makeString(column->colname));
- altseqstmt->options = list_make1(makeDefElem("owned_by",
- (Node *) attnamelist, -1));
-
- cxt->alist = lappend(cxt->alist, altseqstmt);
+ generateSerialExtraStmts(cxt, column,
+ column->typeName->typeOid, NIL, false,
+ &snamespace, &sname);
/*
* Create appropriate constraints for SERIAL. We do this in full,
@@ -540,10 +601,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
saw_nullable = false;
saw_default = false;
+ saw_identity = false;
foreach(clist, column->constraints)
{
- constraint = castNode(Constraint, lfirst(clist));
+ Constraint *constraint = castNode(Constraint, lfirst(clist));
switch (constraint->contype)
{
@@ -584,6 +646,33 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
saw_default = true;
break;
+ case CONSTR_IDENTITY:
+ {
+ Type ctype;
+ Oid typeOid;
+
+ ctype = typenameType(cxt->pstate, column->typeName, NULL);
+ typeOid = HeapTupleGetOid(ctype);
+ ReleaseSysCache(ctype);
+
+ if (saw_identity)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("multiple identity specifications for column \"%s\" of table \"%s\"",
+ column->colname, cxt->relation->relname),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
+
+ generateSerialExtraStmts(cxt, column,
+ typeOid, constraint->options, true,
+ NULL, NULL);
+
+ column->identity = constraint->generated_when;
+ saw_identity = true;
+ column->is_not_null = TRUE;
+ break;
+ }
+
case CONSTR_CHECK:
cxt->ckconstraints = lappend(cxt->ckconstraints, constraint);
break;
@@ -660,6 +749,14 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column)
constraint->contype);
break;
}
+
+ if (saw_default && saw_identity)
+ ereport(ERROR,
+ (errcode(ERRCODE_SYNTAX_ERROR),
+ errmsg("both default and identity specified for column \"%s\" of table \"%s\"",
+ column->colname, cxt->relation->relname),
+ parser_errposition(cxt->pstate,
+ constraint->location)));
}
/*
@@ -932,6 +1029,27 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
def->cooked_default = this_default;
}
+ /*
+ * Copy identity if requested
+ */
+ if (attribute->attidentity &&
+ (table_like_clause->options & CREATE_TABLE_LIKE_IDENTITY))
+ {
+ Oid seq_relid;
+ List *seq_options;
+
+ /*
+ * find sequence owned by old column; extract sequence parameters;
+ * build new create sequence command
+ */
+ seq_relid = getOwnedSequence(RelationGetRelid(relation), attribute->attnum);
+ seq_options = sequence_options(seq_relid);
+ generateSerialExtraStmts(cxt, def,
+ InvalidOid, seq_options, true,
+ NULL, NULL);
+ def->identity = attribute->attidentity;
+ }
+
/* Likewise, copy storage if requested */
if (table_like_clause->options & CREATE_TABLE_LIKE_STORAGE)
def->storage = attribute->attstorage;
@@ -2628,6 +2746,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
case AT_AlterColumnType:
{
ColumnDef *def = (ColumnDef *) cmd->def;
+ AttrNumber attnum;
/*
* For ALTER COLUMN TYPE, transform the USING clause if
@@ -2640,6 +2759,103 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
EXPR_KIND_ALTER_COL_TRANSFORM);
}
+ /*
+ * For identity column, create ALTER SEQUENCE command to
+ * change the data type of the sequence.
+ */
+ attnum = get_attnum(relid, cmd->name);
+ /* if attribute not found, something will error about it later */
+ if (attnum != InvalidAttrNumber && get_attidentity(relid, attnum))
+ {
+ Oid seq_relid = getOwnedSequence(relid, attnum);
+ Oid typeOid = typenameTypeId(pstate, def->typeName);
+ AlterSeqStmt *altseqstmt = makeNode(AlterSeqStmt);
+
+ altseqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
+ get_rel_name(seq_relid),
+ -1);
+ altseqstmt->options = list_make1(makeDefElem("as", (Node *) makeTypeNameFromOid(typeOid, -1), -1));
+ altseqstmt->for_identity = true;
+ cxt.blist = lappend(cxt.blist, altseqstmt);
+ }
+
+ newcmds = lappend(newcmds, cmd);
+ break;
+ }
+
+ case AT_AddIdentity:
+ {
+ Constraint *def = castNode(Constraint, cmd->def);
+ ColumnDef *newdef = makeNode(ColumnDef);
+ AttrNumber attnum;
+
+ newdef->colname = cmd->name;
+ newdef->identity = def->generated_when;
+ cmd->def = (Node *) newdef;
+
+ attnum = get_attnum(relid, cmd->name);
+ /* if attribute not found, something will error about it later */
+ if (attnum != InvalidAttrNumber)
+ generateSerialExtraStmts(&cxt, newdef,
+ get_atttype(relid, attnum),
+ def->options, true,
+ NULL, NULL);
+
+ newcmds = lappend(newcmds, cmd);
+ break;
+ }
+
+ case AT_SetIdentity:
+ {
+ /*
+ * Create an ALTER SEQUENCE statement for the internal
+ * sequence of the identity column.
+ */
+ ListCell *lc;
+ List *newseqopts = NIL;
+ List *newdef = NIL;
+ List *seqlist;
+ AttrNumber attnum;
+
+ /*
+ * Split options into those handled by ALTER SEQUENCE and
+ * those for ALTER TABLE proper.
+ */
+ foreach(lc, castNode(List, cmd->def))
+ {
+ DefElem *def = castNode(DefElem, lfirst(lc));
+
+ if (strcmp(def->defname, "generated") == 0)
+ newdef = lappend(newdef, def);
+ else
+ newseqopts = lappend(newseqopts, def);
+ }
+
+ attnum = get_attnum(relid, cmd->name);
+
+ if (attnum)
+ {
+ seqlist = getOwnedSequences(relid, attnum);
+ if (seqlist)
+ {
+ AlterSeqStmt *seqstmt;
+ Oid seq_relid;
+
+ seqstmt = makeNode(AlterSeqStmt);
+ seq_relid = linitial_oid(seqlist);
+ seqstmt->sequence = makeRangeVar(get_namespace_name(get_rel_namespace(seq_relid)),
+ get_rel_name(seq_relid), -1);
+ seqstmt->options = newseqopts;
+ seqstmt->for_identity = true;
+ seqstmt->missing_ok = false;
+
+ cxt.alist = lappend(cxt.alist, seqstmt);
+ }
+ }
+ /* If column was not found or was not an identity column, we
+ * just let the ALTER TABLE command error out later. */
+
+ cmd->def = (Node *) newdef;
newcmds = lappend(newcmds, cmd);
break;
}