diff options
Diffstat (limited to 'src/backend/utils')
-rw-r--r-- | src/backend/utils/adt/ri_triggers.c | 23 | ||||
-rw-r--r-- | src/backend/utils/adt/ruleutils.c | 28 | ||||
-rw-r--r-- | src/backend/utils/cache/relcache.c | 31 | ||||
-rw-r--r-- | src/backend/utils/time/combocid.c | 5 | ||||
-rw-r--r-- | src/backend/utils/time/tqual.c | 466 |
5 files changed, 441 insertions, 112 deletions
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c index 601d5ec861f..243bdebbd22 100644 --- a/src/backend/utils/adt/ri_triggers.c +++ b/src/backend/utils/adt/ri_triggers.c @@ -299,7 +299,7 @@ RI_FKey_check(TriggerData *trigdata) * Get the relation descriptors of the FK and PK tables. * * pk_rel is opened in RowShareLock mode since that's what our eventual - * SELECT FOR SHARE will get on it. + * SELECT FOR KEY SHARE will get on it. */ fk_rel = trigdata->tg_relation; pk_rel = heap_open(riinfo->pk_relid, RowShareLock); @@ -400,7 +400,8 @@ RI_FKey_check(TriggerData *trigdata) /* ---------- * The query string built is - * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE + * SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...] + * FOR KEY SHARE OF x * The type id's for the $ parameters are those of the * corresponding FK attributes. * ---------- @@ -424,7 +425,7 @@ RI_FKey_check(TriggerData *trigdata) querysep = "AND"; queryoids[i] = fk_type; } - appendStringInfo(&querybuf, " FOR SHARE OF x"); + appendStringInfo(&querybuf, " FOR KEY SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, @@ -535,7 +536,8 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, /* ---------- * The query string built is - * SELECT 1 FROM ONLY <pktable> WHERE pkatt1 = $1 [AND ...] FOR SHARE + * SELECT 1 FROM ONLY <pktable> x WHERE pkatt1 = $1 [AND ...] + * FOR KEY SHARE OF x * The type id's for the $ parameters are those of the * PK attributes themselves. * ---------- @@ -558,7 +560,7 @@ ri_Check_Pk_Match(Relation pk_rel, Relation fk_rel, querysep = "AND"; queryoids[i] = pk_type; } - appendStringInfo(&querybuf, " FOR SHARE OF x"); + appendStringInfo(&querybuf, " FOR KEY SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, @@ -655,7 +657,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action) * Get the relation descriptors of the FK and PK tables and the old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual - * SELECT FOR SHARE will get on it. + * SELECT FOR KEY SHARE will get on it. */ fk_rel = heap_open(riinfo->fk_relid, RowShareLock); pk_rel = trigdata->tg_relation; @@ -724,7 +726,8 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action) /* ---------- * The query string built is - * SELECT 1 FROM ONLY <fktable> WHERE $1 = fkatt1 [AND ...] + * SELECT 1 FROM ONLY <fktable> x WHERE $1 = fkatt1 [AND ...] + * FOR KEY SHARE OF x * The type id's for the $ parameters are those of the * corresponding PK attributes. * ---------- @@ -749,7 +752,7 @@ ri_restrict_del(TriggerData *trigdata, bool is_no_action) querysep = "AND"; queryoids[i] = pk_type; } - appendStringInfo(&querybuf, " FOR SHARE OF x"); + appendStringInfo(&querybuf, " FOR KEY SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, @@ -868,7 +871,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action) * old tuple. * * fk_rel is opened in RowShareLock mode since that's what our eventual - * SELECT FOR SHARE will get on it. + * SELECT FOR KEY SHARE will get on it. */ fk_rel = heap_open(riinfo->fk_relid, RowShareLock); pk_rel = trigdata->tg_relation; @@ -972,7 +975,7 @@ ri_restrict_upd(TriggerData *trigdata, bool is_no_action) querysep = "AND"; queryoids[i] = pk_type; } - appendStringInfo(&querybuf, " FOR SHARE OF x"); + appendStringInfo(&querybuf, " FOR KEY SHARE OF x"); /* Prepare and save the plan */ qplan = ri_PlanCheck(querybuf.data, riinfo->nkeys, queryoids, diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index af104715817..16f56c6adec 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -4194,7 +4194,7 @@ get_select_query_def(Query *query, deparse_context *context, get_rule_expr(query->limitCount, context, false); } - /* Add FOR UPDATE/SHARE clauses if present */ + /* Add FOR [KEY] UPDATE/SHARE clauses if present */ if (query->hasForUpdate) { foreach(l, query->rowMarks) @@ -4205,12 +4205,26 @@ get_select_query_def(Query *query, deparse_context *context, if (rc->pushedDown) continue; - if (rc->forUpdate) - appendContextKeyword(context, " FOR UPDATE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); - else - appendContextKeyword(context, " FOR SHARE", - -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + switch (rc->strength) + { + case LCS_FORKEYSHARE: + appendContextKeyword(context, " FOR KEY SHARE", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + break; + case LCS_FORSHARE: + appendContextKeyword(context, " FOR SHARE", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + break; + case LCS_FORNOKEYUPDATE: + appendContextKeyword(context, " FOR NO KEY UPDATE", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + break; + case LCS_FORUPDATE: + appendContextKeyword(context, " FOR UPDATE", + -PRETTYINDENT_STD, PRETTYINDENT_STD, 0); + break; + } + appendStringInfo(buf, " OF %s", quote_identifier(get_rtable_name(rc->rti, context))); diff --git a/src/backend/utils/cache/relcache.c b/src/backend/utils/cache/relcache.c index 40238e959e6..fa48b1ce1ab 100644 --- a/src/backend/utils/cache/relcache.c +++ b/src/backend/utils/cache/relcache.c @@ -30,10 +30,11 @@ #include <fcntl.h> #include <unistd.h> +#include "access/htup_details.h" +#include "access/multixact.h" #include "access/reloptions.h" #include "access/sysattr.h" #include "access/transam.h" -#include "access/htup_details.h" #include "access/xact.h" #include "catalog/catalog.h" #include "catalog/index.h" @@ -2725,7 +2726,8 @@ RelationBuildLocalRelation(const char *relname, * the XIDs that will be put into the new relation contents. */ void -RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid) +RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid, + MultiXactId minmulti) { Oid newrelfilenode; RelFileNodeBackend newrnode; @@ -2738,6 +2740,7 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid) relation->rd_rel->relkind == RELKIND_SEQUENCE) ? freezeXid == InvalidTransactionId : TransactionIdIsNormal(freezeXid)); + Assert(TransactionIdIsNormal(freezeXid) == MultiXactIdIsValid(minmulti)); /* Allocate a new relfilenode */ newrelfilenode = GetNewRelFileNode(relation->rd_rel->reltablespace, NULL, @@ -2793,6 +2796,7 @@ RelationSetNewRelfilenode(Relation relation, TransactionId freezeXid) classform->relallvisible = 0; } classform->relfrozenxid = freezeXid; + classform->relminmxid = minmulti; simple_heap_update(pg_class, &tuple->t_self, tuple); CatalogUpdateIndexes(pg_class, tuple); @@ -3764,6 +3768,9 @@ RelationGetIndexPredicate(Relation relation) * simple index keys, but attributes used in expressions and partial-index * predicates.) * + * If "keyAttrs" is true, only attributes that can be referenced by foreign + * keys are considered. + * * Attribute numbers are offset by FirstLowInvalidHeapAttributeNumber so that * we can include system attributes (e.g., OID) in the bitmap representation. * @@ -3775,16 +3782,17 @@ RelationGetIndexPredicate(Relation relation) * be bms_free'd when not needed anymore. */ Bitmapset * -RelationGetIndexAttrBitmap(Relation relation) +RelationGetIndexAttrBitmap(Relation relation, bool keyAttrs) { Bitmapset *indexattrs; + Bitmapset *uindexattrs; List *indexoidlist; ListCell *l; MemoryContext oldcxt; /* Quick exit if we already computed the result. */ if (relation->rd_indexattr != NULL) - return bms_copy(relation->rd_indexattr); + return bms_copy(keyAttrs ? relation->rd_keyattr : relation->rd_indexattr); /* Fast path if definitely no indexes */ if (!RelationGetForm(relation)->relhasindex) @@ -3810,26 +3818,38 @@ RelationGetIndexAttrBitmap(Relation relation) * won't be returned at all by RelationGetIndexList. */ indexattrs = NULL; + uindexattrs = NULL; foreach(l, indexoidlist) { Oid indexOid = lfirst_oid(l); Relation indexDesc; IndexInfo *indexInfo; int i; + bool isKey; indexDesc = index_open(indexOid, AccessShareLock); /* Extract index key information from the index's pg_index row */ indexInfo = BuildIndexInfo(indexDesc); + /* Can this index be referenced by a foreign key? */ + isKey = indexInfo->ii_Unique && + indexInfo->ii_Expressions == NIL && + indexInfo->ii_Predicate == NIL; + /* Collect simple attribute references */ for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++) { int attrnum = indexInfo->ii_KeyAttrNumbers[i]; if (attrnum != 0) + { indexattrs = bms_add_member(indexattrs, attrnum - FirstLowInvalidHeapAttributeNumber); + if (isKey) + uindexattrs = bms_add_member(uindexattrs, + attrnum - FirstLowInvalidHeapAttributeNumber); + } } /* Collect all attributes used in expressions, too */ @@ -3846,10 +3866,11 @@ RelationGetIndexAttrBitmap(Relation relation) /* Now save a copy of the bitmap in the relcache entry. */ oldcxt = MemoryContextSwitchTo(CacheMemoryContext); relation->rd_indexattr = bms_copy(indexattrs); + relation->rd_keyattr = bms_copy(uindexattrs); MemoryContextSwitchTo(oldcxt); /* We return our original working copy for caller to play with */ - return indexattrs; + return keyAttrs ? uindexattrs : indexattrs; } /* diff --git a/src/backend/utils/time/combocid.c b/src/backend/utils/time/combocid.c index 38f702892f7..923355d3ceb 100644 --- a/src/backend/utils/time/combocid.c +++ b/src/backend/utils/time/combocid.c @@ -118,9 +118,8 @@ HeapTupleHeaderGetCmax(HeapTupleHeader tup) { CommandId cid = HeapTupleHeaderGetRawCommandId(tup); - /* We do not store cmax when locking a tuple */ - Assert(!(tup->t_infomask & (HEAP_MOVED | HEAP_IS_LOCKED))); - Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tup))); + Assert(!(tup->t_infomask & HEAP_MOVED)); + Assert(TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetUpdateXid(tup))); if (tup->t_infomask & HEAP_COMBOCID) return GetRealCmax(cid); diff --git a/src/backend/utils/time/tqual.c b/src/backend/utils/time/tqual.c index 51f0afded98..f2c9ff2e1c1 100644 --- a/src/backend/utils/time/tqual.c +++ b/src/backend/utils/time/tqual.c @@ -214,12 +214,25 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -250,29 +263,41 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -282,7 +307,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -290,7 +315,7 @@ HeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; } @@ -380,12 +405,25 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } + + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -419,21 +457,38 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) + return true; /* deleted after scan started */ + else + return false; /* deleted before scan started */ + } + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false)) return true; /* deleted after scan started */ @@ -441,10 +496,10 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) return false; /* deleted before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -454,7 +509,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -462,7 +517,7 @@ HeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer) } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; } @@ -627,12 +682,30 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HeapTupleMayBeUpdated; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return HeapTupleMayBeUpdated; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HeapTupleMayBeUpdated; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return HeapTupleMayBeUpdated; + else + { + if (HeapTupleHeaderGetCmax(tuple) >= curcid) + return HeapTupleSelfUpdated; /* updated after scan started */ + else + return HeapTupleInvisible; /* updated before scan started */ + } + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -666,26 +739,62 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; return HeapTupleUpdated; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; - if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple))) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + { + /* + * If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK + * is set, it cannot possibly be running. Otherwise need to + * check. + */ + if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK | + HEAP_XMAX_KEYSHR_LOCK)) && + MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + return HeapTupleBeingUpdated; + + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HeapTupleMayBeUpdated; + } + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + { + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + return HeapTupleBeingUpdated; + + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HeapTupleMayBeUpdated; + } + + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= curcid) + return HeapTupleSelfUpdated; /* updated after scan started */ + else + return HeapTupleInvisible; /* updated before scan started */ + } + + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; - SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, - InvalidTransactionId); + + if (TransactionIdDidCommit(xmax)) + return HeapTupleUpdated; + /* it must have aborted or crashed */ + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); return HeapTupleMayBeUpdated; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HeapTupleMayBeUpdated; if (HeapTupleHeaderGetCmax(tuple) >= curcid) return HeapTupleSelfUpdated; /* updated after scan started */ @@ -693,10 +802,10 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, return HeapTupleInvisible; /* updated before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HeapTupleBeingUpdated; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -706,7 +815,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -714,7 +823,7 @@ HeapTupleSatisfiesUpdate(HeapTupleHeader tuple, CommandId curcid, } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return HeapTupleUpdated; /* updated by other */ } @@ -793,12 +902,25 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else + return false; + } + + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -833,32 +955,47 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_COMMITTED) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; /* updated by other */ } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return true; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + { + snapshot->xmax = xmax; + return true; + } + if (TransactionIdDidCommit(xmax)) + return false; return true; } - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; return false; } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) { - snapshot->xmax = HeapTupleHeaderGetXmax(tuple); + snapshot->xmax = HeapTupleHeaderGetRawXmax(tuple); return true; } - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -868,7 +1005,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, /* xmax transaction committed */ - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); @@ -876,7 +1013,7 @@ HeapTupleSatisfiesDirty(HeapTupleHeader tuple, Snapshot snapshot, } SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); return false; /* updated by other */ } @@ -957,12 +1094,27 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) /* not deleter */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) /* not deleter */ return true; - Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)); + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) + { + TransactionId xmax; + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + + /* updating subtransaction must have aborted */ + if (!TransactionIdIsCurrentTransactionId(xmax)) + return true; + else if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) + return true; /* updated after scan started */ + else + return false; /* updated before scan started */ + } - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { /* deleting subtransaction must have aborted */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -999,19 +1151,41 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid or aborted */ return true; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return true; if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); + TransactionId xmax; + + /* already checked above */ + Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return true; + if (TransactionIdIsCurrentTransactionId(xmax)) + { + if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) + return true; /* deleted after scan started */ + else + return false; /* deleted before scan started */ + } + if (TransactionIdIsInProgress(xmax)) + return true; + if (TransactionIdDidCommit(xmax)) + { + /* updating transaction committed, but when? */ + if (XidInMVCCSnapshot(xmax, snapshot)) + return true; /* treat as still in progress */ + return false; + } return true; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetRawXmax(tuple))) { if (HeapTupleHeaderGetCmax(tuple) >= snapshot->curcid) return true; /* deleted after scan started */ @@ -1019,10 +1193,10 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, return false; /* deleted before scan started */ } - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return true; - if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + if (!TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) { /* it must have aborted or crashed */ SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, @@ -1032,13 +1206,13 @@ HeapTupleSatisfiesMVCC(HeapTupleHeader tuple, Snapshot snapshot, /* xmax transaction committed */ SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); } /* * OK, the deleting transaction committed too ... but when? */ - if (XidInMVCCSnapshot(HeapTupleHeaderGetXmax(tuple), snapshot)) + if (XidInMVCCSnapshot(HeapTupleHeaderGetRawXmax(tuple), snapshot)) return true; /* treat as still in progress */ return false; @@ -1112,7 +1286,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, { if (tuple->t_infomask & HEAP_XMAX_INVALID) /* xid invalid */ return HEAPTUPLE_INSERT_IN_PROGRESS; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) return HEAPTUPLE_INSERT_IN_PROGRESS; /* inserted and then deleted by same xact */ return HEAPTUPLE_DELETE_IN_PROGRESS; @@ -1144,7 +1318,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, if (tuple->t_infomask & HEAP_XMAX_INVALID) return HEAPTUPLE_LIVE; - if (tuple->t_infomask & HEAP_IS_LOCKED) + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) { /* * "Deleting" xact really only locked it, so the tuple is live in any @@ -1158,40 +1332,96 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, { if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - if (MultiXactIdIsRunning(HeapTupleHeaderGetXmax(tuple))) + /* + * If it's only locked but neither EXCL_LOCK nor KEYSHR_LOCK + * are set, it cannot possibly be running; otherwise have to + * check. + */ + if ((tuple->t_infomask & (HEAP_XMAX_EXCL_LOCK | + HEAP_XMAX_KEYSHR_LOCK)) && + MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_LIVE; + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + } else { - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_LIVE; + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, + InvalidTransactionId); } - - /* - * We don't really care whether xmax did commit, abort or crash. - * We know that xmax did lock the tuple, but it did not and will - * never actually update it. - */ - SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, - InvalidTransactionId); } + + /* + * We don't really care whether xmax did commit, abort or crash. + * We know that xmax did lock the tuple, but it did not and will + * never actually update it. + */ + return HEAPTUPLE_LIVE; } if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) { - /* MultiXacts are currently only allowed to lock tuples */ - Assert(tuple->t_infomask & HEAP_IS_LOCKED); - return HEAPTUPLE_LIVE; + TransactionId xmax; + + if (MultiXactIdIsRunning(HeapTupleHeaderGetRawXmax(tuple))) + { + /* already checked above */ + Assert(!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HEAPTUPLE_LIVE; + if (TransactionIdIsInProgress(xmax)) + return HEAPTUPLE_DELETE_IN_PROGRESS; + else if (TransactionIdDidCommit(xmax)) + /* there are still lockers around -- can't return DEAD here */ + return HEAPTUPLE_RECENTLY_DEAD; + /* updating transaction aborted */ + return HEAPTUPLE_LIVE; + } + + Assert(!(tuple->t_infomask & HEAP_XMAX_COMMITTED)); + + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) + return HEAPTUPLE_LIVE; + /* multi is not running -- updating xact cannot be */ + Assert(!TransactionIdIsInProgress(xmax)); + if (TransactionIdDidCommit(xmax)) + { + if (!TransactionIdPrecedes(xmax, OldestXmin)) + return HEAPTUPLE_RECENTLY_DEAD; + else + return HEAPTUPLE_DEAD; + } + else + { + /* + * Not in Progress, Not Committed, so either Aborted or crashed. + */ + SetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); + return HEAPTUPLE_LIVE; + } + + /* + * Deleter committed, but perhaps it was recent enough that some open + * transactions could still see the tuple. + */ + + /* Otherwise, it's dead and removable */ + return HEAPTUPLE_DEAD; } if (!(tuple->t_infomask & HEAP_XMAX_COMMITTED)) { - if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple))) + if (TransactionIdIsInProgress(HeapTupleHeaderGetRawXmax(tuple))) return HEAPTUPLE_DELETE_IN_PROGRESS; - else if (TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple))) + else if (TransactionIdDidCommit(HeapTupleHeaderGetRawXmax(tuple))) SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, - HeapTupleHeaderGetXmax(tuple)); + HeapTupleHeaderGetRawXmax(tuple)); else { /* @@ -1213,7 +1443,7 @@ HeapTupleSatisfiesVacuum(HeapTupleHeader tuple, TransactionId OldestXmin, * Deleter committed, but perhaps it was recent enough that some open * transactions could still see the tuple. */ - if (!TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin)) + if (!TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin)) return HEAPTUPLE_RECENTLY_DEAD; /* Otherwise, it's dead and removable */ @@ -1246,11 +1476,22 @@ HeapTupleIsSurelyDead(HeapTupleHeader tuple, TransactionId OldestXmin) /* * If the inserting transaction committed, but any deleting transaction - * aborted, the tuple is still alive. Likewise, if XMAX is a lock rather - * than a delete, the tuple is still alive. + * aborted, the tuple is still alive. */ - if (tuple->t_infomask & - (HEAP_XMAX_INVALID | HEAP_IS_LOCKED | HEAP_XMAX_IS_MULTI)) + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return false; + + /* + * If the XMAX is just a lock, the tuple is still alive. + */ + if (HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_infomask)) + return false; + + /* + * If the Xmax is a MultiXact, it might be dead or alive, but we cannot + * know without checking pg_multixact. + */ + if (tuple->t_infomask & HEAP_XMAX_IS_MULTI) return false; /* If deleter isn't known to have committed, assume it's still running. */ @@ -1258,7 +1499,7 @@ HeapTupleIsSurelyDead(HeapTupleHeader tuple, TransactionId OldestXmin) return false; /* Deleter committed, so tuple is dead if the XID is old enough. */ - return TransactionIdPrecedes(HeapTupleHeaderGetXmax(tuple), OldestXmin); + return TransactionIdPrecedes(HeapTupleHeaderGetRawXmax(tuple), OldestXmin); } /* @@ -1375,3 +1616,54 @@ XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot) return false; } + +/* + * Is the tuple really only locked? That is, is it not updated? + * + * It's easy to check just infomask bits if the locker is not a multi; but + * otherwise we need to verify that the updating transaction has not aborted. + * + * This function is here because it follows the same time qualification rules + * laid out at the top of this file. + */ +bool +HeapTupleHeaderIsOnlyLocked(HeapTupleHeader tuple) +{ + TransactionId xmax; + + /* if there's no valid Xmax, then there's obviously no update either */ + if (tuple->t_infomask & HEAP_XMAX_INVALID) + return true; + + if (tuple->t_infomask & HEAP_XMAX_LOCK_ONLY) + return true; + + /* invalid xmax means no update */ + if (!TransactionIdIsValid(HeapTupleHeaderGetRawXmax(tuple))) + return true; + + /* + * if HEAP_XMAX_LOCK_ONLY is not set and not a multi, then this + * must necessarily have been updated + */ + if (!(tuple->t_infomask & HEAP_XMAX_IS_MULTI)) + return false; + + /* ... but if it's a multi, then perhaps the updating Xid aborted. */ + xmax = HeapTupleGetUpdateXid(tuple); + if (!TransactionIdIsValid(xmax)) /* shouldn't happen .. */ + return true; + + if (TransactionIdIsCurrentTransactionId(xmax)) + return false; + if (TransactionIdIsInProgress(xmax)) + return false; + if (TransactionIdDidCommit(xmax)) + return false; + + /* + * not current, not in progress, not committed -- must have aborted or + * crashed + */ + return true; +} |