Re: Replacing pg_depend PIN entries with a fixed range check

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: Replacing pg_depend PIN entries with a fixed range check
Дата
Msg-id 1363425.1620858059@sss.pgh.pa.us
обсуждение исходный текст
Ответ на Re: Replacing pg_depend PIN entries with a fixed range check  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: Replacing pg_depend PIN entries with a fixed range check  (Robert Haas <robertmhaas@gmail.com>)
Список pgsql-hackers
I wrote:
> In view of the discussion at [1], there's more pressure on the OID supply
> above 10K than I'd realized.  While I don't have any good ideas about
> eliminating the problem altogether, I did have a thought that would remove
> the extra buffer zone created by my first-draft patch in this thread.
> Namely, let's have genbki.pl write out its final OID assignment counter
> value in a command in the postgres.bki file, say "set_next_oid 12036".
> This would cause the bootstrap backend to set the server's OID counter to
> that value.  Then the initial part of initdb's post-bootstrap processing
> could assign pinned OIDs working forward from there, with no gap.  We'd
> still need a gap before FirstBootstrapObjectId (which we might as well
> rename to FirstUnpinnedObjectId), but we don't need two gaps, and so this
> patch wouldn't make things any worse than they are today.

Here's a v2 that does things that way (and is rebased up to HEAD).
I did some more documentation cleanup, too.

            regards, tom lane

diff --git a/doc/src/sgml/bki.sgml b/doc/src/sgml/bki.sgml
index b33e59d5e4..8dcd6e783c 100644
--- a/doc/src/sgml/bki.sgml
+++ b/doc/src/sgml/bki.sgml
@@ -418,11 +418,12 @@
    <para>
     If <filename>genbki.pl</filename> needs to assign an OID to a catalog
     entry that does not have a manually-assigned OID, it will use a value in
-    the range 10000—12999.  The server's OID counter is set to 13000
-    at the start of a bootstrap run.  Thus objects created by regular SQL
-    commands during the later phases of bootstrap, such as objects created
-    while running the <filename>information_schema.sql</filename> script,
-    receive OIDs of 13000 or above.
+    the range 10000—12999.  Then, during bootstrap, the server's OID
+    counter is set to the first OID not so allocated.  Thus objects created
+    by regular SQL commands during the later phases of bootstrap, such as
+    objects created while running
+    the <filename>information_schema.sql</filename> script, receive
+    non-conflicting OIDs.
    </para>

    <para>
@@ -432,6 +433,17 @@
     during bootstrap.  These automatically-assigned OIDs are not considered
     stable, and may change from one installation to another.
    </para>
+
+   <para>
+    An additional thing that is useful to know is that objects with OIDs
+    below <symbol>FirstUnpinnedObjectId</symbol> (13000) are considered
+    <quote>pinned</quote>, preventing them from being deleted.
+    (There are a small number of exceptions, which are hard-wired
+    into <function>IsPinnedObject()</function>.)
+    <application>initdb</application> forces the OID counter up
+    to <symbol>FirstUnpinnedObjectId</symbol> as soon as it's ready to
+    create unpinned objects.
+   </para>
   </sect2>

   <sect2 id="system-catalog-oid-references">
@@ -952,6 +964,20 @@ $ perl  rewrite_dat_with_prokind.pl  pg_proc.dat
      </para>
     </listitem>
    </varlistentry>
