2017-04-06 08:33:16 -04:00
|
|
|
-- sanity check of system catalog
|
|
|
|
|
SELECT attrelid, attname, attidentity FROM pg_attribute WHERE attidentity NOT IN ('', 'a', 'd');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest1 (a int generated by default as identity, b text);
|
|
|
|
|
CREATE TABLE itest2 (a bigint generated always as identity, b text);
|
|
|
|
|
CREATE TABLE itest3 (a smallint generated by default as identity (start with 7 increment by 5), b text);
|
|
|
|
|
ALTER TABLE itest3 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error
|
|
|
|
|
|
|
|
|
|
SELECT table_name, column_name, column_default, is_nullable, is_identity, identity_generation, identity_start, identity_increment, identity_maximum, identity_minimum, identity_cycle FROM information_schema.columns WHERE table_name LIKE 'itest_' ORDER BY 1, 2;
|
|
|
|
|
|
|
|
|
|
-- internal sequences should not be shown here
|
|
|
|
|
SELECT sequence_name FROM information_schema.sequences WHERE sequence_name LIKE 'itest%';
|
|
|
|
|
|
2017-09-15 14:04:51 -04:00
|
|
|
SELECT pg_get_serial_sequence('itest1', 'a');
|
|
|
|
|
|
2017-09-25 11:59:46 -04:00
|
|
|
\d itest1_a_seq
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
CREATE TABLE itest4 (a int, b text);
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, requires NOT NULL
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a SET NOT NULL;
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- ok
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL; -- error, disallowed
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY; -- error, already set
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN b ADD GENERATED ALWAYS AS IDENTITY; -- error, wrong data type
|
|
|
|
|
|
|
|
|
|
-- for later
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN b SET DEFAULT '';
|
|
|
|
|
|
|
|
|
|
-- invalid column type
|
|
|
|
|
CREATE TABLE itest_err_1 (a text generated by default as identity);
|
|
|
|
|
|
|
|
|
|
-- duplicate identity
|
|
|
|
|
CREATE TABLE itest_err_2 (a int generated always as identity generated by default as identity);
|
|
|
|
|
|
|
|
|
|
-- cannot have default and identity
|
|
|
|
|
CREATE TABLE itest_err_3 (a int default 5 generated by default as identity);
|
|
|
|
|
|
|
|
|
|
-- cannot combine serial and identity
|
|
|
|
|
CREATE TABLE itest_err_4 (a serial generated by default as identity);
|
|
|
|
|
|
|
|
|
|
INSERT INTO itest1 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest1 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest2 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest2 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest3 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest3 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest4 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest4 DEFAULT VALUES;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itest1;
|
|
|
|
|
SELECT * FROM itest2;
|
|
|
|
|
SELECT * FROM itest3;
|
|
|
|
|
SELECT * FROM itest4;
|
|
|
|
|
|
|
|
|
|
|
2018-02-02 14:20:50 -05:00
|
|
|
-- VALUES RTEs
|
|
|
|
|
|
|
|
|
|
INSERT INTO itest3 VALUES (DEFAULT, 'a');
|
|
|
|
|
INSERT INTO itest3 VALUES (DEFAULT, 'b'), (DEFAULT, 'c');
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itest3;
|
|
|
|
|
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
-- OVERRIDING tests
|
|
|
|
|
|
2020-03-31 02:40:32 -04:00
|
|
|
-- GENERATED BY DEFAULT
|
|
|
|
|
|
|
|
|
|
-- This inserts the row as presented:
|
2017-04-06 08:33:16 -04:00
|
|
|
INSERT INTO itest1 VALUES (10, 'xyz');
|
2020-03-31 02:40:32 -04:00
|
|
|
-- With GENERATED BY DEFAULT, OVERRIDING SYSTEM VALUE is not allowed
|
|
|
|
|
-- by the standard, but we allow it as a no-op, since it is of use if
|
|
|
|
|
-- there are multiple identity columns in a table, which is also an
|
|
|
|
|
-- extension.
|
|
|
|
|
INSERT INTO itest1 OVERRIDING SYSTEM VALUE VALUES (20, 'xyz');
|
|
|
|
|
-- This ignores the 30 and uses the sequence value instead:
|
|
|
|
|
INSERT INTO itest1 OVERRIDING USER VALUE VALUES (30, 'xyz');
|
2017-04-06 08:33:16 -04:00
|
|
|
|
|
|
|
|
SELECT * FROM itest1;
|
|
|
|
|
|
2020-03-31 02:40:32 -04:00
|
|
|
-- GENERATED ALWAYS
|
|
|
|
|
|
|
|
|
|
-- This is an error:
|
2017-04-06 08:33:16 -04:00
|
|
|
INSERT INTO itest2 VALUES (10, 'xyz');
|
2020-03-31 02:40:32 -04:00
|
|
|
-- This inserts the row as presented:
|
|
|
|
|
INSERT INTO itest2 OVERRIDING SYSTEM VALUE VALUES (20, 'xyz');
|
|
|
|
|
-- This ignores the 30 and uses the sequence value instead:
|
|
|
|
|
INSERT INTO itest2 OVERRIDING USER VALUE VALUES (30, 'xyz');
|
2017-04-06 08:33:16 -04:00
|
|
|
|
|
|
|
|
SELECT * FROM itest2;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- UPDATE tests
|
|
|
|
|
|
2020-03-31 02:40:32 -04:00
|
|
|
-- GENERATED BY DEFAULT is not restricted.
|
2017-04-06 08:33:16 -04:00
|
|
|
UPDATE itest1 SET a = 101 WHERE a = 1;
|
|
|
|
|
UPDATE itest1 SET a = DEFAULT WHERE a = 2;
|
|
|
|
|
SELECT * FROM itest1;
|
|
|
|
|
|
2020-03-31 02:40:32 -04:00
|
|
|
-- GENERATED ALWAYS allows only DEFAULT.
|
|
|
|
|
UPDATE itest2 SET a = 101 WHERE a = 1; -- error
|
|
|
|
|
UPDATE itest2 SET a = DEFAULT WHERE a = 2; -- ok
|
2017-04-06 08:33:16 -04:00
|
|
|
SELECT * FROM itest2;
|
|
|
|
|
|
|
|
|
|
|
2017-12-08 09:18:18 -05:00
|
|
|
-- COPY tests
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest9 (a int GENERATED ALWAYS AS IDENTITY, b text, c bigint);
|
|
|
|
|
|
|
|
|
|
COPY itest9 FROM stdin;
|
|
|
|
|
100 foo 200
|
|
|
|
|
101 bar 201
|
|
|
|
|
\.
|
|
|
|
|
|
|
|
|
|
COPY itest9 (b, c) FROM stdin;
|
|
|
|
|
foo2 202
|
|
|
|
|
bar2 203
|
|
|
|
|
\.
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itest9 ORDER BY c;
|
|
|
|
|
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
-- DROP IDENTITY tests
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY;
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY; -- error
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a DROP IDENTITY IF EXISTS; -- noop
|
|
|
|
|
|
|
|
|
|
INSERT INTO itest4 DEFAULT VALUES; -- fails because NOT NULL is not dropped
|
|
|
|
|
ALTER TABLE itest4 ALTER COLUMN a DROP NOT NULL;
|
|
|
|
|
INSERT INTO itest4 DEFAULT VALUES;
|
|
|
|
|
SELECT * FROM itest4;
|
|
|
|
|
|
|
|
|
|
-- check that sequence is removed
|
|
|
|
|
SELECT sequence_name FROM itest4_a_seq;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- test views
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest10 (a int generated by default as identity, b text);
|
|
|
|
|
CREATE TABLE itest11 (a int generated always as identity, b text);
|
|
|
|
|
|
|
|
|
|
CREATE VIEW itestv10 AS SELECT * FROM itest10;
|
|
|
|
|
CREATE VIEW itestv11 AS SELECT * FROM itest11;
|
|
|
|
|
|
|
|
|
|
INSERT INTO itestv10 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itestv10 DEFAULT VALUES;
|
|
|
|
|
|
|
|
|
|
INSERT INTO itestv11 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itestv11 DEFAULT VALUES;
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itestv10;
|
|
|
|
|
SELECT * FROM itestv11;
|
|
|
|
|
|
|
|
|
|
INSERT INTO itestv10 VALUES (10, 'xyz');
|
|
|
|
|
INSERT INTO itestv10 OVERRIDING USER VALUE VALUES (11, 'xyz');
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itestv10;
|
|
|
|
|
|
|
|
|
|
INSERT INTO itestv11 VALUES (10, 'xyz');
|
|
|
|
|
INSERT INTO itestv11 OVERRIDING SYSTEM VALUE VALUES (11, 'xyz');
|
|
|
|
|
|
|
|
|
|
SELECT * FROM itestv11;
|
|
|
|
|
|
2019-03-14 19:16:45 -04:00
|
|
|
DROP VIEW itestv10, itestv11;
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
|
2018-02-02 14:20:50 -05:00
|
|
|
-- ADD COLUMN
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest13 (a int);
|
|
|
|
|
-- add column to empty table
|
|
|
|
|
ALTER TABLE itest13 ADD COLUMN b int GENERATED BY DEFAULT AS IDENTITY;
|
|
|
|
|
INSERT INTO itest13 VALUES (1), (2), (3);
|
|
|
|
|
-- add column to populated table
|
|
|
|
|
ALTER TABLE itest13 ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY;
|
|
|
|
|
SELECT * FROM itest13;
|
|
|
|
|
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
-- various ALTER COLUMN tests
|
|
|
|
|
|
|
|
|
|
-- fail, not allowed for identity columns
|
|
|
|
|
ALTER TABLE itest1 ALTER COLUMN a SET DEFAULT 1;
|
|
|
|
|
|
|
|
|
|
-- fail, not allowed, already has a default
|
|
|
|
|
CREATE TABLE itest5 (a serial, b text);
|
|
|
|
|
ALTER TABLE itest5 ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest3 ALTER COLUMN a TYPE int;
|
|
|
|
|
SELECT seqtypid::regtype FROM pg_sequence WHERE seqrelid = 'itest3_a_seq'::regclass;
|
|
|
|
|
\d itest3
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest3 ALTER COLUMN a TYPE text; -- error
|
|
|
|
|
|
Avoid order-of-execution problems with ALTER TABLE ADD PRIMARY KEY.
Up to now, DefineIndex() was responsible for adding attnotnull constraints
to the columns of a primary key, in any case where it hadn't been
convenient for transformIndexConstraint() to mark those columns as
is_not_null. It (or rather its minion index_check_primary_key) did this
by executing an ALTER TABLE SET NOT NULL command for the target table.
The trouble with this solution is that if we're creating the index due
to ALTER TABLE ADD PRIMARY KEY, and the outer ALTER TABLE has additional
sub-commands, the inner ALTER TABLE's operations executed at the wrong
time with respect to the outer ALTER TABLE's operations. In particular,
the inner ALTER would perform a validation scan at a point where the
table's storage might be inconsistent with its catalog entries. (This is
on the hairy edge of being a security problem, but AFAICS it isn't one
because the inner scan would only be interested in the tuples' null
bitmaps.) This can result in unexpected failures, such as the one seen
in bug #15580 from Allison Kaptur.
To fix, let's remove the attempt to do SET NOT NULL from DefineIndex(),
reducing index_check_primary_key's role to verifying that the columns are
already not null. (It shouldn't ever see such a case, but it seems wise
to keep the check for safety.) Instead, make transformIndexConstraint()
generate ALTER TABLE SET NOT NULL subcommands to be executed ahead of
the ADD PRIMARY KEY operation in every case where it can't force the
column to be created already-not-null. This requires only minor surgery
in parse_utilcmd.c, and it makes for a much more satisfying spec for
transformIndexConstraint(): it's no longer having to take it on faith
that someone else will handle addition of NOT NULL constraints.
To make that work, we have to move the execution of AT_SetNotNull into
an ALTER pass that executes ahead of AT_PASS_ADD_INDEX. I moved it to
AT_PASS_COL_ATTRS, and put that after AT_PASS_ADD_COL to avoid failure
when the column is being added in the same command. This incidentally
fixes a bug in the only previous usage of AT_PASS_COL_ATTRS, for
AT_SetIdentity: it didn't work either for a newly-added column.
Playing around with this exposed a separate bug in ALTER TABLE ONLY ...
ADD PRIMARY KEY for partitioned tables. The intent of the ONLY modifier
in that context is to prevent doing anything that would require holding
lock for a long time --- but the implied SET NOT NULL would recurse to
the child partitions, and do an expensive validation scan for any child
where the column(s) were not already NOT NULL. To fix that, invent a
new ALTER subcommand AT_CheckNotNull that just insists that a child
column be already NOT NULL, and apply that, not AT_SetNotNull, when
recursing to children in this scenario. This results in a slightly laxer
definition of ALTER TABLE ONLY ... SET NOT NULL for partitioned tables,
too: that command will now work as long as all children are already NOT
NULL, whereas before it just threw up its hands if there were any
partitions.
In passing, clean up the API of generateClonedIndexStmt(): remove a
useless argument, ensure that the output argument is not left undefined,
update the header comment.
A small side effect of this change is that no-such-column errors in ALTER
TABLE ADD PRIMARY KEY now produce a different message that includes the
table name, because they are now detected by the SET NOT NULL step which
has historically worded its error that way. That seems fine to me, so
I didn't make any effort to avoid the wording change.
The basic bug #15580 is of very long standing, and these other bugs
aren't new in v12 either. However, this is a pretty significant change
in the way ALTER TABLE ADD PRIMARY KEY works. On balance it seems best
not to back-patch, at least not till we get some more confidence that
this patch has no new bugs.
Patch by me, but thanks to Jie Zhang for a preliminary version.
Discussion: https://postgr.es/m/15580-d1a6de5a3d65da51@postgresql.org
Discussion: https://postgr.es/m/1396E95157071C4EBBA51892C5368521017F2E6E63@G08CNEXMBPEKD02.g08.fujitsu.local
2019-04-23 12:25:27 -04:00
|
|
|
-- kinda silly to change property in the same command, but it should work
|
|
|
|
|
ALTER TABLE itest3
|
|
|
|
|
ADD COLUMN c int GENERATED BY DEFAULT AS IDENTITY,
|
|
|
|
|
ALTER COLUMN c SET GENERATED ALWAYS;
|
|
|
|
|
\d itest3
|
|
|
|
|
|
2017-04-06 08:33:16 -04:00
|
|
|
|
|
|
|
|
-- ALTER COLUMN ... SET
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest6 (a int GENERATED ALWAYS AS IDENTITY, b text);
|
|
|
|
|
INSERT INTO itest6 DEFAULT VALUES;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest6 ALTER COLUMN a SET GENERATED BY DEFAULT SET INCREMENT BY 2 SET START WITH 100 RESTART;
|
|
|
|
|
INSERT INTO itest6 DEFAULT VALUES;
|
|
|
|
|
INSERT INTO itest6 DEFAULT VALUES;
|
|
|
|
|
SELECT * FROM itest6;
|
|
|
|
|
|
|
|
|
|
SELECT table_name, column_name, is_identity, identity_generation FROM information_schema.columns WHERE table_name = 'itest6';
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest6 ALTER COLUMN b SET INCREMENT BY 2; -- fail, not identity
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- prohibited direct modification of sequence
|
|
|
|
|
|
|
|
|
|
ALTER SEQUENCE itest6_a_seq OWNED BY NONE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- inheritance
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest7 (a int GENERATED ALWAYS AS IDENTITY);
|
|
|
|
|
INSERT INTO itest7 DEFAULT VALUES;
|
|
|
|
|
SELECT * FROM itest7;
|
|
|
|
|
|
|
|
|
|
-- identity property is not inherited
|
|
|
|
|
CREATE TABLE itest7a (b text) INHERITS (itest7);
|
|
|
|
|
|
|
|
|
|
-- make column identity in child table
|
|
|
|
|
CREATE TABLE itest7b (a int);
|
|
|
|
|
CREATE TABLE itest7c (a int GENERATED ALWAYS AS IDENTITY) INHERITS (itest7b);
|
|
|
|
|
INSERT INTO itest7c DEFAULT VALUES;
|
|
|
|
|
SELECT * FROM itest7c;
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest7d (a int not null);
|
|
|
|
|
CREATE TABLE itest7e () INHERITS (itest7d);
|
|
|
|
|
ALTER TABLE itest7d ALTER COLUMN a ADD GENERATED ALWAYS AS IDENTITY;
|
|
|
|
|
ALTER TABLE itest7d ADD COLUMN b int GENERATED ALWAYS AS IDENTITY; -- error
|
|
|
|
|
|
|
|
|
|
SELECT table_name, column_name, is_nullable, is_identity, identity_generation FROM information_schema.columns WHERE table_name LIKE 'itest7%' ORDER BY 1, 2;
|
|
|
|
|
|
|
|
|
|
-- These ALTER TABLE variants will not recurse.
|
|
|
|
|
ALTER TABLE itest7 ALTER COLUMN a SET GENERATED BY DEFAULT;
|
|
|
|
|
ALTER TABLE itest7 ALTER COLUMN a RESTART;
|
|
|
|
|
ALTER TABLE itest7 ALTER COLUMN a DROP IDENTITY;
|
|
|
|
|
|
|
|
|
|
-- privileges
|
2018-03-15 14:00:31 -04:00
|
|
|
CREATE USER regress_identity_user1;
|
2017-04-06 08:33:16 -04:00
|
|
|
CREATE TABLE itest8 (a int GENERATED ALWAYS AS IDENTITY, b text);
|
2018-03-15 14:00:31 -04:00
|
|
|
GRANT SELECT, INSERT ON itest8 TO regress_identity_user1;
|
|
|
|
|
SET ROLE regress_identity_user1;
|
2017-04-06 08:33:16 -04:00
|
|
|
INSERT INTO itest8 DEFAULT VALUES;
|
|
|
|
|
SELECT * FROM itest8;
|
|
|
|
|
RESET ROLE;
|
|
|
|
|
DROP TABLE itest8;
|
2018-03-15 14:00:31 -04:00
|
|
|
DROP USER regress_identity_user1;
|
2017-12-08 12:13:04 -05:00
|
|
|
|
Restructure ALTER TABLE execution to fix assorted bugs.
We've had numerous bug reports about how (1) IF NOT EXISTS clauses in
ALTER TABLE don't behave as-expected, and (2) combining certain actions
into one ALTER TABLE doesn't work, though executing the same actions as
separate statements does. This patch cleans up all of the cases so far
reported from the field, though there are still some oddities associated
with identity columns.
The core problem behind all of these bugs is that we do parse analysis
of ALTER TABLE subcommands too soon, before starting execution of the
statement. The root of the bugs in group (1) is that parse analysis
schedules derived commands (such as a CREATE SEQUENCE for a serial
column) before it's known whether the IF NOT EXISTS clause should cause
a subcommand to be skipped. The root of the bugs in group (2) is that
earlier subcommands may change the catalog state that later subcommands
need to be parsed against.
Hence, postpone parse analysis of ALTER TABLE's subcommands, and do
that one subcommand at a time, during "phase 2" of ALTER TABLE which
is the phase that does catalog rewrites. Thus the catalog effects
of earlier subcommands are already visible when we analyze later ones.
(The sole exception is that we do parse analysis for ALTER COLUMN TYPE
subcommands during phase 1, so that their USING expressions can be
parsed against the table's original state, which is what we need.
Arguably, these bugs stem from falsely concluding that because ALTER
COLUMN TYPE must do early parse analysis, every other command subtype
can too.)
This means that ALTER TABLE itself must deal with execution of any
non-ALTER-TABLE derived statements that are generated by parse analysis.
Add a suitable entry point to utility.c to accept those recursive
calls, and create a struct to pass through the information needed by
the recursive call, rather than making the argument lists of
AlterTable() and friends even longer.
Getting this to work correctly required a little bit of fiddling
with the subcommand pass structure, in particular breaking up
AT_PASS_ADD_CONSTR into multiple passes. But otherwise it's mostly
a pretty straightforward application of the above ideas.
Fixing the residual issues for identity columns requires refactoring of
where the dependency link from an identity column to its sequence gets
set up. So that seems like suitable material for a separate patch,
especially since this one is pretty big already.
Discussion: https://postgr.es/m/10365.1558909428@sss.pgh.pa.us
2020-01-15 18:49:24 -05:00
|
|
|
-- multiple steps in ALTER TABLE
|
|
|
|
|
CREATE TABLE itest8 (f1 int);
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ADD COLUMN f2 int NOT NULL,
|
|
|
|
|
ALTER COLUMN f2 ADD GENERATED ALWAYS AS IDENTITY;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ADD COLUMN f3 int NOT NULL,
|
|
|
|
|
ALTER COLUMN f3 ADD GENERATED ALWAYS AS IDENTITY,
|
|
|
|
|
ALTER COLUMN f3 SET GENERATED BY DEFAULT SET INCREMENT 10;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ADD COLUMN f4 int;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ALTER COLUMN f4 SET NOT NULL,
|
|
|
|
|
ALTER COLUMN f4 ADD GENERATED ALWAYS AS IDENTITY,
|
|
|
|
|
ALTER COLUMN f4 SET DATA TYPE bigint;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ADD COLUMN f5 int GENERATED ALWAYS AS IDENTITY;
|
|
|
|
|
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ALTER COLUMN f5 DROP IDENTITY,
|
|
|
|
|
ALTER COLUMN f5 DROP NOT NULL,
|
|
|
|
|
ALTER COLUMN f5 SET DATA TYPE bigint;
|
|
|
|
|
|
|
|
|
|
INSERT INTO itest8 VALUES(0), (1);
|
|
|
|
|
|
Clarify behavior of adding and altering a column in same ALTER command.
The behavior of something like
ALTER TABLE transactions
ADD COLUMN status varchar(30) DEFAULT 'old',
ALTER COLUMN status SET default 'current';
is to fill existing table rows with 'old', not 'current'. That's
intentional and desirable for a couple of reasons:
* It makes the behavior the same whether you merge the sub-commands
into one ALTER command or give them separately;
* If we applied the new default while filling the table, there would
be no way to get the existing behavior in one SQL command.
The same reasoning applies in cases that add a column and then
manipulate its GENERATED/IDENTITY status in a second sub-command,
since the generation expression is really just a kind of default.
However, that wasn't very obvious (at least not to me; earlier in
the referenced discussion thread I'd thought it was a bug to be
fixed). And it certainly wasn't documented.
Hence, add documentation, code comments, and a test case to clarify
that this behavior is all intentional.
In passing, adjust ATExecAddColumn's defaults-related relkind check
so that it matches up exactly with ATRewriteTables, instead of being
effectively (though not literally) the negated inverse condition.
The reasoning can be explained a lot more concisely that way, too
(not to mention that the comment now matches the code, which it
did not before).
Discussion: https://postgr.es/m/10365.1558909428@sss.pgh.pa.us
2020-01-21 16:17:21 -05:00
|
|
|
-- This does not work when the table isn't empty. That's intentional,
|
|
|
|
|
-- since ADD GENERATED should only affect later insertions:
|
|
|
|
|
ALTER TABLE itest8
|
|
|
|
|
ADD COLUMN f22 int NOT NULL,
|
|
|
|
|
ALTER COLUMN f22 ADD GENERATED ALWAYS AS IDENTITY;
|
|
|
|
|
|
Restructure ALTER TABLE execution to fix assorted bugs.
We've had numerous bug reports about how (1) IF NOT EXISTS clauses in
ALTER TABLE don't behave as-expected, and (2) combining certain actions
into one ALTER TABLE doesn't work, though executing the same actions as
separate statements does. This patch cleans up all of the cases so far
reported from the field, though there are still some oddities associated
with identity columns.
The core problem behind all of these bugs is that we do parse analysis
of ALTER TABLE subcommands too soon, before starting execution of the
statement. The root of the bugs in group (1) is that parse analysis
schedules derived commands (such as a CREATE SEQUENCE for a serial
column) before it's known whether the IF NOT EXISTS clause should cause
a subcommand to be skipped. The root of the bugs in group (2) is that
earlier subcommands may change the catalog state that later subcommands
need to be parsed against.
Hence, postpone parse analysis of ALTER TABLE's subcommands, and do
that one subcommand at a time, during "phase 2" of ALTER TABLE which
is the phase that does catalog rewrites. Thus the catalog effects
of earlier subcommands are already visible when we analyze later ones.
(The sole exception is that we do parse analysis for ALTER COLUMN TYPE
subcommands during phase 1, so that their USING expressions can be
parsed against the table's original state, which is what we need.
Arguably, these bugs stem from falsely concluding that because ALTER
COLUMN TYPE must do early parse analysis, every other command subtype
can too.)
This means that ALTER TABLE itself must deal with execution of any
non-ALTER-TABLE derived statements that are generated by parse analysis.
Add a suitable entry point to utility.c to accept those recursive
calls, and create a struct to pass through the information needed by
the recursive call, rather than making the argument lists of
AlterTable() and friends even longer.
Getting this to work correctly required a little bit of fiddling
with the subcommand pass structure, in particular breaking up
AT_PASS_ADD_CONSTR into multiple passes. But otherwise it's mostly
a pretty straightforward application of the above ideas.
Fixing the residual issues for identity columns requires refactoring of
where the dependency link from an identity column to its sequence gets
set up. So that seems like suitable material for a separate patch,
especially since this one is pretty big already.
Discussion: https://postgr.es/m/10365.1558909428@sss.pgh.pa.us
2020-01-15 18:49:24 -05:00
|
|
|
TABLE itest8;
|
|
|
|
|
\d+ itest8
|
|
|
|
|
\d itest8_f2_seq
|
|
|
|
|
\d itest8_f3_seq
|
|
|
|
|
\d itest8_f4_seq
|
|
|
|
|
\d itest8_f5_seq
|
|
|
|
|
DROP TABLE itest8;
|
|
|
|
|
|
2017-12-08 12:13:04 -05:00
|
|
|
|
|
|
|
|
-- typed tables (currently not supported)
|
|
|
|
|
|
|
|
|
|
CREATE TYPE itest_type AS (f1 integer, f2 text, f3 bigint);
|
|
|
|
|
CREATE TABLE itest12 OF itest_type (f1 WITH OPTIONS GENERATED ALWAYS AS IDENTITY); -- error
|
|
|
|
|
DROP TYPE itest_type CASCADE;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- table partitions (currently not supported)
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest_parent (f1 date NOT NULL, f2 text, f3 bigint) PARTITION BY RANGE (f1);
|
|
|
|
|
CREATE TABLE itest_child PARTITION OF itest_parent (
|
|
|
|
|
f3 WITH OPTIONS GENERATED ALWAYS AS IDENTITY
|
|
|
|
|
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01'); -- error
|
|
|
|
|
DROP TABLE itest_parent;
|
2019-07-22 06:05:03 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
-- test that sequence of half-dropped serial column is properly ignored
|
|
|
|
|
|
|
|
|
|
CREATE TABLE itest14 (id serial);
|
|
|
|
|
ALTER TABLE itest14 ALTER id DROP DEFAULT;
|
|
|
|
|
ALTER TABLE itest14 ALTER id ADD GENERATED BY DEFAULT AS IDENTITY;
|
|
|
|
|
INSERT INTO itest14 (id) VALUES (DEFAULT);
|