📄 heapam.c
字号:
} /* * fill in *tuple fields */ tuple->t_datamcxt = NULL; 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 */ HeapTupleSatisfies(tuple, relation, buffer, dp, snapshot, 0, NULL, valid); 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 in *pgstat_info, if given. */ if (pgstat_info != NULL) pgstat_count_heap_fetch(pgstat_info); 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_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)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); break; } lp = PageGetItemId(dp, offnum); if (!ItemIdIsUsed(lp)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); break; } /* OK to access the tuple */ tp.t_self = ctid; tp.t_datamcxt = NULL; 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))) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); break; } /* * Check time qualification of tuple; if visible, set it as the new * result candidate. */ HeapTupleSatisfies(&tp, relation, buffer, dp, snapshot, 0, NULL, valid); 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)) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); break; } ctid = tp.t_data->t_ctid; priorXmax = HeapTupleHeaderGetXmax(tp.t_data); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); } /* end of loop */}/* * 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, that the relation gets fsync'd before commit, and that the * transaction emits at least one WAL record to ensure RecordTransactionCommit * will decide to WAL-log the commit. * * use_fsm is passed directly to RelationGetBufferForTuple, which see for * more info. * * 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_infomask |= HEAP_XMAX_INVALID; HeapTupleHeaderSetXmin(tup->t_data, xid); HeapTupleHeaderSetCmin(tup->t_data, cid); HeapTupleHeaderSetXmax(tup->t_data, 0); /* zero out Datum fields */ HeapTupleHeaderSetCmax(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 (HeapTupleHasExternal(tup) || (MAXALIGN(tup->t_len) > TOAST_TUPLE_THRESHOLD)) heaptup = toast_insert_or_update(relation, tup, NULL); 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); /* XLOG stuff */ if (relation->rd_istemp) { /* No XLOG record, but still need to flag that XID exists on disk */ MyXactMadeTempRelUpdate = true; } else if (use_wal) { 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; rdata[0].len = SizeOfHeapInsert; rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); xlhdr.t_natts = heaptup->t_data->t_natts; xlhdr.t_infomask = heaptup->t_data->t_infomask; xlhdr.t_hoff = heaptup->t_data->t_hoff; /* * note we mark rdata[1] as belonging to buffer; if XLogInsert decides * to write the whole page to the xlog, we don't need to store * xl_heap_header in the xlog. */ rdata[1].data = (char *) &xlhdr; rdata[1].len = SizeOfHeapHeader; rdata[1].buffer = buffer; rdata[1].buffer_std = true; rdata[1].next = &(rdata[2]); /* PG73FORMAT: write bitmap [+ padding] [+ oid] + data */ rdata[2].data = (char *) heaptup->t_data + offsetof(HeapTupleHeaderData, t_bits); rdata[2].len = heaptup->t_len - offsetof(HeapTupleHeaderData, t_bits); rdata[2].buffer = buffer; rdata[2].buffer_std = true; rdata[2].next = NULL; /* * If this is the single and first tuple on page, we can reinit the * page instead of restoring the whole thing. Set flag, and hide * buffer references from XLogInsert. */ if (ItemPointerGetOffsetNumber(&(heaptup->t_self)) == FirstOffsetNumber && PageGetMaxOffsetNumber(page) == FirstOffsetNumber) { info |= XLOG_HEAP_INIT_PAGE; rdata[1].buffer = rdata[2].buffer = InvalidBuffer; } recptr = XLogInsert(RM_HEAP_ID, info, rdata); PageSetLSN(page, recptr); PageSetTLI(page, ThisTimeLineID); } END_CRIT_SECTION(); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); WriteBuffer(buffer); /* * If tuple is cachable, mark it for invalidation from the caches in case * we abort. Note it is OK to do this after WriteBuffer releases the * buffer, because the heaptup data structure is all in local memory, not * in the shared buffer. */ CacheInvalidateHeapTuple(relation, heaptup); pgstat_count_heap_insert(&relation->pgstat_info); /* * If heaptup is a private copy, release it. Don't forget to copy t_self * back to the caller's image, too. */ if (heaptup != tup) { tup->t_self = heaptup->t_self; heap_freetuple(heaptup); } return HeapTupleGetOid(tup);}/* * simple_heap_insert - insert a tuple * * Currently, this routine differs from heap_insert only in supplying * a default command ID. But it should be used rather than using * heap_insert directly in most places where we are modifying system catalogs. */Oidsimple_heap_insert(Relation relation, HeapTuple tup){ return heap_insert(relation, tup, GetCurrentCommandId(), true, true);}/* * heap_delete - delete a tuple * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_delete instead. * * relation - table to be modified (caller must hold suitable lock) * tid - TID of tuple to be deleted * ctid - output parameter, used only for failure case (see below) * update_xmax - output parameter, used only for failure case (see below) * cid - delete command ID (used for visibility test, and stored into * cmax if successful) * crosscheck - if not InvalidSnapshot, also check tuple against this * wait - true if should wait for any conflicting update to commit/abort * * Normal, successful return value is HeapTupleMayBeUpdated, which * actually means we did delete it. Failure return codes are * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated * (the last only possible if wait == false). * * In the failure cases, the routine returns the tuple's t_ctid and t_xmax. * If t_ctid is the same as tid, the tuple was deleted; if different, the * tuple was updated, and t_ctid is the location of the replacement tuple. * (t_xmax is needed to verify that the replacement tuple matches.) */HTSU_Resultheap_delete(Relation relation, ItemPointer tid, ItemPointer ctid, TransactionId *update_xmax, CommandId cid, Snapshot crosscheck, bool wait){ HTSU_Result result; TransactionId xid = GetCurrentTransactionId(); ItemId lp; HeapTupleData tp; PageHeader dp; Buffer buffer; bool have_tuple_lock = false; Assert(ItemPointerIsValid(tid)); buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); dp = (PageHeader) BufferGetPage(buffer); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); tp.t_datamcxt = NULL; tp.t_data = (HeapTupleHeader) PageGetItem(dp, lp); tp.t_len = ItemIdGetLength(lp); tp.t_self = *tid;l1: result = HeapTupleSatisfiesUpdate(tp.t_data, cid, buffer); if (result == HeapTupleInvisible) { LockBuffer(buffer, BUFFER_LOCK_UNLOCK); ReleaseBuffer(buffer); elog(ERROR, "attempted to delete invisible tuple"); } else if (result == HeapTupleBeingUpdated && wait) { TransactionId xwait; uint16 infomask; /* must copy state data before unlocking buffer */ xwait = HeapTupleHeaderGetXmax(tp.t_data); infomask = tp.t_data->t_infomask; LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* * Acquire tuple lock to establish our priority for the tuple (see * heap_lock_tuple). LockTuple will release us when we are * next-in-line for the tuple. * * If we are forced to "start over" below, we keep the tuple lock; * this arranges that we stay at the head of the line while rechecking * tuple state. */ if (!have_tuple_lock) { LockTuple(relation, &(tp.t_self), ExclusiveLock); have_tuple_lock = true; } /* * Sleep until concurrent transaction ends. Note that we don't care * if the locker has an exclusive or shared lock, because we need * exclusive. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -