diff options
Diffstat (limited to 'src/backend/catalog/catalog.c')
-rw-r--r-- | src/backend/catalog/catalog.c | 153 |
1 files changed, 101 insertions, 52 deletions
diff --git a/src/backend/catalog/catalog.c b/src/backend/catalog/catalog.c index 6061428bccd..5a160bf6cc1 100644 --- a/src/backend/catalog/catalog.c +++ b/src/backend/catalog/catalog.c @@ -21,6 +21,8 @@ #include <unistd.h> #include "access/genam.h" +#include "access/heapam.h" +#include "access/htup_details.h" #include "access/sysattr.h" #include "access/transam.h" #include "catalog/catalog.h" @@ -43,7 +45,9 @@ #include "miscadmin.h" #include "storage/fd.h" #include "utils/fmgroids.h" +#include "utils/fmgrprotos.h" #include "utils/rel.h" +#include "utils/syscache.h" #include "utils/tqual.h" @@ -277,17 +281,8 @@ IsSharedRelation(Oid relationId) /* - * GetNewOid - * Generate a new OID that is unique within the given relation. - * - * Caller must have a suitable lock on the relation. - * - * Uniqueness is promised only if the relation has a unique index on OID. - * This is true for all system catalogs that have OIDs, but might not be - * true for user tables. Note that we are effectively assuming that the - * table has a relatively small number of entries (much less than 2^32) - * and there aren't very long runs of consecutive existing OIDs. Again, - * this is reasonable for system catalogs but less so for user tables. + * GetNewOidWithIndex + * Generate a new OID that is unique within the system relation. * * Since the OID is not immediately inserted into the table, there is a * race condition here; but a problem could occur only if someone else @@ -300,44 +295,11 @@ IsSharedRelation(Oid relationId) * of transient conflicts for as long as our own MVCC snapshots think a * recently-deleted row is live. The risk is far higher when selecting TOAST * OIDs, because SnapshotToast considers dead rows as active indefinitely.) - */ -Oid -GetNewOid(Relation relation) -{ - Oid oidIndex; - - /* If relation doesn't have OIDs at all, caller is confused */ - Assert(relation->rd_rel->relhasoids); - - /* In bootstrap mode, we don't have any indexes to use */ - if (IsBootstrapProcessingMode()) - return GetNewObjectId(); - - /* The relcache will cache the identity of the OID index for us */ - oidIndex = RelationGetOidIndex(relation); - - /* If no OID index, just hand back the next OID counter value */ - if (!OidIsValid(oidIndex)) - { - /* - * System catalogs that have OIDs should *always* have a unique OID - * index; we should only take this path for user tables. Give a - * warning if it looks like somebody forgot an index. - */ - if (IsSystemRelation(relation)) - elog(WARNING, "generating possibly-non-unique OID for \"%s\"", - RelationGetRelationName(relation)); - - return GetNewObjectId(); - } - - /* Otherwise, use the index to find a nonconflicting OID */ - return GetNewOidWithIndex(relation, oidIndex, ObjectIdAttributeNumber); -} - -/* - * GetNewOidWithIndex - * Guts of GetNewOid: use the supplied index + * + * Note that we are effectively assuming that the table has a relatively small + * number of entries (much less than 2^32) and there aren't very long runs of + * consecutive existing OIDs. This is a mostly reasonable assumption for + * system catalogs. * * This is exported separately because there are cases where we want to use * an index that will not be recognized by RelationGetOidIndex: TOAST tables @@ -356,6 +318,13 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn) ScanKeyData key; bool collides; + /* Only system relations are supported */ + Assert(IsSystemRelation(relation)); + + /* In bootstrap mode, we don't have any indexes to use */ + if (IsBootstrapProcessingMode()) + return GetNewObjectId(); + /* * We should never be asked to generate a new pg_type OID during * pg_upgrade; doing so would risk collisions with the OIDs it wants to @@ -398,8 +367,8 @@ GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn) * is also an unused OID within pg_class. If the result is to be used only * as a relfilenode for an existing relation, pass NULL for pg_class. * - * As with GetNewOid, there is some theoretical risk of a race condition, - * but it doesn't seem worth worrying about. + * As with GetNewObjectIdWithIndex(), there is some theoretical risk of a race + * condition, but it doesn't seem worth worrying about. * * Note: we don't support using this in bootstrap mode. All relations * created by bootstrap have preassigned OIDs, so there's no need. @@ -450,7 +419,8 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) /* Generate the OID */ if (pg_class) - rnode.node.relNode = GetNewOid(pg_class); + rnode.node.relNode = GetNewOidWithIndex(pg_class, ClassOidIndexId, + Anum_pg_class_oid); else rnode.node.relNode = GetNewObjectId(); @@ -479,3 +449,82 @@ GetNewRelFileNode(Oid reltablespace, Relation pg_class, char relpersistence) return rnode.node.relNode; } + +/* + * SQL callable interface for GetNewOidWithIndex(). Outside of initdb's + * direct insertions into catalog tables, and recovering from corruption, this + * should rarely be needed. + * + * Function is intentionally not documented in the user facing docs. + */ +Datum +pg_nextoid(PG_FUNCTION_ARGS) +{ + Oid reloid = PG_GETARG_OID(0); + Name attname = PG_GETARG_NAME(1); + Oid idxoid = PG_GETARG_OID(2); + Relation rel; + Relation idx; + HeapTuple atttuple; + Form_pg_attribute attform; + AttrNumber attno; + Oid newoid; + + /* + * As this function is not intended to be used during normal running, and + * only supports system catalogs (which require superuser permissions to + * modify), just checking for superuser ought to not obstruct valid + * usecases. + */ + if (!superuser()) + ereport(ERROR, + (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), + errmsg("must be superuser to call pg_nextoid"))); + + rel = heap_open(reloid, RowExclusiveLock); + idx = index_open(idxoid, RowExclusiveLock); + + if (!IsSystemRelation(rel)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("pg_nextoid() can only be used on system relation"))); + + if (idx->rd_index->indrelid != RelationGetRelid(rel)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("index %s does not belong to table %s", + RelationGetRelationName(idx), + RelationGetRelationName(rel)))); + + atttuple = SearchSysCacheAttName(reloid, NameStr(*attname)); + if (!HeapTupleIsValid(atttuple)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("attribute %s does not exists", + NameStr(*attname)))); + + attform = ((Form_pg_attribute) GETSTRUCT(atttuple)); + attno = attform->attnum; + + if (attform->atttypid != OIDOID) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("attribute %s is not of type oid", + NameStr(*attname)))); + + if (IndexRelationGetNumberOfKeyAttributes(idx) != 1 || + idx->rd_index->indkey.values[0] != attno) + ereport(ERROR, + (errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("index %s is not the index for attribute %s", + RelationGetRelationName(idx), + NameStr(*attname)))); + + newoid = GetNewOidWithIndex(rel, idxoid, attno); + + ReleaseSysCache(atttuple); + heap_close(rel, RowExclusiveLock); + index_close(idx, RowExclusiveLock); + + return newoid; +} |