+
+   <varlistentry>
+    <term><literal>set_next_oid</literal> <replaceable class="parameter">nextoid</replaceable></term>
+
+    <listitem>
+     <para>
+      Set the backend's OID generation counter
+      to <replaceable class="parameter">nextoid</replaceable>, so that future
+      OID assignments begin there.  (It is advisable to issue this command as
+      soon as possible, preferably before building indices, in case any
+      processing invoked by that allocates an OID.)
+     </para>
+    </listitem>
+   </varlistentry>
   </variablelist>

  </sect1>
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 6d06ad22b9..ec51d26491 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -3260,8 +3260,7 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
        (references <link
linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
       </para>
       <para>
-       The OID of the system catalog the dependent object is in,
-       or zero for a <symbol>DEPENDENCY_PIN</symbol> entry
+       The OID of the system catalog the dependent object is in
       </para></entry>
      </row>

@@ -3271,8 +3270,7 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
        (references any OID column)
       </para>
       <para>
-       The OID of the specific dependent object,
-       or zero for a <symbol>DEPENDENCY_PIN</symbol> entry
+       The OID of the specific dependent object
       </para></entry>
      </row>

@@ -3463,19 +3461,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
       </para>
      </listitem>
     </varlistentry>
-
-    <varlistentry>
-     <term><symbol>DEPENDENCY_PIN</symbol> (<literal>p</literal>)</term>
-     <listitem>
-      <para>
-       There is no dependent object; this type of entry is a signal
-       that the system itself depends on the referenced object, and so
-       that object must never be deleted.  Entries of this type are
-       created only by <application>initdb</application>.  The columns for the
-       dependent object contain zeroes.
-      </para>
-     </listitem>
-    </varlistentry>
    </variablelist>

    Other dependency flavors might be needed in future.
@@ -3494,6 +3479,19 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
    must be satisfied.
   </para>

+  <para>
+   Most objects created during <application>initdb</application> are
+   considered <quote>pinned</quote>, which means that the system itself
+   depends on them.  Therefore, they are never allowed to be dropped.
+   Also, knowing that pinned objects will not be dropped, the dependency
+   mechanism doesn't bother to make <structname>pg_depend</structname>
+   entries showing dependencies on them.  Thus, for example, a table
+   column of type <type>numeric</type> notionally has
+   a <literal>NORMAL</literal> dependency on the <type>numeric</type>
+   data type, but no such entry actually appears
+   in <structname>pg_depend</structname>.
+  </para>
+
  </sect1>


@@ -6776,7 +6774,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
       <para>
        The OID of the database the dependent object is in,
        or zero for a shared object
-       or a <symbol>SHARED_DEPENDENCY_PIN</symbol> entry
       </para></entry>
      </row>

@@ -6786,8 +6783,7 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
        (references <link
linkend="catalog-pg-class"><structname>pg_class</structname></link>.<structfield>oid</structfield>)
       </para>
       <para>
-       The OID of the system catalog the dependent object is in,
-       or zero for a <symbol>SHARED_DEPENDENCY_PIN</symbol> entry
+       The OID of the system catalog the dependent object is in
       </para></entry>
      </row>

@@ -6797,8 +6793,7 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
        (references any OID column)
       </para>
       <para>
-       The OID of the specific dependent object,
-       or zero for a <symbol>SHARED_DEPENDENCY_PIN</symbol> entry
+       The OID of the specific dependent object
       </para></entry>
      </row>

@@ -6886,19 +6881,6 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
      </listitem>
     </varlistentry>

-    <varlistentry>
-     <term><symbol>SHARED_DEPENDENCY_PIN</symbol> (<literal>p</literal>)</term>
-     <listitem>
-      <para>
-       There is no dependent object; this type of entry is a signal
-       that the system itself depends on the referenced object, and so
-       that object must never be deleted.  Entries of this type are
-       created only by <application>initdb</application>.  The columns for the
-       dependent object contain zeroes.
-      </para>
-     </listitem>
-    </varlistentry>
-
     <varlistentry>
      <term><symbol>SHARED_DEPENDENCY_TABLESPACE</symbol> (<literal>t</literal>)</term>
      <listitem>
@@ -6915,6 +6897,14 @@ SCRAM-SHA-256$<replaceable><iteration count></replaceable>:<replaceable>&l
    objects.
   </para>

+  <para>
+   As in the <structname>pg_depend</structname> catalog, most objects
+   created during <application>initdb</application> are
+   considered <quote>pinned</quote>.  No entries are made
+   in <structname>pg_shdepend</structname> that would have a pinned
+   object as either referenced or dependent object.
+  </para>
+
  </sect1>

  <sect1 id="catalog-pg-shdescription">
diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c
index a22bf375f8..f335d91c17 100644
--- a/src/backend/access/transam/varsup.c
+++ b/src/backend/access/transam/varsup.c
@@ -541,11 +541,11 @@ GetNewObjectId(void)
      * FirstNormalObjectId since that range is reserved for initdb (see
      * IsCatalogRelationOid()).  Note we are relying on unsigned comparison.
      *
-     * During initdb, we start the OID generator at FirstBootstrapObjectId, so
+     * During initdb, we start the OID generator at FirstGenbkiObjectId, so
      * we only wrap if before that point when in bootstrap or standalone mode.
      * The first time through this routine after normal postmaster start, the
      * counter will be forced up to FirstNormalObjectId.  This mechanism
-     * leaves the OIDs between FirstBootstrapObjectId and FirstNormalObjectId
+     * leaves the OIDs between FirstGenbkiObjectId and FirstNormalObjectId
      * available for automatic assignment during initdb, while ensuring they
      * will never conflict with user-assigned OIDs.
      */
@@ -560,7 +560,7 @@ GetNewObjectId(void)
         else
         {
             /* we may be bootstrapping, so don't enforce the full range */
-            if (ShmemVariableCache->nextOid < ((Oid) FirstBootstrapObjectId))
+            if (ShmemVariableCache->nextOid < ((Oid) FirstGenbkiObjectId))
             {
                 /* wraparound in standalone mode (unlikely but possible) */
                 ShmemVariableCache->nextOid = FirstNormalObjectId;
@@ -586,6 +586,47 @@ GetNewObjectId(void)
     return result;
 }

+/*
+ * SetNextObjectId
+ *
+ * This may only be called during initdb; it advances the OID counter
+ * to the specified value.
+ */
+void
+SetNextObjectId(Oid nextOid)
+{
+    /* Safety check, this is only allowable during initdb */
+    if (IsPostmasterEnvironment)
+        elog(ERROR, "cannot advance OID counter anymore");
+
+    /* Taking the lock is, therefore, just pro forma; but do it anyway */
+    LWLockAcquire(OidGenLock, LW_EXCLUSIVE);
+
+    if (ShmemVariableCache->nextOid > nextOid)
+        elog(ERROR, "too late to advance OID counter to %u, it is now %u",
+             nextOid, ShmemVariableCache->nextOid);
+
+    ShmemVariableCache->nextOid = nextOid;
+    ShmemVariableCache->oidCount = 0;
+
+    LWLockRelease(OidGenLock);
+}
+
+/*
+ * StopGeneratingPinnedObjectIds
+ *
+ * This is called once during initdb to force the OID counter up to
+ * FirstUnpinnedObjectId.  This supports letting initdb's post-bootstrap
+ * processing create some pinned objects early on.  Once it's done doing
+ * so, it calls this (via pg_stop_making_pinned_objects()) so that the
+ * remaining objects it makes will be considered un-pinned.
+ */
+void
+StopGeneratingPinnedObjectIds(void)
+{
+    SetNextObjectId(FirstUnpinnedObjectId);
+}
+

 #ifdef USE_ASSERT_CHECKING

diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 8d163f190f..d2e1f38537 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -5288,7 +5288,7 @@ BootStrapXLOG(void)
     checkPoint.fullPageWrites = fullPageWrites;
     checkPoint.nextXid =
         FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
-    checkPoint.nextOid = FirstBootstrapObjectId;
+    checkPoint.nextOid = FirstGenbkiObjectId;
     checkPoint.nextMulti = FirstMultiXactId;
     checkPoint.nextMultiOffset = 0;
     checkPoint.oldestXid = FirstNormalTransactionId;
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 5fcd004e1b..01609100c4 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -103,7 +103,7 @@ static int num_columns_read = 0;
 %token NULLVAL
 /* All the rest are unreserved, and should be handled in boot_ident! */
 %token <kw> OPEN XCLOSE XCREATE INSERT_TUPLE
-%token <kw> XDECLARE INDEX ON USING XBUILD INDICES UNIQUE XTOAST
+%token <kw> XDECLARE INDEX ON USING XBUILD INDICES XSET_NEXT_OID UNIQUE XTOAST
 %token <kw> OBJ_ID XBOOTSTRAP XSHARED_RELATION XROWTYPE_OID
 %token <kw> XFORCE XNOT XNULL

@@ -130,6 +130,7 @@ Boot_Query :
         | Boot_DeclareUniqueIndexStmt
         | Boot_DeclareToastStmt
         | Boot_BuildIndsStmt
+        | Boot_SetNextOidStmt
         ;

 Boot_OpenStmt:
@@ -390,6 +391,15 @@ Boot_BuildIndsStmt:
                 }
         ;

+Boot_SetNextOidStmt:
+          XSET_NEXT_OID oidspec
+                {
+                    do_start();
+                    set_next_oid($2);
+                    do_end();
+                }
+        ;
+

 boot_index_params:
         boot_index_params COMMA boot_index_param    { $$ = lappend($1, $3); }
@@ -475,6 +485,7 @@ boot_ident:
         | USING            { $$ = pstrdup($1); }
         | XBUILD        { $$ = pstrdup($1); }
         | INDICES        { $$ = pstrdup($1); }
+        | XSET_NEXT_OID    { $$ = pstrdup($1); }
         | UNIQUE        { $$ = pstrdup($1); }
         | XTOAST        { $$ = pstrdup($1); }
         | OBJ_ID        { $$ = pstrdup($1); }
diff --git a/src/backend/bootstrap/bootscanner.l b/src/backend/bootstrap/bootscanner.l
index 7aecd895fb..515a7636b8 100644
--- a/src/backend/bootstrap/bootscanner.l
+++ b/src/backend/bootstrap/bootscanner.l
@@ -93,6 +93,7 @@ _null_            { return NULLVAL; }
 declare            { yylval.kw = "declare"; return XDECLARE; }
 build            { yylval.kw = "build"; return XBUILD; }
 indices            { yylval.kw = "indices"; return INDICES; }
+set_next_oid    { yylval.kw = "set_next_oid"; return XSET_NEXT_OID; }
 unique            { yylval.kw = "unique"; return UNIQUE; }
 index            { yylval.kw = "index"; return INDEX; }
 on                { yylval.kw = "on"; return ON; }
diff --git a/src/backend/bootstrap/bootstrap.c b/src/backend/bootstrap/bootstrap.c
index b155237488..e2aba938a1 100644
--- a/src/backend/bootstrap/bootstrap.c
+++ b/src/backend/bootstrap/bootstrap.c
@@ -1155,3 +1155,13 @@ build_indices(void)
         table_close(heap, NoLock);
     }
 }
+
+
+/*
+ * set_next_oid -- set the backend's OID counter
+ */
+void
+set_next_oid(Oid nextOid)
+{
+    SetNextObjectId(nextOid);
+}
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c
index 245d536372..55cc4881e1 100644
--- a/src/backend/catalog/catalog.c
+++ b/src/backend/catalog/catalog.c
@@ -31,6 +31,7 @@
 #include "catalog/pg_authid.h"
 #include "catalog/pg_database.h"
 #include "catalog/pg_db_role_setting.h"
+#include "catalog/pg_largeobject.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_replication_origin.h"
 #include "catalog/pg_shdepend.h"
