aboutsummaryrefslogtreecommitdiff
path: root/src/backend/catalog/catalog.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/catalog/catalog.c')
-rw-r--r--src/backend/catalog/catalog.c153
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;
+}