Fix table rewrites that include a column without a default.

In c2fe139c20 I made ATRewriteTable() use tuple slots. Unfortunately
I did not notice that columns can be added in a rewrite that do not
have a default, when another column is added/altered requiring one.

Initialize columns to NULL again, and add tests.

Bug: #16038
Reported-By: anonymous
Author: Andres Freund
Discussion: https://postgr.es/m/16038-5c974541f2bf6749@postgresql.org
Backpatch: 12, where the bug was introduced in c2fe139c20
This commit is contained in:
Andres Freund 2019-10-09 22:00:50 -07:00
parent 50518ec296
commit 93765bd956
3 changed files with 105 additions and 0 deletions

View File

@ -4890,6 +4890,16 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode)
table_slot_callbacks(oldrel));
newslot = MakeSingleTupleTableSlot(newTupDesc,
table_slot_callbacks(newrel));
/*
* Set all columns in the new slot to NULL initially, to ensure
* columns added as part of the rewrite are initialized to
* NULL. That is necessary as tab->newvals will not contain an
* expression for columns with a NULL default, e.g. when adding a
* column without a default together with a column with a default
* requiring an actual rewrite.
*/
ExecStoreAllNullTuple(newslot);
}
else
{

View File

@ -2436,6 +2436,53 @@ select * from at_view_2;
drop view at_view_2;
drop view at_view_1;
drop table at_base_table;
-- check adding a column not iself requiring a rewrite, together with
-- a column requiring a default (bug #16038)
-- ensure that rewrites aren't silently optimized away, removing the
-- value of the test
CREATE OR REPLACE FUNCTION evtrig_rewrite_log() RETURNS event_trigger
LANGUAGE plpgsql AS $$
BEGIN
RAISE WARNING 'rewriting table %',
pg_event_trigger_table_rewrite_oid()::regclass;
END;
$$;
CREATE EVENT TRIGGER evtrig_rewrite_log ON table_rewrite
EXECUTE PROCEDURE evtrig_rewrite_log();
CREATE TABLE rewrite_test(col text);
INSERT INTO rewrite_test VALUES ('something');
INSERT INTO rewrite_test VALUES (NULL);
-- empty[12] doesn't need rewrite, but notempty[12]_rewrite will force one
ALTER TABLE rewrite_test
ADD COLUMN empty1 text,
ADD COLUMN notempty1_rewrite serial;
WARNING: rewriting table rewrite_test
ALTER TABLE rewrite_test
ADD COLUMN notempty2_rewrite serial,
ADD COLUMN empty2 text;
WARNING: rewriting table rewrite_test
-- also check that fast defaults cause no problem, first without rewrite
ALTER TABLE rewrite_test
ADD COLUMN empty3 text,
ADD COLUMN notempty3_norewrite int default 42;
ALTER TABLE rewrite_test
ADD COLUMN notempty4_norewrite int default 42,
ADD COLUMN empty4 text;
-- then with rewrite
ALTER TABLE rewrite_test
ADD COLUMN empty5 text,
ADD COLUMN notempty5_norewrite int default 42,
ADD COLUMN notempty5_rewrite serial;
WARNING: rewriting table rewrite_test
ALTER TABLE rewrite_test
ADD COLUMN notempty6_rewrite serial,
ADD COLUMN empty6 text,
ADD COLUMN notempty6_norewrite int default 42;
WARNING: rewriting table rewrite_test
-- cleanup
drop event trigger evtrig_rewrite_log;
drop function evtrig_rewrite_log();
DROP TABLE rewrite_test;
--
-- lock levels
--

View File

@ -1550,6 +1550,54 @@ drop view at_view_2;
drop view at_view_1;
drop table at_base_table;
-- check adding a column not iself requiring a rewrite, together with
-- a column requiring a default (bug #16038)
-- ensure that rewrites aren't silently optimized away, removing the
-- value of the test
CREATE OR REPLACE FUNCTION evtrig_rewrite_log() RETURNS event_trigger
LANGUAGE plpgsql AS $$
BEGIN
RAISE WARNING 'rewriting table %',
pg_event_trigger_table_rewrite_oid()::regclass;
END;
$$;
CREATE EVENT TRIGGER evtrig_rewrite_log ON table_rewrite
EXECUTE PROCEDURE evtrig_rewrite_log();
CREATE TABLE rewrite_test(col text);
INSERT INTO rewrite_test VALUES ('something');
INSERT INTO rewrite_test VALUES (NULL);
-- empty[12] doesn't need rewrite, but notempty[12]_rewrite will force one
ALTER TABLE rewrite_test
ADD COLUMN empty1 text,
ADD COLUMN notempty1_rewrite serial;
ALTER TABLE rewrite_test
ADD COLUMN notempty2_rewrite serial,
ADD COLUMN empty2 text;
-- also check that fast defaults cause no problem, first without rewrite
ALTER TABLE rewrite_test
ADD COLUMN empty3 text,
ADD COLUMN notempty3_norewrite int default 42;
ALTER TABLE rewrite_test
ADD COLUMN notempty4_norewrite int default 42,
ADD COLUMN empty4 text;
-- then with rewrite
ALTER TABLE rewrite_test
ADD COLUMN empty5 text,
ADD COLUMN notempty5_norewrite int default 42,
ADD COLUMN notempty5_rewrite serial;
ALTER TABLE rewrite_test
ADD COLUMN notempty6_rewrite serial,
ADD COLUMN empty6 text,
ADD COLUMN notempty6_norewrite int default 42;
-- cleanup
drop event trigger evtrig_rewrite_log;
drop function evtrig_rewrite_log();
DROP TABLE rewrite_test;
--
-- lock levels
--