mirror of
https://github.com/postgres/postgres.git
synced 2025-07-02 09:02:37 +03:00
Fix handling of CREATE TABLE LIKE with inheritance.
If a CREATE TABLE command uses both LIKE and traditional inheritance, Vars in CHECK constraints and expression indexes that are absorbed from a LIKE parent table tended to get mis-numbered, resulting in wrong answers and/or bizarre error messages (though probably not any actual crashes, thanks to validation occurring in the executor). In v12 and up, the same could happen to Vars in GENERATED expressions, even in cases with no LIKE clause but multiple traditional-inheritance parents. The cause of the problem for LIKE is that parse_utilcmd.c supposed it could renumber such Vars correctly during transformCreateStmt(), which it cannot since we have not yet accounted for columns added via inheritance. Fix that by postponing processing of LIKE INCLUDING CONSTRAINTS, DEFAULTS, GENERATED, INDEXES till after we've performed DefineRelation(). The error with GENERATED and multiple inheritance is a simple oversight in MergeAttributes(); it knows it has to renumber Vars in inherited CHECK constraints, but forgot to apply the same processing to inherited GENERATED expressions (a/k/a defaults). Per bug #16272 from Tom Gottfried. The non-GENERATED variants of the issue are ancient, presumably dating right back to the addition of CREATE TABLE LIKE; hence back-patch to all supported branches. Discussion: https://postgr.es/m/16272-6e32da020e9a9381@postgresql.org
This commit is contained in:
@ -135,6 +135,8 @@ CREATE TABLE like_fkey_table (
|
||||
INCLUDING STORAGE
|
||||
);
|
||||
NOTICE: DDL test: type simple, tag CREATE TABLE
|
||||
NOTICE: DDL test: type alter table, tag ALTER TABLE
|
||||
NOTICE: subcommand: ALTER COLUMN SET DEFAULT (precooked)
|
||||
NOTICE: DDL test: type simple, tag CREATE INDEX
|
||||
NOTICE: DDL test: type simple, tag CREATE INDEX
|
||||
-- Volatile table types
|
||||
|
@ -111,6 +111,9 @@ get_altertable_subcmdtypes(PG_FUNCTION_ARGS)
|
||||
case AT_ColumnDefault:
|
||||
strtype = "ALTER COLUMN SET DEFAULT";
|
||||
break;
|
||||
case AT_CookedColumnDefault:
|
||||
strtype = "ALTER COLUMN SET DEFAULT (precooked)";
|
||||
break;
|
||||
case AT_DropNotNull:
|
||||
strtype = "DROP NOT NULL";
|
||||
break;
|
||||
|
@ -160,7 +160,9 @@ SELECT * FROM test_like_gen_3;
|
||||
|
||||
DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3;
|
||||
-- also test generated column with a "forward" reference (bug #16342)
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int);
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42,
|
||||
c int GENERATED ALWAYS AS (a * 2) STORED,
|
||||
a int CHECK (a > 0));
|
||||
\d test_like_4
|
||||
Table "public.test_like_4"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
@ -168,6 +170,8 @@ CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) ST
|
||||
b | integer | | | 42
|
||||
c | integer | | | generated always as (a * 2) stored
|
||||
a | integer | | |
|
||||
Check constraints:
|
||||
"test_like_4_a_check" CHECK (a > 0)
|
||||
|
||||
CREATE TABLE test_like_4a (LIKE test_like_4);
|
||||
CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS);
|
||||
@ -233,7 +237,32 @@ SELECT a, b, c FROM test_like_4d;
|
||||
11 | 42 | 22
|
||||
(1 row)
|
||||
|
||||
-- Test renumbering of Vars when combining LIKE with inheritance
|
||||
CREATE TABLE test_like_5 (x point, y point, z point);
|
||||
CREATE TABLE test_like_5x (p int CHECK (p > 0),
|
||||
q int GENERATED ALWAYS AS (p * 2) STORED);
|
||||
CREATE TABLE test_like_5c (LIKE test_like_4 INCLUDING ALL)
|
||||
INHERITS (test_like_5, test_like_5x);
|
||||
\d test_like_5c
|
||||
Table "public.test_like_5c"
|
||||
Column | Type | Collation | Nullable | Default
|
||||
--------+---------+-----------+----------+------------------------------------
|
||||
x | point | | |
|
||||
y | point | | |
|
||||
z | point | | |
|
||||
p | integer | | |
|
||||
q | integer | | | generated always as (p * 2) stored
|
||||
b | integer | | | 42
|
||||
c | integer | | | generated always as (a * 2) stored
|
||||
a | integer | | |
|
||||
Check constraints:
|
||||
"test_like_4_a_check" CHECK (a > 0)
|
||||
"test_like_5x_p_check" CHECK (p > 0)
|
||||
Inherits: test_like_5,
|
||||
test_like_5x
|
||||
|
||||
DROP TABLE test_like_4, test_like_4a, test_like_4b, test_like_4c, test_like_4d;
|
||||
DROP TABLE test_like_5, test_like_5x, test_like_5c;
|
||||
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
|
||||
INSERT INTO inhg VALUES (5, 10);
|
||||
INSERT INTO inhg VALUES (20, 10); -- should fail
|
||||
@ -269,9 +298,10 @@ ALTER TABLE ctlt1 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE TABLE ctlt2 (c text);
|
||||
ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
COMMENT ON COLUMN ctlt2.c IS 'C';
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text);
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
|
||||
ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
|
||||
COMMENT ON COLUMN ctlt3.a IS 'A3';
|
||||
COMMENT ON COLUMN ctlt3.c IS 'C';
|
||||
COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
|
||||
@ -327,10 +357,11 @@ NOTICE: merging multiple inherited definitions of column "a"
|
||||
Check constraints:
|
||||
"ctlt1_a_check" CHECK (length(a) > 2)
|
||||
"ctlt3_a_check" CHECK (length(a) < 5)
|
||||
"ctlt3_c_check" CHECK (length(c) < 7)
|
||||
Inherits: ctlt1,
|
||||
ctlt3
|
||||
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
NOTICE: merging column "a" with inherited definition
|
||||
\d+ ctlt13_like
|
||||
Table "public.ctlt13_like"
|
||||
@ -339,9 +370,12 @@ NOTICE: merging column "a" with inherited definition
|
||||
a | text | | not null | | main | | A3
|
||||
b | text | | | | extended | |
|
||||
c | text | | | | external | | C
|
||||
Indexes:
|
||||
"ctlt13_like_expr_idx" btree ((a || c))
|
||||
Check constraints:
|
||||
"ctlt1_a_check" CHECK (length(a) > 2)
|
||||
"ctlt3_a_check" CHECK (length(a) < 5)
|
||||
"ctlt3_c_check" CHECK (length(c) < 7)
|
||||
Inherits: ctlt1
|
||||
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
|
||||
|
@ -66,7 +66,9 @@ SELECT * FROM test_like_gen_3;
|
||||
DROP TABLE test_like_gen_1, test_like_gen_2, test_like_gen_3;
|
||||
|
||||
-- also test generated column with a "forward" reference (bug #16342)
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42, c int GENERATED ALWAYS AS (a * 2) STORED, a int);
|
||||
CREATE TABLE test_like_4 (b int DEFAULT 42,
|
||||
c int GENERATED ALWAYS AS (a * 2) STORED,
|
||||
a int CHECK (a > 0));
|
||||
\d test_like_4
|
||||
CREATE TABLE test_like_4a (LIKE test_like_4);
|
||||
CREATE TABLE test_like_4b (LIKE test_like_4 INCLUDING DEFAULTS);
|
||||
@ -84,7 +86,17 @@ SELECT a, b, c FROM test_like_4c;
|
||||
\d test_like_4d
|
||||
INSERT INTO test_like_4d (a) VALUES(11);
|
||||
SELECT a, b, c FROM test_like_4d;
|
||||
|
||||
-- Test renumbering of Vars when combining LIKE with inheritance
|
||||
CREATE TABLE test_like_5 (x point, y point, z point);
|
||||
CREATE TABLE test_like_5x (p int CHECK (p > 0),
|
||||
q int GENERATED ALWAYS AS (p * 2) STORED);
|
||||
CREATE TABLE test_like_5c (LIKE test_like_4 INCLUDING ALL)
|
||||
INHERITS (test_like_5, test_like_5x);
|
||||
\d test_like_5c
|
||||
|
||||
DROP TABLE test_like_4, test_like_4a, test_like_4b, test_like_4c, test_like_4d;
|
||||
DROP TABLE test_like_5, test_like_5x, test_like_5c;
|
||||
|
||||
CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
|
||||
INSERT INTO inhg VALUES (5, 10);
|
||||
@ -119,9 +131,10 @@ CREATE TABLE ctlt2 (c text);
|
||||
ALTER TABLE ctlt2 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
COMMENT ON COLUMN ctlt2.c IS 'C';
|
||||
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text);
|
||||
CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
|
||||
ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
|
||||
ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
|
||||
CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
|
||||
COMMENT ON COLUMN ctlt3.a IS 'A3';
|
||||
COMMENT ON COLUMN ctlt3.c IS 'C';
|
||||
COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
|
||||
@ -138,7 +151,7 @@ CREATE TABLE ctlt1_inh (LIKE ctlt1 INCLUDING CONSTRAINTS INCLUDING COMMENTS) INH
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt1_inh'::regclass;
|
||||
CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
|
||||
\d+ ctlt13_inh
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
|
||||
\d+ ctlt13_like
|
||||
SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
|
||||
|
||||
|
Reference in New Issue
Block a user