Skip to content

Commit 3b174b1

Browse files
committed
Fix missing values when doing ALTER TABLE ALTER COLUMN TYPE
This was an oversight in commit 16828d5. If the table is going to be rewritten, we simply clear all the missing values from all the table's attributes, since there will no longer be any rows with the attributes missing. Otherwise, we repackage the missing value in an array constructed with the new type specifications. Backpatch to release 11. This fixes bug #15446, reported by Dmitry Molotkov Reviewed by Dean Rasheed
1 parent f6cddbd commit 3b174b1

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed

src/backend/commands/tablecmds.c

+77
Original file line numberDiff line numberDiff line change
@@ -9285,6 +9285,21 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
92859285
HeapTuple depTup;
92869286
ObjectAddress address;
92879287

9288+
/*
9289+
* Clear all the missing values if we're rewriting the table, since this
9290+
* renders them pointless.
9291+
*/
9292+
if (tab->rewrite)
9293+
{
9294+
Relation newrel;
9295+
9296+
newrel = heap_open(RelationGetRelid(rel), NoLock);
9297+
RelationClearMissing(newrel);
9298+
relation_close(newrel, NoLock);
9299+
/* make sure we don't conflict with later attribute modifications */
9300+
CommandCounterIncrement();
9301+
}
9302+
92889303
attrelation = heap_open(AttributeRelationId, RowExclusiveLock);
92899304

92909305
/* Look up the target column */
@@ -9601,7 +9616,69 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
96019616
/*
96029617
* Here we go --- change the recorded column type and collation. (Note
96039618
* heapTup is a copy of the syscache entry, so okay to scribble on.)
9619+
* First fix up the missing value if any.
96049620
*/
9621+
if (attTup->atthasmissing)
9622+
{
9623+
Datum missingval;
9624+
bool missingNull;
9625+
9626+
/* if rewrite is true the missing value should already be cleared */
9627+
Assert(tab->rewrite == 0);
9628+
9629+
/* Get the missing value datum */
9630+
missingval = heap_getattr(heapTup,
9631+
Anum_pg_attribute_attmissingval,
9632+
attrelation->rd_att,
9633+
&missingNull);
9634+
9635+
/* if it's a null array there is nothing to do */
9636+
9637+
if (! missingNull)
9638+
{
9639+
/*
9640+
* Get the datum out of the array and repack it in a new array
9641+
* built with the new type data. We assume that since the table
9642+
* doesn't need rewriting, the actual Datum doesn't need to be
9643+
* changed, only the array metadata.
9644+
*/
9645+
9646+
int one = 1;
9647+
bool isNull;
9648+
Datum valuesAtt[Natts_pg_attribute];
9649+
bool nullsAtt[Natts_pg_attribute];
9650+
bool replacesAtt[Natts_pg_attribute];
9651+
9652+
MemSet(valuesAtt, 0, sizeof(valuesAtt));
9653+
MemSet(nullsAtt, false, sizeof(nullsAtt));
9654+
MemSet(replacesAtt, false, sizeof(replacesAtt));
9655+
9656+
missingval = array_get_element(missingval,
9657+
1,
9658+
&one,
9659+
0,
9660+
attTup->attlen,
9661+
attTup->attbyval,
9662+
attTup->attalign,
9663+
&isNull);
9664+
missingval = PointerGetDatum(
9665+
construct_array(&missingval,
9666+
1,
9667+
targettype,
9668+
tform->typlen,
9669+
tform->typbyval,
9670+
tform->typalign));
9671+
9672+
valuesAtt[Anum_pg_attribute_attmissingval - 1] = missingval;
9673+
replacesAtt[Anum_pg_attribute_attmissingval - 1] = true;
9674+
nullsAtt[Anum_pg_attribute_attmissingval - 1] = false;
9675+
9676+
heapTup = heap_modify_tuple(heapTup, RelationGetDescr(attrelation),
9677+
valuesAtt, nullsAtt, replacesAtt);
9678+
attTup = (Form_pg_attribute) GETSTRUCT(heapTup);
9679+
}
9680+
}
9681+
96059682
attTup->atttypid = targettype;
96069683
attTup->atttypmod = targettypmod;
96079684
attTup->attcollation = targetcollid;

src/test/regress/expected/fast_default.out

+37
Original file line numberDiff line numberDiff line change
@@ -735,7 +735,44 @@ INSERT INTO leader VALUES (1, 1), (2, 2);
735735
ALTER TABLE leader ADD c int;
736736
ALTER TABLE leader DROP c;
737737
DELETE FROM leader;
738+
-- check that ALTER TABLE ... ALTER TYPE does the right thing
739+
CREATE TABLE vtype( a integer);
740+
INSERT INTO vtype VALUES (1);
741+
ALTER TABLE vtype ADD COLUMN b DOUBLE PRECISION DEFAULT 0.2;
742+
ALTER TABLE vtype ADD COLUMN c BOOLEAN DEFAULT true;
743+
SELECT * FROM vtype;
744+
a | b | c
745+
---+-----+---
746+
1 | 0.2 | t
747+
(1 row)
748+
749+
ALTER TABLE vtype
750+
ALTER b TYPE text USING b::text,
751+
ALTER c TYPE text USING c::text;
752+
NOTICE: rewriting table vtype for reason 4
753+
SELECT * FROM vtype;
754+
a | b | c
755+
---+-----+------
756+
1 | 0.2 | true
757+
(1 row)
758+
759+
-- also check the case that doesn't rewrite the table
760+
CREATE TABLE vtype2 (a int);
761+
INSERT INTO vtype2 VALUES (1);
762+
ALTER TABLE vtype2 ADD COLUMN b varchar(10) DEFAULT 'xxx';
763+
ALTER TABLE vtype2 ALTER COLUMN b SET DEFAULT 'yyy';
764+
INSERT INTO vtype2 VALUES (2);
765+
ALTER TABLE vtype2 ALTER COLUMN b TYPE varchar(20) USING b::varchar(20);
766+
SELECT * FROM vtype2;
767+
a | b
768+
---+-----
769+
1 | xxx
770+
2 | yyy
771+
(2 rows)
772+
738773
-- cleanup
774+
DROP TABLE vtype;
775+
DROP TABLE vtype2;
739776
DROP TABLE follower;
740777
DROP TABLE leader;
741778
DROP FUNCTION test_trigger();

src/test/regress/sql/fast_default.sql

+26
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,33 @@ ALTER TABLE leader ADD c int;
481481
ALTER TABLE leader DROP c;
482482
DELETE FROM leader;
483483

484+
-- check that ALTER TABLE ... ALTER TYPE does the right thing
485+
486+
CREATE TABLE vtype( a integer);
487+
INSERT INTO vtype VALUES (1);
488+
ALTER TABLE vtype ADD COLUMN b DOUBLE PRECISION DEFAULT 0.2;
489+
ALTER TABLE vtype ADD COLUMN c BOOLEAN DEFAULT true;
490+
SELECT * FROM vtype;
491+
ALTER TABLE vtype
492+
ALTER b TYPE text USING b::text,
493+
ALTER c TYPE text USING c::text;
494+
SELECT * FROM vtype;
495+
496+
-- also check the case that doesn't rewrite the table
497+
498+
CREATE TABLE vtype2 (a int);
499+
INSERT INTO vtype2 VALUES (1);
500+
ALTER TABLE vtype2 ADD COLUMN b varchar(10) DEFAULT 'xxx';
501+
ALTER TABLE vtype2 ALTER COLUMN b SET DEFAULT 'yyy';
502+
INSERT INTO vtype2 VALUES (2);
503+
504+
ALTER TABLE vtype2 ALTER COLUMN b TYPE varchar(20) USING b::varchar(20);
505+
SELECT * FROM vtype2;
506+
507+
484508
-- cleanup
509+
DROP TABLE vtype;
510+
DROP TABLE vtype2;
485511
DROP TABLE follower;
486512
DROP TABLE leader;
487513
DROP FUNCTION test_trigger();

0 commit comments

Comments
 (0)