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