Skip to content

Commit 5b6b5e5

Browse files
committed
Don't set a fast default for anything but a plain table
The fast default code added in Release 11 omitted to check that the table a fast default was being added to was a plain table. Thus one could be added to a foreign table, which predicably blows up. Here we perform that check. In addition, on the back branches, since some of these might have escaped into the wild, if we encounter a missing value for an attribute of something other than a plain table we ignore it. Fixes bug #17056 Backpatch to release 11, Reviewed by: Andres Freund, Álvaro Herrera and Tom Lane
1 parent 357cb8f commit 5b6b5e5

File tree

5 files changed

+63
-4
lines changed

5 files changed

+63
-4
lines changed

src/backend/catalog/heap.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2120,6 +2120,13 @@ SetAttrMissing(Oid relid, char *attname, char *value)
21202120
/* lock the table the attribute belongs to */
21212121
tablerel = table_open(relid, AccessExclusiveLock);
21222122

2123+
/* Don't do anything unless it's a plain table */
2124+
if (tablerel->rd_rel->relkind != RELKIND_RELATION)
2125+
{
2126+
table_close(tablerel, AccessExclusiveLock);
2127+
return;
2128+
}
2129+
21232130
/* Lock the attribute row and get the data */
21242131
attrrel = table_open(AttributeRelationId, RowExclusiveLock);
21252132
atttup = SearchSysCacheAttName(relid, attname);
@@ -2246,7 +2253,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
22462253
valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
22472254
replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
22482255

2249-
if (add_column_mode && !attgenerated)
2256+
if (rel->rd_rel->relkind == RELKIND_RELATION && add_column_mode &&
2257+
!attgenerated)
22502258
{
22512259
expr2 = expression_planner(expr2);
22522260
estate = CreateExecutorState();

src/backend/commands/tablecmds.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11774,9 +11774,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
1177411774
/*
1177511775
* Here we go --- change the recorded column type and collation. (Note
1177611776
* heapTup is a copy of the syscache entry, so okay to scribble on.) First
11777-
* fix up the missing value if any.
11777+
* fix up the missing value if any. There shouldn't be any missing values
11778+
* for anything except plain tables, but if there are, ignore them.
1177811779
*/
11779-
if (attTup->atthasmissing)
11780+
if (rel->rd_rel->relkind == RELKIND_RELATION && attTup->atthasmissing)
1178011781
{
1178111782
Datum missingval;
1178211783
bool missingNull;

src/backend/utils/cache/relcache.c

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -550,6 +550,7 @@ RelationBuildTupleDesc(Relation relation)
550550
{
551551
Form_pg_attribute attp;
552552
int attnum;
553+
bool atthasmissing;
553554

554555
attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
555556

@@ -563,6 +564,22 @@ RelationBuildTupleDesc(Relation relation)
563564
attp,
564565
ATTRIBUTE_FIXED_PART_SIZE);
565566

567+
/*
568+
* Fix atthasmissing flag - it's only for plain tables. Others
569+
* should not have missing values set, but there may be some left from
570+
* before when we placed that check, so this code defensively ignores
571+
* such values.
572+
*/
573+
atthasmissing = attp->atthasmissing;
574+
if (relation->rd_rel->relkind != RELKIND_RELATION && atthasmissing)
575+
{
576+
Form_pg_attribute nattp;
577+
578+
atthasmissing = false;
579+
nattp = TupleDescAttr(relation->rd_att, attnum - 1);
580+
nattp->atthasmissing = false;
581+
}
582+
566583
/* Update constraint/default info */
567584
if (attp->attnotnull)
568585
constr->has_not_null = true;
@@ -584,7 +601,7 @@ RelationBuildTupleDesc(Relation relation)
584601
}
585602

586603
/* Likewise for a missing value */
587-
if (attp->atthasmissing)
604+
if (atthasmissing)
588605
{
589606
Datum missingval;
590607
bool missingNull;

src/test/regress/expected/fast_default.out

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,7 +797,26 @@ SELECT * FROM t WHERE a IS NULL;
797797
(1 row)
798798

799799
ROLLBACK;
800+
-- verify that a default set on a non-plain table doesn't set a missing
801+
-- value on the attribute
802+
CREATE FOREIGN DATA WRAPPER dummy;
803+
CREATE SERVER s0 FOREIGN DATA WRAPPER dummy;
804+
CREATE FOREIGN TABLE ft1 (c1 integer NOT NULL) SERVER s0;
805+
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer DEFAULT 0;
806+
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
807+
SELECT count(*)
808+
FROM pg_attribute
809+
WHERE attrelid = 'ft1'::regclass AND
810+
(attmissingval IS NOT NULL OR atthasmissing);
811+
count
812+
-------
813+
0
814+
(1 row)
815+
800816
-- cleanup
817+
DROP FOREIGN TABLE ft1;
818+
DROP SERVER s0;
819+
DROP FOREIGN DATA WRAPPER dummy;
801820
DROP TABLE vtype;
802821
DROP TABLE vtype2;
803822
DROP TABLE follower;

src/test/regress/sql/fast_default.sql

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,8 +524,22 @@ SET LOCAL enable_seqscan = false;
524524
SELECT * FROM t WHERE a IS NULL;
525525
ROLLBACK;
526526

527+
-- verify that a default set on a non-plain table doesn't set a missing
528+
-- value on the attribute
529+
CREATE FOREIGN DATA WRAPPER dummy;
530+
CREATE SERVER s0 FOREIGN DATA WRAPPER dummy;
531+
CREATE FOREIGN TABLE ft1 (c1 integer NOT NULL) SERVER s0;
532+
ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer DEFAULT 0;
533+
ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
534+
SELECT count(*)
535+
FROM pg_attribute
536+
WHERE attrelid = 'ft1'::regclass AND
537+
(attmissingval IS NOT NULL OR atthasmissing);
527538

528539
-- cleanup
540+
DROP FOREIGN TABLE ft1;
541+
DROP SERVER s0;
542+
DROP FOREIGN DATA WRAPPER dummy;
529543
DROP TABLE vtype;
530544
DROP TABLE vtype2;
531545
DROP TABLE follower;

0 commit comments

Comments
 (0)