📄 heapam.c
字号:
*/ lp = PageGetItemId(dp, offnum); /* * Must check for deleted tuple. */ if (!ItemIdIsNormal(lp)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); if (keep_buf) *userbuf = buffer; else { ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } tuple->t_data = NULL; return false; } /* * fill in *tuple fields */ tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp); tuple->t_len = ItemIdGetLength(lp); tuple->t_tableOid = RelationGetRelid(relation); /* * check time qualification of tuple, then release lock */ valid = HeapTupleSatisfiesVisibility(tuple, snapshot, buffer); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); if (valid) { /* * All checks passed, so return the tuple as valid. Caller is now * responsible for releasing the buffer. */ *userbuf = buffer; /* Count the successful fetch against appropriate rel, if any */ if (stats_relation != NULL) pgstat_count_heap_fetch(stats_relation); return true; } /* Tuple failed time qual, but maybe caller wants to see it anyway. */ if (keep_buf) *userbuf = buffer; else { ReleaseBuffer(buffer); *userbuf = InvalidBuffer; } return false;}/* * heap_hot_search_buffer - search HOT chain for tuple satisfying snapshot * * On entry, *tid is the TID of a tuple (either a simple tuple, or the root * of a HOT chain), and buffer is the buffer holding this tuple. We search * for the first chain member satisfying the given snapshot. If one is * found, we update *tid to reference that tuple's offset number, and * return TRUE. If no match, return FALSE without modifying *tid. * * If all_dead is not NULL, we check non-visible tuples to see if they are * globally dead; *all_dead is set TRUE if all members of the HOT chain * are vacuumable, FALSE if not. * * Unlike heap_fetch, the caller must already have pin and (at least) share * lock on the buffer; it is still pinned/locked at exit. Also unlike * heap_fetch, we do not report any pgstats count; caller may do so if wanted. */boolheap_hot_search_buffer(ItemPointer tid, Buffer buffer, Snapshot snapshot, bool *all_dead){ Page dp = (Page) BufferGetPage(buffer); TransactionId prev_xmax = InvalidTransactionId; OffsetNumber offnum; bool at_chain_start; if (all_dead) *all_dead = true; Assert(TransactionIdIsValid(RecentGlobalXmin)); Assert(ItemPointerGetBlockNumber(tid) == BufferGetBlockNumber(buffer)); offnum = ItemPointerGetOffsetNumber(tid); at_chain_start = true; /* Scan through possible multiple members of HOT-chain */ for (;;) { ItemId lp; HeapTupleData heapTuple; /* check for bogus TID */ if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp)) break; lp = PageGetItemId(dp, offnum); /* check for unused, dead, or redirected items */ if (!ItemIdIsNormal(lp)) { /* We should only see a redirect at start of chain */ if (ItemIdIsRedirected(lp) && at_chain_start) { /* Follow the redirect */ offnum = ItemIdGetRedirect(lp); at_chain_start = false; continue; } /* else must be end of chain */ break; } heapTuple.t_data = (HeapTupleHeader) PageGetItem(dp, lp); heapTuple.t_len = ItemIdGetLength(lp); /* * Shouldn't see a HEAP_ONLY tuple at chain start. */ if (at_chain_start && HeapTupleIsHeapOnly(&heapTuple)) break; /* * The xmin should match the previous xmax value, else chain is * broken. */ if (TransactionIdIsValid(prev_xmax) && !TransactionIdEquals(prev_xmax, HeapTupleHeaderGetXmin(heapTuple.t_data))) break; /* If it's visible per the snapshot, we must return it */ if (HeapTupleSatisfiesVisibility(&heapTuple, snapshot, buffer)) { ItemPointerSetOffsetNumber(tid, offnum); if (all_dead) *all_dead = false; return true; } /* * If we can't see it, maybe no one else can either. At caller * request, check whether all chain members are dead to all * transactions. */ if (all_dead && *all_dead && HeapTupleSatisfiesVacuum(heapTuple.t_data, RecentGlobalXmin, buffer) != HEAPTUPLE_DEAD) *all_dead = false; /* * Check to see if HOT chain continues past this tuple; if so fetch * the next offnum and loop around. */ if (HeapTupleIsHotUpdated(&heapTuple)) { Assert(ItemPointerGetBlockNumber(&heapTuple.t_data->t_ctid) == ItemPointerGetBlockNumber(tid)); offnum = ItemPointerGetOffsetNumber(&heapTuple.t_data->t_ctid); at_chain_start = false; prev_xmax = HeapTupleHeaderGetXmax(heapTuple.t_data); } else break; /* end of chain */ } return false;}/* * heap_hot_search - search HOT chain for tuple satisfying snapshot * * This has the same API as heap_hot_search_buffer, except that the caller * does not provide the buffer containing the page, rather we access it * locally. */boolheap_hot_search(ItemPointer tid, Relation relation, Snapshot snapshot, bool *all_dead){ bool result; Buffer buffer; buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_SHARE); result = heap_hot_search_buffer(tid, buffer, snapshot, all_dead); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); return result;}/* * heap_get_latest_tid - get the latest tid of a specified tuple * * Actually, this gets the latest version that is visible according to * the passed snapshot. You can pass SnapshotDirty to get the very latest, * possibly uncommitted version. * * *tid is both an input and an output parameter: it is updated to * show the latest version of the row. Note that it will not be changed * if no version of the row passes the snapshot test. */voidheap_get_latest_tid(Relation relation, Snapshot snapshot, ItemPointer tid){ BlockNumber blk; ItemPointerData ctid; TransactionId priorXmax; /* this is to avoid Assert failures on bad input */ if (!ItemPointerIsValid(tid)) return; /* * Since this can be called with user-supplied TID, don't trust the input * too much. (RelationGetNumberOfBlocks is an expensive check, so we * don't check t_ctid links again this way. Note that it would not do to * call it just once and save the result, either.) */ blk = ItemPointerGetBlockNumber(tid); if (blk >= RelationGetNumberOfBlocks(relation)) elog(ERROR, "block number %u is out of range for relation \"%s\"", blk, RelationGetRelationName(relation)); /* * Loop to chase down t_ctid links. At top of loop, ctid is the tuple we * need to examine, and *tid is the TID we will return if ctid turns out * to be bogus. * * Note that we will loop until we reach the end of the t_ctid chain. * Depending on the snapshot passed, there might be at most one visible * version of the row, but we don't try to optimize for that. */ ctid = *tid; priorXmax = InvalidTransactionId; /* cannot check first XMIN */ for (;;) { Buffer buffer; PageHeader dp; OffsetNumber offnum; ItemId lp; HeapTupleData tp; bool valid; /* * Read, pin, and lock the page. */ buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(&ctid)); LockBuffer(buffer, BUFFER_LOCK_SHARE); dp = (PageHeader) BufferGetPage(buffer); /* * Check for bogus item number. This is not treated as an error * condition because it can happen while following a t_ctid link. We * just assume that the prior tid is OK and return it unchanged. */ offnum = ItemPointerGetOffsetNumber(&ctid); if (offnum < FirstOffsetNumber || offnum > PageGetMaxOffsetNumber(dp)) { UnlockReleaseBuffer(buffer); break; } lp = PageGetItemId(dp, offnum); if (!ItemIdIsNormal(lp)) { UnlockReleaseBuffer(buffer); break; } /* OK to access the tuple */ tp.t_self = ctid; tp.t_data = (HeapTupleHeader) PageGetItem(dp, lp); tp.t_len = ItemIdGetLength(lp); /* * After following a t_ctid link, we might arrive at an unrelated * tuple. Check for XMIN match. */ if (TransactionIdIsValid(priorXmax) && !TransactionIdEquals(priorXmax, HeapTupleHeaderGetXmin(tp.t_data))) { UnlockReleaseBuffer(buffer); break; } /* * Check time qualification of tuple; if visible, set it as the new * result candidate. */ valid = HeapTupleSatisfiesVisibility(&tp, snapshot, buffer); if (valid) *tid = ctid; /* * If there's a valid t_ctid link, follow it, else we're done. */ if ((tp.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_IS_LOCKED)) || ItemPointerEquals(&tp.t_self, &tp.t_data->t_ctid)) { UnlockReleaseBuffer(buffer); break; } ctid = tp.t_data->t_ctid; priorXmax = HeapTupleHeaderGetXmax(tp.t_data); UnlockReleaseBuffer(buffer); } /* end of loop */}/* * UpdateXmaxHintBits - update tuple hint bits after xmax transaction ends * * This is called after we have waited for the XMAX transaction to terminate. * If the transaction aborted, we guarantee the XMAX_INVALID hint bit will * be set on exit. If the transaction committed, we set the XMAX_COMMITTED * hint bit if possible --- but beware that that may not yet be possible, * if the transaction committed asynchronously. Hence callers should look * only at XMAX_INVALID. */static voidUpdateXmaxHintBits(HeapTupleHeader tuple, Buffer buffer, TransactionId xid){ Assert(TransactionIdEquals(HeapTupleHeaderGetXmax(tuple), xid)); if (!(tuple->t_infomask & (HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID))) { if (TransactionIdDidCommit(xid)) HeapTupleSetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED, xid); else HeapTupleSetHintBits(tuple, buffer, HEAP_XMAX_INVALID, InvalidTransactionId); }}/* * heap_insert - insert tuple into a heap * * The new tuple is stamped with current transaction ID and the specified * command ID. * * If use_wal is false, the new tuple is not logged in WAL, even for a * non-temp relation. Safe usage of this behavior requires that we arrange * that all new tuples go into new pages not containing any tuples from other * transactions, and that the relation gets fsync'd before commit. * (See also heap_sync() comments) * * use_fsm is passed directly to RelationGetBufferForTuple, which see for * more info. * * Note that use_wal and use_fsm will be applied when inserting into the * heap's TOAST table, too, if the tuple requires any out-of-line data. * * The return value is the OID assigned to the tuple (either here or by the * caller), or InvalidOid if no OID. The header fields of *tup are updated * to match the stored tuple; in particular tup->t_self receives the actual * TID where the tuple was stored. But note that any toasting of fields * within the tuple data is NOT reflected into *tup. */Oidheap_insert(Relation relation, HeapTuple tup, CommandId cid, bool use_wal, bool use_fsm){ TransactionId xid = GetCurrentTransactionId(); HeapTuple heaptup; Buffer buffer; if (relation->rd_rel->relhasoids) {#ifdef NOT_USED /* this is redundant with an Assert in HeapTupleSetOid */ Assert(tup->t_data->t_infomask & HEAP_HASOID);#endif /* * If the object id of this tuple has already been assigned, trust the * caller. There are a couple of ways this can happen. At initial db * creation, the backend program sets oids for tuples. When we define * an index, we set the oid. Finally, in the future, we may allow * users to set their own object ids in order to support a persistent * object store (objects need to contain pointers to one another). */ if (!OidIsValid(HeapTupleGetOid(tup))) HeapTupleSetOid(tup, GetNewOid(relation)); } else { /* check there is not space for an OID */ Assert(!(tup->t_data->t_infomask & HEAP_HASOID)); } tup->t_data->t_infomask &= ~(HEAP_XACT_MASK); tup->t_data->t_infomask2 &= ~(HEAP2_XACT_MASK); tup->t_data->t_infomask |= HEAP_XMAX_INVALID; HeapTupleHeaderSetXmin(tup->t_data, xid); HeapTupleHeaderSetCmin(tup->t_data, cid); HeapTupleHeaderSetXmax(tup->t_data, 0); /* for cleanliness */ tup->t_tableOid = RelationGetRelid(relation); /* * If the new tuple is too big for storage or contains already toasted * out-of-line attributes from some other relation, invoke the toaster. * * Note: below this point, heaptup is the data we actually intend to store * into the relation; tup is the caller's original untoasted data. */ if (relation->rd_rel->relkind != RELKIND_RELATION) { /* toast table entries should never be recursively toasted */ Assert(!HeapTupleHasExternal(tup)); heaptup = tup; } else if (HeapTupleHasExternal(tup) || tup->t_len > TOAST_TUPLE_THRESHOLD) heaptup = toast_insert_or_update(relation, tup, NULL, use_wal, use_fsm); else heaptup = tup; /* Find buffer to insert this tuple into */ buffer = RelationGetBufferForTuple(relation, heaptup->t_len, InvalidBuffer, use_fsm); /* NO EREPORT(ERROR) from here till changes are logged */ START_CRIT_SECTION(); RelationPutHeapTuple(relation, buffer, heaptup); /* * XXX Should we set PageSetPrunable on this page ? * * The inserting transaction may eventually abort thus making this tuple * DEAD and hence available for pruning. Though we don't want to optimize * for aborts, if no other tuple in this page is UPDATEd/DELETEd, the * aborted tuple will never be pruned until next vacuum is triggered. * * If you do add PageSetPrunable here, add it in heap_xlog_insert too. */ MarkBufferDirty(buffer); /* XLOG stuff */ if (use_wal && !relation->rd_istemp) { xl_heap_insert xlrec; xl_heap_header xlhdr; XLogRecPtr recptr; XLogRecData rdata[3]; Page page = BufferGetPage(buffer); uint8 info = XLOG_HEAP_INSERT; xlrec.target.node = relation->rd_node; xlrec.target.tid = heaptup->t_self; rdata[0].data = (char *) &xlrec;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -