ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(indexRelation, IndexIndrelidIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid(indexTuple = systable_getnext(scan)))
{
rel = heap_open(catalogId, AccessShareLock);
sscan = systable_beginscan(rel, InvalidOid, false,
- SnapshotNow, 0, NULL);
+ NULL, 0, NULL);
while (HeapTupleIsValid(tuple = systable_getnext(sscan)))
{
Form_pg_database datForm;
When using an MVCC-compliant snapshot, there is no problem because
the new occupant of the slot is certain to be too new to pass the
snapshot test. However, with a non-MVCC-compliant snapshot (such as
- <literal>SnapshotNow</>), it would be possible to accept and return
+ <literal>SnapshotAny</>), it would be possible to accept and return
a row that does not in fact match the scan keys. We could defend
against this scenario by requiring the scan keys to be rechecked
against the heap row in all cases, but that is too expensive. Instead,
Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync,
- bool is_bitmapscan);
+ bool is_bitmapscan, bool temp_snap);
static HeapTuple heap_prepare_insert(Relation relation, HeapTuple tup,
TransactionId xid, CommandId cid, int options);
static XLogRecPtr log_heap_update(Relation reln, Buffer oldbuf,
int nkeys, ScanKey key)
{
return heap_beginscan_internal(relation, snapshot, nkeys, key,
- true, true, false);
+ true, true, false, false);
+}
+
+HeapScanDesc
+heap_beginscan_catalog(Relation relation, int nkeys, ScanKey key)
+{
+ Oid relid = RelationGetRelid(relation);
+ Snapshot snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+
+ return heap_beginscan_internal(relation, snapshot, nkeys, key,
+ true, true, false, true);
}
HeapScanDesc
bool allow_strat, bool allow_sync)
{
return heap_beginscan_internal(relation, snapshot, nkeys, key,
- allow_strat, allow_sync, false);
+ allow_strat, allow_sync, false, false);
}
HeapScanDesc
int nkeys, ScanKey key)
{
return heap_beginscan_internal(relation, snapshot, nkeys, key,
- false, false, true);
+ false, false, true, false);
}
static HeapScanDesc
heap_beginscan_internal(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync,
- bool is_bitmapscan)
+ bool is_bitmapscan, bool temp_snap)
{
HeapScanDesc scan;
scan->rs_strategy = NULL; /* set in initscan */
scan->rs_allow_strat = allow_strat;
scan->rs_allow_sync = allow_sync;
+ scan->rs_temp_snap = temp_snap;
/*
* we can use page-at-a-time mode if it's an MVCC-safe snapshot
if (scan->rs_strategy != NULL)
FreeAccessStrategy(scan->rs_strategy);
+ if (scan->rs_temp_snap)
+ UnregisterSnapshot(scan->rs_snapshot);
+
pfree(scan);
}
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
+#include "utils/snapmgr.h"
#include "utils/tqual.h"
* rel: catalog to scan, already opened and suitably locked
* indexId: OID of index to conditionally use
* indexOK: if false, forces a heap scan (see notes below)
- * snapshot: time qual to use (usually should be SnapshotNow)
+ * snapshot: time qual to use (NULL for a recent catalog snapshot)
* nkeys, key: scan keys
*
* The attribute numbers in the scan key should be set for the heap case.
sysscan->heap_rel = heapRelation;
sysscan->irel = irel;
+ if (snapshot == NULL)
+ {
+ Oid relid = RelationGetRelid(heapRelation);
+
+ snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+ sysscan->snapshot = snapshot;
+ }
+ else
+ {
+ /* Caller is responsible for any snapshot. */
+ sysscan->snapshot = NULL;
+ }
+
if (irel)
{
int i;
else
heap_endscan(sysscan->scan);
+ if (sysscan->snapshot)
+ UnregisterSnapshot(sysscan->snapshot);
+
pfree(sysscan);
}
sysscan->heap_rel = heapRelation;
sysscan->irel = indexRelation;
+ if (snapshot == NULL)
+ {
+ Oid relid = RelationGetRelid(heapRelation);
+
+ snapshot = RegisterSnapshot(GetCatalogSnapshot(relid));
+ sysscan->snapshot = snapshot;
+ }
+ else
+ {
+ /* Caller is responsible for any snapshot. */
+ sysscan->snapshot = NULL;
+ }
+
/* Change attribute numbers to be index column numbers. */
for (i = 0; i < nkeys; i++)
{
{
Assert(sysscan->irel);
index_endscan(sysscan->iscan);
+ if (sysscan->snapshot)
+ UnregisterSnapshot(sysscan->snapshot);
pfree(sysscan);
}
guarantees that VACUUM can't delete any heap tuple that an indexscanning
process might be about to visit. (This guarantee works only for simple
indexscans that visit the heap in sync with the index scan, not for bitmap
-scans. We only need the guarantee when using non-MVCC snapshot rules such
-as SnapshotNow, so in practice this is only important for system catalog
-accesses.)
+scans. We only need the guarantee when using non-MVCC snapshot rules; in
+an MVCC snapshot, it wouldn't matter if the heap tuple were replaced with
+an unrelated tuple at the same TID, because the new tuple wouldn't be
+visible to our scan anyway.)
Because a page can be split even while someone holds a pin on it, it is
possible that an indexscan will return items that are no longer stored on
appendStringInfo(buf, " catalog %u", msg->cat.catId);
else if (msg->id == SHAREDINVALRELCACHE_ID)
appendStringInfo(buf, " relcache %u", msg->rc.relId);
- /* remaining cases not expected, but print something anyway */
+ /* not expected, but print something anyway */
else if (msg->id == SHAREDINVALSMGR_ID)
appendStringInfo(buf, " smgr");
+ /* not expected, but print something anyway */
else if (msg->id == SHAREDINVALRELMAP_ID)
appendStringInfo(buf, " relmap");
+ else if (msg->id == SHAREDINVALSNAPSHOT_ID)
+ appendStringInfo(buf, " snapshot %u", msg->sn.relId);
else
appendStringInfo(buf, " unknown id %d", msg->id);
}
{
/* We can now load the pg_type data */
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
i = 0;
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
++i;
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
app = Typ;
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
}
elog(DEBUG4, "external type: %s", type);
rel = heap_open(TypeRelationId, NoLock);
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
i = 0;
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
++i;
while (i-- > 0)
*app++ = ALLOC(struct typmap, 1);
*app = NULL;
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
app = Typ;
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
ObjectIdGetDatum(namespaceId));
rel = heap_open(ProcedureRelationId, AccessShareLock);
- scan = heap_beginscan(rel, SnapshotNow, 1, key);
+ scan = heap_beginscan_catalog(rel, 1, key);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
CharGetDatum(relkind));
rel = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan(rel, SnapshotNow, 2, key);
+ scan = heap_beginscan_catalog(rel, 2, key);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
ObjectIdGetDatum(objid));
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tuple = systable_getnext(scan);
ObjectIdGetDatum(defaclOid));
scan = systable_beginscan(rel, DefaultAclOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tuple = systable_getnext(scan);
scan = systable_beginscan(relation,
LargeObjectMetadataOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
return pg_language_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_LARGEOBJECT:
return pg_largeobject_aclmask_snapshot(table_oid, roleid,
- mask, how, SnapshotNow);
+ mask, how, NULL);
case ACL_KIND_NAMESPACE:
return pg_namespace_aclmask(table_oid, roleid, mask, how);
case ACL_KIND_TABLESPACE:
* Exported routine for examining a user's privileges for a largeobject
*
* When a large object is opened for reading, it is opened relative to the
- * caller's snapshot, but when it is opened for writing, it is always relative
- * to SnapshotNow, as documented in doc/src/sgml/lobj.sgml. This function
- * takes a snapshot argument so that the permissions check can be made relative
- * to the same snapshot that will be used to read the underlying data.
+ * caller's snapshot, but when it is opened for writing, a current
+ * MVCC snapshot will be used. See doc/src/sgml/lobj.sgml. This function
+ * takes a snapshot argument so that the permissions check can be made
+ * relative to the same snapshot that will be used to read the underlying
+ * data. The caller will actually pass NULL for an instantaneous MVCC
+ * snapshot, since all we do with the snapshot argument is pass it through
+ * to systable_beginscan().
*/
AclMode
pg_largeobject_aclmask_snapshot(Oid lobj_oid, Oid roleid,
* Ownership check for a largeobject (specified by OID)
*
* This is only used for operations like ALTER LARGE OBJECT that are always
- * relative to SnapshotNow.
+ * relative to an up-to-date snapshot.
*/
bool
pg_largeobject_ownercheck(Oid lobj_oid, Oid roleid)
scan = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
scan = systable_beginscan(pg_extension,
ExtensionOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
* Given the OID of a relation, determine whether it's supposed to be
* shared across an entire database cluster.
*
- * Hard-wiring this list is pretty grotty, but we really need it so that
- * we can compute the locktag for a relation (and then lock it) without
- * having already read its pg_class entry. If we try to retrieve relisshared
- * from pg_class with no pre-existing lock, there is a race condition against
- * anyone who is concurrently committing a change to the pg_class entry:
- * since we read system catalog entries under SnapshotNow, it's possible
- * that both the old and new versions of the row are invalid at the instants
- * we scan them. We fix this by insisting that updaters of a pg_class
- * row must hold exclusive lock on the corresponding rel, and that users
- * of a relation must hold at least AccessShareLock on the rel *before*
- * trying to open its relcache entry. But to lock a rel, you have to
- * know if it's shared. Fortunately, the set of shared relations is
- * fairly static, so a hand-maintained list of their OIDs isn't completely
- * impractical.
+ * In older releases, this had to be hard-wired so that we could compute the
+ * locktag for a relation and lock it before examining its catalog entry.
+ * Since we now have MVCC catalog access, the race conditions that made that
+ * a hard requirement are gone, so we could look at relaxing this restriction.
+ * However, if we scanned the pg_class entry to find relisshared, and only
+ * then locked the relation, pg_class could get updated in the meantime,
+ * forcing us to scan the relation again, which would definitely be complex
+ * and might have undesirable performance consequences. Fortunately, the set
+ * of shared relations is fairly static, so a hand-maintained list of their
+ * OIDs isn't completely impractical.
*/
bool
IsSharedRelation(Oid relationId)
nkeys = 2;
scan = systable_beginscan(*depRel, DependDependerIndexId, true,
- SnapshotNow, nkeys, key);
+ NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
nkeys = 2;
scan = systable_beginscan(*depRel, DependReferenceIndexId, true,
- SnapshotNow, nkeys, key);
+ NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
nkeys = 2;
scan = systable_beginscan(*depRel, DependDependerIndexId, true,
- SnapshotNow, nkeys, key);
+ NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(relid));
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId, true,
- SnapshotNow, 1, &key);
+ NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
simple_heap_delete(catalogRelation, &tuple->t_self);
ObjectIdGetDatum(relid));
scan = systable_beginscan(attrel, AttributeRelidNumIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
Int16GetDatum(0));
scan = systable_beginscan(attrel, AttributeRelidNumIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
/* Delete all the matching tuples */
while ((atttup = systable_getnext(scan)) != NULL)
Int16GetDatum(attnum));
scan = systable_beginscan(attrdef_rel, AttrDefaultIndexId, true,
- SnapshotNow, 2, scankeys);
+ NULL, 2, scankeys);
/* There should be at most one matching tuple, but we loop anyway */
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
ObjectIdGetDatum(attrdefId));
scan = systable_beginscan(attrdef_rel, AttrDefaultOidIndexId, true,
- SnapshotNow, 1, scankeys);
+ NULL, 1, scankeys);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
ObjectIdGetDatum(RelationGetNamespace(rel)));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
}
scan = systable_beginscan(pgstatistic, StatisticRelidAttnumInhIndexId, true,
- SnapshotNow, nkeys, key);
+ NULL, nkeys, key);
/* we must loop even when attnum != 0, in case of inherited stats */
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
fkeyRel = heap_open(ConstraintRelationId, AccessShareLock);
fkeyScan = systable_beginscan(fkeyRel, InvalidOid, false,
- SnapshotNow, 0, NULL);
+ NULL, 0, NULL);
while (HeapTupleIsValid(tuple = systable_getnext(fkeyScan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
- pg_class_scan = heap_beginscan(pg_class, SnapshotNow, 1, key);
+ pg_class_scan = heap_beginscan_catalog(pg_class, 1, key);
tuple = heap_getnext(pg_class_scan, ForwardScanDirection);
tuple = heap_copytuple(tuple);
heap_endscan(pg_class_scan);
* Prepare for scan of the base relation. In a normal index build, we use
* SnapshotAny because we must retrieve all tuples and do our own time
* qual checks (because we have to index RECENTLY_DEAD tuples). In a
- * concurrent build, we take a regular MVCC snapshot and index whatever's
- * live according to that. During bootstrap we just use SnapshotNow.
+ * concurrent build, or during bootstrap, we take a regular MVCC snapshot
+ * and index whatever's live according to that.
*/
- if (IsBootstrapProcessingMode())
- {
- snapshot = SnapshotNow;
- OldestXmin = InvalidTransactionId; /* not used */
- }
- else if (indexInfo->ii_Concurrent)
+ if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
{
snapshot = RegisterSnapshot(GetTransactionSnapshot());
OldestXmin = InvalidTransactionId; /* not used */
heap_endscan(scan);
/* we can now forget our snapshot, if set */
- if (indexInfo->ii_Concurrent)
+ if (IsBootstrapProcessingMode() || indexInfo->ii_Concurrent)
UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
*
* When creating an exclusion constraint, we first build the index normally
* and then rescan the heap to check for conflicts. We assume that we only
- * need to validate tuples that are live according to SnapshotNow, and that
- * these were correctly indexed even in the presence of broken HOT chains.
- * This should be OK since we are holding at least ShareLock on the table,
- * meaning there can be no uncommitted updates from other transactions.
+ * need to validate tuples that are live according to an up-to-date snapshot,
+ * and that these were correctly indexed even in the presence of broken HOT
+ * chains. This should be OK since we are holding at least ShareLock on the
+ * table, meaning there can be no uncommitted updates from other transactions.
* (Note: that wouldn't necessarily work for system catalogs, since many
* operations release write lock early on the system catalogs.)
*/
TupleTableSlot *slot;
EState *estate;
ExprContext *econtext;
+ Snapshot snapshot;
/*
* If we are reindexing the target index, mark it as no longer being
/*
* Scan all live tuples in the base relation.
*/
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
scan = heap_beginscan_strat(heapRelation, /* relation */
- SnapshotNow, /* snapshot */
+ snapshot, /* snapshot */
0, /* number of keys */
NULL, /* scan key */
true, /* buffer access strategy OK */
}
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
* a nonexistent object OID, rather than failing. This is to avoid race
* condition errors when a query that's scanning a catalog using an MVCC
* snapshot uses one of these functions. The underlying IsVisible functions
- * operate on SnapshotNow semantics and so might see the object as already
- * gone when it's still visible to the MVCC snapshot. (There is no race
+ * always use an up-to-date snapshot and so might see the object as already
+ * gone when it's still visible to the transaction snapshot. (There is no race
* condition in the current coding because we don't accept sinval messages
* between the SearchSysCacheExists test and the subsequent lookup.)
*/
ObjectIdGetDatum(objectId));
scan = systable_beginscan(catalog, oidIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
{
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(castDesc, CastOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(rcscan);
ObjectIdGetDatum(object->objectId));
adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId,
- true, SnapshotNow, 1, skey);
+ true, NULL, 1, skey);
tup = systable_getnext(adscan);
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(amscan);
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(amscan);
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(ruleDesc, RewriteOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(rcscan);
ObjectIdGetDatum(object->objectId));
tgscan = systable_beginscan(trigDesc, TriggerOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(tgscan);
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
- true, SnapshotNow, 1, skey);
+ true, NULL, 1, skey);
tup = systable_getnext(rcscan);
ObjectIdGetDatum(object->objectId));
adscan = systable_beginscan(attrdefDesc, AttrDefaultOidIndexId,
- true, SnapshotNow, 1, skey);
+ true, NULL, 1, skey);
tup = systable_getnext(adscan);
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amopDesc, AccessMethodOperatorOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(amscan);
ObjectIdGetDatum(object->objectId));
amscan = systable_beginscan(amprocDesc, AccessMethodProcedureOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(amscan);
ObjectIdGetDatum(object->objectId));
rcscan = systable_beginscan(defaclrel, DefaultAclOidIndexId,
- true, SnapshotNow, 1, skey);
+ true, NULL, 1, skey);
tup = systable_getnext(rcscan);
ObjectIdGetDatum(collationOid));
scandesc = systable_beginscan(rel, CollationOidIndexId, true,
- SnapshotNow, 1, &scanKeyData);
+ NULL, 1, &scanKeyData);
tuple = systable_getnext(scandesc);
ObjectIdGetDatum(objNamespace));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
ObjectIdGetDatum(namespaceid));
conscan = systable_beginscan(conDesc, ConstraintNameNspIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
found = (HeapTupleIsValid(systable_getnext(conscan)));
ObjectIdGetDatum(ownerId));
scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
}
else
{
ObjectIdGetDatum(ownerId));
scan = systable_beginscan(conRel, ConstraintRelidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
}
while (HeapTupleIsValid((tup = systable_getnext(scan))))
ObjectIdGetDatum(relid));
scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
ObjectIdGetDatum(typid));
scan = systable_beginscan(pg_constraint, ConstraintTypidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
ObjectIdGetDatum(relid));
scan = systable_beginscan(pg_constraint, ConstraintRelidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
/* open pg_conversion */
rel = heap_open(ConversionRelationId, RowExclusiveLock);
- scan = heap_beginscan(rel, SnapshotNow,
- 1, &scanKeyData);
+ scan = heap_beginscan_catalog(rel, 1, &scanKeyData);
/* search for the target tuple */
if (HeapTupleIsValid(tuple = heap_getnext(scan, ForwardScanDirection)))
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(roleid));
scan = systable_beginscan(rel, DbRoleSettingDatidRolidIndexId, true,
- SnapshotNow, 2, scankey);
+ NULL, 2, scankey);
tuple = systable_getnext(scan);
/*
numkeys++;
}
- scan = heap_beginscan(relsetting, SnapshotNow, numkeys, keys);
+ scan = heap_beginscan_catalog(relsetting, numkeys, keys);
while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
{
simple_heap_delete(relsetting, &tup->t_self);
* databaseid/roleid.
*/
void
-ApplySetting(Oid databaseid, Oid roleid, Relation relsetting, GucSource source)
+ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid,
+ Relation relsetting, GucSource source)
{
SysScanDesc scan;
ScanKeyData keys[2];
ObjectIdGetDatum(roleid));
scan = systable_beginscan(relsetting, DbRoleSettingDatidRolidIndexId, true,
- SnapshotNow, 2, keys);
+ snapshot, 2, keys);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
bool isnull;
ObjectIdGetDatum(objectId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(objectId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(objectId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
ObjectIdGetDatum(object->objectId));
scan = systable_beginscan(rel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
/*
* Since we won't generate additional pg_depend entries for pinned
ObjectIdGetDatum(objectId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
ObjectIdGetDatum(seqId));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
ObjectIdGetDatum(relid));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Int32GetDatum(0));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Int32GetDatum(0));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(enumTypeOid));
scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
* (for example, enum_in and enum_out do so). The worst that can happen
* is a transient failure to find any valid value of the row. This is
* judged acceptable in view of the infrequency of use of RenumberEnumType.
+ *
+ * XXX. Now that we have MVCC catalog scans, the above reasoning is no longer
+ * correct. Should we revisit any decisions here?
*/
static void
RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
ObjectIdGetDatum(parentrelId));
scan = systable_beginscan(relation, InheritsParentIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
while ((inheritsTuple = systable_getnext(scan)) != NULL)
{
ObjectIdGetDatum(this_relid));
inhscan = systable_beginscan(inhrel, InheritsRelidSeqnoIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while ((inhtup = systable_getnext(inhscan)) != NULL)
{
scan = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
scan = systable_beginscan(pg_largeobject,
LargeObjectLOidPNIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
simple_heap_delete(pg_largeobject, &tuple->t_self);
* We don't use the system cache for large object metadata, for fear of
* using too much local memory.
*
- * This function always scans the system catalog using SnapshotNow, so it
- * should not be used when a large object is opened in read-only mode (because
- * large objects opened in read only mode are supposed to be viewed relative
- * to the caller's snapshot, whereas in read-write mode they are relative to
- * SnapshotNow).
+ * This function always scans the system catalog using an up-to-date snapshot,
+ * so it should not be used when a large object is opened in read-only mode
+ * (because large objects opened in read only mode are supposed to be viewed
+ * relative to the caller's snapshot, whereas in read-write mode they are
+ * relative to a current snapshot).
*/
bool
LargeObjectExists(Oid loid)
sd = systable_beginscan(pg_lo_meta,
LargeObjectMetadataOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tuple = systable_getnext(sd);
if (HeapTupleIsValid(tuple))
ObjectIdGetDatum(rangeTypeOid));
scan = systable_beginscan(pg_range, RangeTypidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Int32GetDatum(objsubid));
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 4, key);
+ NULL, 4, key);
while ((scantup = systable_getnext(scan)) != NULL)
{
ObjectIdGetDatum(objectId));
scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(templateDbId));
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
/* Set up to copy the tuples except for inserting newDbId */
memset(values, 0, sizeof(values));
/* We leave the other index fields unspecified */
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
}
scan = systable_beginscan(sdepRel, SharedDependDependerIndexId, true,
- SnapshotNow, nkeys, key);
+ NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
ObjectIdGetDatum(objectId));
scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
/*
* Since we won't generate additional pg_shdepend entries for pinned
ObjectIdGetDatum(roleid));
scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while ((tuple = systable_getnext(scan)) != NULL)
{
ObjectIdGetDatum(roleid));
scan = systable_beginscan(sdepRel, SharedDependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while ((tuple = systable_getnext(scan)) != NULL)
{
* against concurrent SnapshotNow scans of pg_index. Therefore this is unsafe
* to execute with less than full exclusive lock on the parent table;
* otherwise concurrent executions of RelationGetIndexList could miss indexes.
+ *
+ * XXX: Now that we have MVCC catalog access, SnapshotNow scans of pg_index
+ * shouldn't be common enough to worry about. The above comment needs
+ * to be updated, and it may be possible to simplify the logic here in other
+ * ways also.
*/
void
mark_index_clustered(Relation rel, Oid indexOid, bool is_internal)
Anum_pg_index_indisclustered,
BTEqualStrategyNumber, F_BOOLEQ,
BoolGetDatum(true));
- scan = heap_beginscan(indRelation, SnapshotNow, 1, &entry);
+ scan = heap_beginscan_catalog(indRelation, 1, &entry);
while ((indexTuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
index = (Form_pg_index) GETSTRUCT(indexTuple);
description = heap_open(DescriptionRelationId, RowExclusiveLock);
sd = systable_beginscan(description, DescriptionObjIndexId, true,
- SnapshotNow, 3, skey);
+ NULL, 3, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
{
shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
{
description = heap_open(DescriptionRelationId, RowExclusiveLock);
sd = systable_beginscan(description, DescriptionObjIndexId, true,
- SnapshotNow, nkeys, skey);
+ NULL, nkeys, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
simple_heap_delete(description, &oldtuple->t_self);
shdescription = heap_open(SharedDescriptionRelationId, RowExclusiveLock);
sd = systable_beginscan(shdescription, SharedDescriptionObjIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while ((oldtuple = systable_getnext(sd)) != NULL)
simple_heap_delete(shdescription, &oldtuple->t_self);
tupdesc = RelationGetDescr(description);
sd = systable_beginscan(description, DescriptionObjIndexId, true,
- SnapshotNow, 3, skey);
+ NULL, 3, skey);
comment = NULL;
while ((tuple = systable_getnext(sd)) != NULL)
int notherbackends;
int npreparedxacts;
createdb_failure_params fparms;
- Snapshot snapshot;
/* Extract options from the statement node tree */
foreach(option, stmt->options)
*/
RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_FORCE | CHECKPOINT_WAIT);
- /*
- * Take an MVCC snapshot to use while scanning through pg_tablespace. For
- * safety, register the snapshot (this prevents it from changing if
- * something else were to request a snapshot during the loop).
- *
- * Traversing pg_tablespace with an MVCC snapshot is necessary to provide
- * us with a consistent view of the tablespaces that exist. Using
- * SnapshotNow here would risk seeing the same tablespace multiple times,
- * or worse not seeing a tablespace at all, if its tuple is moved around
- * by a concurrent update (eg an ACL change).
- *
- * Inconsistency of this sort is inherent to all SnapshotNow scans, unless
- * some lock is held to prevent concurrent updates of the rows being
- * sought. There should be a generic fix for that, but in the meantime
- * it's worth fixing this case in particular because we are doing very
- * heavyweight operations within the scan, so that the elapsed time for
- * the scan is vastly longer than for most other catalog scans. That
- * means there's a much wider window for concurrent updates to cause
- * trouble here than anywhere else. XXX this code should be changed
- * whenever a generic fix is implemented.
- */
- snapshot = RegisterSnapshot(GetLatestSnapshot());
-
/*
* Once we start copying subdirectories, we need to be able to clean 'em
* up if we fail. Use an ENSURE block to make sure this happens. (This
* each one to the new database.
*/
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid srctablespace = HeapTupleGetOid(tuple);
PG_END_ENSURE_ERROR_CLEANUP(createdb_failure_callback,
PointerGetDatum(&fparms));
- /* Free our snapshot */
- UnregisterSnapshot(snapshot);
-
return dboid;
}
BTEqualStrategyNumber, F_NAMEEQ,
NameGetDatum(dbname));
sysscan = systable_beginscan(pgdbrel, DatabaseNameIndexId, true,
- SnapshotNow, 1, &scankey);
+ NULL, 1, &scankey);
oldtuple = systable_getnext(sysscan);
if (!HeapTupleIsValid(oldtuple)) /* shouldn't happen... */
ereport(ERROR,
BTEqualStrategyNumber, F_NAMEEQ,
NameGetDatum(stmt->dbname));
scan = systable_beginscan(rel, DatabaseNameIndexId, true,
- SnapshotNow, 1, &scankey);
+ NULL, 1, &scankey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
BTEqualStrategyNumber, F_NAMEEQ,
NameGetDatum(dbname));
scan = systable_beginscan(rel, DatabaseNameIndexId, true,
- SnapshotNow, 1, &scankey);
+ NULL, 1, &scankey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
NameGetDatum(name));
scan = systable_beginscan(relation, DatabaseNameIndexId, true,
- SnapshotNow, 1, &scanKey);
+ NULL, 1, &scanKey);
tuple = systable_getnext(scan);
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
- Snapshot snapshot;
-
- /*
- * As in createdb(), we'd better use an MVCC snapshot here, since this
- * scan can run for a long time. Duplicate visits to tablespaces would be
- * harmless, but missing a tablespace could result in permanently leaked
- * files.
- *
- * XXX change this when a generic fix for SnapshotNow races is implemented
- */
- snapshot = RegisterSnapshot(GetLatestSnapshot());
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
heap_endscan(scan);
heap_close(rel, AccessShareLock);
- UnregisterSnapshot(snapshot);
}
/*
Relation rel;
HeapScanDesc scan;
HeapTuple tuple;
- Snapshot snapshot;
-
- /*
- * As in createdb(), we'd better use an MVCC snapshot here; missing a
- * tablespace could result in falsely reporting the OID is unique, with
- * disastrous future consequences per the comment above.
- *
- * XXX change this when a generic fix for SnapshotNow races is implemented
- */
- snapshot = RegisterSnapshot(GetLatestSnapshot());
rel = heap_open(TableSpaceRelationId, AccessShareLock);
- scan = heap_beginscan(rel, snapshot, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid dsttablespace = HeapTupleGetOid(tuple);
heap_endscan(scan);
heap_close(rel, AccessShareLock);
- UnregisterSnapshot(snapshot);
return result;
}
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(dbname));
scan = systable_beginscan(pg_database, DatabaseNameIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
dbtuple = systable_getnext(scan);
CStringGetDatum(extname));
scandesc = systable_beginscan(rel, ExtensionNameIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scandesc);
ObjectIdGetDatum(ext_oid));
scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scandesc);
ObjectIdGetDatum(ext_oid));
scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scandesc);
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(extId));
scandesc = systable_beginscan(rel, ExtensionOidIndexId, true,
- SnapshotNow, 1, entry);
+ NULL, 1, entry);
tuple = systable_getnext(scandesc);
ObjectIdGetDatum(CurrentExtensionObject));
extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
extTup = systable_getnext(extScan);
ObjectIdGetDatum(extensionoid));
extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
extTup = systable_getnext(extScan);
ObjectIdGetDatum(extensionOid));
extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
extTup = systable_getnext(extScan);
ObjectIdGetDatum(extensionOid));
depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
{
CStringGetDatum(stmt->extname));
extScan = systable_beginscan(extRel, ExtensionNameIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
extTup = systable_getnext(extScan);
ObjectIdGetDatum(extensionOid));
extScan = systable_beginscan(extRel, ExtensionOidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
extTup = systable_getnext(extScan);
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(castOid));
scan = systable_beginscan(relation, CastOidIndexId, true,
- SnapshotNow, 1, &scankey);
+ NULL, 1, &scankey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
ObjectIdGetDatum(am_id));
scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
* indirectly by reindex_relation).
*/
relationRelation = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan(relationRelation, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(relationRelation, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Form_pg_class classtuple = (Form_pg_class) GETSTRUCT(tuple);
ObjectIdGetDatum(amoid));
scan = systable_beginscan(rel, OpclassAmNameNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
rel = heap_open(AccessMethodOperatorRelationId, RowExclusiveLock);
scan = systable_beginscan(rel, AccessMethodOperatorOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
/* we expect exactly one match */
tup = systable_getnext(scan);
rel = heap_open(AccessMethodProcedureRelationId, RowExclusiveLock);
scan = systable_beginscan(rel, AccessMethodProcedureOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
/* we expect exactly one match */
tup = systable_getnext(scan);
BTEqualStrategyNumber, F_NAMEEQ,
NameGetDatum(languageName));
scan = systable_beginscan(rel, PLTemplateNameIndexId, true,
- SnapshotNow, 1, &key);
+ NULL, 1, &key);
tup = systable_getnext(scan);
if (HeapTupleIsValid(tup))
pg_shseclabel = heap_open(SharedSecLabelRelationId, AccessShareLock);
scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
- SnapshotNow, 3, keys);
+ NULL, 3, keys);
tuple = systable_getnext(scan);
if (HeapTupleIsValid(tuple))
pg_seclabel = heap_open(SecLabelRelationId, AccessShareLock);
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
- SnapshotNow, 4, keys);
+ NULL, 4, keys);
tuple = systable_getnext(scan);
if (HeapTupleIsValid(tuple))
pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
- SnapshotNow, 3, keys);
+ NULL, 3, keys);
oldtup = systable_getnext(scan);
if (HeapTupleIsValid(oldtup))
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
- SnapshotNow, 4, keys);
+ NULL, 4, keys);
oldtup = systable_getnext(scan);
if (HeapTupleIsValid(oldtup))
pg_shseclabel = heap_open(SharedSecLabelRelationId, RowExclusiveLock);
scan = systable_beginscan(pg_shseclabel, SharedSecLabelObjectIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
simple_heap_delete(pg_shseclabel, &oldtup->t_self);
systable_endscan(scan);
pg_seclabel = heap_open(SecLabelRelationId, RowExclusiveLock);
scan = systable_beginscan(pg_seclabel, SecLabelObjectIndexId, true,
- SnapshotNow, nkeys, skey);
+ NULL, nkeys, skey);
while (HeapTupleIsValid(oldtup = systable_getnext(scan)))
simple_heap_delete(pg_seclabel, &oldtup->t_self);
systable_endscan(scan);
* multiple DDL operations occur in a stream against frequently accessed
* tables.
*
- * 1. Catalog tables are read using SnapshotNow, which has a race bug that
+ * 1. Catalog tables were read using SnapshotNow, which has a race bug that
* allows a scan to return no valid rows even when one is present in the
* case of a commit of a concurrent update of the catalog table.
* SnapshotNow also ignores transactions in progress, so takes the latest
MemoryContext oldCxt;
List *dropped_attrs = NIL;
ListCell *lc;
+ Snapshot snapshot;
if (newrel)
ereport(DEBUG1,
* Scan through the rows, generating a new row if needed and then
* checking all the constraints.
*/
- scan = heap_beginscan(oldrel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(oldrel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
MemoryContextSwitchTo(oldCxt);
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(oldslot);
ExecDropSingleTupleTableSlot(newslot);
ObjectIdGetDatum(typeOid));
depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(typeOid));
- scan = heap_beginscan(classRel, SnapshotNow, 1, key);
+ scan = heap_beginscan_catalog(classRel, 1, key);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(conrel, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
while (HeapTupleIsValid(contuple = systable_getnext(scan)))
{
ObjectIdGetDatum(HeapTupleGetOid(contuple)));
tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
- SnapshotNow, 1, &tgkey);
+ NULL, 1, &tgkey);
while (HeapTupleIsValid(tgtuple = systable_getnext(tgscan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(conrel, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
TupleTableSlot *slot;
Form_pg_constraint constrForm;
bool isnull;
+ Snapshot snapshot;
constrForm = (Form_pg_constraint) GETSTRUCT(constrtup);
slot = MakeSingleTupleTableSlot(tupdesc);
econtext->ecxt_scantuple = slot;
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(rel, snapshot, 0, NULL);
/*
* Switch to per-tuple memory context and reset it for each tuple
MemoryContextSwitchTo(oldcxt);
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
ExecDropSingleTupleTableSlot(slot);
FreeExecutorState(estate);
}
HeapScanDesc scan;
HeapTuple tuple;
Trigger trig;
+ Snapshot snapshot;
ereport(DEBUG1,
(errmsg("validating foreign key constraint \"%s\"", conname)));
* if that tuple had just been inserted. If any of those fail, it should
* ereport(ERROR) and that's that.
*/
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(rel, snapshot, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
}
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
}
static void
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(conrel, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(childrelid));
scan = systable_beginscan(conrel, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
/* scan for matching tuple - there should only be one */
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
Int32GetDatum((int32) attnum));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(depTup = systable_getnext(scan)))
{
Int32GetDatum((int32) attnum));
scan = systable_beginscan(depRel, DependDependerIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(depTup = systable_getnext(scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relationOid));
scan = systable_beginscan(attRelation, AttributeRelidNumIndexId,
- true, SnapshotNow, 1, key);
+ true, NULL, 1, key);
while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
{
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
/* we leave refobjsubid unspecified */
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(child_rel)));
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
/* inhseqno sequences start at 1 */
inhseqno = 0;
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(parent_rel)));
parent_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &parent_key);
+ true, NULL, 1, &parent_key);
while (HeapTupleIsValid(parent_tuple = systable_getnext(parent_scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(child_rel)));
child_scan = systable_beginscan(catalog_relation, ConstraintRelidIndexId,
- true, SnapshotNow, 1, &child_key);
+ true, NULL, 1, &child_key);
while (HeapTupleIsValid(child_tuple = systable_getnext(child_scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
- true, SnapshotNow, 1, key);
+ true, NULL, 1, key);
while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(catalogRelation, AttributeRelidNumIndexId,
- true, SnapshotNow, 1, key);
+ true, NULL, 1, key);
while (HeapTupleIsValid(attributeTuple = systable_getnext(scan)))
{
Form_pg_attribute att = (Form_pg_attribute) GETSTRUCT(attributeTuple);
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(parent_rel)));
scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
- true, SnapshotNow, 1, key);
+ true, NULL, 1, key);
connames = NIL;
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
scan = systable_beginscan(catalogRelation, ConstraintRelidIndexId,
- true, SnapshotNow, 1, key);
+ true, NULL, 1, key);
while (HeapTupleIsValid(constraintTuple = systable_getnext(scan)))
{
Int32GetDatum(0));
scan = systable_beginscan(catalogRelation, DependDependerIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(depTuple = systable_getnext(scan)))
{
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(relid));
scan = systable_beginscan(inheritsRelation, InheritsRelidSeqnoIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
if (HeapTupleIsValid(systable_getnext(scan)))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
/* we leave refobjsubid unspecified */
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scandesc = heap_beginscan_catalog(rel, 1, entry);
tuple = heap_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tuple))
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(oldname));
- scan = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scan = heap_beginscan_catalog(rel, 1, entry);
tup = heap_getnext(scan, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(newname));
- scan = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scan = heap_beginscan_catalog(rel, 1, entry);
tup = heap_getnext(scan, ForwardScanDirection);
if (HeapTupleIsValid(tup))
ereport(ERROR,
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(stmt->tablespacename));
- scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scandesc = heap_beginscan_catalog(rel, 1, entry);
tup = heap_getnext(scandesc, ForwardScanDirection);
if (!HeapTupleIsValid(tup))
ereport(ERROR,
Anum_pg_tablespace_spcname,
BTEqualStrategyNumber, F_NAMEEQ,
CStringGetDatum(tablespacename));
- scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scandesc = heap_beginscan_catalog(rel, 1, entry);
tuple = heap_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(spc_oid));
- scandesc = heap_beginscan(rel, SnapshotNow, 1, entry);
+ scandesc = heap_beginscan_catalog(rel, 1, entry);
tuple = heap_getnext(scandesc, ForwardScanDirection);
/* We assume that there can be at most one matching tuple */
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(RelationGetRelid(rel)));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 1, &key);
+ NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
{
Form_pg_trigger pg_trigger = (Form_pg_trigger) GETSTRUCT(tuple);
ObjectIdGetDatum(trigOid));
tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tup = systable_getnext(tgscan);
if (!HeapTupleIsValid(tup))
CStringGetDatum(trigname));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
tup = systable_getnext(tgscan);
BTEqualStrategyNumber, F_NAMEEQ,
PointerGetDatum(stmt->newname));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
BTEqualStrategyNumber, F_NAMEEQ,
PointerGetDatum(stmt->subname));
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
if (HeapTupleIsValid(tuple = systable_getnext(tgscan)))
{
tgoid = HeapTupleGetOid(tuple);
nkeys = 1;
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, nkeys, keys);
+ NULL, nkeys, keys);
found = changed = false;
tgrel = heap_open(TriggerRelationId, AccessShareLock);
tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
{
ObjectIdGetDatum(namespaceId));
conscan = systable_beginscan(conrel, ConstraintNameNspIndexId,
- true, SnapshotNow, 2, skey);
+ true, NULL, 2, skey);
while (HeapTupleIsValid(tup = systable_getnext(conscan)))
{
ObjectIdGetDatum(conoid));
tgscan = systable_beginscan(tgrel, TriggerConstraintIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid(htup = systable_getnext(tgscan)))
{
ObjectIdGetDatum(myself.objectId));
scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid((maptup = systable_getnext(scan))))
{
ObjectIdGetDatum(sourceOid));
scan = systable_beginscan(mapRel, TSConfigMapIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid((maptup = systable_getnext(scan))))
{
ObjectIdGetDatum(cfgId));
scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid((tup = systable_getnext(scan))))
{
Int32GetDatum(tokens[i]));
scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while (HeapTupleIsValid((maptup = systable_getnext(scan))))
{
ObjectIdGetDatum(cfgId));
scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid((maptup = systable_getnext(scan))))
{
Int32GetDatum(tokens[i]));
scan = systable_beginscan(relMap, TSConfigMapIndexId, true,
- SnapshotNow, 2, skey);
+ NULL, 2, skey);
while (HeapTupleIsValid((maptup = systable_getnext(scan))))
{
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/rel.h"
+#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
TupleDesc tupdesc = RelationGetDescr(testrel);
HeapScanDesc scan;
HeapTuple tuple;
+ Snapshot snapshot;
/* Scan all tuples in this relation */
- scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(testrel, snapshot, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
}
}
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
/* Close each rel after processing, but keep lock */
heap_close(testrel, NoLock);
ObjectIdGetDatum(HeapTupleGetOid(tup)));
conscan = systable_beginscan(conrel, ConstraintTypidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
/*
* Scan over the result set, removing any matching entries.
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(domainoid));
scan = systable_beginscan(conrel, ConstraintTypidIndexId,
- true, SnapshotNow, 1, &key);
+ true, NULL, 1, &key);
while (HeapTupleIsValid(tuple = systable_getnext(scan)))
{
TupleDesc tupdesc = RelationGetDescr(testrel);
HeapScanDesc scan;
HeapTuple tuple;
+ Snapshot snapshot;
/* Scan all tuples in this relation */
- scan = heap_beginscan(testrel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(testrel, snapshot, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
int i;
ResetExprContext(econtext);
}
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
/* Hold relation lock till commit (XXX bad for concurrency) */
heap_close(testrel, NoLock);
ObjectIdGetDatum(domainOid));
depScan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 2, key);
+ NULL, 2, key);
while (HeapTupleIsValid(depTup = systable_getnext(depScan)))
{
ObjectIdGetDatum(typeOid));
scan = systable_beginscan(conRel, ConstraintTypidIndexId, true,
- SnapshotNow, 1, key);
+ NULL, 1, key);
while (HeapTupleIsValid(conTup = systable_getnext(scan)))
{
ObjectIdGetDatum(roleid));
sscan = systable_beginscan(pg_auth_members_rel, AuthMemRoleMemIndexId,
- true, SnapshotNow, 1, &scankey);
+ true, NULL, 1, &scankey);
while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
{
ObjectIdGetDatum(roleid));
sscan = systable_beginscan(pg_auth_members_rel, AuthMemMemRoleIndexId,
- true, SnapshotNow, 1, &scankey);
+ true, NULL, 1, &scankey);
while (HeapTupleIsValid(tmp_tuple = systable_getnext(sscan)))
{
pgclass = heap_open(RelationRelationId, AccessShareLock);
- scan = heap_beginscan(pgclass, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(pgclass, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
relation = heap_open(RelationRelationId, AccessShareLock);
scan = systable_beginscan(relation, InvalidOid, false,
- SnapshotNow, 0, NULL);
+ NULL, 0, NULL);
while ((classTup = systable_getnext(scan)) != NULL)
{
*/
relation = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan(relation, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(relation, 0, NULL);
while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
* Routines to support bitmapped scans of relations
*
* NOTE: it is critical that this plan type only be used with MVCC-compliant
- * snapshots (ie, regular snapshots, not SnapshotNow or one of the other
+ * snapshots (ie, regular snapshots, not SnapshotAny or one of the other
* special snapshots). The reason is that since index and heap scans are
* decoupled, there can be no assurance that the index tuple prompting a
* visit to a particular heap TID still exists when the visit is made.
* Therefore the tuple might not exist anymore either (which is OK because
* heap_fetch will cope) --- but worse, the tuple slot could have been
* re-used for a newer tuple. With an MVCC snapshot the newer tuple is
- * certain to fail the time qual and so it will not be mistakenly returned.
- * With SnapshotNow we might return a tuple that doesn't meet the required
- * index qual conditions.
+ * certain to fail the time qual and so it will not be mistakenly returned,
+ * but with anything else we might return a tuple that doesn't meet the
+ * required index qual conditions.
*
*
* Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
(void) GetTransactionSnapshot();
rel = heap_open(DatabaseRelationId, AccessShareLock);
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(rel, 0, NULL);
while (HeapTupleIsValid(tup = heap_getnext(scan, ForwardScanDirection)))
{
* wide tables there might be proportionally much more activity in the
* TOAST table than in its parent.
*/
- relScan = heap_beginscan(classRel, SnapshotNow, 0, NULL);
+ relScan = heap_beginscan_catalog(classRel, 0, NULL);
/*
* On the first pass, we collect main tables to vacuum, and also the main
BTEqualStrategyNumber, F_CHAREQ,
CharGetDatum(RELKIND_TOASTVALUE));
- relScan = heap_beginscan(classRel, SnapshotNow, 1, &key);
+ relScan = heap_beginscan_catalog(classRel, 1, &key);
while ((tuple = heap_getnext(relScan, ForwardScanDirection)) != NULL)
{
Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
#include "utils/memutils.h"
#include "utils/ps_status.h"
#include "utils/rel.h"
+#include "utils/snapmgr.h"
#include "utils/timestamp.h"
#include "utils/tqual.h"
Relation rel;
HeapScanDesc scan;
HeapTuple tup;
+ Snapshot snapshot;
memset(&hash_ctl, 0, sizeof(hash_ctl));
hash_ctl.keysize = sizeof(Oid);
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
rel = heap_open(catalogid, AccessShareLock);
- scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scan = heap_beginscan(rel, snapshot, 0, NULL);
while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
{
Oid thisoid = HeapTupleGetOid(tup);
(void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
}
heap_endscan(scan);
+ UnregisterSnapshot(snapshot);
heap_close(rel, AccessShareLock);
return htab;
#include "utils/inval.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
+#include "utils/snapmgr.h"
#include "utils/syscache.h"
#include "utils/tqual.h"
event_relation->rd_rel->relkind != RELKIND_MATVIEW)
{
HeapScanDesc scanDesc;
+ Snapshot snapshot;
- scanDesc = heap_beginscan(event_relation, SnapshotNow, 0, NULL);
+ snapshot = RegisterSnapshot(GetLatestSnapshot());
+ scanDesc = heap_beginscan(event_relation, snapshot, 0, NULL);
if (heap_getnext(scanDesc, ForwardScanDirection) != NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("could not convert table \"%s\" to a view because it is not empty",
RelationGetRelationName(event_relation))));
heap_endscan(scanDesc);
+ UnregisterSnapshot(snapshot);
if (event_relation->rd_rel->relhastriggers)
ereport(ERROR,
/*
* If the relation doesn't exist, return zero rather than throwing an
* error. This is helpful since scanning an information_schema view under
- * MVCC rules can result in referencing rels that were just deleted
- * according to a SnapshotNow probe.
+ * MVCC rules can result in referencing rels that have actually been
+ * deleted already.
*/
if (rel == NULL)
return 0;
ObjectIdGetDatum(ruleOid));
rcscan = systable_beginscan(RewriteRelation, RewriteOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
tuple = systable_getnext(rcscan);
CStringGetDatum(rulename));
RewriteRelation = heap_open(RewriteRelationId, AccessShareLock);
- scanDesc = heap_beginscan(RewriteRelation, SnapshotNow, 1, &scanKeyData);
+ scanDesc = heap_beginscan_catalog(RewriteRelation, 1, &scanKeyData);
htup = heap_getnext(scanDesc, ForwardScanDirection);
if (!HeapTupleIsValid(htup))
if (flags & INV_WRITE)
{
- retval->snapshot = SnapshotNow;
+ retval->snapshot = NULL; /* instantaneous MVCC snapshot */
retval->flags = IFS_WRLOCK | IFS_RDLOCK;
}
else if (flags & INV_READ)
errmsg("invalid flags for opening a large object: %d",
flags)));
- /* Can't use LargeObjectExists here because it always uses SnapshotNow */
+ /* Can't use LargeObjectExists here because we need to specify snapshot */
if (!myLargeObjectExists(lobjId, retval->snapshot))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
{
Assert(PointerIsValid(obj_desc));
- if (obj_desc->snapshot != SnapshotNow)
- UnregisterSnapshotFromOwner(obj_desc->snapshot,
- TopTransactionResourceOwner);
+ UnregisterSnapshotFromOwner(obj_desc->snapshot,
+ TopTransactionResourceOwner);
pfree(obj_desc);
}
* That leads to a couple of choices. We work from the pg_class row alone
* rather than actually opening each relation, for efficiency. We don't
* fail if we can't find the relation --- some rows might be visible in
- * the query's MVCC snapshot but already dead according to SnapshotNow.
+ * the query's MVCC snapshot even though the relations have been dropped.
* (Note: we could avoid using the catcache, but there's little point
* because the relation mapper also works "in the now".) We also don't
* fail if the relation doesn't have storage. In all these cases it
hdesc = heap_open(ProcedureRelationId, AccessShareLock);
sysscan = systable_beginscan(hdesc, ProcedureNameArgsNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
{
hdesc = heap_open(OperatorRelationId, AccessShareLock);
sysscan = systable_beginscan(hdesc, OperatorNameNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
{
hdesc = heap_open(RelationRelationId, AccessShareLock);
sysscan = systable_beginscan(hdesc, ClassNameNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
result = HeapTupleGetOid(tuple);
hdesc = heap_open(TypeRelationId, AccessShareLock);
sysscan = systable_beginscan(hdesc, TypeNameNspIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
if (HeapTupleIsValid(tuple = systable_getnext(sysscan)))
result = HeapTupleGetOid(tuple);
ObjectIdGetDatum(trigid));
tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
ht_trig = systable_getnext(tgscan);
Int32GetDatum(attnum));
scan = systable_beginscan(depRel, DependReferenceIndexId, true,
- SnapshotNow, 3, key);
+ NULL, 3, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
scandesc = systable_beginscan(relation,
cache->cc_indexoid,
IndexScanOK(cache, cur_skey),
- SnapshotNow,
+ NULL,
cache->cc_nkeys,
cur_skey);
scandesc = systable_beginscan(relation,
cache->cc_indexoid,
IndexScanOK(cache, cur_skey),
- SnapshotNow,
+ NULL,
nkeys,
cur_skey);
HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
/*
- * Prepare to scan pg_event_trigger in name order. We use an MVCC
- * snapshot to avoid getting inconsistent results if the table is being
- * concurrently updated.
+ * Prepare to scan pg_event_trigger in name order.
*/
rel = relation_open(EventTriggerRelationId, AccessShareLock);
irel = index_open(EventTriggerNameIndexId, AccessShareLock);
- scan = systable_beginscan_ordered(rel, irel, GetLatestSnapshot(), 0, NULL);
+ scan = systable_beginscan_ordered(rel, irel, NULL, 0, NULL);
/*
* Build a cache item for each pg_event_trigger tuple, and append each one
* consider that it is *still valid* so long as we are in the same command,
* ie, until the next CommandCounterIncrement() or transaction commit.
* (See utils/time/tqual.c, and note that system catalogs are generally
- * scanned under SnapshotNow rules by the system, or plain user snapshots
- * for user queries.) At the command boundary, the old tuple stops
+ * scanned under the most current snapshot available, rather than the
+ * transaction snapshot.) At the command boundary, the old tuple stops
* being valid and the new version, if any, becomes valid. Therefore,
* we cannot simply flush a tuple from the system caches during heap_update()
* or heap_delete(). The tuple is still good at that point; what's more,
#include "utils/memutils.h"
#include "utils/rel.h"
#include "utils/relmapper.h"
+#include "utils/snapmgr.h"
#include "utils/syscache.h"
AddInvalidationMessage(&hdr->rclist, &msg);
}
+/*
+ * Add a snapshot inval entry
+ */
+static void
+AddSnapshotInvalidationMessage(InvalidationListHeader *hdr,
+ Oid dbId, Oid relId)
+{
+ SharedInvalidationMessage msg;
+
+ /* Don't add a duplicate item */
+ /* We assume dbId need not be checked because it will never change */
+ ProcessMessageList(hdr->rclist,
+ if (msg->sn.id == SHAREDINVALSNAPSHOT_ID &&
+ msg->sn.relId == relId)
+ return);
+
+ /* OK, add the item */
+ msg.sn.id = SHAREDINVALSNAPSHOT_ID;
+ msg.sn.dbId = dbId;
+ msg.sn.relId = relId;
+ AddInvalidationMessage(&hdr->rclist, &msg);
+}
+
/*
* Append one list of invalidation messages to another, resetting
* the source list to empty.
transInvalInfo->RelcacheInitFileInval = true;
}
+/*
+ * RegisterSnapshotInvalidation
+ *
+ * Register a invalidation event for MVCC scans against a given catalog.
+ * Only needed for catalogs that don't have catcaches.
+ */
+static void
+RegisterSnapshotInvalidation(Oid dbId, Oid relId)
+{
+ AddSnapshotInvalidationMessage(&transInvalInfo->CurrentCmdInvalidMsgs,
+ dbId, relId);
+}
+
/*
* LocalExecuteInvalidationMessage
*
{
if (msg->cc.dbId == MyDatabaseId || msg->cc.dbId == InvalidOid)
{
+ InvalidateCatalogSnapshot();
+
CatalogCacheIdInvalidate(msg->cc.id, msg->cc.hashValue);
CallSyscacheCallbacks(msg->cc.id, msg->cc.hashValue);
{
if (msg->cat.dbId == MyDatabaseId || msg->cat.dbId == InvalidOid)
{
+ InvalidateCatalogSnapshot();
+
CatalogCacheFlushCatalog(msg->cat.catId);
/* CatalogCacheFlushCatalog calls CallSyscacheCallbacks as needed */
else if (msg->rm.dbId == MyDatabaseId)
RelationMapInvalidate(false);
}
+ else if (msg->id == SHAREDINVALSNAPSHOT_ID)
+ {
+ /* We only care about our own database and shared catalogs */
+ if (msg->rm.dbId == InvalidOid)
+ InvalidateCatalogSnapshot();
+ else if (msg->rm.dbId == MyDatabaseId)
+ InvalidateCatalogSnapshot();
+ }
else
elog(FATAL, "unrecognized SI message ID: %d", msg->id);
}
{
int i;
+ InvalidateCatalogSnapshot();
ResetCatalogCaches();
RelationCacheInvalidate(); /* gets smgr and relmap too */
/*
* First let the catcache do its thing
*/
- PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
- RegisterCatcacheInvalidation);
+ tupleRelId = RelationGetRelid(relation);
+ if (RelationInvalidatesSnapshotsOnly(tupleRelId))
+ {
+ databaseId = IsSharedRelation(tupleRelId) ? InvalidOid : MyDatabaseId;
+ RegisterSnapshotInvalidation(databaseId, tupleRelId);
+ }
+ else
+ PrepareToInvalidateCacheTuple(relation, tuple, newtuple,
+ RegisterCatcacheInvalidation);
/*
* Now, is this tuple one of the primary definers of a relcache entry?
* Note we ignore newtuple here; we assume an update cannot move a tuple
* from being part of one relcache entry to being part of another.
*/
- tupleRelId = RelationGetRelid(relation);
-
if (tupleRelId == RelationRelationId)
{
Form_pg_class classtup = (Form_pg_class) GETSTRUCT(tuple);
* This is used by RelationBuildDesc to find a pg_class
* tuple matching targetRelId. The caller must hold at least
* AccessShareLock on the target relid to prevent concurrent-update
- * scenarios --- else our SnapshotNow scan might fail to find any
- * version that it thinks is live.
+ * scenarios; it isn't guaranteed that all scans used to build the
+ * relcache entry will use the same snapshot. If, for example,
+ * an attribute were to be added after scanning pg_class and before
+ * scanning pg_attribute, relnatts wouldn't match.
*
* NB: the returned tuple has been copied into palloc'd storage
* and must eventually be freed with heap_freetuple.
pg_class_desc = heap_open(RelationRelationId, AccessShareLock);
pg_class_scan = systable_beginscan(pg_class_desc, ClassOidIndexId,
indexOK && criticalRelcachesBuilt,
- SnapshotNow,
+ NULL,
1, key);
pg_class_tuple = systable_getnext(pg_class_scan);
pg_attribute_scan = systable_beginscan(pg_attribute_desc,
AttributeRelidNumIndexId,
criticalRelcachesBuilt,
- SnapshotNow,
+ NULL,
2, skey);
/*
rewrite_tupdesc = RelationGetDescr(rewrite_desc);
rewrite_scan = systable_beginscan(rewrite_desc,
RewriteRelRulenameIndexId,
- true, SnapshotNow,
+ true, NULL,
1, &key);
while (HeapTupleIsValid(rewrite_tuple = systable_getnext(rewrite_scan)))
ObjectIdGetDatum(operatorClassOid));
rel = heap_open(OperatorClassRelationId, AccessShareLock);
scan = systable_beginscan(rel, OpclassOidIndexId, indexOK,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
if (HeapTupleIsValid(htup = systable_getnext(scan)))
{
ObjectIdGetDatum(opcentry->opcintype));
rel = heap_open(AccessMethodProcedureRelationId, AccessShareLock);
scan = systable_beginscan(rel, AccessMethodProcedureIndexId, indexOK,
- SnapshotNow, 3, skey);
+ NULL, 3, skey);
while (HeapTupleIsValid(htup = systable_getnext(scan)))
{
adrel = heap_open(AttrDefaultRelationId, AccessShareLock);
adscan = systable_beginscan(adrel, AttrDefaultIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
found = 0;
while (HeapTupleIsValid(htup = systable_getnext(adscan)))
conrel = heap_open(ConstraintRelationId, AccessShareLock);
conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
{
indrel = heap_open(IndexRelationId, AccessShareLock);
indscan = systable_beginscan(indrel, IndexIndrelidIndexId, true,
- SnapshotNow, 1, &skey);
+ NULL, 1, &skey);
while (HeapTupleIsValid(htup = systable_getnext(indscan)))
{
conrel = heap_open(ConstraintRelationId, AccessShareLock);
conscan = systable_beginscan(conrel, ConstraintRelidIndexId, true,
- SnapshotNow, 1, skey);
+ NULL, 1, skey);
found = false;
while (HeapTupleIsValid(htup = systable_getnext(conscan)))
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+#include "catalog/pg_db_role_setting.h"
#include "catalog/pg_default_acl.h"
+#include "catalog/pg_depend.h"
+#include "catalog/pg_description.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_range.h"
#include "catalog/pg_rewrite.h"
+#include "catalog/pg_seclabel.h"
+#include "catalog/pg_shdepend.h"
+#include "catalog/pg_shdescription.h"
+#include "catalog/pg_shseclabel.h"
#include "catalog/pg_statistic.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_ts_config.h"
static int SysCacheSize = lengthof(cacheinfo);
static bool CacheInitialized = false;
+static Oid SysCacheRelationOid[lengthof(cacheinfo)];
+static int SysCacheRelationOidSize;
+
+static int oid_compare(const void *a, const void *b);
/*
* InitCatalogCache - initialize the caches
InitCatalogCache(void)
{
int cacheId;
+ int i,
+ j = 0;
Assert(!CacheInitialized);
if (!PointerIsValid(SysCache[cacheId]))
elog(ERROR, "could not initialize cache %u (%d)",
cacheinfo[cacheId].reloid, cacheId);
+ SysCacheRelationOid[SysCacheRelationOidSize++] =
+ cacheinfo[cacheId].reloid;
+ /* see comments for RelationInvalidatesSnapshotsOnly */
+ Assert(!RelationInvalidatesSnapshotsOnly(cacheinfo[cacheId].reloid));
}
+
+ /* Sort and dedup OIDs. */
+ pg_qsort(SysCacheRelationOid, SysCacheRelationOidSize,
+ sizeof(Oid), oid_compare);
+ for (i = 1; i < SysCacheRelationOidSize; ++i)
+ if (SysCacheRelationOid[i] != SysCacheRelationOid[j])
+ SysCacheRelationOid[++j] = SysCacheRelationOid[i];
+ SysCacheRelationOidSize = j + 1;
+
CacheInitialized = true;
}
-
/*
* InitCatalogCachePhase2 - finish initializing the caches
*
return SearchCatCacheList(SysCache[cacheId], nkeys,
key1, key2, key3, key4);
}
+
+/*
+ * Certain relations that do not have system caches send snapshot invalidation
+ * messages in lieu of catcache messages. This is for the benefit of
+ * GetCatalogSnapshot(), which can then reuse its existing MVCC snapshot
+ * for scanning one of those catalogs, rather than taking a new one, if no
+ * invalidation has been received.
+ *
+ * Relations that have syscaches need not (and must not) be listed here. The
+ * catcache invalidation messages will also flush the snapshot. If you add a
+ * syscache for one of these relations, remove it from this list.
+ */
+bool
+RelationInvalidatesSnapshotsOnly(Oid relid)
+{
+ switch (relid)
+ {
+ case DbRoleSettingRelationId:
+ case DependRelationId:
+ case SharedDependRelationId:
+ case DescriptionRelationId:
+ case SharedDescriptionRelationId:
+ case SecLabelRelationId:
+ case SharedSecLabelRelationId:
+ return true;
+ default:
+ break;
+ }
+
+ return false;
+}
+
+/*
+ * Test whether a relation has a system cache.
+ */
+bool
+RelationHasSysCache(Oid relid)
+{
+ int low = 0,
+ high = SysCacheRelationOidSize - 1;
+
+ while (low <= high)
+ {
+ int middle = low + (high - low) / 2;
+
+ if (SysCacheRelationOid[middle] == relid)
+ return true;
+ if (SysCacheRelationOid[middle] < relid)
+ low = middle + 1;
+ else
+ high = middle - 1;
+ }
+
+ return false;
+}
+
+
+/*
+ * OID comparator for pg_qsort
+ */
+static int
+oid_compare(const void *a, const void *b)
+{
+ Oid oa = *((Oid *) a);
+ Oid ob = *((Oid *) b);
+
+ if (oa == ob)
+ return 0;
+ return (oa > ob) ? 1 : -1;
+}
maprel = heap_open(TSConfigMapRelationId, AccessShareLock);
mapidx = index_open(TSConfigMapIndexId, AccessShareLock);
mapscan = systable_beginscan_ordered(maprel, mapidx,
- SnapshotNow, 1, &mapskey);
+ NULL, 1, &mapskey);
while ((maptup = systable_getnext_ordered(mapscan, ForwardScanDirection)) != NULL)
{
items = (EnumItem *) palloc(sizeof(EnumItem) * maxitems);
numitems = 0;
- /*
- * Scan pg_enum for the members of the target enum type. We use a current
- * MVCC snapshot, *not* SnapshotNow, so that we see a consistent set of
- * rows even if someone commits a renumbering of the enum meanwhile. See
- * comments for RenumberEnumType in catalog/pg_enum.c for more info.
- */
+ /* Scan pg_enum for the members of the target enum type. */
ScanKeyInit(&skey,
Anum_pg_enum_enumtypid,
BTEqualStrategyNumber, F_OIDEQ,
enum_rel = heap_open(EnumRelationId, AccessShareLock);
enum_scan = systable_beginscan(enum_rel,
EnumTypIdLabelIndexId,
- true, GetLatestSnapshot(),
+ true, NULL,
1, &skey);
while (HeapTupleIsValid(enum_tuple = systable_getnext(enum_scan)))
relation = heap_open(DatabaseRelationId, AccessShareLock);
scan = systable_beginscan(relation, DatabaseNameIndexId,
criticalSharedRelcachesBuilt,
- SnapshotNow,
+ NULL,
1, key);
tuple = systable_getnext(scan);
relation = heap_open(DatabaseRelationId, AccessShareLock);
scan = systable_beginscan(relation, DatabaseOidIndexId,
criticalSharedRelcachesBuilt,
- SnapshotNow,
+ NULL,
1, key);
tuple = systable_getnext(scan);
process_settings(Oid databaseid, Oid roleid)
{
Relation relsetting;
+ Snapshot snapshot;
if (!IsUnderPostmaster)
return;
relsetting = heap_open(DbRoleSettingRelationId, AccessShareLock);
+ /* read all the settings under the same snapsot for efficiency */
+ snapshot = RegisterSnapshot(GetCatalogSnapshot(DbRoleSettingRelationId));
+
/* Later settings are ignored if set earlier. */
- ApplySetting(databaseid, roleid, relsetting, PGC_S_DATABASE_USER);
- ApplySetting(InvalidOid, roleid, relsetting, PGC_S_USER);
- ApplySetting(databaseid, InvalidOid, relsetting, PGC_S_DATABASE);
- ApplySetting(InvalidOid, InvalidOid, relsetting, PGC_S_GLOBAL);
+ ApplySetting(snapshot, databaseid, roleid, relsetting, PGC_S_DATABASE_USER);
+ ApplySetting(snapshot, InvalidOid, roleid, relsetting, PGC_S_USER);
+ ApplySetting(snapshot, databaseid, InvalidOid, relsetting, PGC_S_DATABASE);
+ ApplySetting(snapshot, InvalidOid, InvalidOid, relsetting, PGC_S_GLOBAL);
+ UnregisterSnapshot(snapshot);
heap_close(relsetting, AccessShareLock);
}
pg_authid_rel = heap_open(AuthIdRelationId, AccessShareLock);
- scan = heap_beginscan(pg_authid_rel, SnapshotNow, 0, NULL);
+ scan = heap_beginscan_catalog(pg_authid_rel, 0, NULL);
result = (heap_getnext(scan, ForwardScanDirection) != NULL);
heap_endscan(scan);
#include "storage/predicate.h"
#include "storage/proc.h"
#include "storage/procarray.h"
+#include "storage/sinval.h"
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/resowner_private.h"
#include "utils/snapmgr.h"
+#include "utils/syscache.h"
#include "utils/tqual.h"
* mode, and to the latest one taken in a read-committed transaction.
* SecondarySnapshot is a snapshot that's always up-to-date as of the current
* instant, even in transaction-snapshot mode. It should only be used for
- * special-purpose code (say, RI checking.)
+ * special-purpose code (say, RI checking.) CatalogSnapshot points to an
+ * MVCC snapshot intended to be used for catalog scans; we must refresh it
+ * whenever a system catalog change occurs.
*
* These SnapshotData structs are static to simplify memory allocation
* (see the hack in GetSnapshotData to avoid repeated malloc/free).
*/
static SnapshotData CurrentSnapshotData = {HeapTupleSatisfiesMVCC};
static SnapshotData SecondarySnapshotData = {HeapTupleSatisfiesMVCC};
+static SnapshotData CatalogSnapshotData = {HeapTupleSatisfiesMVCC};
/* Pointers to valid snapshots */
static Snapshot CurrentSnapshot = NULL;
static Snapshot SecondarySnapshot = NULL;
+static Snapshot CatalogSnapshot = NULL;
+
+/*
+ * Staleness detection for CatalogSnapshot.
+ */
+static bool CatalogSnapshotStale = true;
/*
* These are updated by GetSnapshotData. We initialize them this way
else
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
+ /* Don't allow catalog snapshot to be older than xact snapshot. */
+ CatalogSnapshotStale = true;
+
FirstSnapshotSet = true;
return CurrentSnapshot;
}
if (IsolationUsesXactSnapshot())
return CurrentSnapshot;
+ /* Don't allow catalog snapshot to be older than xact snapshot. */
+ CatalogSnapshotStale = true;
+
CurrentSnapshot = GetSnapshotData(&CurrentSnapshotData);
return CurrentSnapshot;
return SecondarySnapshot;
}
+/*
+ * GetCatalogSnapshot
+ * Get a snapshot that is sufficiently up-to-date for scan of the
+ * system catalog with the specified OID.
+ */
+Snapshot
+GetCatalogSnapshot(Oid relid)
+{
+ /*
+ * If the caller is trying to scan a relation that has no syscache,
+ * no catcache invalidations will be sent when it is updated. For a
+ * a few key relations, snapshot invalidations are sent instead. If
+ * we're trying to scan a relation for which neither catcache nor
+ * snapshot invalidations are sent, we must refresh the snapshot every
+ * time.
+ */
+ if (!CatalogSnapshotStale && !RelationInvalidatesSnapshotsOnly(relid) &&
+ !RelationHasSysCache(relid))
+ CatalogSnapshotStale = true;
+
+ if (CatalogSnapshotStale)
+ {
+ /* Get new snapshot. */
+ CatalogSnapshot = GetSnapshotData(&CatalogSnapshotData);
+
+ /*
+ * Mark new snapshost as valid. We must do this last, in case an
+ * ERROR occurs inside GetSnapshotData().
+ */
+ CatalogSnapshotStale = false;
+ }
+
+ return CatalogSnapshot;
+}
+
+/*
+ * Mark the current catalog snapshot as invalid. We could change this API
+ * to allow the caller to provide more fine-grained invalidation details, so
+ * that a change to relation A wouldn't prevent us from using our cached
+ * snapshot to scan relation B, but so far there's no evidence that the CPU
+ * cycles we spent tracking such fine details would be well-spent.
+ */
+void
+InvalidateCatalogSnapshot()
+{
+ CatalogSnapshotStale = true;
+}
+
/*
* SnapshotSetCommandId
* Propagate CommandCounterIncrement into the static snapshots, if set
* Note that pg_dump runs in a transaction-snapshot mode transaction,
* so it sees a consistent snapshot of the database including system
* catalogs. However, it relies in part on various specialized backend
- * functions like pg_get_indexdef(), and those things tend to run on
- * SnapshotNow time, ie they look at the currently committed state. So
- * it is possible to get 'cache lookup failed' error if someone
- * performs DDL changes while a dump is happening. The window for this
- * sort of thing is from the acquisition of the transaction snapshot to
- * getSchemaData() (when pg_dump acquires AccessShareLock on every
- * table it intends to dump). It isn't very large, but it can happen.
+ * functions like pg_get_indexdef(), and those things tend to look at
+ * the currently committed state. So it is possible to get 'cache
+ * lookup failed' error if someone performs DDL changes while a dump is
+ * happening. The window for this sort of thing is from the acquisition
+ * of the transaction snapshot to getSchemaData() (when pg_dump acquires
+ * AccessShareLock on every table it intends to dump). It isn't very large,
+ * but it can happen.
*
* http://archives.postgresql.org/pgsql-bugs/2010-02/msg00187.php
*
extern HeapScanDesc heap_beginscan(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key);
+extern HeapScanDesc heap_beginscan_catalog(Relation relation, int nkeys,
+ ScanKey key);
extern HeapScanDesc heap_beginscan_strat(Relation relation, Snapshot snapshot,
int nkeys, ScanKey key,
bool allow_strat, bool allow_sync);
bool rs_pageatatime; /* verify visibility page-at-a-time? */
bool rs_allow_strat; /* allow or disallow use of access strategy */
bool rs_allow_sync; /* allow or disallow use of syncscan */
+ bool rs_temp_snap; /* unregister snapshot at scan end? */
/* state set up at initscan time */
BlockNumber rs_nblocks; /* number of blocks to scan */
Relation irel; /* NULL if doing heap scan */
HeapScanDesc scan; /* only valid in heap-scan case */
IndexScanDesc iscan; /* only valid in index-scan case */
+ Snapshot snapshot; /* snapshot to unregister at end of scan */
} SysScanDescData;
#endif /* RELSCAN_H */
*
* OAT_POST_ALTER should be invoked just after the object is altered,
* but before the command counter is incremented. An extension using the
- * hook can use SnapshotNow and SnapshotSelf to get the old and new
- * versions of the tuple.
+ * hook can use a current MVCC snapshot to get the old version of the tuple,
+ * and can use SnapshotSelf to get the new version of the tuple.
*
* OAT_NAMESPACE_SEARCH should be invoked prior to object name lookup under
* a particular namespace. This event is equivalent to usage permission
*/
extern void AlterSetting(Oid databaseid, Oid roleid, VariableSetStmt *setstmt);
extern void DropSetting(Oid databaseid, Oid roleid);
-extern void ApplySetting(Oid databaseid, Oid roleid, Relation relsetting,
- GucSource source);
+extern void ApplySetting(Snapshot snapshot, Oid databaseid, Oid roleid,
+ Relation relsetting, GucSource source);
#endif /* PG_DB_ROLE_SETTING_H */
* * invalidate a relcache entry for a specific logical relation
* * invalidate an smgr cache entry for a specific physical relation
* * invalidate the mapped-relation mapping for a given database
+ * * invalidate any saved snapshot that might be used to scan a given relation
* More types could be added if needed. The message type is identified by
* the first "int8" field of the message struct. Zero or positive means a
* specific-catcache inval message (and also serves as the catcache ID field).
* catcache inval messages must be generated for each of its caches, since
* the hash keys will generally be different.
*
- * Catcache and relcache invalidations are transactional, and so are sent
- * to other backends upon commit. Internally to the generating backend,
- * they are also processed at CommandCounterIncrement so that later commands
- * in the same transaction see the new state. The generating backend also
- * has to process them at abort, to flush out any cache state it's loaded
+ * Catcache, relcache, and snapshot invalidations are transactional, and so
+ * are sent to other backends upon commit. Internally to the generating
+ * backend, they are also processed at CommandCounterIncrement so that later
+ * commands in the same transaction see the new state. The generating backend
+ * also has to process them at abort, to flush out any cache state it's loaded
* from no-longer-valid entries.
*
* smgr and relation mapping invalidations are non-transactional: they are
Oid dbId; /* database ID, or 0 for shared catalogs */
} SharedInvalRelmapMsg;
+#define SHAREDINVALSNAPSHOT_ID (-5)
+
+typedef struct
+{
+ int8 id; /* type field --- must be first */
+ Oid dbId; /* database ID, or 0 if a shared relation */
+ Oid relId; /* relation ID */
+} SharedInvalSnapshotMsg;
+
typedef union
{
int8 id; /* type field --- must be first */
SharedInvalRelcacheMsg rc;
SharedInvalSmgrMsg sm;
SharedInvalRelmapMsg rm;
+ SharedInvalSnapshotMsg sn;
} SharedInvalidationMessage;
extern Snapshot GetLatestSnapshot(void);
extern void SnapshotSetCommandId(CommandId curcid);
+extern Snapshot GetCatalogSnapshot(Oid relid);
+extern void InvalidateCatalogSnapshot(void);
+
extern void PushActiveSnapshot(Snapshot snapshot);
extern void PushCopiedSnapshot(Snapshot snapshot);
extern void UpdateActiveSnapshotCommandId(void);
extern struct catclist *SearchSysCacheList(int cacheId, int nkeys,
Datum key1, Datum key2, Datum key3, Datum key4);
+extern bool RelationInvalidatesSnapshotsOnly(Oid);
+extern bool RelationHasSysCache(Oid);
+
/*
* The use of the macros below rather than direct calls to the corresponding
* functions is encouraged, as it insulates the caller from changes in the