From a6caf5eac2741759c29255d562571f60878515f5 Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 9 Nov 2017 11:31:58 +0900 Subject: [PATCH 1/2] Make IDENTITY work correctly with typed tables In the case of CREATE TABLE OF, transformColumnDefinition should look at the ColumnDef added by transformOfType(), not the one passed down by gram.y which is mostly dummy. Authors: Michael Paquier, Amit Langote --- src/backend/commands/tablecmds.c | 1 + src/backend/parser/parse_utilcmd.c | 53 ++++++++++++++++++++++++++++++---- src/test/regress/expected/identity.out | 21 ++++++++++++++ src/test/regress/sql/identity.sql | 11 +++++++ 4 files changed, 81 insertions(+), 5 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index b7ddb335d2..192de790c4 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1754,6 +1754,7 @@ MergeAttributes(List *schema, List *supers, char relpersistence, coldef->raw_default = restdef->raw_default; coldef->cooked_default = restdef->cooked_default; coldef->constraints = restdef->constraints; + coldef->identity = restdef->identity; coldef->is_from_type = false; list_delete_cell(schema, rest, prev); } diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 30fc2d9ff8..1be36b846a 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -92,6 +92,7 @@ typedef struct IndexStmt *pkey; /* PRIMARY KEY index, if any */ bool ispartitioned; /* true if table is partitioned */ PartitionBoundSpec *partbound; /* transformed FOR VALUES */ + bool ofType; /* true if statement contains OF typeName */ } CreateStmtContext; /* State shared by transformCreateSchemaStmt and its subroutines */ @@ -240,6 +241,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) cxt.alist = NIL; cxt.pkey = NULL; cxt.ispartitioned = stmt->partspec != NULL; + cxt.ofType = stmt->ofTypename != NULL; /* * Notice that we allow OIDs here only for plain tables, even though @@ -659,13 +661,8 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) 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), @@ -674,6 +671,51 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) parser_errposition(cxt->pstate, constraint->location))); + /* + * If this is CREATE TABLE OF, use the ColumnDef added by + * transformOfType(). + */ + if (cxt->ofType) + { + ListCell *lc; + ColumnDef *col; + + foreach (lc, cxt->columns) + { + col = lfirst_node(ColumnDef, lc); + + /* + * ColumnDef's coming from the type, which have + * a valid typeName set, are ahead in the list; + * only look at those. + */ + if (col->typeName && + strcmp(col->colname, column->colname) == 0) + break; + } + + /* + * None of the ColumnDef's coming from the type + * matched. + */ + if (col->typeName == NULL) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_COLUMN), + errmsg("column \"%s\" does not exist", + column->colname))); + + typeOid = col->typeName->typeOid; + } + else + { + Type ctype; + + ctype = typenameType(cxt->pstate, column->typeName, NULL); + typeOid = HeapTupleGetOid(ctype); + ReleaseSysCache(ctype); + } + + Assert(OidIsValid(typeOid)); generateSerialExtraStmts(cxt, column, typeOid, constraint->options, true, NULL, NULL); @@ -2697,6 +2739,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, cxt.pkey = NULL; cxt.ispartitioned = (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE); cxt.partbound = NULL; + cxt.ofType = false; /* * The only subtypes that currently require parse transformation handling diff --git a/src/test/regress/expected/identity.out b/src/test/regress/expected/identity.out index 5fa585d6cc..787edd4d06 100644 --- a/src/test/regress/expected/identity.out +++ b/src/test/regress/expected/identity.out @@ -333,3 +333,24 @@ SELECT * FROM itest8; RESET ROLE; DROP TABLE itest8; DROP USER regress_user1; +-- typed table +CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint); +CREATE TABLE itest9 OF itest_type ( + f4 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error +ERROR: column "f4" does not exist +CREATE TABLE itest9 OF itest_type ( + f2 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error +ERROR: identity column type must be smallint, integer, or bigint +CREATE TABLE itest9 OF itest_type ( + f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- ok +\d itest9 + Table "public.itest9" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+------------------------------ + f1 | integer | | not null | generated always as identity + f2 | text | | | + f3 | bigint | | | +Typed table of type: itest_type + +DROP TYPE itest_type CASCADE; +NOTICE: drop cascades to table itest9 diff --git a/src/test/regress/sql/identity.sql b/src/test/regress/sql/identity.sql index e1b5a074c9..b98180f644 100644 --- a/src/test/regress/sql/identity.sql +++ b/src/test/regress/sql/identity.sql @@ -194,3 +194,14 @@ SELECT * FROM itest8; RESET ROLE; DROP TABLE itest8; DROP USER regress_user1; + +-- typed table +CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint); +CREATE TABLE itest9 OF itest_type ( + f4 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error +CREATE TABLE itest9 OF itest_type ( + f2 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error +CREATE TABLE itest9 OF itest_type ( + f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- ok +\d itest9 +DROP TYPE itest_type CASCADE; -- 2.11.0