From b80931c9e7c73daf109199c1dfecf636e21a26c7 Mon Sep 17 00:00:00 2001 From: Hari Babu Date: Wed, 3 Jan 2018 17:21:07 +1100 Subject: [PATCH 06/12] Tuple Insert API is added to Storage AM heap_insert, heap_delete, heap_fetch, heap_update, heap_get_latest_oid, heap_lock_tuple and heap_multi_insert functions are added to storage AM. Move the index insertion logic into storage AM, The Index insert still outside for the case of multi_insert (Yet to change). In case of delete also, the index tuple delete function pointer is avaiable. Replaced the usage of HeapTuple with storageTuple in some places, increased the use of slot. --- src/backend/access/common/heaptuple.c | 24 +++ src/backend/access/heap/heapam.c | 125 +++++++++------- src/backend/access/heap/heapam_storage.c | 235 +++++++++++++++++++++++++++++ src/backend/access/heap/rewriteheap.c | 5 +- src/backend/access/heap/tuptoaster.c | 9 +- src/backend/access/storage/storageam.c | 117 +++++++++++++++ src/backend/commands/copy.c | 39 ++--- src/backend/commands/createas.c | 24 +-- src/backend/commands/matview.c | 22 +-- src/backend/commands/tablecmds.c | 6 +- src/backend/commands/trigger.c | 50 ++++--- src/backend/executor/execIndexing.c | 2 +- src/backend/executor/execMain.c | 131 ++++++++--------- src/backend/executor/execReplication.c | 72 ++++----- src/backend/executor/nodeLockRows.c | 47 +++--- src/backend/executor/nodeModifyTable.c | 245 ++++++++++++++----------------- src/backend/executor/nodeTidscan.c | 23 ++- src/backend/utils/adt/tid.c | 5 +- src/include/access/heapam.h | 14 +- src/include/access/htup_details.h | 1 + src/include/access/storage_common.h | 3 - src/include/access/storageam.h | 84 +++++++++++ src/include/access/storageamapi.h | 73 ++++++++- src/include/commands/trigger.h | 2 +- src/include/executor/executor.h | 15 +- src/include/executor/tuptable.h | 1 + src/include/nodes/execnodes.h | 8 +- 27 files changed, 957 insertions(+), 425 deletions(-) create mode 100644 src/include/access/storageam.h diff --git a/src/backend/access/common/heaptuple.c b/src/backend/access/common/heaptuple.c index a50a76dc76..627cf6fc64 100644 --- a/src/backend/access/common/heaptuple.c +++ b/src/backend/access/common/heaptuple.c @@ -685,6 +685,30 @@ heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc) return PointerGetDatum(td); } +/* + * heap_form_tuple_by_datum + * construct a tuple from the given dataum + * + * The result is allocated in the current memory context. + */ +HeapTuple +heap_form_tuple_by_datum(Datum data, Oid tableoid) +{ + HeapTuple newTuple; + HeapTupleHeader td; + + td = DatumGetHeapTupleHeader(data); + + newTuple = (HeapTuple) palloc(HEAPTUPLESIZE + HeapTupleHeaderGetDatumLength(td)); + newTuple->t_len = HeapTupleHeaderGetDatumLength(td); + newTuple->t_self = td->t_ctid; + newTuple->t_tableOid = tableoid; + newTuple->t_data = (HeapTupleHeader) ((char *) newTuple + HEAPTUPLESIZE); + memcpy((char *) newTuple->t_data, (char *) td, newTuple->t_len); + + return newTuple; +} + /* * heap_form_tuple * construct a tuple from the given values[] and isnull[] arrays, diff --git a/src/backend/access/heap/heapam.c b/src/backend/access/heap/heapam.c index bc092056f8..c1da192f14 100644 --- a/src/backend/access/heap/heapam.c +++ b/src/backend/access/heap/heapam.c @@ -1893,13 +1893,13 @@ heap_getnext(HeapScanDesc scan, ScanDirection direction) */ bool heap_fetch(Relation relation, + ItemPointer tid, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation) { - ItemPointer tid = &(tuple->t_self); ItemId lp; Buffer buffer; Page page; @@ -1933,7 +1933,6 @@ heap_fetch(Relation relation, ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } - tuple->t_data = NULL; return false; } @@ -1955,13 +1954,13 @@ heap_fetch(Relation relation, ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } - tuple->t_data = NULL; return false; } /* - * fill in *tuple fields + * fill in tuple fields and place it in stuple */ + ItemPointerCopy(tid, &(tuple->t_self)); tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); tuple->t_len = ItemIdGetLength(lp); tuple->t_tableOid = RelationGetRelid(relation); @@ -2312,7 +2311,6 @@ heap_get_latest_tid(Relation relation, } /* end of loop */ } - /* * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends * @@ -4576,13 +4574,12 @@ get_mxact_status_for_lock(LockTupleMode mode, bool is_update) * See README.tuplock for a thorough explanation of this mechanism. */ HTSU_Result -heap_lock_tuple(Relation relation, HeapTuple tuple, +heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd) { HTSU_Result result; - ItemPointer tid = &(tuple->t_self); ItemId lp; Page page; Buffer vmbuffer = InvalidBuffer; @@ -4595,6 +4592,9 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, bool first_time = true; bool have_tuple_lock = false; bool cleared_all_frozen = false; + HeapTupleData tuple; + + Assert(stuple != NULL); *buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); block = ItemPointerGetBlockNumber(tid); @@ -4614,12 +4614,13 @@ heap_lock_tuple(Relation relation, HeapTuple tuple, lp = PageGetItemId(page, ItemPointerGetOffsetNumber(tid)); Assert(ItemIdIsNormal(lp)); - tuple->t_data = (HeapTupleHeader) PageGetItem(page, lp); - tuple->t_len = ItemIdGetLength(lp); - tuple->t_tableOid = RelationGetRelid(relation); + tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp); + tuple.t_len = ItemIdGetLength(lp); + tuple.t_tableOid = RelationGetRelid(relation); + ItemPointerCopy(tid, &tuple.t_self); l3: - result = relation->rd_stamroutine->snapshot_satisfiesUpdate(tuple, cid, *buffer); + result = HeapTupleSatisfiesUpdate(&tuple, cid, *buffer); if (result == HeapTupleInvisible) { @@ -4641,10 +4642,10 @@ l3: ItemPointerData t_ctid; /* must copy state data before unlocking buffer */ - xwait = HeapTupleHeaderGetRawXmax(tuple->t_data); - infomask = tuple->t_data->t_infomask; - infomask2 = tuple->t_data->t_infomask2; - ItemPointerCopy(&tuple->t_data->t_ctid, &t_ctid); + xwait = HeapTupleHeaderGetRawXmax(tuple.t_data); + infomask = tuple.t_data->t_infomask; + infomask2 = tuple.t_data->t_infomask2; + ItemPointerCopy(&tuple.t_data->t_ctid, &t_ctid); LockBuffer(*buffer, BUFFER_LOCK_UNLOCK); @@ -4776,7 +4777,7 @@ l3: { HTSU_Result res; - res = heap_lock_updated_tuple(relation, tuple, &t_ctid, + res = heap_lock_updated_tuple(relation, &tuple, &t_ctid, GetCurrentTransactionId(), mode); if (res != HeapTupleMayBeUpdated) @@ -4797,8 +4798,8 @@ l3: * now need to follow the update chain to lock the new * versions. */ - if (!HeapTupleHeaderIsOnlyLocked(tuple->t_data) && - ((tuple->t_data->t_infomask2 & HEAP_KEYS_UPDATED) || + if (!HeapTupleHeaderIsOnlyLocked(tuple.t_data) && + ((tuple.t_data->t_infomask2 & HEAP_KEYS_UPDATED) || !updated)) goto l3; @@ -4829,8 +4830,8 @@ l3: * Make sure it's still an appropriate lock, else start over. * See above about allowing xmax to change. */ - if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) || - HEAP_XMAX_IS_EXCL_LOCKED(tuple->t_data->t_infomask)) + if (!HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) || + HEAP_XMAX_IS_EXCL_LOCKED(tuple.t_data->t_infomask)) goto l3; require_sleep = false; } @@ -4852,8 +4853,8 @@ l3: * meantime, start over. */ LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); - if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) || - !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data), + if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) || + !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data), xwait)) goto l3; @@ -4866,9 +4867,9 @@ l3: LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); /* if the xmax changed in the meantime, start over */ - if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) || + if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) || !TransactionIdEquals( - HeapTupleHeaderGetRawXmax(tuple->t_data), + HeapTupleHeaderGetRawXmax(tuple.t_data), xwait)) goto l3; /* otherwise, we're good */ @@ -4893,11 +4894,11 @@ l3: { /* ... but if the xmax changed in the meantime, start over */ LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE); - if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) || - !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data), + if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) || + !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data), xwait)) goto l3; - Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask)); + Assert(HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask)); require_sleep = false; } @@ -4954,7 +4955,7 @@ l3: { case LockWaitBlock: MultiXactIdWait((MultiXactId) xwait, status, infomask, - relation, &tuple->t_self, XLTW_Lock, NULL); + relation, &tuple.t_self, XLTW_Lock, NULL); break; case LockWaitSkip: if (!ConditionalMultiXactIdWait((MultiXactId) xwait, @@ -4995,7 +4996,7 @@ l3: switch (wait_policy) { case LockWaitBlock: - XactLockTableWait(xwait, relation, &tuple->t_self, + XactLockTableWait(xwait, relation, &tuple.t_self, XLTW_Lock); break; case LockWaitSkip: @@ -5022,7 +5023,7 @@ l3: { HTSU_Result res; - res = heap_lock_updated_tuple(relation, tuple, &t_ctid, + res = heap_lock_updated_tuple(relation, &tuple, &t_ctid, GetCurrentTransactionId(), mode); if (res != HeapTupleMayBeUpdated) @@ -5041,8 +5042,8 @@ l3: * other xact could update this tuple before we get to this point. * Check for xmax change, and start over if so. */ - if (xmax_infomask_changed(tuple->t_data->t_infomask, infomask) || - !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple->t_data), + if (xmax_infomask_changed(tuple.t_data->t_infomask, infomask) || + !TransactionIdEquals(HeapTupleHeaderGetRawXmax(tuple.t_data), xwait)) goto l3; @@ -5056,7 +5057,7 @@ l3: * don't check for this in the multixact case, because some * locker transactions might still be running. */ - UpdateXmaxHintBits(tuple->t_data, *buffer, xwait); + UpdateXmaxHintBits(tuple.t_data, *buffer, xwait); } } @@ -5068,9 +5069,9 @@ l3: * at all for whatever reason. */ if (!require_sleep || - (tuple->t_data->t_infomask & HEAP_XMAX_INVALID) || - HEAP_XMAX_IS_LOCKED_ONLY(tuple->t_data->t_infomask) || - HeapTupleHeaderIsOnlyLocked(tuple->t_data)) + (tuple.t_data->t_infomask & HEAP_XMAX_INVALID) || + HEAP_XMAX_IS_LOCKED_ONLY(tuple.t_data->t_infomask) || + HeapTupleHeaderIsOnlyLocked(tuple.t_data)) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; @@ -5081,11 +5082,11 @@ failed: { Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated || result == HeapTupleWouldBlock); - Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVALID)); - hufd->ctid = tuple->t_data->t_ctid; - hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple->t_data); + Assert(!(tuple.t_data->t_infomask & HEAP_XMAX_INVALID)); + hufd->ctid = tuple.t_data->t_ctid; + hufd->xmax = HeapTupleHeaderGetUpdateXid(tuple.t_data); if (result == HeapTupleSelfUpdated) - hufd->cmax = HeapTupleHeaderGetCmax(tuple->t_data); + hufd->cmax = HeapTupleHeaderGetCmax(tuple.t_data); else hufd->cmax = InvalidCommandId; goto out_locked; @@ -5108,8 +5109,8 @@ failed: goto l3; } - xmax = HeapTupleHeaderGetRawXmax(tuple->t_data); - old_infomask = tuple->t_data->t_infomask; + xmax = HeapTupleHeaderGetRawXmax(tuple.t_data); + old_infomask = tuple.t_data->t_infomask; /* * If this is the first possibly-multixact-able operation in the current @@ -5126,7 +5127,7 @@ failed: * not modify the tuple just yet, because that would leave it in the wrong * state if multixact.c elogs. */ - compute_new_xmax_infomask(xmax, old_infomask, tuple->t_data->t_infomask2, + compute_new_xmax_infomask(xmax, old_infomask, tuple.t_data->t_infomask2, GetCurrentTransactionId(), mode, false, &xid, &new_infomask, &new_infomask2); @@ -5142,13 +5143,13 @@ failed: * Also reset the HOT UPDATE bit, but only if there's no update; otherwise * we would break the HOT chain. */ - tuple->t_data->t_infomask &= ~HEAP_XMAX_BITS; - tuple->t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED; - tuple->t_data->t_infomask |= new_infomask; - tuple->t_data->t_infomask2 |= new_infomask2; + tuple.t_data->t_infomask &= ~HEAP_XMAX_BITS; + tuple.t_data->t_infomask2 &= ~HEAP_KEYS_UPDATED; + tuple.t_data->t_infomask |= new_infomask; + tuple.t_data->t_infomask2 |= new_infomask2; if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask)) - HeapTupleHeaderClearHotUpdated(tuple->t_data); - HeapTupleHeaderSetXmax(tuple->t_data, xid); + HeapTupleHeaderClearHotUpdated(tuple.t_data); + HeapTupleHeaderSetXmax(tuple.t_data, xid); /* * Make sure there is no forward chain link in t_ctid. Note that in the @@ -5158,7 +5159,7 @@ failed: * the tuple as well. */ if (HEAP_XMAX_IS_LOCKED_ONLY(new_infomask)) - tuple->t_data->t_ctid = *tid; + tuple.t_data->t_ctid = *tid; /* Clear only the all-frozen bit on visibility map if needed */ if (PageIsAllVisible(page) && @@ -5189,10 +5190,10 @@ failed: XLogBeginInsert(); XLogRegisterBuffer(0, *buffer, REGBUF_STANDARD); - xlrec.offnum = ItemPointerGetOffsetNumber(&tuple->t_self); + xlrec.offnum = ItemPointerGetOffsetNumber(&tuple.t_self); xlrec.locking_xid = xid; xlrec.infobits_set = compute_infobits(new_infomask, - tuple->t_data->t_infomask2); + tuple.t_data->t_infomask2); xlrec.flags = cleared_all_frozen ? XLH_LOCK_ALL_FROZEN_CLEARED : 0; XLogRegisterData((char *) &xlrec, SizeOfHeapLock); @@ -5226,6 +5227,7 @@ out_unlocked: if (have_tuple_lock) UnlockTupleTuplock(relation, tid, mode); + *stuple = heap_copytuple(&tuple); return result; } @@ -5683,9 +5685,8 @@ heap_lock_updated_tuple_rec(Relation rel, ItemPointer tid, TransactionId xid, new_infomask = 0; new_xmax = InvalidTransactionId; block = ItemPointerGetBlockNumber(&tupid); - ItemPointerCopy(&tupid, &(mytup.t_self)); - if (!heap_fetch(rel, SnapshotAny, &mytup, &buf, false, NULL)) + if (!heap_fetch(rel, &tupid, SnapshotAny, &mytup, &buf, false, NULL)) { /* * if we fail to find the updated version of the tuple, it's @@ -6032,14 +6033,18 @@ heap_lock_updated_tuple(Relation rel, HeapTuple tuple, ItemPointer ctid, * An explicit confirmation WAL record also makes logical decoding simpler. */ void -heap_finish_speculative(Relation relation, HeapTuple tuple) +heap_finish_speculative(Relation relation, TupleTableSlot *slot) { + HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage; + HeapTuple tuple = stuple->hst_heaptuple; Buffer buffer; Page page; OffsetNumber offnum; ItemId lp = NULL; HeapTupleHeader htup; + Assert(slot->tts_speculativeToken != 0); + buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&(tuple->t_self))); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); page = (Page) BufferGetPage(buffer); @@ -6094,6 +6099,7 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) END_CRIT_SECTION(); UnlockReleaseBuffer(buffer); + slot->tts_speculativeToken = 0; } /* @@ -6123,8 +6129,10 @@ heap_finish_speculative(Relation relation, HeapTuple tuple) * confirmation records. */ void -heap_abort_speculative(Relation relation, HeapTuple tuple) +heap_abort_speculative(Relation relation, TupleTableSlot *slot) { + HeapamTuple *stuple = (HeapamTuple *) slot->tts_storage; + HeapTuple tuple = stuple->hst_heaptuple; TransactionId xid = GetCurrentTransactionId(); ItemPointer tid = &(tuple->t_self); ItemId lp; @@ -6133,6 +6141,10 @@ heap_abort_speculative(Relation relation, HeapTuple tuple) BlockNumber block; Buffer buffer; + /* + * Assert(slot->tts_speculativeToken != 0); This needs some update in + * toast + */ Assert(ItemPointerIsValid(tid)); block = ItemPointerGetBlockNumber(tid); @@ -6246,6 +6258,7 @@ heap_abort_speculative(Relation relation, HeapTuple tuple) /* count deletion, as we counted the insertion too */ pgstat_count_heap_delete(relation); + slot->tts_speculativeToken = 0; } /* diff --git a/src/backend/access/heap/heapam_storage.c b/src/backend/access/heap/heapam_storage.c index a953a690b3..438063cbe1 100644 --- a/src/backend/access/heap/heapam_storage.c +++ b/src/backend/access/heap/heapam_storage.c @@ -23,8 +23,231 @@ #include "access/heapam.h" #include "access/storageamapi.h" #include "utils/builtins.h" +#include "utils/rel.h" +/* ---------------------------------------------------------------- + * storage AM support routines for heapam + * ---------------------------------------------------------------- + */ + +static bool +heapam_fetch(Relation relation, + ItemPointer tid, + Snapshot snapshot, + StorageTuple * stuple, + Buffer *userbuf, + bool keep_buf, + Relation stats_relation) +{ + HeapTupleData tuple; + + *stuple = NULL; + if (heap_fetch(relation, tid, snapshot, &tuple, userbuf, keep_buf, stats_relation)) + { + *stuple = heap_copytuple(&tuple); + return true; + } + + return false; +} + +/* + * Insert a heap tuple from a slot, which may contain an OID and speculative + * insertion token. + */ +static Oid +heapam_heap_insert(Relation relation, TupleTableSlot *slot, CommandId cid, + int options, BulkInsertState bistate, InsertIndexTuples IndexFunc, + EState *estate, List *arbiterIndexes, List **recheckIndexes) +{ + Oid oid; + HeapTuple tuple = NULL; + + if (slot->tts_storage) + { + HeapamTuple *htuple = slot->tts_storage; + + tuple = htuple->hst_heaptuple; + + if (relation->rd_rel->relhasoids) + HeapTupleSetOid(tuple, InvalidOid); + } + else + { + /* + * Obtain the physical tuple to insert, building from the slot values. + * XXX: maybe the slot already contains a physical tuple in the right + * format? In fact, if the slot isn't fully deformed, this is + * completely bogus ... + */ + tuple = heap_form_tuple(slot->tts_tupleDescriptor, + slot->tts_values, + slot->tts_isnull); + } + + /* Set the OID, if the slot has one */ + if (slot->tts_tupleOid != InvalidOid) + HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid); + + /* Update the tuple with table oid */ + if (slot->tts_tableOid != InvalidOid) + tuple->t_tableOid = slot->tts_tableOid; + + /* Set the speculative insertion token, if the slot has one */ + if ((options & HEAP_INSERT_SPECULATIVE) && slot->tts_speculativeToken) + HeapTupleHeaderSetSpeculativeToken(tuple->t_data, slot->tts_speculativeToken); + + /* Perform the insertion, and copy the resulting ItemPointer */ + oid = heap_insert(relation, tuple, cid, options, bistate); + ItemPointerCopy(&tuple->t_self, &slot->tts_tid); + + if (slot->tts_storage == NULL) + ExecStoreTuple(tuple, slot, InvalidBuffer, true); + + if ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0)) + { + Assert(IndexFunc != NULL); + + if (options & HEAP_INSERT_SPECULATIVE) + { + bool specConflict = false; + + *recheckIndexes = (IndexFunc) (slot, estate, true, + &specConflict, + arbiterIndexes); + + /* adjust the tuple's state accordingly */ + if (!specConflict) + heap_finish_speculative(relation, slot); + else + { + heap_abort_speculative(relation, slot); + slot->tts_specConflict = true; + } + } + else + { + *recheckIndexes = (IndexFunc) (slot, estate, false, + NULL, arbiterIndexes); + } + } + + return oid; +} + +static HTSU_Result +heapam_heap_delete(Relation relation, ItemPointer tid, CommandId cid, + Snapshot crosscheck, bool wait, DeleteIndexTuples IndexFunc, + HeapUpdateFailureData *hufd) +{ + /* + * Currently Deleting of index tuples are handled at vacuum, in case + * if the storage itself is cleaning the dead tuples by itself, it is + * the time to call the index tuple deletion also. + */ + return heap_delete(relation, tid, cid, crosscheck, wait, hufd); +} + + + +static HTSU_Result +heapam_heap_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, + EState *estate, CommandId cid, Snapshot crosscheck, + bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode, + InsertIndexTuples IndexFunc, List **recheckIndexes) +{ + HeapTuple tuple; + HTSU_Result result; + + if (slot->tts_storage) + { + HeapamTuple *htuple = slot->tts_storage; + + tuple = htuple->hst_heaptuple; + } + else + { + tuple = heap_form_tuple(slot->tts_tupleDescriptor, + slot->tts_values, + slot->tts_isnull); + } + + /* Set the OID, if the slot has one */ + if (slot->tts_tupleOid != InvalidOid) + HeapTupleHeaderSetOid(tuple->t_data, slot->tts_tupleOid); + + /* Update the tuple with table oid */ + if (slot->tts_tableOid != InvalidOid) + tuple->t_tableOid = slot->tts_tableOid; + + result = heap_update(relation, otid, tuple, cid, crosscheck, wait, + hufd, lockmode); + ItemPointerCopy(&tuple->t_self, &slot->tts_tid); + + if (slot->tts_storage == NULL) + ExecStoreTuple(tuple, slot, InvalidBuffer, true); + + /* + * Note: instead of having to update the old index tuples associated with + * the heap tuple, all we do is form and insert new index tuples. This is + * because UPDATEs are actually DELETEs and INSERTs, and index tuple + * deletion is done later by VACUUM (see notes in ExecDelete). All we do + * here is insert new index tuples. -cim 9/27/89 + */ + + /* + * insert index entries for tuple + * + * Note: heap_update returns the tid (location) of the new tuple in the + * t_self field. + * + * If it's a HOT update, we mustn't insert new index entries. + */ + if ((result == HeapTupleMayBeUpdated) && + ((estate != NULL) && (estate->es_result_relation_info->ri_NumIndices > 0)) && + (!HeapTupleIsHeapOnly(tuple))) + *recheckIndexes = (IndexFunc) (slot, estate, false, NULL, NIL); + + return result; +} + +static tuple_data +heapam_get_tuple_data(StorageTuple tuple, tuple_data_flags flags) +{ + tuple_data result; + + switch (flags) + { + case XMIN: + result.xid = HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data); + break; + case UPDATED_XID: + result.xid = HeapTupleHeaderGetUpdateXid(((HeapTuple) tuple)->t_data); + break; + case CMIN: + result.cid = HeapTupleHeaderGetCmin(((HeapTuple) tuple)->t_data); + break; + case TID: + result.tid = ((HeapTuple) tuple)->t_self; + break; + case CTID: + result.tid = ((HeapTuple) tuple)->t_data->t_ctid; + break; + default: + Assert(0); + break; + } + + return result; +} + +static StorageTuple +heapam_form_tuple_by_datum(Datum data, Oid tableoid) +{ + return heap_form_tuple_by_datum(data, tableoid); +} + Datum heapam_storage_handler(PG_FUNCTION_ARGS) { @@ -37,5 +260,17 @@ heapam_storage_handler(PG_FUNCTION_ARGS) amroutine->slot_storageam = heapam_storage_slot_handler; + amroutine->tuple_fetch = heapam_fetch; + amroutine->tuple_insert = heapam_heap_insert; + amroutine->tuple_delete = heapam_heap_delete; + amroutine->tuple_update = heapam_heap_update; + amroutine->tuple_lock = heap_lock_tuple; + amroutine->multi_insert = heap_multi_insert; + + amroutine->get_tuple_data = heapam_get_tuple_data; + amroutine->tuple_from_datum = heapam_form_tuple_by_datum; + amroutine->tuple_get_latest_tid = heap_get_latest_tid; + amroutine->relation_sync = heap_sync; + PG_RETURN_POINTER(amroutine); } diff --git a/src/backend/access/heap/rewriteheap.c b/src/backend/access/heap/rewriteheap.c index 7d466c2588..2e961a7694 100644 --- a/src/backend/access/heap/rewriteheap.c +++ b/src/backend/access/heap/rewriteheap.c @@ -110,6 +110,7 @@ #include "access/heapam.h" #include "access/heapam_xlog.h" #include "access/rewriteheap.h" +#include "access/storageam.h" #include "access/transam.h" #include "access/tuptoaster.h" #include "access/xact.h" @@ -126,13 +127,13 @@ #include "storage/bufmgr.h" #include "storage/fd.h" +#include "storage/procarray.h" #include "storage/smgr.h" #include "utils/memutils.h" #include "utils/rel.h" #include "utils/tqual.h" -#include "storage/procarray.h" /* * State associated with a rewrite operation. This is opaque to the user @@ -357,7 +358,7 @@ end_heap_rewrite(RewriteState state) * wrote before the checkpoint. */ if (RelationNeedsWAL(state->rs_new_rel)) - heap_sync(state->rs_new_rel); + storage_sync(state->rs_new_rel); logical_end_heap_rewrite(state); diff --git a/src/backend/access/heap/tuptoaster.c b/src/backend/access/heap/tuptoaster.c index 546f80f05c..34a06570f1 100644 --- a/src/backend/access/heap/tuptoaster.c +++ b/src/backend/access/heap/tuptoaster.c @@ -32,6 +32,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "access/storageam.h" #include "access/tuptoaster.h" #include "access/xact.h" #include "catalog/catalog.h" @@ -1777,7 +1778,13 @@ toast_delete_datum(Relation rel, Datum value, bool is_speculative) * Have a chunk, delete it */ if (is_speculative) - heap_abort_speculative(toastrel, toasttup); + { + TupleTableSlot *slot = MakeSingleTupleTableSlot(RelationGetDescr(toastrel)); + + ExecStoreTuple(toasttup, slot, InvalidBuffer, false); + heap_abort_speculative(toastrel, slot); + ExecDropSingleTupleTableSlot(slot); + } else simple_heap_delete(toastrel, &toasttup->t_self); } diff --git a/src/backend/access/storage/storageam.c b/src/backend/access/storage/storageam.c index 8541c75782..a8b971d004 100644 --- a/src/backend/access/storage/storageam.c +++ b/src/backend/access/storage/storageam.c @@ -13,3 +13,120 @@ *------------------------------------------------------------------------- */ #include "postgres.h" + +#include "access/storageam.h" +#include "access/storageamapi.h" +#include "utils/rel.h" + +/* + * storage_fetch - retrieve tuple with given tid + */ +bool +storage_fetch(Relation relation, + ItemPointer tid, + Snapshot snapshot, + StorageTuple * stuple, + Buffer *userbuf, + bool keep_buf, + Relation stats_relation) +{ + return relation->rd_stamroutine->tuple_fetch(relation, tid, snapshot, stuple, + userbuf, keep_buf, stats_relation); +} + + +/* + * storage_lock_tuple - lock a tuple in shared or exclusive mode + */ +HTSU_Result +storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple, + CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, + bool follow_updates, Buffer *buffer, HeapUpdateFailureData *hufd) +{ + return relation->rd_stamroutine->tuple_lock(relation, tid, stuple, + cid, mode, wait_policy, + follow_updates, buffer, hufd); +} + +/* + * Insert a tuple from a slot into storage AM routine + */ +Oid +storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid, + int options, BulkInsertState bistate, InsertIndexTuples IndexFunc, + EState *estate, List *arbiterIndexes, List **recheckIndexes) +{ + return relation->rd_stamroutine->tuple_insert(relation, slot, cid, options, + bistate, IndexFunc, estate, + arbiterIndexes, recheckIndexes); +} + +/* + * Delete a tuple from tid using storage AM routine + */ +HTSU_Result +storage_delete(Relation relation, ItemPointer tid, CommandId cid, + Snapshot crosscheck, bool wait, DeleteIndexTuples IndexFunc, + HeapUpdateFailureData *hufd) +{ + return relation->rd_stamroutine->tuple_delete(relation, tid, cid, + crosscheck, wait, IndexFunc, hufd); +} + +/* + * update a tuple from tid using storage AM routine + */ +HTSU_Result +storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, + EState *estate, CommandId cid, Snapshot crosscheck, bool wait, + HeapUpdateFailureData *hufd, LockTupleMode *lockmode, + InsertIndexTuples IndexFunc, List **recheckIndexes) +{ + return relation->rd_stamroutine->tuple_update(relation, otid, slot, estate, + cid, crosscheck, wait, hufd, + lockmode, IndexFunc, recheckIndexes); +} + + +/* + * storage_multi_insert - insert multiple tuple into a storage + */ +void +storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples, + CommandId cid, int options, BulkInsertState bistate) +{ + relation->rd_stamroutine->multi_insert(relation, tuples, ntuples, + cid, options, bistate); +} + +tuple_data +storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags) +{ + return relation->rd_stamroutine->get_tuple_data(tuple, flags); +} + +StorageTuple +storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid) +{ + if (relation) + return relation->rd_stamroutine->tuple_from_datum(data, tableoid); + else + return heap_form_tuple_by_datum(data, tableoid); +} + +void +storage_get_latest_tid(Relation relation, + Snapshot snapshot, + ItemPointer tid) +{ + relation->rd_stamroutine->tuple_get_latest_tid(relation, snapshot, tid); +} + +/* + * storage_sync - sync a heap, for use when no WAL has been written + */ +void +storage_sync(Relation rel) +{ + rel->rd_stamroutine->relation_sync(rel); +} diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index f44566591a..2dfc6e0a20 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -20,6 +20,7 @@ #include "access/heapam.h" #include "access/htup_details.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "access/xact.h" #include "access/xlog.h" @@ -2719,8 +2720,6 @@ CopyFrom(CopyState cstate) if (slot == NULL) /* "do nothing" */ skip_tuple = true; - else /* trigger might have changed tuple */ - tuple = ExecHeapifySlot(slot); } if (!skip_tuple) @@ -2783,19 +2782,11 @@ CopyFrom(CopyState cstate) List *recheckIndexes = NIL; /* OK, store the tuple and create index entries for it */ - heap_insert(resultRelInfo->ri_RelationDesc, tuple, mycid, - hi_options, bistate); - - if (resultRelInfo->ri_NumIndices > 0) - recheckIndexes = ExecInsertIndexTuples(slot, - &(tuple->t_self), - estate, - false, - NULL, - NIL); + storage_insert(resultRelInfo->ri_RelationDesc, slot, mycid, hi_options, + bistate, ExecInsertIndexTuples, estate, NIL, &recheckIndexes); /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, + ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes, cstate->transition_capture); list_free(recheckIndexes); @@ -2891,7 +2882,7 @@ CopyFrom(CopyState cstate) * indexes since those use WAL anyway) */ if (hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(cstate->rel); + storage_sync(cstate->rel); return processed; } @@ -2924,12 +2915,12 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid, * before calling it. */ oldcontext = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); - heap_multi_insert(cstate->rel, - bufferedTuples, - nBufferedTuples, - mycid, - hi_options, - bistate); + storage_multi_insert(cstate->rel, + bufferedTuples, + nBufferedTuples, + mycid, + hi_options, + bistate); MemoryContextSwitchTo(oldcontext); /* @@ -2945,10 +2936,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid, cstate->cur_lineno = firstBufferedLineNo + i; ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false); recheckIndexes = - ExecInsertIndexTuples(myslot, &(bufferedTuples[i]->t_self), - estate, false, NULL, NIL); + ExecInsertIndexTuples(myslot, estate, false, NULL, NIL); ExecARInsertTriggers(estate, resultRelInfo, - bufferedTuples[i], + myslot, recheckIndexes, cstate->transition_capture); list_free(recheckIndexes); } @@ -2965,8 +2955,9 @@ CopyFromInsertBatch(CopyState cstate, EState *estate, CommandId mycid, for (i = 0; i < nBufferedTuples; i++) { cstate->cur_lineno = firstBufferedLineNo + i; + ExecStoreTuple(bufferedTuples[i], myslot, InvalidBuffer, false); ExecARInsertTriggers(estate, resultRelInfo, - bufferedTuples[i], + myslot, NIL, cstate->transition_capture); } } diff --git a/src/backend/commands/createas.c b/src/backend/commands/createas.c index ff2b7b75e9..f39551ac83 100644 --- a/src/backend/commands/createas.c +++ b/src/backend/commands/createas.c @@ -26,6 +26,7 @@ #include "access/reloptions.h" #include "access/htup_details.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "access/xact.h" #include "access/xlog.h" @@ -582,25 +583,28 @@ static bool intorel_receive(TupleTableSlot *slot, DestReceiver *self) { DR_intorel *myState = (DR_intorel *) self; - HeapTuple tuple; /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecHeapifySlot(slot); + ExecMaterializeSlot(slot); /* * force assignment of new OID (see comments in ExecInsert) */ if (myState->rel->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); - - heap_insert(myState->rel, - tuple, - myState->output_cid, - myState->hi_options, - myState->bistate); + slot->tts_tupleOid = InvalidOid; + + storage_insert(myState->rel, + slot, + myState->output_cid, + myState->hi_options, + myState->bistate, + NULL, + NULL, + NIL, + NULL); /* We know this is a newly created relation, so there are no indexes */ @@ -619,7 +623,7 @@ intorel_shutdown(DestReceiver *self) /* If we skipped using WAL, must heap_sync before commit */ if (myState->hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(myState->rel); + storage_sync(myState->rel); /* close rel, but keep lock until commit */ heap_close(myState->rel, NoLock); diff --git a/src/backend/commands/matview.c b/src/backend/commands/matview.c index 467695160a..b681f4ef5d 100644 --- a/src/backend/commands/matview.c +++ b/src/backend/commands/matview.c @@ -16,6 +16,7 @@ #include "access/htup_details.h" #include "access/multixact.h" +#include "access/storageam.h" #include "access/xact.h" #include "access/xlog.h" #include "catalog/catalog.h" @@ -491,19 +492,22 @@ static bool transientrel_receive(TupleTableSlot *slot, DestReceiver *self) { DR_transientrel *myState = (DR_transientrel *) self; - HeapTuple tuple; /* * get the heap tuple out of the tuple table slot, making sure we have a * writable copy */ - tuple = ExecHeapifySlot(slot); - - heap_insert(myState->transientrel, - tuple, - myState->output_cid, - myState->hi_options, - myState->bistate); + ExecMaterializeSlot(slot); + + storage_insert(myState->transientrel, + slot, + myState->output_cid, + myState->hi_options, + myState->bistate, + NULL, + NULL, + NIL, + NULL); /* We know this is a newly created relation, so there are no indexes */ @@ -522,7 +526,7 @@ transientrel_shutdown(DestReceiver *self) /* If we skipped using WAL, must heap_sync before commit */ if (myState->hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(myState->transientrel); + storage_sync(myState->transientrel); /* close transientrel, but keep lock until commit */ heap_close(myState->transientrel, NoLock); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 62cf81e95a..9722d92849 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -19,6 +19,7 @@ #include "access/multixact.h" #include "access/reloptions.h" #include "access/relscan.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "access/tupconvert.h" #include "access/xact.h" @@ -4663,7 +4664,8 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) /* Write the tuple out to the new relation */ if (newrel) - heap_insert(newrel, tuple, mycid, hi_options, bistate); + storage_insert(newrel, newslot, mycid, hi_options, bistate, + NULL, NULL, NIL, NULL); ResetExprContext(econtext); @@ -4687,7 +4689,7 @@ ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode) /* If we skipped writing WAL, then we need to sync the heap. */ if (hi_options & HEAP_INSERT_SKIP_WAL) - heap_sync(newrel); + storage_sync(newrel); heap_close(newrel, NoLock); } diff --git a/src/backend/commands/trigger.c b/src/backend/commands/trigger.c index 502f1dee1f..ca413df263 100644 --- a/src/backend/commands/trigger.c +++ b/src/backend/commands/trigger.c @@ -15,6 +15,7 @@ #include "access/genam.h" #include "access/heapam.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "access/htup_details.h" #include "access/xact.h" @@ -2352,17 +2353,21 @@ ExecBRInsertTriggers(EState *estate, ResultRelInfo *relinfo, void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, - HeapTuple trigtuple, List *recheckIndexes, + TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture) { TriggerDesc *trigdesc = relinfo->ri_TrigDesc; if ((trigdesc && trigdesc->trig_insert_after_row) || (transition_capture && transition_capture->tcs_insert_new_table)) + { + HeapTuple trigtuple = ExecHeapifySlot(slot); + AfterTriggerSaveEvent(estate, relinfo, TRIGGER_EVENT_INSERT, true, NULL, trigtuple, recheckIndexes, NULL, transition_capture); + } } TupleTableSlot * @@ -3012,9 +3017,10 @@ GetTupleForTrigger(EState *estate, TupleTableSlot **newSlot) { Relation relation = relinfo->ri_RelationDesc; - HeapTupleData tuple; + StorageTuple tuple; HeapTuple result; Buffer buffer; + tuple_data t_data; if (newSlot != NULL) { @@ -3030,11 +3036,11 @@ GetTupleForTrigger(EState *estate, * lock tuple for update */ ltrmark:; - tuple.t_self = *tid; - test = heap_lock_tuple(relation, &tuple, - estate->es_output_cid, - lockmode, LockWaitBlock, - false, &buffer, &hufd); + test = storage_lock_tuple(relation, tid, &tuple, + estate->es_output_cid, + lockmode, LockWaitBlock, + false, &buffer, &hufd); + result = tuple; switch (test) { case HeapTupleSelfUpdated: @@ -3066,7 +3072,8 @@ ltrmark:; ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); - if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self)) + t_data = relation->rd_stamroutine->get_tuple_data(tuple, TID); + if (!ItemPointerEquals(&hufd.ctid, &(t_data.tid))) { /* it was updated, so look at the updated version */ TupleTableSlot *epqslot; @@ -3112,6 +3119,7 @@ ltrmark:; { Page page; ItemId lp; + HeapTupleData tupledata; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); @@ -3130,17 +3138,17 @@ ltrmark:; Assert(ItemIdIsNormal(lp)); - tuple.t_data = (HeapTupleHeader) PageGetItem(page, lp); - tuple.t_len = ItemIdGetLength(lp); - tuple.t_self = *tid; - tuple.t_tableOid = RelationGetRelid(relation); + tupledata.t_data = (HeapTupleHeader) PageGetItem(page, lp); + tupledata.t_len = ItemIdGetLength(lp); + tupledata.t_self = *tid; + tupledata.t_tableOid = RelationGetRelid(relation); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); + + result = heap_copytuple(&tupledata); } - result = heap_copytuple(&tuple); ReleaseBuffer(buffer); - return result; } @@ -3946,8 +3954,8 @@ AfterTriggerExecute(AfterTriggerEvent event, AfterTriggerShared evtshared = GetTriggerSharedData(event); Oid tgoid = evtshared->ats_tgoid; TriggerData LocTriggerData; - HeapTupleData tuple1; - HeapTupleData tuple2; + StorageTuple tuple1; + StorageTuple tuple2; HeapTuple rettuple; Buffer buffer1 = InvalidBuffer; Buffer buffer2 = InvalidBuffer; @@ -4020,10 +4028,9 @@ AfterTriggerExecute(AfterTriggerEvent event, default: if (ItemPointerIsValid(&(event->ate_ctid1))) { - ItemPointerCopy(&(event->ate_ctid1), &(tuple1.t_self)); - if (!heap_fetch(rel, SnapshotAny, &tuple1, &buffer1, false, NULL)) + if (!storage_fetch(rel, &(event->ate_ctid1), SnapshotAny, &tuple1, &buffer1, false, NULL)) elog(ERROR, "failed to fetch tuple1 for AFTER trigger"); - LocTriggerData.tg_trigtuple = &tuple1; + LocTriggerData.tg_trigtuple = tuple1; LocTriggerData.tg_trigtuplebuf = buffer1; } else @@ -4037,10 +4044,9 @@ AfterTriggerExecute(AfterTriggerEvent event, AFTER_TRIGGER_2CTID && ItemPointerIsValid(&(event->ate_ctid2))) { - ItemPointerCopy(&(event->ate_ctid2), &(tuple2.t_self)); - if (!heap_fetch(rel, SnapshotAny, &tuple2, &buffer2, false, NULL)) + if (!storage_fetch(rel, &(event->ate_ctid2), SnapshotAny, &tuple2, &buffer2, false, NULL)) elog(ERROR, "failed to fetch tuple2 for AFTER trigger"); - LocTriggerData.tg_newtuple = &tuple2; + LocTriggerData.tg_newtuple = tuple2; LocTriggerData.tg_newtuplebuf = buffer2; } else diff --git a/src/backend/executor/execIndexing.c b/src/backend/executor/execIndexing.c index 62e51f1ef3..1038957c59 100644 --- a/src/backend/executor/execIndexing.c +++ b/src/backend/executor/execIndexing.c @@ -269,12 +269,12 @@ ExecCloseIndices(ResultRelInfo *resultRelInfo) */ List * ExecInsertIndexTuples(TupleTableSlot *slot, - ItemPointer tupleid, EState *estate, bool noDupErr, bool *specConflict, List *arbiterIndexes) { + ItemPointer tupleid = &slot->tts_tid; List *result = NIL; ResultRelInfo *resultRelInfo; int i; diff --git a/src/backend/executor/execMain.c b/src/backend/executor/execMain.c index d8bc5028e8..db9196924b 100644 --- a/src/backend/executor/execMain.c +++ b/src/backend/executor/execMain.c @@ -38,6 +38,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "access/transam.h" #include "access/xact.h" @@ -1894,7 +1895,7 @@ ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, /* See the comment above. */ if (resultRelInfo->ri_PartitionRoot) { - HeapTuple tuple = ExecFetchSlotTuple(slot); + StorageTuple tuple = ExecFetchSlotTuple(slot); TupleDesc old_tupdesc = RelationGetDescr(rel); TupleConversionMap *map; @@ -1974,7 +1975,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, */ if (resultRelInfo->ri_PartitionRoot) { - HeapTuple tuple = ExecFetchSlotTuple(slot); + StorageTuple tuple = ExecFetchSlotTuple(slot); TupleConversionMap *map; rel = resultRelInfo->ri_PartitionRoot; @@ -2021,7 +2022,7 @@ ExecConstraints(ResultRelInfo *resultRelInfo, /* See the comment above. */ if (resultRelInfo->ri_PartitionRoot) { - HeapTuple tuple = ExecFetchSlotTuple(slot); + StorageTuple tuple = ExecFetchSlotTuple(slot); TupleDesc old_tupdesc = RelationGetDescr(rel); TupleConversionMap *map; @@ -2480,7 +2481,8 @@ EvalPlanQual(EState *estate, EPQState *epqstate, ItemPointer tid, TransactionId priorXmax) { TupleTableSlot *slot; - HeapTuple copyTuple; + StorageTuple copyTuple; + tuple_data t_data; Assert(rti > 0); @@ -2497,7 +2499,9 @@ EvalPlanQual(EState *estate, EPQState *epqstate, * For UPDATE/DELETE we have to return tid of actual row we're executing * PQ for. */ - *tid = copyTuple->t_self; + + t_data = storage_tuple_get_data(relation, copyTuple, TID); + *tid = t_data.tid; /* * Need to run a recheck subquery. Initialize or reinitialize EPQ state. @@ -2528,7 +2532,7 @@ EvalPlanQual(EState *estate, EPQState *epqstate, * is to guard against early re-use of the EPQ query. */ if (!TupIsNull(slot)) - (void) ExecMaterializeSlot(slot); + ExecMaterializeSlot(slot); /* * Clear out the test tuple. This is needed in case the EPQ query is @@ -2561,14 +2565,14 @@ EvalPlanQual(EState *estate, EPQState *epqstate, * Note: properly, lockmode should be declared as enum LockTupleMode, * but we use "int" to avoid having to include heapam.h in executor.h. */ -HeapTuple +StorageTuple EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, LockWaitPolicy wait_policy, ItemPointer tid, TransactionId priorXmax) { - HeapTuple copyTuple = NULL; - HeapTupleData tuple; + StorageTuple tuple = NULL; SnapshotData SnapshotDirty; + tuple_data t_data; /* * fetch target tuple @@ -2576,12 +2580,12 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, * Loop here to deal with updated or busy tuples */ InitDirtySnapshot(SnapshotDirty); - tuple.t_self = *tid; for (;;) { Buffer buffer; + ItemPointerData ctid; - if (heap_fetch(relation, &SnapshotDirty, &tuple, &buffer, true, NULL)) + if (storage_fetch(relation, tid, &SnapshotDirty, &tuple, &buffer, true, NULL)) { HTSU_Result test; HeapUpdateFailureData hufd; @@ -2595,7 +2599,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, * atomic, and Xmin never changes in an existing tuple, except to * invalid or frozen, and neither of those can match priorXmax.) */ - if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data), + if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data), priorXmax)) { ReleaseBuffer(buffer); @@ -2617,7 +2621,8 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, { case LockWaitBlock: XactLockTableWait(SnapshotDirty.xmax, - relation, &tuple.t_self, + relation, + tid, XLTW_FetchUpdated); break; case LockWaitSkip: @@ -2646,20 +2651,23 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, * that priorXmax == xmin, so we can test that variable instead of * doing HeapTupleHeaderGetXmin again. */ - if (TransactionIdIsCurrentTransactionId(priorXmax) && - HeapTupleHeaderGetCmin(tuple.t_data) >= estate->es_output_cid) + if (TransactionIdIsCurrentTransactionId(priorXmax)) { - ReleaseBuffer(buffer); - return NULL; + t_data = storage_tuple_get_data(relation, tuple, CMIN); + if (t_data.cid >= estate->es_output_cid) + { + ReleaseBuffer(buffer); + return NULL; + } } /* * This is a live tuple, so now try to lock it. */ - test = heap_lock_tuple(relation, &tuple, - estate->es_output_cid, - lockmode, wait_policy, - false, &buffer, &hufd); + test = storage_lock_tuple(relation, tid, &tuple, + estate->es_output_cid, + lockmode, wait_policy, + false, &buffer, &hufd); /* We now have two pins on the buffer, get rid of one */ ReleaseBuffer(buffer); @@ -2695,12 +2703,15 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); +#if 0 //hari /* Should not encounter speculative tuple on recheck */ Assert(!HeapTupleHeaderIsSpeculative(tuple.t_data)); - if (!ItemPointerEquals(&hufd.ctid, &tuple.t_self)) +#endif + t_data = storage_tuple_get_data(relation, tuple, TID); + if (!ItemPointerEquals(&hufd.ctid, &t_data.tid)) { /* it was updated, so look at the updated version */ - tuple.t_self = hufd.ctid; + *tid = hufd.ctid; /* updated row should have xmin matching this xmax */ priorXmax = hufd.xmax; continue; @@ -2722,10 +2733,6 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, return NULL; /* keep compiler quiet */ } - /* - * We got tuple - now copy it for use by recheck query. - */ - copyTuple = heap_copytuple(&tuple); ReleaseBuffer(buffer); break; } @@ -2734,7 +2741,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, * If the referenced slot was actually empty, the latest version of * the row must have been deleted, so we need do nothing. */ - if (tuple.t_data == NULL) + if (tuple == NULL) { ReleaseBuffer(buffer); return NULL; @@ -2743,7 +2750,7 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, /* * As above, if xmin isn't what we're expecting, do nothing. */ - if (!TransactionIdEquals(HeapTupleHeaderGetXmin(tuple.t_data), + if (!TransactionIdEquals(HeapTupleHeaderGetXmin(((HeapTuple) tuple)->t_data), priorXmax)) { ReleaseBuffer(buffer); @@ -2762,7 +2769,9 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, * As above, it should be safe to examine xmax and t_ctid without the * buffer content lock, because they can't be changing. */ - if (ItemPointerEquals(&tuple.t_self, &tuple.t_data->t_ctid)) + t_data = storage_tuple_get_data(relation, tuple, CTID); + ctid = t_data.tid; + if (ItemPointerEquals(tid, &ctid)) { /* deleted, so forget about it */ ReleaseBuffer(buffer); @@ -2770,17 +2779,19 @@ EvalPlanQualFetch(EState *estate, Relation relation, int lockmode, } /* updated, so look at the updated row */ - tuple.t_self = tuple.t_data->t_ctid; + *tid = ctid; + /* updated row should have xmin matching this xmax */ - priorXmax = HeapTupleHeaderGetUpdateXid(tuple.t_data); + t_data = storage_tuple_get_data(relation, tuple, UPDATED_XID); + priorXmax = t_data.xid; ReleaseBuffer(buffer); /* loop back to fetch next in chain */ } /* - * Return the copied tuple + * Return the tuple */ - return copyTuple; + return tuple; } /* @@ -2826,7 +2837,7 @@ EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks) * NB: passed tuple must be palloc'd; it may get freed later */ void -EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple) +EvalPlanQualSetTuple(EPQState *epqstate, Index rti, StorageTuple tuple) { EState *estate = epqstate->estate; @@ -2845,7 +2856,7 @@ EvalPlanQualSetTuple(EPQState *epqstate, Index rti, HeapTuple tuple) /* * Fetch back the current test tuple (if any) for the specified RTI */ -HeapTuple +StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti) { EState *estate = epqstate->estate; @@ -2873,7 +2884,7 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) ExecRowMark *erm = aerm->rowmark; Datum datum; bool isNull; - HeapTupleData tuple; + StorageTuple tuple; if (RowMarkRequiresRowShareLock(erm->markType)) elog(ERROR, "EvalPlanQual doesn't support locking rowmarks"); @@ -2904,8 +2915,6 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) if (erm->markType == ROW_MARK_REFERENCE) { - HeapTuple copyTuple; - Assert(erm->relation != NULL); /* fetch the tuple's ctid */ @@ -2929,11 +2938,11 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cannot lock rows in foreign table \"%s\"", RelationGetRelationName(erm->relation)))); - copyTuple = fdwroutine->RefetchForeignRow(epqstate->estate, - erm, - datum, - &updated); - if (copyTuple == NULL) + tuple = fdwroutine->RefetchForeignRow(epqstate->estate, + erm, + datum, + &updated); + if (tuple == NULL) elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); /* @@ -2947,23 +2956,18 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) /* ordinary table, fetch the tuple */ Buffer buffer; - tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); - if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer, - false, NULL)) + if (!storage_fetch(erm->relation, (ItemPointer) DatumGetPointer(datum), SnapshotAny, &tuple, &buffer, + false, NULL)) elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); - /* successful, copy tuple */ - copyTuple = heap_copytuple(&tuple); ReleaseBuffer(buffer); } /* store tuple */ - EvalPlanQualSetTuple(epqstate, erm->rti, copyTuple); + EvalPlanQualSetTuple(epqstate, erm->rti, tuple); } else { - HeapTupleHeader td; - Assert(erm->markType == ROW_MARK_COPY); /* fetch the whole-row Var for the relation */ @@ -2973,19 +2977,12 @@ EvalPlanQualFetchRowMarks(EPQState *epqstate) /* non-locked rels could be on the inside of outer joins */ if (isNull) continue; - td = DatumGetHeapTupleHeader(datum); - - /* build a temporary HeapTuple control structure */ - tuple.t_len = HeapTupleHeaderGetDatumLength(td); - tuple.t_data = td; - /* relation might be a foreign table, if so provide tableoid */ - tuple.t_tableOid = erm->relid; - /* also copy t_ctid in case there's valid data there */ - tuple.t_self = td->t_ctid; - - /* copy and store tuple */ - EvalPlanQualSetTuple(epqstate, erm->rti, - heap_copytuple(&tuple)); + + tuple = storage_tuple_by_datum(erm->relation, datum, erm->relid); + + /* store tuple */ + EvalPlanQualSetTuple(epqstate, erm->rti, tuple); + } } } @@ -3154,8 +3151,8 @@ EvalPlanQualStart(EPQState *epqstate, EState *parentestate, Plan *planTree) } else { - estate->es_epqTuple = (HeapTuple *) - palloc0(rtsize * sizeof(HeapTuple)); + estate->es_epqTuple = (StorageTuple *) + palloc0(rtsize * sizeof(StorageTuple)); estate->es_epqTupleSet = (bool *) palloc0(rtsize * sizeof(bool)); } diff --git a/src/backend/executor/execReplication.c b/src/backend/executor/execReplication.c index 864ca769e9..c89631fe00 100644 --- a/src/backend/executor/execReplication.c +++ b/src/backend/executor/execReplication.c @@ -15,6 +15,7 @@ #include "postgres.h" #include "access/relscan.h" +#include "access/storageam.h" #include "access/transam.h" #include "access/xact.h" #include "commands/trigger.h" @@ -169,19 +170,19 @@ retry: Buffer buf; HeapUpdateFailureData hufd; HTSU_Result res; - HeapTupleData locktup; - - ItemPointerCopy(&outslot->tts_tid, &locktup.t_self); + StorageTuple locktup; PushActiveSnapshot(GetLatestSnapshot()); - res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false), - lockmode, - LockWaitBlock, - false /* don't follow updates */ , - &buf, &hufd); + res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false), + lockmode, + LockWaitBlock, + false /* don't follow updates */ , + &buf, &hufd); /* the tuple slot already has the buffer pinned */ - ReleaseBuffer(buf); + if (BufferIsValid(buf)) + ReleaseBuffer(buf); + pfree(locktup); PopActiveSnapshot(); @@ -277,19 +278,20 @@ retry: Buffer buf; HeapUpdateFailureData hufd; HTSU_Result res; - HeapTupleData locktup; - - ItemPointerCopy(&outslot->tts_tid, &locktup.t_self); + StorageTuple locktup; PushActiveSnapshot(GetLatestSnapshot()); - res = heap_lock_tuple(rel, &locktup, GetCurrentCommandId(false), - lockmode, - LockWaitBlock, - false /* don't follow updates */ , - &buf, &hufd); + res = storage_lock_tuple(rel, &(outslot->tts_tid), &locktup, GetCurrentCommandId(false), + lockmode, + LockWaitBlock, + false /* don't follow updates */ , + &buf, &hufd); /* the tuple slot already has the buffer pinned */ - ReleaseBuffer(buf); + if (BufferIsValid(buf)) + ReleaseBuffer(buf); + + pfree(locktup); PopActiveSnapshot(); @@ -327,7 +329,6 @@ void ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) { bool skip_tuple = false; - HeapTuple tuple; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; @@ -354,19 +355,12 @@ ExecSimpleRelationInsert(EState *estate, TupleTableSlot *slot) if (rel->rd_att->constr) ExecConstraints(resultRelInfo, slot, estate); - /* Store the slot into tuple that we can inspect. */ - tuple = ExecHeapifySlot(slot); - - /* OK, store the tuple and create index entries for it */ - simple_heap_insert(rel, tuple); - - if (resultRelInfo->ri_NumIndices > 0) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate, false, NULL, - NIL); + storage_insert(resultRelInfo->ri_RelationDesc, slot, + GetCurrentCommandId(true), 0, NULL, + ExecInsertIndexTuples, estate, NIL, &recheckIndexes); /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, + ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes, NULL); /* @@ -390,7 +384,7 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, TupleTableSlot *searchslot, TupleTableSlot *slot) { bool skip_tuple = false; - HeapTuple tuple; + StorageTuple tuple; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; Relation rel = resultRelInfo->ri_RelationDesc; ItemPointer tid = &(searchslot->tts_tid); @@ -415,22 +409,18 @@ ExecSimpleRelationUpdate(EState *estate, EPQState *epqstate, if (!skip_tuple) { List *recheckIndexes = NIL; + HeapUpdateFailureData hufd; + LockTupleMode lockmode; + InsertIndexTuples IndexFunc = ExecInsertIndexTuples; /* Check the constraints of the tuple */ if (rel->rd_att->constr) ExecConstraints(resultRelInfo, slot, estate); - /* Store the slot into tuple that we can write. */ - tuple = ExecHeapifySlot(slot); + storage_update(rel, tid, slot, estate, GetCurrentCommandId(true), InvalidSnapshot, + true, &hufd, &lockmode, IndexFunc, &recheckIndexes); - /* OK, update the tuple and index entries for it */ - simple_heap_update(rel, tid, tuple); - - if (resultRelInfo->ri_NumIndices > 0 && - !HeapTupleIsHeapOnly(tuple)) - recheckIndexes = ExecInsertIndexTuples(slot, tid, - estate, false, NULL, - NIL); + tuple = ExecHeapifySlot(slot); /* AFTER ROW UPDATE Triggers */ ExecARUpdateTriggers(estate, resultRelInfo, diff --git a/src/backend/executor/nodeLockRows.c b/src/backend/executor/nodeLockRows.c index 7961b4be6a..c8327ee1ce 100644 --- a/src/backend/executor/nodeLockRows.c +++ b/src/backend/executor/nodeLockRows.c @@ -22,6 +22,7 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/storageam.h" #include "access/xact.h" #include "executor/executor.h" #include "executor/nodeLockRows.h" @@ -74,18 +75,20 @@ lnext: { ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc); ExecRowMark *erm = aerm->rowmark; - HeapTuple *testTuple; + StorageTuple *testTuple; Datum datum; bool isNull; - HeapTupleData tuple; + StorageTuple tuple; Buffer buffer; HeapUpdateFailureData hufd; LockTupleMode lockmode; HTSU_Result test; - HeapTuple copyTuple; + StorageTuple copyTuple; + ItemPointerData tid; + tuple_data t_data; /* clear any leftover test tuple for this rel */ - testTuple = &(node->lr_curtuples[erm->rti - 1]); + testTuple = (StorageTuple) (&(node->lr_curtuples[erm->rti - 1])); if (*testTuple != NULL) heap_freetuple(*testTuple); *testTuple = NULL; @@ -159,7 +162,7 @@ lnext: } /* okay, try to lock the tuple */ - tuple.t_self = *((ItemPointer) DatumGetPointer(datum)); + tid = *((ItemPointer) DatumGetPointer(datum)); switch (erm->markType) { case ROW_MARK_EXCLUSIVE: @@ -180,11 +183,13 @@ lnext: break; } - test = heap_lock_tuple(erm->relation, &tuple, - estate->es_output_cid, - lockmode, erm->waitPolicy, true, - &buffer, &hufd); - ReleaseBuffer(buffer); + test = storage_lock_tuple(erm->relation, &tid, &tuple, + estate->es_output_cid, + lockmode, erm->waitPolicy, true, + &buffer, &hufd); + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + switch (test) { case HeapTupleWouldBlock: @@ -218,7 +223,8 @@ lnext: ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); - if (ItemPointerEquals(&hufd.ctid, &tuple.t_self)) + t_data = erm->relation->rd_stamroutine->get_tuple_data(tuple, TID); + if (ItemPointerEquals(&hufd.ctid, &(t_data.tid))) { /* Tuple was deleted, so don't return it */ goto lnext; @@ -238,7 +244,8 @@ lnext: goto lnext; } /* remember the actually locked tuple's TID */ - tuple.t_self = copyTuple->t_self; + t_data = erm->relation->rd_stamroutine->get_tuple_data(copyTuple, TID); + tid = t_data.tid; /* Save locked tuple for EvalPlanQual testing below */ *testTuple = copyTuple; @@ -258,7 +265,7 @@ lnext: } /* Remember locked tuple's TID for EPQ testing and WHERE CURRENT OF */ - erm->curCtid = tuple.t_self; + erm->curCtid = tid; } /* @@ -280,7 +287,7 @@ lnext: { ExecAuxRowMark *aerm = (ExecAuxRowMark *) lfirst(lc); ExecRowMark *erm = aerm->rowmark; - HeapTupleData tuple; + StorageTuple tuple; Buffer buffer; /* skip non-active child tables, but clear their test tuples */ @@ -308,14 +315,12 @@ lnext: Assert(ItemPointerIsValid(&(erm->curCtid))); /* okay, fetch the tuple */ - tuple.t_self = erm->curCtid; - if (!heap_fetch(erm->relation, SnapshotAny, &tuple, &buffer, - false, NULL)) + if (!storage_fetch(erm->relation, &erm->curCtid, SnapshotAny, &tuple, &buffer, + false, NULL)) elog(ERROR, "failed to fetch tuple for EvalPlanQual recheck"); /* successful, copy and store tuple */ - EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, - heap_copytuple(&tuple)); + EvalPlanQualSetTuple(&node->lr_epqstate, erm->rti, tuple); ReleaseBuffer(buffer); } @@ -394,8 +399,8 @@ ExecInitLockRows(LockRows *node, EState *estate, int eflags) * Create workspace in which we can remember per-RTE locked tuples */ lrstate->lr_ntables = list_length(estate->es_range_table); - lrstate->lr_curtuples = (HeapTuple *) - palloc0(lrstate->lr_ntables * sizeof(HeapTuple)); + lrstate->lr_curtuples = (StorageTuple *) + palloc0(lrstate->lr_ntables * sizeof(StorageTuple)); /* * Locate the ExecRowMark(s) that this node is responsible for, and diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c index 9b62ed0a3a..c8cc1e0f12 100644 --- a/src/backend/executor/nodeModifyTable.c +++ b/src/backend/executor/nodeModifyTable.c @@ -38,7 +38,10 @@ #include "postgres.h" #include "access/htup_details.h" +#include "access/storageam.h" +#include "access/storageam.h" #include "access/xact.h" +#include "catalog/pg_am.h" #include "commands/trigger.h" #include "executor/execPartition.h" #include "executor/executor.h" @@ -165,15 +168,13 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, econtext->ecxt_scantuple = tupleSlot; else { - HeapTuple tuple; - /* * RETURNING expressions might reference the tableoid column, so * initialize t_tableOid before evaluating them. */ Assert(!TupIsNull(econtext->ecxt_scantuple)); - tuple = ExecHeapifySlot(econtext->ecxt_scantuple); - tuple->t_tableOid = RelationGetRelid(resultRelInfo->ri_RelationDesc); + ExecSlotUpdateTupleTableoid(econtext->ecxt_scantuple, + RelationGetRelid(resultRelInfo->ri_RelationDesc)); } econtext->ecxt_outertuple = planSlot; @@ -192,7 +193,7 @@ ExecProcessReturning(ResultRelInfo *resultRelInfo, static void ExecCheckHeapTupleVisible(EState *estate, Relation rel, - HeapTuple tuple, + StorageTuple tuple, Buffer buffer) { if (!IsolationUsesXactSnapshot()) @@ -205,13 +206,15 @@ ExecCheckHeapTupleVisible(EState *estate, LockBuffer(buffer, BUFFER_LOCK_SHARE); if (!HeapTupleSatisfiesVisibility(rel->rd_stamroutine, tuple, estate->es_snapshot, buffer)) { + tuple_data t_data = storage_tuple_get_data(rel, tuple, XMIN); + /* * We should not raise a serialization failure if the conflict is * against a tuple inserted by our own transaction, even if it's not * visible to our snapshot. (This would happen, for example, if * conflicting keys are proposed for insertion in a single command.) */ - if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple->t_data))) + if (!TransactionIdIsCurrentTransactionId(t_data.xid)) ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("could not serialize access due to concurrent update"))); @@ -227,19 +230,20 @@ ExecCheckTIDVisible(EState *estate, ResultRelInfo *relinfo, ItemPointer tid) { - Relation rel = relinfo->ri_RelationDesc; Buffer buffer; - HeapTupleData tuple; + Relation rel = relinfo->ri_RelationDesc; + StorageTuple tuple; /* Redundantly check isolation level */ if (!IsolationUsesXactSnapshot()) return; - tuple.t_self = *tid; - if (!heap_fetch(rel, SnapshotAny, &tuple, &buffer, false, NULL)) + if (!storage_fetch(rel, tid, SnapshotAny, &tuple, &buffer, false, NULL)) elog(ERROR, "failed to fetch conflicting tuple for ON CONFLICT"); - ExecCheckHeapTupleVisible(estate, rel, &tuple, buffer); - ReleaseBuffer(buffer); + ExecCheckHeapTupleVisible(estate, rel, tuple, buffer); + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + pfree(tuple); } /* ---------------------------------------------------------------- @@ -260,7 +264,7 @@ ExecInsert(ModifyTableState *mtstate, EState *estate, bool canSetTag) { - HeapTuple tuple; + StorageTuple tuple; ResultRelInfo *resultRelInfo; ResultRelInfo *saved_resultRelInfo = NULL; Relation resultRelationDesc; @@ -268,12 +272,6 @@ ExecInsert(ModifyTableState *mtstate, List *recheckIndexes = NIL; TupleTableSlot *result = NULL; - /* - * get the heap tuple out of the tuple table slot, making sure we have a - * writable copy - */ - tuple = ExecHeapifySlot(slot); - /* * get information on the (current) result relation */ @@ -285,6 +283,8 @@ ExecInsert(ModifyTableState *mtstate, int leaf_part_index; TupleConversionMap *map; + tuple = ExecHeapifySlot(slot); + /* * Away we go ... If we end up not finding a partition after all, * ExecFindPartition() does not return and errors out instead. @@ -375,19 +375,31 @@ ExecInsert(ModifyTableState *mtstate, resultRelationDesc = resultRelInfo->ri_RelationDesc; /* - * If the result relation has OIDs, force the tuple's OID to zero so that - * heap_insert will assign a fresh OID. Usually the OID already will be - * zero at this point, but there are corner cases where the plan tree can - * return a tuple extracted literally from some table with the same - * rowtype. + * get the heap tuple out of the tuple table slot, making sure we have a + * writable copy <-- obsolete comment XXX explain what we really do here + * + * Do we really need to do this here? + */ + ExecMaterializeSlot(slot); + + + /* + * If the result relation uses heapam and has OIDs, force the tuple's OID + * to zero so that heap_insert will assign a fresh OID. Usually the OID + * already will be zero at this point, but there are corner cases where + * the plan tree can return a tuple extracted literally from some table + * with the same rowtype. * * XXX if we ever wanted to allow users to assign their own OIDs to new * rows, this'd be the place to do it. For the moment, we make a point of * doing this before calling triggers, so that a user-supplied trigger * could hack the OID if desired. */ - if (resultRelationDesc->rd_rel->relhasoids) - HeapTupleSetOid(tuple, InvalidOid); + if (resultRelationDesc->rd_rel->relam == HEAPAM_STORAGE_AM_OID && + resultRelationDesc->rd_rel->relhasoids) + { + slot->tts_tupleOid = InvalidOid; + } /* * BEFORE ROW INSERT Triggers. @@ -405,9 +417,6 @@ ExecInsert(ModifyTableState *mtstate, if (slot == NULL) /* "do nothing" */ return NULL; - - /* trigger might have changed tuple */ - tuple = ExecHeapifySlot(slot); } /* INSTEAD OF ROW INSERT Triggers */ @@ -419,9 +428,6 @@ ExecInsert(ModifyTableState *mtstate, if (slot == NULL) /* "do nothing" */ return NULL; - /* trigger might have changed tuple */ - tuple = ExecHeapifySlot(slot); - newId = InvalidOid; } else if (resultRelInfo->ri_FdwRoutine) @@ -437,14 +443,12 @@ ExecInsert(ModifyTableState *mtstate, if (slot == NULL) /* "do nothing" */ return NULL; - /* FDW might have changed tuple */ - tuple = ExecHeapifySlot(slot); - /* * AFTER ROW Triggers or RETURNING expressions might reference the * tableoid column, so initialize t_tableOid before evaluating them. */ - tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + slot->tts_tableOid = RelationGetRelid(resultRelationDesc); + ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid); newId = InvalidOid; } @@ -464,7 +468,8 @@ ExecInsert(ModifyTableState *mtstate, * Constraints might reference the tableoid column, so initialize * t_tableOid before evaluating them. */ - tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + slot->tts_tableOid = RelationGetRelid(resultRelationDesc); + ExecSlotUpdateTupleTableoid(slot, slot->tts_tableOid); /* * Check any RLS INSERT WITH CHECK policies @@ -494,7 +499,6 @@ ExecInsert(ModifyTableState *mtstate, /* Perform a speculative insertion. */ uint32 specToken; ItemPointerData conflictTid; - bool specConflict; /* * Do a non-conclusive check for conflicts first. @@ -509,7 +513,7 @@ ExecInsert(ModifyTableState *mtstate, * speculatively. */ vlock: - specConflict = false; + slot->tts_specConflict = false; if (!ExecCheckIndexConstraints(slot, estate, &conflictTid, arbiterIndexes)) { @@ -555,24 +559,17 @@ ExecInsert(ModifyTableState *mtstate, * waiting for the whole transaction to complete. */ specToken = SpeculativeInsertionLockAcquire(GetCurrentTransactionId()); - HeapTupleHeaderSetSpeculativeToken(tuple->t_data, specToken); + slot->tts_speculativeToken = specToken; /* insert the tuple, with the speculative token */ - newId = heap_insert(resultRelationDesc, tuple, - estate->es_output_cid, - HEAP_INSERT_SPECULATIVE, - NULL); - - /* insert index entries for tuple */ - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate, true, &specConflict, - arbiterIndexes); - - /* adjust the tuple's state accordingly */ - if (!specConflict) - heap_finish_speculative(resultRelationDesc, tuple); - else - heap_abort_speculative(resultRelationDesc, tuple); + newId = storage_insert(resultRelationDesc, slot, + estate->es_output_cid, + HEAP_INSERT_SPECULATIVE, + NULL, + ExecInsertIndexTuples, + estate, + arbiterIndexes, + &recheckIndexes); /* * Wake up anyone waiting for our decision. They will re-check @@ -588,7 +585,7 @@ ExecInsert(ModifyTableState *mtstate, * the pre-check again, which will now find the conflicting tuple * (unless it aborts before we get there). */ - if (specConflict) + if (slot->tts_specConflict) { list_free(recheckIndexes); goto vlock; @@ -600,19 +597,14 @@ ExecInsert(ModifyTableState *mtstate, { /* * insert the tuple normally. - * - * Note: heap_insert returns the tid (location) of the new tuple - * in the t_self field. */ - newId = heap_insert(resultRelationDesc, tuple, - estate->es_output_cid, - 0, NULL); - - /* insert index entries for tuple */ - if (resultRelInfo->ri_NumIndices > 0) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate, false, NULL, - arbiterIndexes); + newId = storage_insert(resultRelationDesc, slot, + estate->es_output_cid, + 0, NULL, + ExecInsertIndexTuples, + estate, + arbiterIndexes, + &recheckIndexes); } } @@ -620,11 +612,11 @@ ExecInsert(ModifyTableState *mtstate, { (estate->es_processed)++; estate->es_lastoid = newId; - setLastTid(&(tuple->t_self)); + setLastTid(&(slot->tts_tid)); } /* AFTER ROW INSERT Triggers */ - ExecARInsertTriggers(estate, resultRelInfo, tuple, recheckIndexes, + ExecARInsertTriggers(estate, resultRelInfo, slot, recheckIndexes, mtstate->mt_transition_capture); list_free(recheckIndexes); @@ -675,7 +667,7 @@ ExecInsert(ModifyTableState *mtstate, static TupleTableSlot * ExecDelete(ModifyTableState *mtstate, ItemPointer tupleid, - HeapTuple oldtuple, + StorageTuple oldtuple, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, @@ -720,8 +712,6 @@ ExecDelete(ModifyTableState *mtstate, } else if (resultRelInfo->ri_FdwRoutine) { - HeapTuple tuple; - /* * delete from foreign table: let the FDW do it * @@ -747,8 +737,10 @@ ExecDelete(ModifyTableState *mtstate, */ if (slot->tts_isempty) ExecStoreAllNullTuple(slot); - tuple = ExecHeapifySlot(slot); - tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + + ExecMaterializeSlot(slot); + + slot->tts_tableOid = RelationGetRelid(resultRelationDesc); } else { @@ -762,11 +754,12 @@ ExecDelete(ModifyTableState *mtstate, * mode transactions. */ ldelete:; - result = heap_delete(resultRelationDesc, tupleid, - estate->es_output_cid, - estate->es_crosscheck_snapshot, - true /* wait for commit */ , - &hufd); + result = storage_delete(resultRelationDesc, tupleid, + estate->es_output_cid, + estate->es_crosscheck_snapshot, + true /* wait for commit */ , + NULL, + &hufd); switch (result) { case HeapTupleSelfUpdated: @@ -862,7 +855,7 @@ ldelete:; * gotta fetch it. We can use the trigger tuple slot. */ TupleTableSlot *rslot; - HeapTupleData deltuple; + StorageTuple deltuple = NULL; Buffer delbuffer; if (resultRelInfo->ri_FdwRoutine) @@ -876,20 +869,19 @@ ldelete:; slot = estate->es_trig_tuple_slot; if (oldtuple != NULL) { - deltuple = *oldtuple; + deltuple = heap_copytuple(oldtuple); delbuffer = InvalidBuffer; } else { - deltuple.t_self = *tupleid; - if (!heap_fetch(resultRelationDesc, SnapshotAny, - &deltuple, &delbuffer, false, NULL)) + if (!storage_fetch(resultRelationDesc, tupleid, SnapshotAny, + &deltuple, &delbuffer, false, NULL)) elog(ERROR, "failed to fetch deleted tuple for DELETE RETURNING"); } if (slot->tts_tupleDescriptor != RelationGetDescr(resultRelationDesc)) ExecSetSlotDescriptor(slot, RelationGetDescr(resultRelationDesc)); - ExecStoreTuple(&deltuple, slot, InvalidBuffer, false); + ExecStoreTuple(deltuple, slot, InvalidBuffer, false); } rslot = ExecProcessReturning(resultRelInfo, slot, planSlot); @@ -898,7 +890,7 @@ ldelete:; * Before releasing the target tuple again, make sure rslot has a * local copy of any pass-by-reference values. */ - ExecHeapifySlot(rslot); + ExecMaterializeSlot(rslot); ExecClearTuple(slot); if (BufferIsValid(delbuffer)) @@ -935,14 +927,14 @@ ldelete:; static TupleTableSlot * ExecUpdate(ModifyTableState *mtstate, ItemPointer tupleid, - HeapTuple oldtuple, + StorageTuple oldtuple, TupleTableSlot *slot, TupleTableSlot *planSlot, EPQState *epqstate, EState *estate, bool canSetTag) { - HeapTuple tuple; + StorageTuple tuple; ResultRelInfo *resultRelInfo; Relation resultRelationDesc; HTSU_Result result; @@ -1007,14 +999,14 @@ ExecUpdate(ModifyTableState *mtstate, if (slot == NULL) /* "do nothing" */ return NULL; - /* FDW might have changed tuple */ - tuple = ExecHeapifySlot(slot); - /* * AFTER ROW Triggers or RETURNING expressions might reference the * tableoid column, so initialize t_tableOid before evaluating them. */ - tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + ExecSlotUpdateTupleTableoid(slot, RelationGetRelid(resultRelationDesc)); + + /* FDW might have changed tuple */ + tuple = ExecHeapifySlot(slot); } else { @@ -1024,7 +1016,7 @@ ExecUpdate(ModifyTableState *mtstate, * Constraints might reference the tableoid column, so initialize * t_tableOid before evaluating them. */ - tuple->t_tableOid = RelationGetRelid(resultRelationDesc); + slot->tts_tableOid = RelationGetRelid(resultRelationDesc); /* * Check any RLS UPDATE WITH CHECK policies @@ -1060,11 +1052,14 @@ lreplace:; * needed for referential integrity updates in transaction-snapshot * mode transactions. */ - result = heap_update(resultRelationDesc, tupleid, tuple, - estate->es_output_cid, - estate->es_crosscheck_snapshot, - true /* wait for commit */ , - &hufd, &lockmode); + result = storage_update(resultRelationDesc, tupleid, slot, + estate, + estate->es_output_cid, + estate->es_crosscheck_snapshot, + true /* wait for commit */ , + &hufd, &lockmode, + ExecInsertIndexTuples, + &recheckIndexes); switch (result) { case HeapTupleSelfUpdated: @@ -1135,26 +1130,6 @@ lreplace:; elog(ERROR, "unrecognized heap_update status: %u", result); return NULL; } - - /* - * Note: instead of having to update the old index tuples associated - * with the heap tuple, all we do is form and insert new index tuples. - * This is because UPDATEs are actually DELETEs and INSERTs, and index - * tuple deletion is done later by VACUUM (see notes in ExecDelete). - * All we do here is insert new index tuples. -cim 9/27/89 - */ - - /* - * insert index entries for tuple - * - * Note: heap_update returns the tid (location) of the new tuple in - * the t_self field. - * - * If it's a HOT update, we mustn't insert new index entries. - */ - if (resultRelInfo->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tuple)) - recheckIndexes = ExecInsertIndexTuples(slot, &(tuple->t_self), - estate, false, NULL, NIL); } if (canSetTag) @@ -1212,11 +1187,12 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, ExprContext *econtext = mtstate->ps.ps_ExprContext; Relation relation = resultRelInfo->ri_RelationDesc; ExprState *onConflictSetWhere = resultRelInfo->ri_onConflictSetWhere; - HeapTupleData tuple; + StorageTuple tuple = NULL; HeapUpdateFailureData hufd; LockTupleMode lockmode; HTSU_Result test; Buffer buffer; + tuple_data t_data; /* Determine lock mode to use */ lockmode = ExecUpdateLockMode(estate, resultRelInfo); @@ -1227,10 +1203,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * previous conclusion that the tuple is conclusively committed is not * true anymore. */ - tuple.t_self = *conflictTid; - test = heap_lock_tuple(relation, &tuple, estate->es_output_cid, - lockmode, LockWaitBlock, false, &buffer, - &hufd); + test = storage_lock_tuple(relation, conflictTid, &tuple, estate->es_output_cid, + lockmode, LockWaitBlock, false, &buffer, &hufd); switch (test) { case HeapTupleMayBeUpdated: @@ -1255,7 +1229,8 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * that for SQL MERGE, an exception must be raised in the event of * an attempt to update the same row twice. */ - if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple.t_data))) + t_data = storage_tuple_get_data(relation, tuple, XMIN); + if (TransactionIdIsCurrentTransactionId(t_data.xid)) ereport(ERROR, (errcode(ERRCODE_CARDINALITY_VIOLATION), errmsg("ON CONFLICT DO UPDATE command cannot affect row a second time"), @@ -1286,7 +1261,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * loop here, as the new version of the row might not conflict * anymore, or the conflicting tuple has actually been deleted. */ - ReleaseBuffer(buffer); + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + pfree(tuple); return false; default: @@ -1314,10 +1291,10 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, * snapshot. This is in line with the way UPDATE deals with newer tuple * versions. */ - ExecCheckHeapTupleVisible(estate, relation, &tuple, buffer); + ExecCheckHeapTupleVisible(estate, relation, tuple, buffer); /* Store target's existing tuple in the state's dedicated slot */ - ExecStoreTuple(&tuple, mtstate->mt_existing, buffer, false); + ExecStoreTuple(tuple, mtstate->mt_existing, buffer, false); /* * Make tuple and any needed join variables available to ExecQual and @@ -1332,7 +1309,9 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, if (!ExecQual(onConflictSetWhere, econtext)) { - ReleaseBuffer(buffer); + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + pfree(tuple); InstrCountFiltered1(&mtstate->ps, 1); return true; /* done with the tuple */ } @@ -1372,12 +1351,14 @@ ExecOnConflictUpdate(ModifyTableState *mtstate, */ /* Execute UPDATE with projection */ - *returning = ExecUpdate(mtstate, &tuple.t_self, NULL, + *returning = ExecUpdate(mtstate, conflictTid, NULL, mtstate->mt_conflproj, planSlot, &mtstate->mt_epqstate, mtstate->ps.state, canSetTag); - ReleaseBuffer(buffer); + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); + pfree(tuple); return true; } @@ -1580,7 +1561,7 @@ ExecModifyTable(PlanState *pstate) ItemPointer tupleid; ItemPointerData tuple_ctid; HeapTupleData oldtupdata; - HeapTuple oldtuple; + StorageTuple oldtuple; CHECK_FOR_INTERRUPTS(); diff --git a/src/backend/executor/nodeTidscan.c b/src/backend/executor/nodeTidscan.c index f2737bb7ef..766f6f4193 100644 --- a/src/backend/executor/nodeTidscan.c +++ b/src/backend/executor/nodeTidscan.c @@ -22,6 +22,7 @@ */ #include "postgres.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "catalog/pg_type.h" #include "executor/execdebug.h" @@ -306,7 +307,7 @@ TidNext(TidScanState *node) ScanDirection direction; Snapshot snapshot; Relation heapRelation; - HeapTuple tuple; + StorageTuple tuple; TupleTableSlot *slot; Buffer buffer = InvalidBuffer; ItemPointerData *tidList; @@ -331,12 +332,6 @@ TidNext(TidScanState *node) tidList = node->tss_TidList; numTids = node->tss_NumTids; - /* - * We use node->tss_htup as the tuple pointer; note this can't just be a - * local variable here, as the scan tuple slot will keep a pointer to it. - */ - tuple = &(node->tss_htup); - /* * Initialize or advance scan position, depending on direction. */ @@ -364,7 +359,7 @@ TidNext(TidScanState *node) while (node->tss_TidPtr >= 0 && node->tss_TidPtr < numTids) { - tuple->t_self = tidList[node->tss_TidPtr]; + ItemPointerData tid = tidList[node->tss_TidPtr]; /* * For WHERE CURRENT OF, the tuple retrieved from the cursor might @@ -372,9 +367,9 @@ TidNext(TidScanState *node) * current according to our snapshot. */ if (node->tss_isCurrentOf) - heap_get_latest_tid(heapRelation, snapshot, &tuple->t_self); + storage_get_latest_tid(heapRelation, snapshot, &tid); - if (heap_fetch(heapRelation, snapshot, tuple, &buffer, false, NULL)) + if (storage_fetch(heapRelation, &tid, snapshot, &tuple, &buffer, false, NULL)) { /* * store the scanned tuple in the scan tuple slot of the scan @@ -385,14 +380,16 @@ TidNext(TidScanState *node) */ ExecStoreTuple(tuple, /* tuple to store */ slot, /* slot to store in */ - buffer, /* buffer associated with tuple */ - false); /* don't pfree */ + InvalidBuffer, /* buffer associated with tuple */ + true); /* don't pfree */ /* * At this point we have an extra pin on the buffer, because * ExecStoreTuple incremented the pin count. Drop our local pin. */ - ReleaseBuffer(buffer); + /* hari */ + if (BufferIsValid(buffer)) + ReleaseBuffer(buffer); return slot; } diff --git a/src/backend/utils/adt/tid.c b/src/backend/utils/adt/tid.c index 5ed5fdaffe..8cf759bc40 100644 --- a/src/backend/utils/adt/tid.c +++ b/src/backend/utils/adt/tid.c @@ -21,6 +21,7 @@ #include #include "access/heapam.h" +#include "access/storageam.h" #include "access/sysattr.h" #include "catalog/namespace.h" #include "catalog/pg_type.h" @@ -352,7 +353,7 @@ currtid_byreloid(PG_FUNCTION_ARGS) ItemPointerCopy(tid, result); snapshot = RegisterSnapshot(GetLatestSnapshot()); - heap_get_latest_tid(rel, snapshot, result); + storage_get_latest_tid(rel, snapshot, result); UnregisterSnapshot(snapshot); heap_close(rel, AccessShareLock); @@ -387,7 +388,7 @@ currtid_byrelname(PG_FUNCTION_ARGS) ItemPointerCopy(tid, result); snapshot = RegisterSnapshot(GetLatestSnapshot()); - heap_get_latest_tid(rel, snapshot, result); + storage_get_latest_tid(rel, snapshot, result); UnregisterSnapshot(snapshot); heap_close(rel, AccessShareLock); diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h index 5bbe55f18b..a55d701c88 100644 --- a/src/include/access/heapam.h +++ b/src/include/access/heapam.h @@ -134,7 +134,7 @@ extern void heap_parallelscan_initialize(ParallelHeapScanDesc target, extern void heap_parallelscan_reinitialize(ParallelHeapScanDesc parallel_scan); extern HeapScanDesc heap_beginscan_parallel(Relation, ParallelHeapScanDesc); -extern bool heap_fetch(Relation relation, Snapshot snapshot, +extern bool heap_fetch(Relation relation, ItemPointer tid, Snapshot snapshot, HeapTuple tuple, Buffer *userbuf, bool keep_buf, Relation stats_relation); extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation, @@ -142,7 +142,6 @@ extern bool heap_hot_search_buffer(ItemPointer tid, Relation relation, bool *all_dead, bool first_call); extern bool heap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot, bool *all_dead); - extern void heap_get_latest_tid(Relation relation, Snapshot snapshot, ItemPointer tid); extern void setLastTid(const ItemPointer tid); @@ -158,16 +157,17 @@ extern void heap_multi_insert(Relation relation, HeapTuple *tuples, int ntuples, extern HTSU_Result heap_delete(Relation relation, ItemPointer tid, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd); -extern void heap_finish_speculative(Relation relation, HeapTuple tuple); -extern void heap_abort_speculative(Relation relation, HeapTuple tuple); +extern void heap_finish_speculative(Relation relation, TupleTableSlot *slot); +extern void heap_abort_speculative(Relation relation, TupleTableSlot *slot); extern HTSU_Result heap_update(Relation relation, ItemPointer otid, HeapTuple newtup, CommandId cid, Snapshot crosscheck, bool wait, HeapUpdateFailureData *hufd, LockTupleMode *lockmode); -extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple, +extern HTSU_Result heap_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * tuple, CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, bool follow_update, Buffer *buffer, HeapUpdateFailureData *hufd); + extern void heap_inplace_update(Relation relation, HeapTuple tuple); extern bool heap_freeze_tuple(HeapTupleHeader tuple, TransactionId relfrozenxid, TransactionId relminmxid, @@ -184,6 +184,10 @@ extern void simple_heap_update(Relation relation, ItemPointer otid, extern void heap_sync(Relation relation); extern void heap_update_snapshot(HeapScanDesc scan, Snapshot snapshot); +/* in heap/heapam_visibility.c */ +extern HTSU_Result HeapTupleSatisfiesUpdate(StorageTuple stup, CommandId curcid, + Buffer buffer); + /* in heap/pruneheap.c */ extern void heap_page_prune_opt(Relation relation, Buffer buffer); extern int heap_page_prune(Relation relation, Buffer buffer, diff --git a/src/include/access/htup_details.h b/src/include/access/htup_details.h index b1ceb854cd..3eb93c9112 100644 --- a/src/include/access/htup_details.h +++ b/src/include/access/htup_details.h @@ -816,6 +816,7 @@ extern Datum heap_getsysattr(HeapTuple tup, int attnum, TupleDesc tupleDesc, extern HeapTuple heap_copytuple(HeapTuple tuple); extern void heap_copytuple_with_tuple(HeapTuple src, HeapTuple dest); extern Datum heap_copy_tuple_as_datum(HeapTuple tuple, TupleDesc tupleDesc); +extern HeapTuple heap_form_tuple_by_datum(Datum data, Oid relid); extern HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull); extern HeapTuple heap_modify_tuple(HeapTuple tuple, diff --git a/src/include/access/storage_common.h b/src/include/access/storage_common.h index 6128607313..baba7261d4 100644 --- a/src/include/access/storage_common.h +++ b/src/include/access/storage_common.h @@ -55,9 +55,6 @@ typedef MinimalTuple (*SlotGetMinTuple_function) (TupleTableSlot *slot, bool pal typedef void (*SlotUpdateTableoid_function) (TupleTableSlot *slot, Oid tableoid); -typedef void (*SpeculativeAbort_function) (Relation rel, - TupleTableSlot *slot); - typedef struct StorageSlotAmRoutine { /* Operations on TupleTableSlot */ diff --git a/src/include/access/storageam.h b/src/include/access/storageam.h new file mode 100644 index 0000000000..d6500ae5f2 --- /dev/null +++ b/src/include/access/storageam.h @@ -0,0 +1,84 @@ +/*------------------------------------------------------------------------- + * + * storageam.h + * POSTGRES storage access method definitions. + * + * + * Portions Copyright (c) 1996-2017, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * src/include/access/storageam.h + * + *------------------------------------------------------------------------- + */ +#ifndef STORAGEAM_H +#define STORAGEAM_H + +#include "access/heapam.h" +#include "access/storage_common.h" +#include "executor/tuptable.h" +#include "nodes/execnodes.h" + +typedef union tuple_data +{ + TransactionId xid; + CommandId cid; + ItemPointerData tid; +} tuple_data; + +typedef enum tuple_data_flags +{ + XMIN = 0, + UPDATED_XID, + CMIN, + TID, + CTID +} tuple_data_flags; + +/* Function pointer to let the index tuple insert from storage am */ +typedef List *(*InsertIndexTuples) (TupleTableSlot *slot, EState *estate, bool noDupErr, + bool *specConflict, List *arbiterIndexes); + +/* Function pointer to let the index tuple delete from storage am */ +typedef void (*DeleteIndexTuples) (Relation rel, ItemPointer tid, TransactionId old_xmin); + +extern bool storage_fetch(Relation relation, + ItemPointer tid, + Snapshot snapshot, + StorageTuple * stuple, + Buffer *userbuf, + bool keep_buf, + Relation stats_relation); + +extern HTSU_Result storage_lock_tuple(Relation relation, ItemPointer tid, StorageTuple * stuple, + CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy, + bool follow_updates, + Buffer *buffer, HeapUpdateFailureData *hufd); + +extern Oid storage_insert(Relation relation, TupleTableSlot *slot, CommandId cid, + int options, BulkInsertState bistate, InsertIndexTuples IndexFunc, + EState *estate, List *arbiterIndexes, List **recheckIndexes); + +extern HTSU_Result storage_delete(Relation relation, ItemPointer tid, CommandId cid, + Snapshot crosscheck, bool wait, DeleteIndexTuples IndexFunc, + HeapUpdateFailureData *hufd); + +extern HTSU_Result storage_update(Relation relation, ItemPointer otid, TupleTableSlot *slot, + EState *estate, CommandId cid, Snapshot crosscheck, bool wait, + HeapUpdateFailureData *hufd, LockTupleMode *lockmode, + InsertIndexTuples IndexFunc, List **recheckIndexes); + +extern void storage_multi_insert(Relation relation, HeapTuple *tuples, int ntuples, + CommandId cid, int options, BulkInsertState bistate); + +extern tuple_data storage_tuple_get_data(Relation relation, StorageTuple tuple, tuple_data_flags flags); + +extern StorageTuple storage_tuple_by_datum(Relation relation, Datum data, Oid tableoid); + +extern void storage_get_latest_tid(Relation relation, + Snapshot snapshot, + ItemPointer tid); + +extern void storage_sync(Relation rel); + +#endif diff --git a/src/include/access/storageamapi.h b/src/include/access/storageamapi.h index 7df51c4167..03c814783f 100644 --- a/src/include/access/storageamapi.h +++ b/src/include/access/storageamapi.h @@ -11,7 +11,9 @@ #ifndef STORAGEAMAPI_H #define STORAGEAMAPI_H -#include "access/storage_common.h" +#include "access/heapam.h" +#include "access/storageam.h" +#include "nodes/execnodes.h" #include "nodes/nodes.h" #include "utils/snapshot.h" #include "fmgr.h" @@ -25,6 +27,61 @@ typedef HTSU_Result (*SnapshotSatisfiesUpdate_function) (StorageTuple htup, Comm typedef HTSV_Result (*SnapshotSatisfiesVacuum_function) (StorageTuple htup, TransactionId OldestXmin, Buffer buffer); +typedef Oid (*TupleInsert_function) (Relation rel, TupleTableSlot *slot, CommandId cid, + int options, BulkInsertState bistate, InsertIndexTuples IndexFunc, + EState *estate, List *arbiterIndexes, List **recheckIndexes); + +typedef HTSU_Result (*TupleDelete_function) (Relation relation, + ItemPointer tid, + CommandId cid, + Snapshot crosscheck, + bool wait, + DeleteIndexTuples IndexFunc, + HeapUpdateFailureData *hufd); + +typedef HTSU_Result (*TupleUpdate_function) (Relation relation, + ItemPointer otid, + TupleTableSlot *slot, + EState *estate, + CommandId cid, + Snapshot crosscheck, + bool wait, + HeapUpdateFailureData *hufd, + LockTupleMode *lockmode, + InsertIndexTuples IndexFunc, + List **recheckIndexes); + +typedef bool (*TupleFetch_function) (Relation relation, + ItemPointer tid, + Snapshot snapshot, + StorageTuple * tuple, + Buffer *userbuf, + bool keep_buf, + Relation stats_relation); + +typedef HTSU_Result (*TupleLock_function) (Relation relation, + ItemPointer tid, + StorageTuple * tuple, + CommandId cid, + LockTupleMode mode, + LockWaitPolicy wait_policy, + bool follow_update, + Buffer *buffer, + HeapUpdateFailureData *hufd); + +typedef void (*MultiInsert_function) (Relation relation, HeapTuple *tuples, int ntuples, + CommandId cid, int options, BulkInsertState bistate); + +typedef void (*TupleGetLatestTid_function) (Relation relation, + Snapshot snapshot, + ItemPointer tid); + +typedef tuple_data(*GetTupleData_function) (StorageTuple tuple, tuple_data_flags flags); + +typedef StorageTuple(*TupleFromDatum_function) (Datum data, Oid tableoid); + +typedef void (*RelationSync_function) (Relation relation); + /* * API struct for a storage AM. Note this must be stored in a single palloc'd * chunk of memory. @@ -44,6 +101,20 @@ typedef struct StorageAmRoutine slot_storageam_hook slot_storageam; + /* Operations on physical tuples */ + TupleInsert_function tuple_insert; /* heap_insert */ + TupleUpdate_function tuple_update; /* heap_update */ + TupleDelete_function tuple_delete; /* heap_delete */ + TupleFetch_function tuple_fetch; /* heap_fetch */ + TupleLock_function tuple_lock; /* heap_lock_tuple */ + MultiInsert_function multi_insert; /* heap_multi_insert */ + TupleGetLatestTid_function tuple_get_latest_tid; /* heap_get_latest_tid */ + + GetTupleData_function get_tuple_data; + TupleFromDatum_function tuple_from_datum; + + RelationSync_function relation_sync; /* heap_sync */ + } StorageAmRoutine; extern StorageAmRoutine * GetStorageAmRoutine(Oid amhandler); diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h index ff5546cf28..093d1ae112 100644 --- a/src/include/commands/trigger.h +++ b/src/include/commands/trigger.h @@ -190,7 +190,7 @@ extern TupleTableSlot *ExecBRInsertTriggers(EState *estate, TupleTableSlot *slot); extern void ExecARInsertTriggers(EState *estate, ResultRelInfo *relinfo, - HeapTuple trigtuple, + TupleTableSlot *slot, List *recheckIndexes, TransitionCaptureState *transition_capture); extern TupleTableSlot *ExecIRInsertTriggers(EState *estate, diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index e6569e1038..612d468f1f 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -198,16 +198,16 @@ extern ExecAuxRowMark *ExecBuildAuxRowMark(ExecRowMark *erm, List *targetlist); extern TupleTableSlot *EvalPlanQual(EState *estate, EPQState *epqstate, Relation relation, Index rti, int lockmode, ItemPointer tid, TransactionId priorXmax); -extern HeapTuple EvalPlanQualFetch(EState *estate, Relation relation, - int lockmode, LockWaitPolicy wait_policy, ItemPointer tid, - TransactionId priorXmax); +extern StorageTuple EvalPlanQualFetch(EState *estate, Relation relation, + int lockmode, LockWaitPolicy wait_policy, ItemPointer tid, + TransactionId priorXmax); extern void EvalPlanQualInit(EPQState *epqstate, EState *estate, Plan *subplan, List *auxrowmarks, int epqParam); extern void EvalPlanQualSetPlan(EPQState *epqstate, Plan *subplan, List *auxrowmarks); extern void EvalPlanQualSetTuple(EPQState *epqstate, Index rti, - HeapTuple tuple); -extern HeapTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti); + StorageTuple tuple); +extern StorageTuple EvalPlanQualGetTuple(EPQState *epqstate, Index rti); #define EvalPlanQualSetSlot(epqstate, slot) ((epqstate)->origslot = (slot)) extern void EvalPlanQualFetchRowMarks(EPQState *epqstate); @@ -522,9 +522,8 @@ extern int ExecCleanTargetListLength(List *targetlist); */ extern void ExecOpenIndices(ResultRelInfo *resultRelInfo, bool speculative); extern void ExecCloseIndices(ResultRelInfo *resultRelInfo); -extern List *ExecInsertIndexTuples(TupleTableSlot *slot, ItemPointer tupleid, - EState *estate, bool noDupErr, bool *specConflict, - List *arbiterIndexes); +extern List *ExecInsertIndexTuples(TupleTableSlot *slot, EState *estate, bool noDupErr, + bool *specConflict, List *arbiterIndexes); extern bool ExecCheckIndexConstraints(TupleTableSlot *slot, EState *estate, ItemPointer conflictTid, List *arbiterIndexes); extern void check_exclusion_constraint(Relation heap, Relation index, diff --git a/src/include/executor/tuptable.h b/src/include/executor/tuptable.h index df0fd0101c..6c024f34be 100644 --- a/src/include/executor/tuptable.h +++ b/src/include/executor/tuptable.h @@ -133,6 +133,7 @@ typedef struct TupleTableSlot Oid tts_tupleOid; /* XXX describe */ int tts_nvalid; /* # of valid values in tts_values */ uint32 tts_speculativeToken; /* XXX describe */ + bool tts_specConflict; /* XXX describe */ bool tts_shouldFree; bool tts_shouldFreeMin; Datum *tts_values; /* current per-attribute values */ diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index b121e16688..55b8a64506 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -515,7 +515,7 @@ typedef struct EState * remember if the tuple has been returned already. Arrays are of size * list_length(es_range_table) and are indexed by scan node scanrelid - 1. */ - HeapTuple *es_epqTuple; /* array of EPQ substitute tuples */ + StorageTuple *es_epqTuple; /* array of EPQ substitute tuples */ bool *es_epqTupleSet; /* true if EPQ tuple is provided */ bool *es_epqScanDone; /* true if EPQ tuple has been fetched */ @@ -2012,7 +2012,7 @@ typedef struct HashInstrumentation int nbatch; /* number of batches at end of execution */ int nbatch_original; /* planned number of batches */ size_t space_peak; /* speak memory usage in bytes */ -} HashInstrumentation; +} HashInstrumentation; /* ---------------- * Shared memory container for per-worker hash information @@ -2022,7 +2022,7 @@ typedef struct SharedHashInfo { int num_workers; HashInstrumentation hinstrument[FLEXIBLE_ARRAY_MEMBER]; -} SharedHashInfo; +} SharedHashInfo; /* ---------------- * HashState information @@ -2083,7 +2083,7 @@ typedef struct LockRowsState PlanState ps; /* its first field is NodeTag */ List *lr_arowMarks; /* List of ExecAuxRowMarks */ EPQState lr_epqstate; /* for evaluating EvalPlanQual rechecks */ - HeapTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */ + StorageTuple *lr_curtuples; /* locked tuples (one entry per RT entry) */ int lr_ntables; /* length of lr_curtuples[] array */ } LockRowsState; -- 2.15.0.windows.1