From c3c322c277e58205b7074bbc504f94029f98a0bd Mon Sep 17 00:00:00 2001 From: Peter Eisentraut Date: Tue, 11 Oct 2022 08:24:34 +0200 Subject: [PATCH v2 1/2] WIP: Change serial types to map to identity columns This changes serial types to convert internally to identity columns, instead of the custom construction involving nextval defaults that they previously did. --- contrib/test_decoding/expected/ddl.out | 50 ++++++------- doc/src/sgml/datatype.sgml | 37 +++------ doc/src/sgml/extend.sgml | 10 +-- doc/src/sgml/func.sgml | 12 +-- src/backend/parser/parse_utilcmd.c | 75 +++---------------- src/test/regress/expected/publication.out | 30 ++++---- .../regress/expected/replica_identity.out | 30 ++++---- 7 files changed, 80 insertions(+), 164 deletions(-) diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index 9a28b5ddc5aa..9363d140d2af 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -484,12 +484,12 @@ CREATE TABLE replication_metadata ( WITH (user_catalog_table = true) ; \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | | not null | | plain | | - options | text[] | | | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=true @@ -498,12 +498,12 @@ INSERT INTO replication_metadata(relation, options) VALUES ('foo', ARRAY['a', 'b']); ALTER TABLE replication_metadata RESET (user_catalog_table); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | | not null | | plain | | - options | text[] | | | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) @@ -511,12 +511,12 @@ INSERT INTO replication_metadata(relation, options) VALUES ('bar', ARRAY['a', 'b']); ALTER TABLE replication_metadata SET (user_catalog_table = true); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | | not null | | plain | | - options | text[] | | | | extended | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=true @@ -529,13 +529,13 @@ ALTER TABLE replication_metadata ALTER COLUMN rewritemeornot TYPE text; ERROR: cannot rewrite table "replication_metadata" used as a catalog table ALTER TABLE replication_metadata SET (user_catalog_table = false); \d+ replication_metadata - Table "public.replication_metadata" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description -----------------+---------+-----------+----------+--------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('replication_metadata_id_seq'::regclass) | plain | | - relation | name | | not null | | plain | | - options | text[] | | | | extended | | - rewritemeornot | integer | | | | plain | | + Table "public.replication_metadata" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +----------------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + relation | name | | not null | | plain | | + options | text[] | | | | extended | | + rewritemeornot | integer | | | | plain | | Indexes: "replication_metadata_pkey" PRIMARY KEY, btree (id) Options: user_catalog_table=false diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index b030b36002f4..7c5c0a002c24 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -951,21 +951,11 @@ Serial Types and serial type - - - This section describes a PostgreSQL-specific way to create an - autoincrementing column. Another way is to use the SQL-standard - identity column feature, described at . - - - The data types smallserial, serial and bigserial are not true types, but merely - a notational convenience for creating unique identifier columns - (similar to the AUTO_INCREMENT property - supported by some other databases). In the current - implementation, specifying: + a notational convenience for creating unique identifier columns. + In the current implementation, specifying: CREATE TABLE tablename ( @@ -976,21 +966,17 @@ Serial Types is equivalent to specifying: -CREATE SEQUENCE tablename_colname_seq AS integer; CREATE TABLE tablename ( - colname integer NOT NULL DEFAULT nextval('tablename_colname_seq') + colname integer GENERATED BY DEFAULT AS IDENTITY ); -ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname; - Thus, we have created an integer column and arranged for its default - values to be assigned from a sequence generator. A NOT NULL - constraint is applied to ensure that a null value cannot be - inserted. (In most cases you would also want to attach a - UNIQUE or PRIMARY KEY constraint to prevent - duplicate values from being inserted by accident, but this is - not automatic.) Lastly, the sequence is marked as owned by - the column, so that it will be dropped if the column or table is dropped. + Thus, we have created an integer column and arranged for its values to be + assigned from a sequence generator by default. (In most cases you would + also want to attach a UNIQUE or PRIMARY + KEY constraint to prevent duplicate values from being inserted + by accident, but this is not automatic.) See for more details about this syntax. @@ -1031,8 +1017,9 @@ Serial Types The sequence created for a serial column is automatically dropped when the owning column is dropped. - You can drop the sequence without dropping the column, but this - will force removal of the column default expression. + You cannot drop the sequence directly, but this can be done using the + command ALTER TABLE ... ALTER COLUMN .... DROP + IDENTITY. diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index 46e873a1661b..d0b0d42c776a 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -1003,9 +1003,7 @@ Extension Configuration Tables SELECT pg_catalog.pg_extension_config_dump('my_config', ''); SELECT pg_catalog.pg_extension_config_dump('my_config_seq', ''); - Any number of tables or sequences can be marked this way. Sequences - associated with serial or bigserial columns can - be marked as well. + Any number of tables or sequences can be marked this way. @@ -1055,12 +1053,6 @@ Extension Configuration Tables out but the dump will not be able to be restored directly and user intervention will be required. - - - Sequences associated with serial or bigserial columns - need to be directly marked to dump their state. Marking their parent - relation is not enough for this purpose. - diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index b2bdbc7d1ccd..15e22867cd02 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -23746,15 +23746,9 @@ System Catalog Information Functions or NULL if no sequence is associated with the column. If the column is an identity column, the associated sequence is the sequence internally created for that column. - For columns created using one of the serial types - (serial, smallserial, bigserial), - it is the sequence created for that serial column definition. - In the latter case, the association can be modified or removed - with ALTER SEQUENCE OWNED BY. - (This function probably should have been - called pg_get_owned_sequence; its current name - reflects the fact that it has historically been used with serial-type - columns.) The first parameter is a table name with optional + For sequences associated with a column by with ALTER SEQUENCE + OWNED BY, it is that sequence. + The first parameter is a table name with optional schema, and the second parameter is a column name. Because the first parameter potentially contains both schema and table names, it is parsed per usual SQL rules, meaning it is lower-cased by default. diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index bd068bba05e4..f29a48060755 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -361,15 +361,12 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString) * Generate CREATE SEQUENCE and ALTER SEQUENCE ... OWNED BY statements * to create the sequence for a serial or identity column. * - * This includes determining the name the sequence will have. The caller - * can ask to get back the name components by passing non-null pointers - * for snamespace_p and sname_p. + * This includes determining the name the sequence will have. */ static void generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, Oid seqtypid, List *seqoptions, - bool for_identity, bool col_exists, - char **snamespace_p, char **sname_p) + bool col_exists) { ListCell *option; DefElem *nameEl = NULL; @@ -453,7 +450,7 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, * it to the list of things to be done before this CREATE/ALTER TABLE. */ seqstmt = makeNode(CreateSeqStmt); - seqstmt->for_identity = for_identity; + seqstmt->for_identity = true; seqstmt->sequence = makeRangeVar(snamespace, sname, -1); seqstmt->sequence->relpersistence = cxt->relation->relpersistence; seqstmt->options = seqoptions; @@ -508,17 +505,12 @@ generateSerialExtraStmts(CreateStmtContext *cxt, ColumnDef *column, makeString(column->colname)); altseqstmt->options = list_make1(makeDefElem("owned_by", (Node *) attnamelist, -1)); - altseqstmt->for_identity = for_identity; + altseqstmt->for_identity = true; if (col_exists) cxt->blist = lappend(cxt->blist, altseqstmt); else cxt->alist = lappend(cxt->alist, altseqstmt); - - if (snamespace_p) - *snamespace_p = snamespace; - if (sname_p) - *sname_p = sname; } /* @@ -588,51 +580,11 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) /* Special actions for SERIAL pseudo-types */ if (is_serial) { - char *snamespace; - char *sname; - char *qstring; - A_Const *snamenode; - TypeCast *castnode; - FuncCall *funccallnode; Constraint *constraint; - generateSerialExtraStmts(cxt, column, - column->typeName->typeOid, NIL, - false, false, - &snamespace, &sname); - - /* - * Create appropriate constraints for SERIAL. We do this in full, - * rather than shortcutting, so that we will detect any conflicting - * constraints the user wrote (like a different DEFAULT). - * - * Create an expression tree representing the function call - * nextval('sequencename'). We cannot reduce the raw tree to cooked - * form until after the sequence is created, but there's no need to do - * so. - */ - qstring = quote_qualified_identifier(snamespace, sname); - snamenode = makeNode(A_Const); - snamenode->val.node.type = T_String; - snamenode->val.sval.sval = qstring; - snamenode->location = -1; - castnode = makeNode(TypeCast); - castnode->typeName = SystemTypeName("regclass"); - castnode->arg = (Node *) snamenode; - castnode->location = -1; - funccallnode = makeFuncCall(SystemFuncName("nextval"), - list_make1(castnode), - COERCE_EXPLICIT_CALL, - -1); - constraint = makeNode(Constraint); - constraint->contype = CONSTR_DEFAULT; - constraint->location = -1; - constraint->raw_expr = (Node *) funccallnode; - constraint->cooked_expr = NULL; - column->constraints = lappend(column->constraints, constraint); - constraint = makeNode(Constraint); - constraint->contype = CONSTR_NOTNULL; + constraint->contype = CONSTR_IDENTITY; + constraint->generated_when = ATTRIBUTE_IDENTITY_BY_DEFAULT; constraint->location = -1; column->constraints = lappend(column->constraints, constraint); } @@ -714,10 +666,7 @@ transformColumnDefinition(CreateStmtContext *cxt, ColumnDef *column) parser_errposition(cxt->pstate, constraint->location))); - generateSerialExtraStmts(cxt, column, - typeOid, constraint->options, - true, false, - NULL, NULL); + generateSerialExtraStmts(cxt, column, typeOid, constraint->options, false); column->identity = constraint->generated_when; saw_identity = true; @@ -1082,10 +1031,7 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla */ seq_relid = getIdentitySequence(RelationGetRelid(relation), attribute->attnum, false); seq_options = sequence_options(seq_relid); - generateSerialExtraStmts(cxt, def, - InvalidOid, seq_options, - true, false, - NULL, NULL); + generateSerialExtraStmts(cxt, def, InvalidOid, seq_options, false); def->identity = attribute->attidentity; } @@ -3468,10 +3414,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt, errmsg("column \"%s\" of relation \"%s\" does not exist", cmd->name, RelationGetRelationName(rel)))); - generateSerialExtraStmts(&cxt, newdef, - get_atttype(relid, attnum), - def->options, true, true, - NULL, NULL); + generateSerialExtraStmts(&cxt, newdef, get_atttype(relid, attnum), def->options, true); newcmds = lappend(newcmds, cmd); break; diff --git a/src/test/regress/expected/publication.out b/src/test/regress/expected/publication.out index 427f87ea0771..0011f8e2e3c3 100644 --- a/src/test/regress/expected/publication.out +++ b/src/test/regress/expected/publication.out @@ -175,11 +175,11 @@ SELECT pubname, puballtables FROM pg_publication WHERE pubname = 'testpub_forall (1 row) \d+ testpub_tbl2 - Table "public.testpub_tbl2" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl2_id_seq'::regclass) | plain | | - data | text | | | | extended | | + Table "public.testpub_tbl2" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + data | text | | | | extended | | Indexes: "testpub_tbl2_pkey" PRIMARY KEY, btree (id) Publications: @@ -1127,11 +1127,11 @@ Publications: "testpub_fortbl" \d+ testpub_tbl1 - Table "public.testpub_tbl1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | - data | text | | | | extended | | + Table "public.testpub_tbl1" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + data | text | | | | extended | | Indexes: "testpub_tbl1_pkey" PRIMARY KEY, btree (id) Publications: @@ -1153,11 +1153,11 @@ ALTER PUBLICATION testpub_default DROP TABLE testpub_tbl1, pub_test.testpub_nopk ALTER PUBLICATION testpub_default DROP TABLE pub_test.testpub_nopk; ERROR: relation "testpub_nopk" is not part of the publication \d+ testpub_tbl1 - Table "public.testpub_tbl1" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('testpub_tbl1_id_seq'::regclass) | plain | | - data | text | | | | extended | | + Table "public.testpub_tbl1" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + data | text | | | | extended | | Indexes: "testpub_tbl1_pkey" PRIMARY KEY, btree (id) Publications: diff --git a/src/test/regress/expected/replica_identity.out b/src/test/regress/expected/replica_identity.out index e25ec06a8424..15cebeab62d6 100644 --- a/src/test/regress/expected/replica_identity.out +++ b/src/test/regress/expected/replica_identity.out @@ -75,10 +75,10 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) + Table "public.test_replica_identity" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+---------------------------------- + id | integer | | not null | generated by default as identity keya | text | | not null | keyb | text | | not null | nonkey | text | | | @@ -105,10 +105,10 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default ---------+---------+-----------+----------+--------------------------------------------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) + Table "public.test_replica_identity" + Column | Type | Collation | Nullable | Default +--------+---------+-----------+----------+---------------------------------- + id | integer | | not null | generated by default as identity keya | text | | not null | keyb | text | | not null | nonkey | text | | | @@ -153,13 +153,13 @@ SELECT relreplident FROM pg_class WHERE oid = 'test_replica_identity'::regclass; (1 row) \d+ test_replica_identity - Table "public.test_replica_identity" - Column | Type | Collation | Nullable | Default | Storage | Stats target | Description ---------+---------+-----------+----------+---------------------------------------------------+----------+--------------+------------- - id | integer | | not null | nextval('test_replica_identity_id_seq'::regclass) | plain | | - keya | text | | not null | | extended | | - keyb | text | | not null | | extended | | - nonkey | text | | | | extended | | + Table "public.test_replica_identity" + Column | Type | Collation | Nullable | Default | Storage | Stats target | Description +--------+---------+-----------+----------+----------------------------------+----------+--------------+------------- + id | integer | | not null | generated by default as identity | plain | | + keya | text | | not null | | extended | | + keyb | text | | not null | | extended | | + nonkey | text | | | | extended | | Indexes: "test_replica_identity_pkey" PRIMARY KEY, btree (id) "test_replica_identity_expr" UNIQUE, btree (keya, keyb, (3)) -- 2.37.3