@@ -120,9 +121,9 @@ bool
 IsCatalogRelationOid(Oid relid)
 {
     /*
-     * We consider a relation to be a system catalog if it has an OID that was
-     * manually assigned or assigned by genbki.pl.  This includes all the
-     * defined catalogs, their indexes, and their TOAST tables and indexes.
+     * We consider a relation to be a system catalog if it has a pinned OID.
+     * This includes all the defined catalogs, their indexes, and their TOAST
+     * tables and indexes.
      *
      * This rule excludes the relations in information_schema, which are not
      * integral to the system and can be treated the same as user relations.
@@ -132,7 +133,7 @@ IsCatalogRelationOid(Oid relid)
      * This test is reliable since an OID wraparound will skip this range of
      * OIDs; see GetNewObjectId().
      */
-    return (relid < (Oid) FirstBootstrapObjectId);
+    return (relid < (Oid) FirstUnpinnedObjectId);
 }

 /*
@@ -294,6 +295,64 @@ IsSharedRelation(Oid relationId)
     return false;
 }

+/*
+ * IsPinnedObject
+ *        Given the class + OID identity of a database object, report whether
+ *        it is "pinned", that is not droppable because the system requires it.
+ *
+ * We used to represent this explicitly in pg_depend, but that proved to be
+ * an undesirable amount of overhead, so now we rely on an OID range test.
+ */
+bool
+IsPinnedObject(Oid classId, Oid objectId)
+{
+    /*
+     * Objects with OIDs above FirstUnpinnedObjectId are never pinned.  Since
+     * the OID generator skips this range when wrapping around, this check
+     * guarantees that user-defined objects are never considered pinned.
+     */
+    if (objectId >= FirstUnpinnedObjectId)
+        return false;
+
+    /*
+     * Large objects are never pinned.  We need this special case because
+     * their OIDs can be user-assigned.
+     */
+    if (classId == LargeObjectRelationId)
+        return false;
+
+    /*
+     * There are a few objects defined in the catalog .dat files that, as a
+     * matter of policy, we prefer not to treat as pinned.  We used to handle
+     * that by excluding them from pg_depend, but it's just as easy to
+     * hard-wire their OIDs here.  (If the user does indeed drop and recreate
+     * them, they'll have new but certainly-unpinned OIDs, so no problem.)
+     *
+     * Checking both classId and objectId is overkill, since OIDs below
+     * FirstUnpinnedObjectId should be globally unique, but do it anyway for
+     * robustness.
+     */
+
+    /* template1 is not pinned */
+    if (classId == DatabaseRelationId &&
+        objectId == TemplateDbOid)
+        return false;
+
+    /* the public namespace is not pinned */
+    if (classId == NamespaceRelationId &&
+        objectId == PG_PUBLIC_NAMESPACE)
+        return false;
+
+    /*
+     * All other initdb-created objects are pinned.  This is overkill (the
+     * system doesn't really depend on having every last weird datatype, for
+     * instance) but generating only the minimum required set of dependencies
+     * seems hard, and enforcing an accurate list would be much more expensive
+     * than the simple range test used here.
+     */
+    return true;
+}
+

 /*
  * GetNewOidWithIndex
@@ -529,7 +588,8 @@ pg_nextoid(PG_FUNCTION_ARGS)
     if (!superuser())
         ereport(ERROR,
                 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
-                 errmsg("must be superuser to call pg_nextoid()")));
+                 errmsg("must be superuser to call %s()",
+                        "pg_nextoid")));

     rel = table_open(reloid, RowExclusiveLock);
     idx = index_open(idxoid, RowExclusiveLock);
@@ -576,5 +636,29 @@ pg_nextoid(PG_FUNCTION_ARGS)
     table_close(rel, RowExclusiveLock);
     index_close(idx, RowExclusiveLock);

-    return newoid;
+    PG_RETURN_OID(newoid);
+}
+
+/*
+ * SQL callable interface for StopGeneratingPinnedObjectIds().
+ *
+ * This is only to be used by initdb, so it's intentionally not documented in
+ * the user facing docs.
+ */
+Datum
+pg_stop_making_pinned_objects(PG_FUNCTION_ARGS)
+{
+    /*
+     * Belt-and-suspenders check, since StopGeneratingPinnedObjectIds will
+     * fail anyway in non-single-user mode.
+     */
+    if (!superuser())
+        ereport(ERROR,
+                (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+                 errmsg("must be superuser to call %s()",
+                        "pg_stop_making_pinned_objects")));
+
+    StopGeneratingPinnedObjectIds();
+
+    PG_RETURN_VOID();
 }
diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c
index 0c37fc1d53..76b65e39c4 100644
--- a/src/backend/catalog/dependency.c
+++ b/src/backend/catalog/dependency.c
@@ -18,6 +18,7 @@
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "access/xact.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
@@ -520,6 +521,16 @@ findDependentObjects(const ObjectAddress *object,
     if (object_address_present_add_flags(object, objflags, targetObjects))
         return;

+    /*
+     * If the target object is pinned, we can just error out immediately; it
+     * won't have any objects recorded as depending on it.
+     */
+    if (IsPinnedObject(object->classId, object->objectId))
+        ereport(ERROR,
+                (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                 errmsg("cannot drop %s because it is required by the database system",
+                        getObjectDescription(object, false))));
+
     /*
      * The target object might be internally dependent on some other object
      * (its "owner"), and/or be a member of an extension (also considered its
@@ -783,15 +794,6 @@ findDependentObjects(const ObjectAddress *object,
                 objflags |= DEPFLAG_IS_PART;
                 break;

-            case DEPENDENCY_PIN:
-
-                /*
-                 * Should not happen; PIN dependencies should have zeroes in
-                 * the depender fields...
-                 */
-                elog(ERROR, "incorrect use of PIN dependency with %s",
-                     getObjectDescription(object, false));
-                break;
             default:
                 elog(ERROR, "unrecognized dependency type '%c' for %s",
                      foundDep->deptype, getObjectDescription(object, false));
@@ -920,18 +922,6 @@ findDependentObjects(const ObjectAddress *object,
             case DEPENDENCY_EXTENSION:
                 subflags = DEPFLAG_EXTENSION;
                 break;
-            case DEPENDENCY_PIN:
-
-                /*
-                 * For a PIN dependency we just ereport immediately; there
-                 * won't be any others to report.
-                 */
-                ereport(ERROR,
-                        (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
-                         errmsg("cannot drop %s because it is required by the database system",
-                                getObjectDescription(object, false))));
-                subflags = 0;    /* keep compiler quiet */
-                break;
             default:
                 elog(ERROR, "unrecognized dependency type '%c' for %s",
                      foundDep->deptype, getObjectDescription(object, false));
diff --git a/src/backend/catalog/genbki.pl b/src/backend/catalog/genbki.pl
index bf080b5f12..83dcf38c2d 100644
--- a/src/backend/catalog/genbki.pl
+++ b/src/backend/catalog/genbki.pl
@@ -168,13 +168,13 @@ die "found $found duplicate OID(s) in catalog data\n" if $found;


 # Oids not specified in the input files are automatically assigned,
-# starting at FirstGenbkiObjectId, extending up to FirstBootstrapObjectId.
+# starting at FirstGenbkiObjectId, extending up to FirstUnpinnedObjectId.
 my $FirstGenbkiObjectId =
   Catalog::FindDefinedSymbol('access/transam.h', $include_path,
     'FirstGenbkiObjectId');
-my $FirstBootstrapObjectId =
+my $FirstUnpinnedObjectId =
   Catalog::FindDefinedSymbol('access/transam.h', $include_path,
-    'FirstBootstrapObjectId');
+    'FirstUnpinnedObjectId');
 my $GenbkiNextOid = $FirstGenbkiObjectId;


@@ -655,6 +655,14 @@ EOM
 # Any information needed for the BKI that is not contained in a pg_*.h header
 # (i.e., not contained in a header with a CATALOG() statement) comes here

+# Check that we didn't overrun available OIDs, and write out the next OID
+# (do this before any index creation could consume OIDs)
+die
+  "genbki OID counter reached $GenbkiNextOid, overrunning FirstUnpinnedObjectId\n"
+  if $GenbkiNextOid > $FirstUnpinnedObjectId;
+
+print $bki "set_next_oid $GenbkiNextOid\n";
+
 # Write out declare toast/index statements
 foreach my $declaration (@toast_decls)
 {
@@ -669,11 +677,6 @@ foreach my $declaration (@index_decls)
 # last command in the BKI file: build the indexes declared above
 print $bki "build indices\n";

-# check that we didn't overrun available OIDs
-die
-  "genbki OID counter reached $GenbkiNextOid, overrunning FirstBootstrapObjectId\n"
-  if $GenbkiNextOid > $FirstBootstrapObjectId;
-
 # Now generate system_constraints.sql

 foreach my $c (@system_constraints)
diff --git a/src/backend/catalog/pg_depend.c b/src/backend/catalog/pg_depend.c
index 54688094f5..10f3119670 100644
--- a/src/backend/catalog/pg_depend.c
+++ b/src/backend/catalog/pg_depend.c
@@ -17,6 +17,7 @@
 #include "access/genam.h"
 #include "access/htup_details.h"
 #include "access/table.h"
+#include "catalog/catalog.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_constraint.h"
@@ -29,7 +30,7 @@
 #include "utils/rel.h"


-static bool isObjectPinned(const ObjectAddress *object, Relation rel);
+static bool isObjectPinned(const ObjectAddress *object);


 /*
@@ -69,8 +70,11 @@ recordMultipleDependencies(const ObjectAddress *depender,
         return;                    /* nothing to do */

     /*
-     * During bootstrap, do nothing since pg_depend may not exist yet. initdb
-     * will fill in appropriate pg_depend entries after bootstrap.
+     * During bootstrap, do nothing since pg_depend may not exist yet.
+     *
+     * Objects created during bootstrap are most likely pinned, and the few
+     * that are not do not have dependencies on each other, so that there
+     * would be no need to make a pg_depend entry anyway.
      */
     if (IsBootstrapProcessingMode())
         return;
@@ -99,7 +103,7 @@ recordMultipleDependencies(const ObjectAddress *depender,
          * need to record dependencies on it.  This saves lots of space in
          * pg_depend, so it's worth the time taken to check.
          */
-        if (isObjectPinned(referenced, dependDesc))
+        if (isObjectPinned(referenced))
             continue;

         if (slot_init_count < max_slots)
@@ -399,8 +403,6 @@ changeDependencyFor(Oid classId, Oid objectId,
     bool        oldIsPinned;
     bool        newIsPinned;

-    depRel = table_open(DependRelationId, RowExclusiveLock);
-
     /*
      * Check to see if either oldRefObjectId or newRefObjectId is pinned.
      * Pinned objects should not have any dependency entries pointing to them,
@@ -411,16 +413,14 @@ changeDependencyFor(Oid classId, Oid objectId,
     objAddr.objectId = oldRefObjectId;
     objAddr.objectSubId = 0;

-    oldIsPinned = isObjectPinned(&objAddr, depRel);
+    oldIsPinned = isObjectPinned(&objAddr);

     objAddr.objectId = newRefObjectId;

-    newIsPinned = isObjectPinned(&objAddr, depRel);
+    newIsPinned = isObjectPinned(&objAddr);

     if (oldIsPinned)
     {
-        table_close(depRel, RowExclusiveLock);
-
         /*
          * If both are pinned, we need do nothing.  However, return 1 not 0,
          * else callers will think this is an error case.
@@ -440,6 +440,8 @@ changeDependencyFor(Oid classId, Oid objectId,
         return 1;
     }

+    depRel = table_open(DependRelationId, RowExclusiveLock);
+
     /* There should be existing dependency record(s), so search. */
     ScanKeyInit(&key[0],
                 Anum_pg_depend_classid,
@@ -574,7 +576,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
     objAddr.objectId = oldRefObjectId;
     objAddr.objectSubId = 0;

-    if (isObjectPinned(&objAddr, depRel))
+    if (isObjectPinned(&objAddr))
         ereport(ERROR,
                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                  errmsg("cannot remove dependency on %s because it is a system object",
@@ -586,7 +588,7 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
      */
     objAddr.objectId = newRefObjectId;

-    newIsPinned = isObjectPinned(&objAddr, depRel);
+    newIsPinned = isObjectPinned(&objAddr);

     /* Now search for dependency records */
     ScanKeyInit(&key[0],
@@ -634,50 +636,14 @@ changeDependenciesOn(Oid refClassId, Oid oldRefObjectId,
  * isObjectPinned()
  *
  * Test if an object is required for basic database functionality.
- * Caller must already have opened pg_depend.
  *
  * The passed subId, if any, is ignored; we assume that only whole objects
  * are pinned (and that this implies pinning their components).
  */
 static bool
-isObjectPinned(const ObjectAddress *object, Relation rel)
+isObjectPinned(const ObjectAddress *object)
 {
-    bool        ret = false;
-    SysScanDesc scan;
-    HeapTuple    tup;
-    ScanKeyData key[2];
-
-    ScanKeyInit(&key[0],
-                Anum_pg_depend_refclassid,
-                BTEqualStrategyNumber, F_OIDEQ,
-                ObjectIdGetDatum(object->classId));
-
-    ScanKeyInit(&key[1],
-                Anum_pg_depend_refobjid,
-                BTEqualStrategyNumber, F_OIDEQ,
-                ObjectIdGetDatum(object->objectId));
-
-    scan = systable_beginscan(rel, DependReferenceIndexId, true,
-                              NULL, 2, key);
-
-    /*
-     * Since we won't generate additional pg_depend entries for pinned
-     * objects, there can be at most one entry referencing a pinned object.
-     * Hence, it's sufficient to look at the first returned tuple; we don't
-     * need to loop.
-     */
-    tup = systable_getnext(scan);
-    if (HeapTupleIsValid(tup))
-    {
-        Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
-
-        if (foundDep->deptype == DEPENDENCY_PIN)
-            ret = true;
-    }
-
-    systable_endscan(scan);
-
-    return ret;
+    return IsPinnedObject(object->classId, object->objectId);
 }


diff --git a/src/backend/catalog/pg_publication.c b/src/backend/catalog/pg_publication.c
index 86e415af89..36bfff9706 100644
--- a/src/backend/catalog/pg_publication.c
+++ b/src/backend/catalog/pg_publication.c
@@ -85,7 +85,7 @@ check_publication_add_relation(Relation targetrel)
  * XXX  This also excludes all tables with relid < FirstNormalObjectId,
  * ie all tables created during initdb.  This mainly affects the preinstalled
  * information_schema.  IsCatalogRelationOid() only excludes tables with
- * relid < FirstBootstrapObjectId, making that test rather redundant,
+ * relid < FirstUnpinnedObjectId, making that test rather redundant,
  * but really we should get rid of the FirstNormalObjectId test not
  * IsCatalogRelationOid.  We can't do so today because we don't want
  * information_schema tables to be considered publishable; but this test
diff --git a/src/backend/catalog/pg_shdepend.c b/src/backend/catalog/pg_shdepend.c
index 420ad96565..94989119ed 100644
--- a/src/backend/catalog/pg_shdepend.c
+++ b/src/backend/catalog/pg_shdepend.c
@@ -101,7 +101,7 @@ static void storeObjectDescription(StringInfo descs,
                                    ObjectAddress *object,
                                    SharedDependencyType deptype,
                                    int count);
-static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel);
+static bool isSharedObjectPinned(Oid classId, Oid objectId);


 /*
@@ -140,8 +140,7 @@ recordSharedDependencyOn(ObjectAddress *depender,
     sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

     /* If the referenced object is pinned, do nothing. */
-    if (!isSharedObjectPinned(referenced->classId, referenced->objectId,
-                              sdepRel))
+    if (!isSharedObjectPinned(referenced->classId, referenced->objectId))
     {
         shdepAddDependency(sdepRel, depender->classId, depender->objectId,
                            depender->objectSubId,
@@ -255,7 +254,7 @@ shdepChangeDep(Relation sdepRel,

     systable_endscan(scan);

-    if (isSharedObjectPinned(refclassid, refobjid, sdepRel))
+    if (isSharedObjectPinned(refclassid, refobjid))
     {
         /* No new entry needed, so just delete existing entry if any */
         if (oldtup)
@@ -513,7 +512,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
                 continue;

             /* Skip pinned roles; they don't need dependency entries */
-            if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+            if (isSharedObjectPinned(AuthIdRelationId, roleid))
                 continue;

             shdepAddDependency(sdepRel, classId, objectId, objsubId,
@@ -531,7 +530,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId,
                 continue;

             /* Skip pinned roles */
-            if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+            if (isSharedObjectPinned(AuthIdRelationId, roleid))
                 continue;

             shdepDropDependency(sdepRel, classId, objectId, objsubId,
@@ -626,8 +625,6 @@ shared_dependency_comparator(const void *a, const void *b)
  * on objects local to other databases.  We can (and do) provide descriptions
  * of the two former kinds of objects, but we can't do that for "remote"
  * objects, so we just provide a count of them.
- *
- * If we find a SHARED_DEPENDENCY_PIN entry, we can error out early.
  */
 bool
 checkSharedDependencies(Oid classId, Oid objectId,
@@ -649,6 +646,18 @@ checkSharedDependencies(Oid classId, Oid objectId,
     StringInfoData descs;
     StringInfoData alldescs;

+    /* This case can be dispatched quickly */
+    if (isSharedObjectPinned(classId, objectId))
+    {
+        object.classId = classId;
+        object.objectId = objectId;
+        object.objectSubId = 0;
+        ereport(ERROR,
+                (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
+                 errmsg("cannot drop %s because it is required by the database system",
+                        getObjectDescription(&object, false))));
+    }
+
     /*
      * We limit the number of dependencies reported to the client to
      * MAX_REPORTED_DEPS, since client software may not deal well with
@@ -685,18 +694,6 @@ checkSharedDependencies(Oid classId, Oid objectId,
     {
         Form_pg_shdepend sdepForm = (Form_pg_shdepend) GETSTRUCT(tup);

-        /* This case can be dispatched quickly */
-        if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
-        {
-            object.classId = classId;
-            object.objectId = objectId;
-            object.objectSubId = 0;
-            ereport(ERROR,
-                    (errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
-                     errmsg("cannot drop %s because it is required by the database system",
-                            getObjectDescription(&object, false))));
-        }
-
         object.classId = sdepForm->classid;
         object.objectId = sdepForm->objid;
         object.objectSubId = sdepForm->objsubid;
@@ -1274,49 +1271,15 @@ storeObjectDescription(StringInfo descs,

 /*
  * isSharedObjectPinned
- *        Return whether a given shared object has a SHARED_DEPENDENCY_PIN entry.
- *
- * sdepRel must be the pg_shdepend relation, already opened and suitably
- * locked.
+ *        Return true if a shared object is pinned.
  */
 static bool
-isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel)
+isSharedObjectPinned(Oid classId, Oid objectId)
 {
-    bool        result = false;
-    ScanKeyData key[2];
-    SysScanDesc scan;
-    HeapTuple    tup;
-
-    ScanKeyInit(&key[0],
-                Anum_pg_shdepend_refclassid,
-                BTEqualStrategyNumber, F_OIDEQ,
-                ObjectIdGetDatum(classId));
-    ScanKeyInit(&key[1],
-                Anum_pg_shdepend_refobjid,
-                BTEqualStrategyNumber, F_OIDEQ,
-                ObjectIdGetDatum(objectId));
-
-    scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
-                              NULL, 2, key);
-
     /*
-     * Since we won't generate additional pg_shdepend entries for pinned
-     * objects, there can be at most one entry referencing a pinned object.
-     * Hence, it's sufficient to look at the first returned tuple; we don't
-     * need to loop.
+     * This is currently just the same as the normal pin test.
      */
-    tup = systable_getnext(scan);
-    if (HeapTupleIsValid(tup))
-    {
-        Form_pg_shdepend shdepForm = (Form_pg_shdepend) GETSTRUCT(tup);
-
-        if (shdepForm->deptype == SHARED_DEPENDENCY_PIN)
-            result = true;
-    }
-
-    systable_endscan(scan);
-
-    return result;
+    return IsPinnedObject(classId, objectId);
 }

 /*
@@ -1359,7 +1322,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
         HeapTuple    tuple;

         /* Doesn't work for pinned objects */
-        if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+        if (isSharedObjectPinned(AuthIdRelationId, roleid))
         {
             ObjectAddress obj;

@@ -1402,7 +1365,6 @@ shdepDropOwned(List *roleids, DropBehavior behavior)
             switch (sdepForm->deptype)
             {
                     /* Shouldn't happen */
-                case SHARED_DEPENDENCY_PIN:
                 case SHARED_DEPENDENCY_INVALID:
                     elog(ERROR, "unexpected dependency type");
                     break;
@@ -1506,7 +1468,7 @@ shdepReassignOwned(List *roleids, Oid newrole)
         Oid            roleid = lfirst_oid(cell);

         /* Refuse to work on pinned roles */
-        if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel))
+        if (isSharedObjectPinned(AuthIdRelationId, roleid))
         {
             ObjectAddress obj;

@@ -1549,10 +1511,6 @@ shdepReassignOwned(List *roleids, Oid newrole)
                 sdepForm->dbid != InvalidOid)
                 continue;

-            /* Unexpected because we checked for pins above */
-            if (sdepForm->deptype == SHARED_DEPENDENCY_PIN)
-                elog(ERROR, "unexpected shared pin");
-
             /* We leave non-owner dependencies alone */
             if (sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
                 continue;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index ebc62034d2..18d2ea7723 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -12000,10 +12000,6 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
         Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(depTup);
         ObjectAddress foundObject;

-        /* We don't expect any PIN dependencies on columns */
-        if (foundDep->deptype == DEPENDENCY_PIN)
-            elog(ERROR, "cannot alter type of a pinned column");
-
         foundObject.classId = foundDep->classid;
         foundObject.objectId = foundDep->objid;
         foundObject.objectSubId = foundDep->objsubid;
diff --git a/src/backend/commands/tablespace.c b/src/backend/commands/tablespace.c
index 69ea155d50..0385fd6121 100644
--- a/src/backend/commands/tablespace.c
+++ b/src/backend/commands/tablespace.c
@@ -449,7 +449,6 @@ DropTableSpace(DropTableSpaceStmt *stmt)
             ereport(NOTICE,
                     (errmsg("tablespace \"%s\" does not exist, skipping",
                             tablespacename)));
-            /* XXX I assume I need one or both of these next two calls */
             table_endscan(scandesc);
             table_close(rel, NoLock);
         }
@@ -465,8 +464,7 @@ DropTableSpace(DropTableSpaceStmt *stmt)
                        tablespacename);

     /* Disallow drop of the standard tablespaces, even by superuser */
-    if (tablespaceoid == GLOBALTABLESPACE_OID ||
-        tablespaceoid == DEFAULTTABLESPACE_OID)
+    if (IsPinnedObject(TableSpaceRelationId, tablespaceoid))
         aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_TABLESPACE,
                        tablespacename);

diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c
index 9f40ed77e6..7cb51563fa 100644
--- a/src/backend/optimizer/plan/setrefs.c
+++ b/src/backend/optimizer/plan/setrefs.c
@@ -2946,11 +2946,11 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid)
      * For performance reasons, we don't bother to track built-in functions;
      * we just assume they'll never change (or at least not in ways that'd
      * invalidate plans using them).  For this purpose we can consider a
-     * built-in function to be one with OID less than FirstBootstrapObjectId.
+     * built-in function to be one with OID less than FirstUnpinnedObjectId.
      * Note that the OID generator guarantees never to generate such an OID
      * after startup, even at OID wraparound.
      */
-    if (funcid >= (Oid) FirstBootstrapObjectId)
+    if (funcid >= (Oid) FirstUnpinnedObjectId)
     {
         PlanInvalItem *inval_item = makeNode(PlanInvalItem);

@@ -2986,7 +2986,7 @@ record_plan_type_dependency(PlannerInfo *root, Oid typid)
      * As in record_plan_function_dependency, ignore the possibility that
      * someone would change a built-in domain.
      */
-    if (typid >= (Oid) FirstBootstrapObjectId)
+    if (typid >= (Oid) FirstUnpinnedObjectId)
     {
         PlanInvalItem *inval_item = makeNode(PlanInvalItem);

diff --git a/src/backend/storage/lmgr/predicate.c b/src/backend/storage/lmgr/predicate.c
index d493aeef0f..56267bdc3c 100644
--- a/src/backend/storage/lmgr/predicate.c
+++ b/src/backend/storage/lmgr/predicate.c
@@ -494,7 +494,7 @@ static void ReleasePredicateLocksLocal(void);
 static inline bool
 PredicateLockingNeededForRelation(Relation relation)
 {
-    return !(relation->rd_id < FirstBootstrapObjectId ||
+    return !(relation->rd_id < FirstUnpinnedObjectId ||
              RelationUsesLocalBuffers(relation) ||
              relation->rd_rel->relkind == RELKIND_MATVIEW);
 }
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 152d21e88b..39646ee88d 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1522,83 +1522,10 @@ setup_depend(FILE *cmdfd)
     const char *const *line;
     static const char *const pg_depend_setup[] = {
         /*
-         * Make PIN entries in pg_depend for all objects made so far in the
-         * tables that the dependency code handles.  This is overkill (the
-         * system doesn't really depend on having every last weird datatype,
-         * for instance) but generating only the minimum required set of
-         * dependencies seems hard.
-         *
-         * Catalogs that are intentionally not scanned here are:
-         *
-         * pg_database: it's a feature, not a bug, that template1 is not
+         * Advance the OID counter so that subsequently-created objects aren't
          * pinned.
-         *
-         * pg_extension: a pinned extension isn't really an extension, hmm?
-         *
-         * pg_tablespace: tablespaces don't participate in the dependency
-         * code, and DropTableSpace() explicitly protects the built-in
-         * tablespaces.
-         *
-         * First delete any already-made entries; PINs override all else, and
-         * must be the only entries for their objects.
-         */
-        "DELETE FROM pg_depend;\n\n",
-        "VACUUM pg_depend;\n\n",
-        "DELETE FROM pg_shdepend;\n\n",
-        "VACUUM pg_shdepend;\n\n",
-
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_class;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_proc;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_type;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_cast;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_constraint;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_conversion;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_attrdef;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_language;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_operator;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_opclass;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_opfamily;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_am;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_amop;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_amproc;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_rewrite;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_trigger;\n\n",
-
-        /*
-         * restriction here to avoid pinning the public namespace
          */
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_namespace "
-        "    WHERE nspname LIKE 'pg%';\n\n",
-
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_ts_parser;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_ts_dict;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_ts_template;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_ts_config;\n\n",
-        "INSERT INTO pg_depend SELECT 0,0,0, tableoid,oid,0, 'p' "
-        " FROM pg_collation;\n\n",
-        "INSERT INTO pg_shdepend SELECT 0,0,0,0, tableoid,oid, 'p' "
-        " FROM pg_authid;\n\n",
+        "SELECT pg_stop_making_pinned_objects();\n\n",
         NULL
     };

diff --git a/src/bin/pg_resetwal/pg_resetwal.c b/src/bin/pg_resetwal/pg_resetwal.c
index 805dafef07..2601f70a04 100644
--- a/src/bin/pg_resetwal/pg_resetwal.c
+++ b/src/bin/pg_resetwal/pg_resetwal.c
@@ -686,7 +686,7 @@ GuessControlValues(void)
     ControlFile.checkPointCopy.fullPageWrites = false;
     ControlFile.checkPointCopy.nextXid =
         FullTransactionIdFromEpochAndXid(0, FirstNormalTransactionId);
-    ControlFile.checkPointCopy.nextOid = FirstBootstrapObjectId;
+    ControlFile.checkPointCopy.nextOid = FirstGenbkiObjectId;
     ControlFile.checkPointCopy.nextMulti = FirstMultiXactId;
     ControlFile.checkPointCopy.nextMultiOffset = 0;
     ControlFile.checkPointCopy.oldestXid = FirstNormalTransactionId;
diff --git a/src/include/access/transam.h b/src/include/access/transam.h
index 05c6fbffe4..5be7bb3f95 100644
--- a/src/include/access/transam.h
+++ b/src/include/access/transam.h
@@ -163,10 +163,15 @@ FullTransactionIdAdvance(FullTransactionId *dest)
  *
  *        OIDs 10000-12999 are reserved for assignment by genbki.pl, for use
  *        when the .dat files in src/include/catalog/ do not specify an OID
- *        for a catalog entry that requires one.
+ *        for a catalog entry that requires one.  Furthermore, initdb's
+ *        post-bootstrap processing can also assign OIDs in this range,
+ *        for objects it makes that should be treated as pinned.  genbki.pl
+ *        records the first OID that it didn't use in postgres.bki, causing
+ *        the bootstrap backend's OID generator to be set to that value.
  *
- *        OIDS 13000-16383 are reserved for assignment during initdb
- *        using the OID generator.  (We start the generator at 13000.)
+ *        OIDs 13000-16383 are reserved for unpinned objects created by initdb's
+ *        post-bootstrap processing.  initdb forces the OID generator up to
+ *        13000 as soon as it's made the pinned objects it's responsible for.
  *
  *        OIDs beginning at 16384 are assigned from the OID generator
  *        during normal multiuser operation.  (We force the generator up to
@@ -182,11 +187,12 @@ FullTransactionIdAdvance(FullTransactionId *dest)
  *
  * NOTE: if the OID generator wraps around, we skip over OIDs 0-16383
  * and resume with 16384.  This minimizes the odds of OID conflict, by not
- * reassigning OIDs that might have been assigned during initdb.
+ * reassigning OIDs that might have been assigned during initdb.  Critically,
+ * it also ensures that no user-created object will be considered pinned.
  * ----------
  */
 #define FirstGenbkiObjectId        10000
-#define FirstBootstrapObjectId    13000
+#define FirstUnpinnedObjectId    13000
 #define FirstNormalObjectId        16384

 /*
@@ -287,6 +293,8 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid,
 extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid);
 extern bool ForceTransactionIdLimitUpdate(void);
 extern Oid    GetNewObjectId(void);
+extern void SetNextObjectId(Oid nextOid);
+extern void StopGeneratingPinnedObjectIds(void);

 #ifdef USE_ASSERT_CHECKING
 extern void AssertTransactionIdInAllowableRange(TransactionId xid);
diff --git a/src/include/bootstrap/bootstrap.h b/src/include/bootstrap/bootstrap.h
index 8290d4c6c4..cc3370bfa3 100644
--- a/src/include/bootstrap/bootstrap.h
+++ b/src/include/bootstrap/bootstrap.h
@@ -45,6 +45,8 @@ extern void InsertOneNull(int i);
 extern void index_register(Oid heap, Oid ind, IndexInfo *indexInfo);
 extern void build_indices(void);

+extern void set_next_oid(Oid nextOid);
+
 extern void boot_get_type_io_data(Oid typid,
                                   int16 *typlen,
                                   bool *typbyval,
diff --git a/src/include/catalog/catalog.h b/src/include/catalog/catalog.h
index f247be50b4..ef2e88fe45 100644
--- a/src/include/catalog/catalog.h
+++ b/src/include/catalog/catalog.h
@@ -34,6 +34,8 @@ extern bool IsReservedName(const char *name);

 extern bool IsSharedRelation(Oid relationId);

+extern bool IsPinnedObject(Oid classId, Oid objectId);
+
 extern Oid    GetNewOidWithIndex(Relation relation, Oid indexId,
                                AttrNumber oidcolumn);
 extern Oid    GetNewRelFileNode(Oid reltablespace, Relation pg_class,
diff --git a/src/include/catalog/dependency.h b/src/include/catalog/dependency.h
index fd44081e74..2885f35ccd 100644
--- a/src/include/catalog/dependency.h
+++ b/src/include/catalog/dependency.h
@@ -36,8 +36,7 @@ typedef enum DependencyType
     DEPENDENCY_PARTITION_PRI = 'P',
     DEPENDENCY_PARTITION_SEC = 'S',
     DEPENDENCY_EXTENSION = 'e',
-    DEPENDENCY_AUTO_EXTENSION = 'x',
-    DEPENDENCY_PIN = 'p'
+    DEPENDENCY_AUTO_EXTENSION = 'x'
 } DependencyType;

 /*
@@ -47,27 +46,21 @@ typedef enum DependencyType
  * unless the dependent object is dropped at the same time.  There are some
  * additional rules however:
  *
- * (a) For a SHARED_DEPENDENCY_PIN entry, there is no dependent object --
- * rather, the referenced object is an essential part of the system.  This
- * applies to the initdb-created superuser.  Entries of this type are only
- * created by initdb; objects in this category don't need further pg_shdepend
- * entries if more objects come to depend on them.
- *
- * (b) a SHARED_DEPENDENCY_OWNER entry means that the referenced object is
+ * (a) a SHARED_DEPENDENCY_OWNER entry means that the referenced object is
  * the role owning the dependent object.  The referenced object must be
  * a pg_authid entry.
  *
- * (c) a SHARED_DEPENDENCY_ACL entry means that the referenced object is
+ * (b) a SHARED_DEPENDENCY_ACL entry means that the referenced object is
  * a role mentioned in the ACL field of the dependent object.  The referenced
  * object must be a pg_authid entry.  (SHARED_DEPENDENCY_ACL entries are not
  * created for the owner of an object; hence two objects may be linked by
  * one or the other, but not both, of these dependency types.)
  *
- * (d) a SHARED_DEPENDENCY_POLICY entry means that the referenced object is
+ * (c) a SHARED_DEPENDENCY_POLICY entry means that the referenced object is
  * a role mentioned in a policy object.  The referenced object must be a
  * pg_authid entry.
  *
- * (e) a SHARED_DEPENDENCY_TABLESPACE entry means that the referenced
+ * (d) a SHARED_DEPENDENCY_TABLESPACE entry means that the referenced
  * object is a tablespace mentioned in a relation without storage.  The
  * referenced object must be a pg_tablespace entry.  (Relations that have
  * storage don't need this: they are protected by the existence of a physical
@@ -78,7 +71,6 @@ typedef enum DependencyType
  */
 typedef enum SharedDependencyType
 {
-    SHARED_DEPENDENCY_PIN = 'p',
     SHARED_DEPENDENCY_OWNER = 'o',
     SHARED_DEPENDENCY_ACL = 'a',
     SHARED_DEPENDENCY_POLICY = 'r',
diff --git a/src/include/catalog/pg_depend.h b/src/include/catalog/pg_depend.h
index e0bc114145..555523697f 100644
--- a/src/include/catalog/pg_depend.h
+++ b/src/include/catalog/pg_depend.h
@@ -4,8 +4,9 @@
  *      definition of the "dependency" system catalog (pg_depend)
  *
  * pg_depend has no preloaded contents, so there is no pg_depend.dat
- * file; system-defined dependencies are loaded into it during a late stage
- * of the initdb process.
+ * file; dependencies for system-defined objects are loaded into it
+ * on-the-fly during initdb.  Most built-in objects are pinned anyway,
+ * and hence need no explicit entries in pg_depend.
  *
  * NOTE: we do not represent all possible dependency pairs in pg_depend;
  * for example, there's not much value in creating an explicit dependency
@@ -42,11 +43,9 @@ CATALOG(pg_depend,2608,DependRelationId)
 {
     /*
      * Identification of the dependent (referencing) object.
-     *
-     * These fields are all zeroes for a DEPENDENCY_PIN entry.
      */
-    Oid            classid BKI_LOOKUP_OPT(pg_class);    /* OID of table containing
-                                                     * object */
+    Oid            classid BKI_LOOKUP(pg_class);    /* OID of table containing
+                                                 * object */
     Oid            objid;            /* OID of object itself */
     int32        objsubid;        /* column number, or 0 if not used */

diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat
index acbcae4607..00748bd898 100644
--- a/src/include/catalog/pg_proc.dat
+++ b/src/include/catalog/pg_proc.dat
@@ -3295,6 +3295,10 @@
   proname => 'pg_nextoid', provolatile => 'v', proparallel => 'u',
   prorettype => 'oid', proargtypes => 'regclass name regclass',
   prosrc => 'pg_nextoid' },
+{ oid => '8922', descr => 'stop making pinned objects during initdb',
+  proname => 'pg_stop_making_pinned_objects', provolatile => 'v',
+  proparallel => 'u', prorettype => 'void', proargtypes => '',
+  prosrc => 'pg_stop_making_pinned_objects' },

 { oid => '1579', descr => 'I/O',
   proname => 'varbit_in', prorettype => 'varbit',
diff --git a/src/include/catalog/pg_shdepend.h b/src/include/catalog/pg_shdepend.h
index 4faa95794d..4223717805 100644
--- a/src/include/catalog/pg_shdepend.h
+++ b/src/include/catalog/pg_shdepend.h
@@ -4,8 +4,9 @@
  *      definition of the "shared dependency" system catalog (pg_shdepend)
  *
  * pg_shdepend has no preloaded contents, so there is no pg_shdepend.dat
- * file; system-defined dependencies are loaded into it during a late stage
- * of the initdb process.
+ * file; dependencies for system-defined objects are loaded into it
+ * on-the-fly during initdb.  Most built-in objects are pinned anyway,
+ * and hence need no explicit entries in pg_shdepend.
  *
  * NOTE: we do not represent all possible dependency pairs in pg_shdepend;
  * for example, there's not much value in creating an explicit dependency
@@ -39,13 +40,12 @@ CATALOG(pg_shdepend,1214,SharedDependRelationId) BKI_SHARED_RELATION
     /*
      * Identification of the dependent (referencing) object.
      *
-     * These fields are all zeroes for a DEPENDENCY_PIN entry.  Also, dbid can
-     * be zero to denote a shared object.
+     * Note that dbid can be zero to denote a shared object.
      */
     Oid            dbid BKI_LOOKUP_OPT(pg_database);    /* OID of database
                                                      * containing object */
-    Oid            classid BKI_LOOKUP_OPT(pg_class);    /* OID of table containing
-                                                     * object */
+    Oid            classid BKI_LOOKUP(pg_class);    /* OID of table containing
+                                                 * object */
     Oid            objid;            /* OID of object itself */
     int32        objsubid;        /* column number, or 0 if not used */

diff --git a/src/test/regress/expected/misc_sanity.out b/src/test/regress/expected/misc_sanity.out
index a67f40198a..a57fd142a9 100644
--- a/src/test/regress/expected/misc_sanity.out
+++ b/src/test/regress/expected/misc_sanity.out
@@ -11,75 +11,26 @@
 -- NB: run this test early, because some later tests create bogus entries.
 -- **************** pg_depend ****************
 -- Look for illegal values in pg_depend fields.
--- classid/objid can be zero, but only in 'p' entries
 SELECT *
 FROM pg_depend as d1
 WHERE refclassid = 0 OR refobjid = 0 OR
-      deptype NOT IN ('a', 'e', 'i', 'n', 'p') OR
-      (deptype != 'p' AND (classid = 0 OR objid = 0)) OR
-      (deptype = 'p' AND (classid != 0 OR objid != 0 OR objsubid != 0));
+      classid = 0 OR objid = 0 OR
+      deptype NOT IN ('a', 'e', 'i', 'n', 'x', 'P', 'S');
  classid | objid | objsubid | refclassid | refobjid | refobjsubid | deptype
 ---------+-------+----------+------------+----------+-------------+---------
 (0 rows)

 -- **************** pg_shdepend ****************
 -- Look for illegal values in pg_shdepend fields.
--- classid/objid can be zero, but only in 'p' entries
 SELECT *
 FROM pg_shdepend as d1
 WHERE refclassid = 0 OR refobjid = 0 OR
-      deptype NOT IN ('a', 'o', 'p', 'r') OR
-      (deptype != 'p' AND (classid = 0 OR objid = 0)) OR
-      (deptype = 'p' AND (dbid != 0 OR classid != 0 OR objid != 0 OR objsubid != 0));
+      classid = 0 OR objid = 0 OR
+      deptype NOT IN ('a', 'o', 'r', 't');
  dbid | classid | objid | objsubid | refclassid | refobjid | deptype
 ------+---------+-------+----------+------------+----------+---------
 (0 rows)

--- Check each OID-containing system catalog to see if its lowest-numbered OID
--- is pinned.  If not, and if that OID was generated during initdb, then
--- perhaps initdb forgot to scan that catalog for pinnable entries.
--- Generally, it's okay for a catalog to be listed in the output of this
--- test if that catalog is scanned by initdb.c's setup_depend() function;
--- whatever OID the test is complaining about must have been added later
--- in initdb, where it intentionally isn't pinned.  Legitimate exceptions
--- to that rule are listed in the comments in setup_depend().
--- Currently, pg_rewrite is also listed by this check, even though it is
--- covered by setup_depend().  That happens because there are no rules in
--- the pinned data, but initdb creates some intentionally-not-pinned views.
-do $$
-declare relnm text;
-  reloid oid;
-  shared bool;
-  lowoid oid;
-  pinned bool;
-begin
-for relnm, reloid, shared in
-  select relname, oid, relisshared from pg_class
-  where EXISTS(
-      SELECT * FROM pg_attribute
-      WHERE attrelid = pg_class.oid AND attname = 'oid')
-    and relkind = 'r' and oid < 16384 order by 1
-loop
-  execute 'select min(oid) from ' || relnm into lowoid;
-  continue when lowoid is null or lowoid >= 16384;
-  if shared then
-    pinned := exists(select 1 from pg_shdepend
-                     where refclassid = reloid and refobjid = lowoid
-                     and deptype = 'p');
-  else
-    pinned := exists(select 1 from pg_depend
-                     where refclassid = reloid and refobjid = lowoid
-                     and deptype = 'p');
-  end if;
-  if not pinned then
-    raise notice '% contains unpinned initdb-created object(s)', relnm;
-  end if;
-end loop;
-end$$;
-NOTICE:  pg_database contains unpinned initdb-created object(s)
-NOTICE:  pg_extension contains unpinned initdb-created object(s)
-NOTICE:  pg_rewrite contains unpinned initdb-created object(s)
-NOTICE:  pg_tablespace contains unpinned initdb-created object(s)
 -- **************** pg_class ****************
 -- Look for system tables with varlena columns but no toast table. All
 -- system tables with toastable columns should have toast tables, with
diff --git a/src/test/regress/sql/misc_sanity.sql b/src/test/regress/sql/misc_sanity.sql
index 9699f5cc3b..2c0f87a651 100644
--- a/src/test/regress/sql/misc_sanity.sql
+++ b/src/test/regress/sql/misc_sanity.sql
@@ -14,70 +14,24 @@
 -- **************** pg_depend ****************

 -- Look for illegal values in pg_depend fields.
--- classid/objid can be zero, but only in 'p' entries

 SELECT *
 FROM pg_depend as d1
 WHERE refclassid = 0 OR refobjid = 0 OR
-      deptype NOT IN ('a', 'e', 'i', 'n', 'p') OR
-      (deptype != 'p' AND (classid = 0 OR objid = 0)) OR
-      (deptype = 'p' AND (classid != 0 OR objid != 0 OR objsubid != 0));
+      classid = 0 OR objid = 0 OR
+      deptype NOT IN ('a', 'e', 'i', 'n', 'x', 'P', 'S');
+

 -- **************** pg_shdepend ****************

 -- Look for illegal values in pg_shdepend fields.
--- classid/objid can be zero, but only in 'p' entries

 SELECT *
 FROM pg_shdepend as d1
 WHERE refclassid = 0 OR refobjid = 0 OR
-      deptype NOT IN ('a', 'o', 'p', 'r') OR
-      (deptype != 'p' AND (classid = 0 OR objid = 0)) OR
-      (deptype = 'p' AND (dbid != 0 OR classid != 0 OR objid != 0 OR objsubid != 0));
-
-
--- Check each OID-containing system catalog to see if its lowest-numbered OID
--- is pinned.  If not, and if that OID was generated during initdb, then
--- perhaps initdb forgot to scan that catalog for pinnable entries.
--- Generally, it's okay for a catalog to be listed in the output of this
--- test if that catalog is scanned by initdb.c's setup_depend() function;
--- whatever OID the test is complaining about must have been added later
--- in initdb, where it intentionally isn't pinned.  Legitimate exceptions
--- to that rule are listed in the comments in setup_depend().
--- Currently, pg_rewrite is also listed by this check, even though it is
--- covered by setup_depend().  That happens because there are no rules in
--- the pinned data, but initdb creates some intentionally-not-pinned views.
-
-do $$
-declare relnm text;
-  reloid oid;
-  shared bool;
-  lowoid oid;
-  pinned bool;
-begin
-for relnm, reloid, shared in
-  select relname, oid, relisshared from pg_class
-  where EXISTS(
-      SELECT * FROM pg_attribute
-      WHERE attrelid = pg_class.oid AND attname = 'oid')
-    and relkind = 'r' and oid < 16384 order by 1
-loop
-  execute 'select min(oid) from ' || relnm into lowoid;
-  continue when lowoid is null or lowoid >= 16384;
-  if shared then
-    pinned := exists(select 1 from pg_shdepend
-                     where refclassid = reloid and refobjid = lowoid
-                     and deptype = 'p');
-  else
-    pinned := exists(select 1 from pg_depend
-                     where refclassid = reloid and refobjid = lowoid
-                     and deptype = 'p');
-  end if;
-  if not pinned then
-    raise notice '% contains unpinned initdb-created object(s)', relnm;
-  end if;
-end loop;
-end$$;
+      classid = 0 OR objid = 0 OR
+      deptype NOT IN ('a', 'o', 'r', 't');
+

 -- **************** pg_class ****************


В списке pgsql-hackers по дате отправления:

Предыдущее
От: Alvaro Herrera
Дата:
Сообщение: Re: PG 14 release notes, first draft
Следующее
От: Alvaro Herrera
Дата:
Сообщение: Re: AlterSubscription_refresh "wrconn" wrong variable?