]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
Don't set a fast default for anything but a plain table
authorAndrew Dunstan <andrew@dunslane.net>
Fri, 18 Jun 2021 11:44:58 +0000 (07:44 -0400)
committerAndrew Dunstan <andrew@dunslane.net>
Fri, 18 Jun 2021 11:44:58 +0000 (07:44 -0400)
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

src/backend/catalog/heap.c
src/backend/commands/tablecmds.c
src/backend/utils/cache/relcache.c
src/test/regress/expected/fast_default.out
src/test/regress/sql/fast_default.sql

index b34b8b94b3a2d1410dc75960260e9e4852403351..a42070fd9250b3e2d382d2e8e37ea255314cf2c1 100644 (file)
@@ -2120,6 +2120,13 @@ SetAttrMissing(Oid relid, char *attname, char *value)
        /* lock the table the attribute belongs to */
        tablerel = table_open(relid, AccessExclusiveLock);
 
+       /* Don't do anything unless it's a plain table */
+       if (tablerel->rd_rel->relkind != RELKIND_RELATION)
+       {
+               table_close(tablerel, AccessExclusiveLock);
+               return;
+       }
+
        /* Lock the attribute row and get the data */
        attrrel = table_open(AttributeRelationId, RowExclusiveLock);
        atttup = SearchSysCacheAttName(relid, attname);
@@ -2246,7 +2253,8 @@ StoreAttrDefault(Relation rel, AttrNumber attnum,
                valuesAtt[Anum_pg_attribute_atthasdef - 1] = true;
                replacesAtt[Anum_pg_attribute_atthasdef - 1] = true;
 
-               if (add_column_mode && !attgenerated)
+               if (rel->rd_rel->relkind == RELKIND_RELATION  && add_column_mode &&
+                       !attgenerated)
                {
                        expr2 = expression_planner(expr2);
                        estate = CreateExecutorState();
index 70337c7c07205b5fc58506eacca7bbcd8853b01f..135fa4698172241b53d4b254b95f1fa91a894386 100644 (file)
@@ -11774,9 +11774,10 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
        /*
         * Here we go --- change the recorded column type and collation.  (Note
         * heapTup is a copy of the syscache entry, so okay to scribble on.) First
-        * fix up the missing value if any.
+        * fix up the missing value if any. There shouldn't be any missing values
+        * for anything except plain tables, but if there are, ignore them.
         */
-       if (attTup->atthasmissing)
+       if (rel->rd_rel->relkind == RELKIND_RELATION  && attTup->atthasmissing)
        {
                Datum           missingval;
                bool            missingNull;
index b53d92b4daed623aacf123dea26f3206047ccf9c..23405711d1de5f589d9a4224848ed7f9c9c17e1d 100644 (file)
@@ -550,6 +550,7 @@ RelationBuildTupleDesc(Relation relation)
        {
                Form_pg_attribute attp;
                int                     attnum;
+               bool        atthasmissing;
 
                attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
 
@@ -563,6 +564,22 @@ RelationBuildTupleDesc(Relation relation)
                           attp,
                           ATTRIBUTE_FIXED_PART_SIZE);
 
+               /*
+                * Fix atthasmissing flag - it's only for plain tables. Others
+                * should not have missing values set, but there may be some left from
+                * before when we placed that check, so this code defensively ignores
+                * such values.
+                */
+               atthasmissing = attp->atthasmissing;
+               if (relation->rd_rel->relkind != RELKIND_RELATION && atthasmissing)
+               {
+                       Form_pg_attribute nattp;
+
+                       atthasmissing = false;
+                       nattp = TupleDescAttr(relation->rd_att, attnum - 1);
+                       nattp->atthasmissing = false;
+               }
+
                /* Update constraint/default info */
                if (attp->attnotnull)
                        constr->has_not_null = true;
@@ -584,7 +601,7 @@ RelationBuildTupleDesc(Relation relation)
                }
 
                /* Likewise for a missing value */
-               if (attp->atthasmissing)
+               if (atthasmissing)
                {
                        Datum           missingval;
                        bool            missingNull;
index 10bc5ff757c211c4fa67f4bede754d5cb744c19a..91f25717b5a5b064ec9ceb01c6743100cf38930c 100644 (file)
@@ -797,7 +797,26 @@ SELECT * FROM t WHERE a IS NULL;
 (1 row)
 
 ROLLBACK;
+-- verify that a default set on a non-plain table doesn't set a missing
+-- value on the attribute
+CREATE FOREIGN DATA WRAPPER dummy;
+CREATE SERVER s0 FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 (c1 integer NOT NULL) SERVER s0;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+SELECT count(*)
+  FROM pg_attribute
+  WHERE attrelid = 'ft1'::regclass AND
+    (attmissingval IS NOT NULL OR atthasmissing);
+ count 
+-------
+     0
+(1 row)
+
 -- cleanup
+DROP FOREIGN TABLE ft1;
+DROP SERVER s0;
+DROP FOREIGN DATA WRAPPER dummy;
 DROP TABLE vtype;
 DROP TABLE vtype2;
 DROP TABLE follower;
index 4589b9e58d1708fb26acbd15e1d428776aaf97f4..16a3b7ca51d1a3e764638c1ea08ebcf9aeea2470 100644 (file)
@@ -524,8 +524,22 @@ SET LOCAL enable_seqscan = false;
 SELECT * FROM t WHERE a IS NULL;
 ROLLBACK;
 
+-- verify that a default set on a non-plain table doesn't set a missing
+-- value on the attribute
+CREATE FOREIGN DATA WRAPPER dummy;
+CREATE SERVER s0 FOREIGN DATA WRAPPER dummy;
+CREATE FOREIGN TABLE ft1 (c1 integer NOT NULL) SERVER s0;
+ALTER FOREIGN TABLE ft1 ADD COLUMN c8 integer DEFAULT 0;
+ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE char(10);
+SELECT count(*)
+  FROM pg_attribute
+  WHERE attrelid = 'ft1'::regclass AND
+    (attmissingval IS NOT NULL OR atthasmissing);
 
 -- cleanup
+DROP FOREIGN TABLE ft1;
+DROP SERVER s0;
+DROP FOREIGN DATA WRAPPER dummy;
 DROP TABLE vtype;
 DROP TABLE vtype2;
 DROP TABLE follower;