From a49d081235997c67e8aab7a523b17e8d1cb93184 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Thu, 15 Jul 2021 11:41:47 -0400 Subject: [PATCH] Replace explicit PIN entries in pg_depend with an OID range test. As of v14, pg_depend contains almost 7000 "pin" entries recording the OIDs of built-in objects. This is a fair amount of bloat for every database, and it adds time to pg_depend lookups as well as initdb. We can get rid of all of those entries in favor of an OID range check, i.e. "OIDs below FirstUnpinnedObjectId are pinned". (template1 and the public schema are exceptions. Those exceptions are now wired into IsPinnedObject() instead of initdb's code for filling pg_depend, but it's the same amount of cruft either way.) The contents of pg_shdepend are modified likewise. Discussion: https://postgr.es/m/3737988.1618451008@sss.pgh.pa.us --- doc/src/sgml/bki.sgml | 28 +++++-- doc/src/sgml/catalogs.sgml | 60 ++++++-------- src/backend/access/transam/varsup.c | 49 +++++++++++- src/backend/access/transam/xlog.c | 2 +- src/backend/catalog/catalog.c | 96 +++++++++++++++++++++-- src/backend/catalog/dependency.c | 32 +++----- src/backend/catalog/genbki.pl | 10 +-- src/backend/catalog/pg_depend.c | 66 ++++------------ src/backend/catalog/pg_publication.c | 2 +- src/backend/catalog/pg_shdepend.c | 92 +++++----------------- src/backend/commands/tablecmds.c | 16 ++-- src/backend/commands/tablespace.c | 4 +- src/backend/optimizer/plan/setrefs.c | 6 +- src/backend/storage/lmgr/predicate.c | 2 +- src/bin/initdb/initdb.c | 77 +----------------- src/bin/pg_resetwal/pg_resetwal.c | 2 +- src/include/access/transam.h | 16 ++-- src/include/catalog/catalog.h | 2 + src/include/catalog/catversion.h | 2 +- src/include/catalog/dependency.h | 18 ++--- src/include/catalog/pg_depend.h | 11 ++- src/include/catalog/pg_proc.dat | 4 + src/include/catalog/pg_shdepend.h | 12 +-- src/test/regress/expected/misc_sanity.out | 57 +------------- src/test/regress/sql/misc_sanity.sql | 56 ++----------- 25 files changed, 290 insertions(+), 432 deletions(-) diff --git a/doc/src/sgml/bki.sgml b/doc/src/sgml/bki.sgml index db1b3d5e9a..f32208b550 100644 --- a/doc/src/sgml/bki.sgml +++ b/doc/src/sgml/bki.sgml @@ -418,19 +418,33 @@ If genbki.pl 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—11999. The server's OID counter is set to 12000 - 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 information_schema.sql script, - receive OIDs of 12000 or above. + the range 10000—11999. The server's OID counter is set to 10000 + at the start of a bootstrap run, so that any objects created on-the-fly + during bootstrap processing also receive OIDs in this range. (The + usual OID assignment mechanism takes care of preventing any conflicts.) + + + + Objects with OIDs below FirstUnpinnedObjectId (12000) + are considered pinned, preventing them from being + deleted. (There are a small number of exceptions, which are + hard-wired into IsPinnedObject().) + initdb forces the OID counter up + to FirstUnpinnedObjectId as soon as it's ready to + create unpinned objects. Thus objects created during the later phases + of initdb, such as objects created while + running the information_schema.sql script, will + not be pinned, while all objects known + to genbki.pl will be. OIDs assigned during normal database operation are constrained to be 16384 or higher. This ensures that the range 10000—16383 is free for OIDs assigned automatically by genbki.pl or - during bootstrap. These automatically-assigned OIDs are not considered - stable, and may change from one installation to another. + during initdb. These + automatically-assigned OIDs are not considered stable, and may change + from one installation to another. diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 0f5d25b948..5128f34d40 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -3264,8 +3264,7 @@ SCRAM-SHA-256$<iteration count>:&l (references pg_class.oid) - The OID of the system catalog the dependent object is in, - or zero for a DEPENDENCY_PIN entry + The OID of the system catalog the dependent object is in @@ -3275,8 +3274,7 @@ SCRAM-SHA-256$<iteration count>:&l (references any OID column) - The OID of the specific dependent object, - or zero for a DEPENDENCY_PIN entry + The OID of the specific dependent object @@ -3467,19 +3465,6 @@ SCRAM-SHA-256$<iteration count>:&l - - - DEPENDENCY_PIN (p) - - - 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 initdb. The columns for the - dependent object contain zeroes. - - - Other dependency flavors might be needed in future. @@ -3498,6 +3483,19 @@ SCRAM-SHA-256$<iteration count>:&l must be satisfied. + + Most objects created during initdb are + considered pinned, 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 pg_depend + entries showing dependencies on them. Thus, for example, a table + column of type numeric notionally has + a NORMAL dependency on the numeric + data type, but no such entry actually appears + in pg_depend. + + @@ -6779,7 +6777,6 @@ SCRAM-SHA-256$<iteration count>:&l The OID of the database the dependent object is in, or zero for a shared object - or a SHARED_DEPENDENCY_PIN entry @@ -6789,8 +6786,7 @@ SCRAM-SHA-256$<iteration count>:&l (references pg_class.oid) - The OID of the system catalog the dependent object is in, - or zero for a SHARED_DEPENDENCY_PIN entry + The OID of the system catalog the dependent object is in @@ -6800,8 +6796,7 @@ SCRAM-SHA-256$<iteration count>:&l (references any OID column) - The OID of the specific dependent object, - or zero for a SHARED_DEPENDENCY_PIN entry + The OID of the specific dependent object @@ -6889,19 +6884,6 @@ SCRAM-SHA-256$<iteration count>:&l - - SHARED_DEPENDENCY_PIN (p) - - - 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 initdb. The columns for the - dependent object contain zeroes. - - - - SHARED_DEPENDENCY_TABLESPACE (t) @@ -6918,6 +6900,14 @@ SCRAM-SHA-256$<iteration count>:&l objects. + + As in the pg_depend catalog, most objects + created during initdb are + considered pinned. No entries are made + in pg_shdepend that would have a pinned + object as either referenced or dependent object. + + diff --git a/src/backend/access/transam/varsup.c b/src/backend/access/transam/varsup.c index a22bf375f8..5b4898bb78 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 - * we only wrap if before that point when in bootstrap or standalone mode. + * 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. + */ +static 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 c7c928f50b..edb15fe58d 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -5318,7 +5318,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/catalog/catalog.c b/src/backend/catalog/catalog.c index 7cabe741c6..aa7d4d5456 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 + * FirstGenbkiObjectId 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 @@ -533,7 +592,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); @@ -580,5 +640,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 b82df348b8..70987b1487 100644 --- a/src/backend/catalog/genbki.pl +++ b/src/backend/catalog/genbki.pl @@ -170,14 +170,14 @@ 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. # We allow such OIDs to be assigned independently within each catalog. my $FirstGenbkiObjectId = Catalog::FindDefinedSymbol('access/transam.h', $include_path, 'FirstGenbkiObjectId'); -my $FirstBootstrapObjectId = +my $FirstUnpinnedObjectId = Catalog::FindDefinedSymbol('access/transam.h', $include_path, - 'FirstBootstrapObjectId'); + 'FirstUnpinnedObjectId'); # Hash of next available OID, indexed by catalog name. my %GenbkiNextOids; @@ -1101,8 +1101,8 @@ sub assign_next_oid # Check that we didn't overrun available OIDs die - "genbki OID counter for $catname reached $result, overrunning FirstBootstrapObjectId\n" - if $result >= $FirstBootstrapObjectId; + "genbki OID counter for $catname reached $result, overrunning FirstUnpinnedObjectId\n" + if $result >= $FirstUnpinnedObjectId; return $result; } 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..4b676f5607 100644 --- a/src/backend/catalog/pg_shdepend.c +++ b/src/backend/catalog/pg_shdepend.c @@ -101,7 +101,6 @@ static void storeObjectDescription(StringInfo descs, ObjectAddress *object, SharedDependencyType deptype, int count); -static bool isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel); /* @@ -140,8 +139,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 (!IsPinnedObject(referenced->classId, referenced->objectId)) { shdepAddDependency(sdepRel, depender->classId, depender->objectId, depender->objectSubId, @@ -255,7 +253,7 @@ shdepChangeDep(Relation sdepRel, systable_endscan(scan); - if (isSharedObjectPinned(refclassid, refobjid, sdepRel)) + if (IsPinnedObject(refclassid, refobjid)) { /* No new entry needed, so just delete existing entry if any */ if (oldtup) @@ -513,7 +511,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId, continue; /* Skip pinned roles; they don't need dependency entries */ - if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + if (IsPinnedObject(AuthIdRelationId, roleid)) continue; shdepAddDependency(sdepRel, classId, objectId, objsubId, @@ -531,7 +529,7 @@ updateAclDependencies(Oid classId, Oid objectId, int32 objsubId, continue; /* Skip pinned roles */ - if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + if (IsPinnedObject(AuthIdRelationId, roleid)) continue; shdepDropDependency(sdepRel, classId, objectId, objsubId, @@ -626,8 +624,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 +645,18 @@ checkSharedDependencies(Oid classId, Oid objectId, StringInfoData descs; StringInfoData alldescs; + /* This case can be dispatched quickly */ + if (IsPinnedObject(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 +693,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; @@ -1272,53 +1268,6 @@ 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. - */ -static bool -isSharedObjectPinned(Oid classId, Oid objectId, Relation sdepRel) -{ - 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. - */ - 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; -} - /* * shdepDropOwned * @@ -1359,7 +1308,7 @@ shdepDropOwned(List *roleids, DropBehavior behavior) HeapTuple tuple; /* Doesn't work for pinned objects */ - if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + if (IsPinnedObject(AuthIdRelationId, roleid)) { ObjectAddress obj; @@ -1402,7 +1351,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 +1454,7 @@ shdepReassignOwned(List *roleids, Oid newrole) Oid roleid = lfirst_oid(cell); /* Refuse to work on pinned roles */ - if (isSharedObjectPinned(AuthIdRelationId, roleid, sdepRel)) + if (IsPinnedObject(AuthIdRelationId, roleid)) { ObjectAddress obj; @@ -1549,10 +1497,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 96375814a8..28b178f208 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -5960,7 +5960,7 @@ alter_table_type_to_string(AlterTableType cmdtype) case AT_DropExpression: return "ALTER COLUMN ... DROP EXPRESSION"; case AT_CheckNotNull: - return NULL; /* not real grammar */ + return NULL; /* not real grammar */ case AT_SetStatistics: return "ALTER COLUMN ... SET STATISTICS"; case AT_SetOptions: @@ -5976,7 +5976,7 @@ alter_table_type_to_string(AlterTableType cmdtype) return "DROP COLUMN"; case AT_AddIndex: case AT_ReAddIndex: - return NULL; /* not real grammar */ + return NULL; /* not real grammar */ case AT_AddConstraint: case AT_AddConstraintRecurse: case AT_ReAddConstraint: @@ -5992,7 +5992,7 @@ alter_table_type_to_string(AlterTableType cmdtype) case AT_DropConstraintRecurse: return "DROP CONSTRAINT"; case AT_ReAddComment: - return NULL; /* not real grammar */ + return NULL; /* not real grammar */ case AT_AlterColumnType: return "ALTER COLUMN ... SET DATA TYPE"; case AT_AlterColumnGenericOptions: @@ -6016,7 +6016,7 @@ alter_table_type_to_string(AlterTableType cmdtype) case AT_ResetRelOptions: return "RESET"; case AT_ReplaceRelOptions: - return NULL; /* not real grammar */ + return NULL; /* not real grammar */ case AT_EnableTrig: return "ENABLE TRIGGER"; case AT_EnableAlwaysTrig: @@ -6074,7 +6074,7 @@ alter_table_type_to_string(AlterTableType cmdtype) case AT_DropIdentity: return "ALTER COLUMN ... DROP IDENTITY"; case AT_ReAddStatistics: - return NULL; /* not real grammar */ + return NULL; /* not real grammar */ } return NULL; @@ -6129,7 +6129,7 @@ ATSimplePermissions(AlterTableType cmdtype, Relation rel, int allowed_targets) if (action_str) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), - /* translator: %s is a group of some SQL keywords */ + /* translator: %s is a group of some SQL keywords */ errmsg("ALTER action %s cannot be performed on relation \"%s\"", action_str, RelationGetRelationName(rel)), errdetail_relkind_not_supported(rel->rd_rel->relkind))); @@ -12080,10 +12080,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 26f6872b4b..b145c5f45f 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -2955,11 +2955,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); @@ -2995,7 +2995,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 77e621a767..994bf07f3b 100644 --- a/src/bin/initdb/initdb.c +++ b/src/bin/initdb/initdb.c @@ -1525,83 +1525,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 2fe8a59110..d22de19c94 100644 --- a/src/include/access/transam.h +++ b/src/include/access/transam.h @@ -165,10 +165,14 @@ FullTransactionIdAdvance(FullTransactionId *dest) * when the .dat files in src/include/catalog/ do not specify an OID * for a catalog entry that requires one. Note that genbki.pl assigns * these OIDs independently in each catalog, so they're not guaranteed - * to be globally unique. + * to be globally unique. Furthermore, the bootstrap backend and + * initdb's post-bootstrap processing can also assign OIDs in this range. + * The normal OID-generation logic takes care of any OID conflicts that + * might arise from that. * - * OIDS 12000-16383 are reserved for assignment during initdb - * using the OID generator. (We start the generator at 12000.) + * OIDs 12000-16383 are reserved for unpinned objects created by initdb's + * post-bootstrap processing. initdb forces the OID generator up to + * 12000 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 @@ -184,11 +188,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 12000 +#define FirstUnpinnedObjectId 12000 #define FirstNormalObjectId 16384 /* @@ -289,6 +294,7 @@ extern void SetTransactionIdLimit(TransactionId oldest_datfrozenxid, extern void AdvanceOldestClogXid(TransactionId oldest_datfrozenxid); extern bool ForceTransactionIdLimitUpdate(void); extern Oid GetNewObjectId(void); +extern void StopGeneratingPinnedObjectIds(void); #ifdef USE_ASSERT_CHECKING extern void AssertTransactionIdInAllowableRange(TransactionId xid); 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/catversion.h b/src/include/catalog/catversion.h index f2ecafa1da..358dfdbbd3 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202107141 +#define CATALOG_VERSION_NO 202107151 #endif 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 f41ae3add1..90a5699b6e 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 fde251fa4f..8bf9d704b7 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 c77452c584..06616b9086 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,71 +14,25 @@ -- **************** 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)); + classid = 0 OR objid = 0 OR + deptype NOT IN ('a', 'o', 'r', 't'); --- 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$$; - -- **************** pg_class **************** -- Look for system tables with varlena columns but no toast table. All