Don't create pg_type entries for sequences or toast tables.
authorTom Lane
Tue, 7 Jul 2020 19:43:22 +0000 (15:43 -0400)
committerTom Lane
Tue, 7 Jul 2020 19:43:22 +0000 (15:43 -0400)
Commit f7f70d5e2 left one inconsistency behind: we're still creating
pg_type entries for the composite types of sequences and toast tables,
but not arrays over those composites.  But there seems precious little
reason to have named composite types for toast tables, and not much more
to have them for sequences (especially given the thought that sequences
may someday not be standalone relations at all).

So, let's close that inconsistency by removing these composite types,
rather than adding arrays for them.  This buys back a little bit of
the initial pg_type bloat added by the previous patch, and could be
a significant savings in a large database with many toast tables.

Aside from a small logic rearrangement in heap_create_with_catalog,
this patch mostly needs to clean up some places that were assuming that
pg_class.reltype always has a valid value.  Those are really pre-existing
bugs, given that it's documented otherwise; notably, the plpgsql changes
fix code that gives "cache lookup failed for type 0" on indexes today.
But none of these seem interesting enough to back-patch.

Also, remove the pg_dump/pg_upgrade infrastructure for propagating
a toast table's pg_type OID into the new database, since we no longer
need that.

Discussion: https://postgr.es/m/761F1389-C6A8-4C15-80CE-950C961F5341@gmail.com

12 files changed:
doc/src/sgml/catalogs.sgml
src/backend/catalog/heap.c
src/backend/catalog/toasting.c
src/backend/commands/tablecmds.c
src/backend/nodes/makefuncs.c
src/backend/utils/adt/pg_upgrade_support.c
src/backend/utils/cache/relcache.c
src/bin/pg_dump/pg_dump.c
src/include/catalog/binary_upgrade.h
src/include/catalog/catversion.h
src/include/catalog/pg_proc.dat
src/pl/plpgsql/src/pl_comp.c

index 361793b337aa750905037ad09902cf3ce206062b..e9cdff486415b7d73cb489861ae3389a0d963078 100644 (file)
@@ -1895,7 +1895,8 @@ SCRAM-SHA-256$<iteration count>:&l
       
       
        The OID of the data type that corresponds to this table's row type,
-       if any (zero for indexes, which have no pg_type entry)
+       if any (zero for indexes, sequences, and toast tables, which have
+       no pg_type entry)
       
      
 
