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 ****************