]> 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:46:21 +0000 (07:46 -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 7ac0edd6693b7cbe91e55d8cbc57b7ebf203082c..faab16167594c07b3f007c6fda6f47a347c53e3f 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 85895bc675b0acfc4d760ab77b28d0cb8ef5647e..816ce8521fe9d8cc3e18a82a4adde0384284a5d4 100644 (file)
@@ -11313,9 +11313,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 2babf5253809bdac07ccf07662cff876bc2ba47a..be50de462ed1ee37aefc9c683957db9a8f2f98bc 100644 (file)
@@ -552,6 +552,7 @@ RelationBuildTupleDesc(Relation relation)
        {
                Form_pg_attribute attp;
                int                     attnum;
+               bool        atthasmissing;
 
                attp = (Form_pg_attribute) GETSTRUCT(pg_attribute_tuple);
 
@@ -565,6 +566,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;
@@ -586,7 +603,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;