index fd04e82b20ec52d4ec4b14a2a6d244b4bd930afb..3985326df62f766faf6182d30b6c52772cf0c10a 100644 (file)
@@ -1000,7 +1000,9 @@ AddNewRelationTuple(Relation pg_class_desc,
    /* relispartition is always set by updating this tuple later */
    new_rel_reltup->relispartition = false;
 
-   new_rel_desc->rd_att->tdtypeid = new_type_oid;
+   /* fill rd_att's type ID with something sane even if reltype is zero */
+   new_rel_desc->rd_att->tdtypeid = new_type_oid ? new_type_oid : RECORDOID;
+   new_rel_desc->rd_att->tdtypmod = -1;
 
    /* Now build and insert the tuple */
    InsertPgClassTuple(pg_class_desc, new_rel_desc, new_rel_oid,
@@ -1085,6 +1087,7 @@ AddNewRelationType(const char *typeName,
  *
  * Output parameters:
  * typaddress: if not null, gets the object address of the new pg_type entry
+ * (this must be null if the relkind is one that doesn't get a pg_type entry)
  *
  * Returns the OID of the new relation
  * --------------------------------
@@ -1118,8 +1121,6 @@ heap_create_with_catalog(const char *relname,
    Oid         existing_relid;
    Oid         old_type_oid;
    Oid         new_type_oid;
-   ObjectAddress new_type_addr;
-   Oid         new_array_oid = InvalidOid;
    TransactionId relfrozenxid;
    MultiXactId relminmxid;
 
@@ -1262,44 +1263,46 @@ heap_create_with_catalog(const char *relname,
    new_rel_desc->rd_rel->relrewrite = relrewrite;
 
    /*
-    * Decide whether to create an array type over the relation's rowtype.
-    * Array types are made except where the use of a relation as such is an
+    * Decide whether to create a pg_type entry for the relation's rowtype.
+    * These types are made except where the use of a relation as such is an
     * implementation detail: toast tables, sequences and indexes.
     */
    if (!(relkind == RELKIND_SEQUENCE ||
          relkind == RELKIND_TOASTVALUE ||
          relkind == RELKIND_INDEX ||
          relkind == RELKIND_PARTITIONED_INDEX))
-       new_array_oid = AssignTypeArrayOid();
-
-   /*
-    * Since defining a relation also defines a complex type, we add a new
-    * system type corresponding to the new relation.  The OID of the type can
-    * be preselected by the caller, but if reltypeid is InvalidOid, we'll
-    * generate a new OID for it.
-    *
-    * NOTE: we could get a unique-index failure here, in case someone else is
-    * creating the same type name in parallel but hadn't committed yet when
-    * we checked for a duplicate name above.
-    */
-   new_type_addr = AddNewRelationType(relname,
-                                      relnamespace,
-                                      relid,
-                                      relkind,
-                                      ownerid,
-                                      reltypeid,
-                                      new_array_oid);
-   new_type_oid = new_type_addr.objectId;
-   if (typaddress)
-       *typaddress = new_type_addr;
-
-   /*
-    * Now make the array type if wanted.
-    */
-   if (OidIsValid(new_array_oid))
    {
+       Oid         new_array_oid;
+       ObjectAddress new_type_addr;
        char       *relarrayname;
 
+       /*
+        * We'll make an array over the composite type, too.  For largely
+        * historical reasons, the array type's OID is assigned first.
+        */
+       new_array_oid = AssignTypeArrayOid();
+
+       /*
+        * Make the pg_type entry for the composite type.  The OID of the
+        * composite type can be preselected by the caller, but if reltypeid
+        * is InvalidOid, we'll generate a new OID for it.
+        *
+        * NOTE: we could get a unique-index failure here, in case someone
+        * else is creating the same type name in parallel but hadn't
+        * committed yet when we checked for a duplicate name above.
+        */
+       new_type_addr = AddNewRelationType(relname,
+                                          relnamespace,
+                                          relid,
+                                          relkind,
+                                          ownerid,
+                                          reltypeid,
+                                          new_array_oid);
+       new_type_oid = new_type_addr.objectId;
+       if (typaddress)
+           *typaddress = new_type_addr;
+
+       /* Now create the array type. */
        relarrayname = makeArrayTypeName(relname, relnamespace);
 
        TypeCreate(new_array_oid,   /* force the type's OID to this */
@@ -1336,6 +1339,14 @@ heap_create_with_catalog(const char *relname,
 
        pfree(relarrayname);
    }
+   else
+   {
+       /* Caller should not be expecting a type to be created. */
+       Assert(reltypeid == InvalidOid);
+       Assert(typaddress == NULL);
+
+       new_type_oid = InvalidOid;
+   }
 
    /*
     * now create an entry in pg_class for the relation.
index 3f7ab8d389be8fbf35b389b435c4ebd16e64f513..8b8888af5ed583b0d05187eb848ce06c2397215d 100644 (file)
@@ -34,9 +34,6 @@
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
-/* Potentially set by pg_upgrade_support functions */
-Oid            binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-
 static void CheckAndCreateToastTable(Oid relOid, Datum reloptions,
                                     LOCKMODE lockmode, bool check);
 static bool create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
@@ -135,7 +132,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
    Relation    toast_rel;
    Relation    class_rel;
    Oid         toast_relid;
-   Oid         toast_typid = InvalidOid;
    Oid         namespaceid;
    char        toast_relname[NAMEDATALEN];
    char        toast_idxname[NAMEDATALEN];
@@ -181,8 +177,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
         * problem that it might take up an OID that will conflict with some
         * old-cluster table we haven't seen yet.
         */
-       if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid) ||
-           !OidIsValid(binary_upgrade_next_toast_pg_type_oid))
+       if (!OidIsValid(binary_upgrade_next_toast_pg_class_oid))
            return false;
    }
 
@@ -234,17 +229,6 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
    else
        namespaceid = PG_TOAST_NAMESPACE;
 
-   /*
-    * Use binary-upgrade override for pg_type.oid, if supplied.  We might be
-    * in the post-schema-restore phase where we are doing ALTER TABLE to
-    * create TOAST tables that didn't exist in the old cluster.
-    */
-   if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_toast_pg_type_oid))
-   {
-       toast_typid = binary_upgrade_next_toast_pg_type_oid;
-       binary_upgrade_next_toast_pg_type_oid = InvalidOid;
-   }
-
    /* Toast table is shared if and only if its parent is. */
    shared_relation = rel->rd_rel->relisshared;
 
@@ -255,7 +239,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid,
                                           namespaceid,
                                           rel->rd_rel->reltablespace,
                                           toastOid,
-                                          toast_typid,
+                                          InvalidOid,
                                           InvalidOid,
                                           rel->rd_rel->relowner,
                                           table_relation_toast_am(rel),
index f79044f39fccaaa0bc866cf887a0f413110663f8..42330692e769f7a9bff9585c66506f4ae4a4eb75 100644 (file)
@@ -11001,8 +11001,8 @@ ATPrepAlterColumnType(List **wqueue,
        tab->relkind == RELKIND_FOREIGN_TABLE)
    {
        /*
-        * For composite types, do this check now.  Tables will check it later
-        * when the table is being rewritten.
+        * For composite types and foreign tables, do this check now.  Regular
+        * tables will check it later when the table is being rewritten.
         */
        find_composite_type_dependencies(rel->rd_rel->reltype, rel, NULL);
    }
@@ -12564,8 +12564,7 @@ ATExecChangeOwner(Oid relationOid, Oid newOwnerId, bool recursing, LOCKMODE lock
        /*
         * Also change the ownership of the table's row type, if it has one
         */
-       if (tuple_class->relkind != RELKIND_INDEX &&
-           tuple_class->relkind != RELKIND_PARTITIONED_INDEX)
+       if (OidIsValid(tuple_class->reltype))
            AlterTypeOwnerInternal(tuple_class->reltype, newOwnerId);
 
        /*
@@ -15009,9 +15008,10 @@ AlterTableNamespaceInternal(Relation rel, Oid oldNspOid, Oid nspOid,
    AlterRelationNamespaceInternal(classRel, RelationGetRelid(rel), oldNspOid,
                                   nspOid, true, objsMoved);
 
-   /* Fix the table's row type too */
-   AlterTypeNamespaceInternal(rel->rd_rel->reltype,
-                              nspOid, false, false, objsMoved);
+   /* Fix the table's row type too, if it has one */
+   if (OidIsValid(rel->rd_rel->reltype))
+       AlterTypeNamespaceInternal(rel->rd_rel->reltype,
+                                  nspOid, false, false, objsMoved);
 
    /* Fix other dependent stuff */
    if (rel->rd_rel->relkind == RELKIND_RELATION ||
@@ -15206,11 +15206,11 @@ AlterSeqNamespaces(Relation classRel, Relation rel,
                                       true, objsMoved);
 
        /*
-        * Sequences have entries in pg_type. We need to be careful to move
-        * them to the new namespace, too.
+        * Sequences used to have entries in pg_type, but no longer do.  If we
+        * ever re-instate that, we'll need to move the pg_type entry to the
+        * new namespace, too (using AlterTypeNamespaceInternal).
         */
-       AlterTypeNamespaceInternal(RelationGetForm(seqRel)->reltype,
-                                  newNspOid, false, false, objsMoved);
+       Assert(RelationGetForm(seqRel)->reltype == InvalidOid);
 
        /* Now we can close it.  Keep the lock till end of transaction. */
        relation_close(seqRel, NoLock);
index b442b5a29ef4547c90dd4f965e62a0d3db8f4bb4..49de285f01e21886683f5da3f3c6c8ed8c716be5 100644 (file)
@@ -145,8 +145,10 @@ makeWholeRowVar(RangeTblEntry *rte,
            /* relation: the rowtype is a named composite type */
            toid = get_rel_type_id(rte->relid);
            if (!OidIsValid(toid))
-               elog(ERROR, "could not find type OID for relation %u",
-                    rte->relid);
+               ereport(ERROR,
+                       (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                        errmsg("relation \"%s\" does not have a composite type",
+                               get_rel_name(rte->relid))));
            result = makeVar(varno,
                             InvalidAttrNumber,
                             toid,
index 18f2ee8226cc2077e365822d332b5ab0d002a0f8..14d9eb2b5b3db92d2b2317a842175a8e9a90fb5d 100644 (file)
@@ -51,17 +51,6 @@ binary_upgrade_set_next_array_pg_type_oid(PG_FUNCTION_ARGS)
    PG_RETURN_VOID();
 }
 
-Datum
-binary_upgrade_set_next_toast_pg_type_oid(PG_FUNCTION_ARGS)
-{
-   Oid         typoid = PG_GETARG_OID(0);
-
-   CHECK_IS_BINARY_UPGRADE;
-   binary_upgrade_next_toast_pg_type_oid = typoid;
-
-   PG_RETURN_VOID();
-}
-
 Datum
 binary_upgrade_set_next_heap_pg_class_oid(PG_FUNCTION_ARGS)
 {
index 0b9eb00d2de4370b41120f209673b4ff4522e6f5..a2453cf1f421192732a88f528688e720f942e7ca 100644 (file)
@@ -506,9 +506,10 @@ RelationBuildTupleDesc(Relation relation)
    AttrMissing *attrmiss = NULL;
    int         ndef = 0;
 
-   /* copy some fields from pg_class row to rd_att */
-   relation->rd_att->tdtypeid = relation->rd_rel->reltype;
-   relation->rd_att->tdtypmod = -1;    /* unnecessary, but... */
+   /* fill rd_att's type ID fields (compare heap.c's AddNewRelationTuple) */
+   relation->rd_att->tdtypeid =
+       relation->rd_rel->reltype ? relation->rd_rel->reltype : RECORDOID;
+   relation->rd_att->tdtypmod = -1;    /* just to be sure */
 
    constr = (TupleConstr *) MemoryContextAlloc(CacheMemoryContext,
                                                sizeof(TupleConstr));
@@ -1886,7 +1887,7 @@ formrdesc(const char *relationName, Oid relationReltype,
    relation->rd_att->tdrefcount = 1;   /* mark as refcounted */
 
    relation->rd_att->tdtypeid = relationReltype;
-   relation->rd_att->tdtypmod = -1;    /* unnecessary, but... */
+   relation->rd_att->tdtypmod = -1;    /* just to be sure */
 
    /*
     * initialize tuple desc info
@@ -5692,8 +5693,8 @@ load_relcache_init_file(bool shared)
        rel->rd_att = CreateTemplateTupleDesc(relform->relnatts);
        rel->rd_att->tdrefcount = 1;    /* mark as refcounted */
 
-       rel->rd_att->tdtypeid = relform->reltype;
-       rel->rd_att->tdtypmod = -1; /* unnecessary, but... */
+       rel->rd_att->tdtypeid = relform->reltype ? relform->reltype : RECORDOID;
+       rel->rd_att->tdtypmod = -1; /* just to be sure */
 
        /* next read all the attribute tuple form data entries */
        has_not_null = false;
index a41a3db876caa0ac29bd2af6ef971b4a740dcf4c..fd7b3e092037730bbe688d7062d2d3f56c296751 100644 (file)
@@ -272,7 +272,7 @@ static void binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
                                                     PQExpBuffer upgrade_buffer,
                                                     Oid pg_type_oid,
                                                     bool force_array_type);
-static bool binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
+static void binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
                                                    PQExpBuffer upgrade_buffer, Oid pg_rel_oid);
 static void binary_upgrade_set_pg_class_oids(Archive *fout,
                                             PQExpBuffer upgrade_buffer,
@@ -4493,7 +4493,7 @@ binary_upgrade_set_type_oids_by_type_oid(Archive *fout,
    destroyPQExpBuffer(upgrade_query);
 }
 
-static bool
+static void
 binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
                                        PQExpBuffer upgrade_buffer,
                                        Oid pg_rel_oid)
@@ -4501,48 +4501,23 @@ binary_upgrade_set_type_oids_by_rel_oid(Archive *fout,
    PQExpBuffer upgrade_query = createPQExpBuffer();
    PGresult   *upgrade_res;
    Oid         pg_type_oid;
-   bool        toast_set = false;
 
-   /*
-    * We only support old >= 8.3 for binary upgrades.
-    *
-    * We purposefully ignore toast OIDs for partitioned tables; the reason is
-    * that versions 10 and 11 have them, but 12 does not, so emitting them
-    * causes the upgrade to fail.
-    */
    appendPQExpBuffer(upgrade_query,
-                     "SELECT c.reltype AS crel, t.reltype AS trel "
+                     "SELECT c.reltype AS crel "
                      "FROM pg_catalog.pg_class c "
-                     "LEFT JOIN pg_catalog.pg_class t ON "
-                     "  (c.reltoastrelid = t.oid AND c.relkind <> '%c') "
                      "WHERE c.oid = '%u'::pg_catalog.oid;",
-                     RELKIND_PARTITIONED_TABLE, pg_rel_oid);
+                     pg_rel_oid);
 
    upgrade_res = ExecuteSqlQueryForSingleRow(fout, upgrade_query->data);
 
    pg_type_oid = atooid(PQgetvalue(upgrade_res, 0, PQfnumber(upgrade_res, "crel")));
 
-   binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
-                                            pg_type_oid, false);
-
-   if (!PQgetisnull(upgrade_res, 0, PQfnumber(upgrade_res, "trel")))
-   {
-       /* Toast tables do not have pg_type array rows */
-       Oid         pg_type_toast_oid = atooid(PQgetvalue(upgrade_res, 0,
-                                                         PQfnumber(upgrade_res, "trel")));
-
-       appendPQExpBufferStr(upgrade_buffer, "\n-- For binary upgrade, must preserve pg_type toast oid\n");
-       appendPQExpBuffer(upgrade_buffer,
-                         "SELECT pg_catalog.binary_upgrade_set_next_toast_pg_type_oid('%u'::pg_catalog.oid);\n\n",
-                         pg_type_toast_oid);
-
-       toast_set = true;
-   }
+   if (OidIsValid(pg_type_oid))
+       binary_upgrade_set_type_oids_by_type_oid(fout, upgrade_buffer,
+                                                pg_type_oid, false);
 
    PQclear(upgrade_res);
    destroyPQExpBuffer(upgrade_query);
-
-   return toast_set;
 }
 
 static void
@@ -17209,8 +17184,11 @@ dumpSequence(Archive *fout, TableInfo *tbinfo)
    {
        binary_upgrade_set_pg_class_oids(fout, query,
                                         tbinfo->dobj.catId.oid, false);
-       binary_upgrade_set_type_oids_by_rel_oid(fout, query,
-                                               tbinfo->dobj.catId.oid);
+
+       /*
+        * In older PG versions a sequence will have a pg_type entry, but v14
+        * and up don't use that, so don't attempt to preserve the type OID.
+        */
    }
 
    if (tbinfo->is_identity_sequence)
index 12d94fe1b3c43cad311a766e23f9976279e81774..02fecb90f79af448698e1c604f17c8cf41a61ba0 100644 (file)
@@ -16,7 +16,6 @@
 
 extern PGDLLIMPORT Oid binary_upgrade_next_pg_type_oid;
 extern PGDLLIMPORT Oid binary_upgrade_next_array_pg_type_oid;
-extern PGDLLIMPORT Oid binary_upgrade_next_toast_pg_type_oid;
 
 extern PGDLLIMPORT Oid binary_upgrade_next_heap_pg_class_oid;
 extern PGDLLIMPORT Oid binary_upgrade_next_index_pg_class_oid;
index 1b35510d46dc03a006dd24b028e2beaa6a6dbf82..60e5361af66e0fbd44afa16612dfcec0eecba28e 100644 (file)
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 202007071
+#define CATALOG_VERSION_NO 202007072
 
 #endif
index 3c89c53aa28d4b3da0693aa10e9be8cb4ead8b31..d951b4a36f240cbb623b85a480e1113e41757a9b 100644 (file)
   proname => 'binary_upgrade_set_next_array_pg_type_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
   prosrc => 'binary_upgrade_set_next_array_pg_type_oid' },
-{ oid => '3585', descr => 'for use by pg_upgrade',
-  proname => 'binary_upgrade_set_next_toast_pg_type_oid', provolatile => 'v',
-  proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
-  prosrc => 'binary_upgrade_set_next_toast_pg_type_oid' },
 { oid => '3586', descr => 'for use by pg_upgrade',
   proname => 'binary_upgrade_set_next_heap_pg_class_oid', provolatile => 'v',
   proparallel => 'r', prorettype => 'void', proargtypes => 'oid',
index 828ff5a288fcc49e2e0ca8937007db550a8dc0c6..e7f4a5f291d1772de7c808db1ca72e6be52ca77f 100644 (file)
@@ -1778,6 +1778,7 @@ PLpgSQL_type *
 plpgsql_parse_wordrowtype(char *ident)
 {
    Oid         classOid;
+   Oid         typOid;
 
    /*
     * Look up the relation.  Note that because relation rowtypes have the
@@ -1792,8 +1793,16 @@ plpgsql_parse_wordrowtype(char *ident)
                (errcode(ERRCODE_UNDEFINED_TABLE),
                 errmsg("relation \"%s\" does not exist", ident)));
 
+   /* Some relkinds lack type OIDs */
+   typOid = get_rel_type_id(classOid);
+   if (!OidIsValid(typOid))
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("relation \"%s\" does not have a composite type",
+                       ident)));
+
    /* Build and return the row type struct */
-   return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid,
+   return plpgsql_build_datatype(typOid, -1, InvalidOid,
                                  makeTypeName(ident));
 }
 
@@ -1806,6 +1815,7 @@ PLpgSQL_type *
 plpgsql_parse_cwordrowtype(List *idents)
 {
    Oid         classOid;
+   Oid         typOid;
    RangeVar   *relvar;
    MemoryContext oldCxt;
 
@@ -1825,10 +1835,18 @@ plpgsql_parse_cwordrowtype(List *idents)
                          -1);
    classOid = RangeVarGetRelid(relvar, NoLock, false);
 
+   /* Some relkinds lack type OIDs */
+   typOid = get_rel_type_id(classOid);
+   if (!OidIsValid(typOid))
+       ereport(ERROR,
+               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                errmsg("relation \"%s\" does not have a composite type",
+                       strVal(lsecond(idents)))));
+
    MemoryContextSwitchTo(oldCxt);
 
    /* Build and return the row type struct */
-   return plpgsql_build_datatype(get_rel_type_id(classOid), -1, InvalidOid,
+   return plpgsql_build_datatype(typOid, -1, InvalidOid,
                                  makeTypeNameFromNameList(idents));
 }