heapam.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,332 行 · 第 1/5 页
C
2,332 行
rdata[0].len = SizeOfHeapInsert; rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); xlhdr.t_infomask2 = heaptup->t_data->t_infomask2; 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(); UnlockReleaseBuffer(buffer); /* * If tuple is cachable, mark it for invalidation from the caches in case * we abort. Note it is OK to do this after releasing 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); /* * 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 and not allowing access to the speedup options. * * This 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, 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; bool iscombo; Assert(ItemPointerIsValid(tid)); buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); dp = (PageHeader) BufferGetPage(buffer); lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid)); Assert(ItemIdIsNormal(lp)); 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) { UnlockReleaseBuffer(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. */ if (infomask & HEAP_XMAX_IS_MULTI) { /* wait for multixact */ MultiXactIdWait((MultiXactId) xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* * If xwait had just locked the tuple then some other xact could * update this tuple before we get to this point. Check for xmax * change, and start over if so. */ if (!(tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI) || !TransactionIdEquals(HeapTupleHeaderGetXmax(tp.t_data), xwait)) goto l1; /* * You might think the multixact is necessarily done here, but not * so: it could have surviving members, namely our own xact or * other subxacts of this backend. It is legal for us to delete * the tuple in either case, however (the latter case is * essentially a situation of upgrading our former shared lock to * exclusive). We don't bother changing the on-disk hint bits * since we are about to overwrite the xmax altogether. */ } else { /* wait for regular transaction to end */ XactLockTableWait(xwait); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); /* * xwait is done, but if xwait had just locked the tuple then some * other xact could update this tuple before we get to this point. * Check for xmax change, and start over if so. */ if ((tp.t_data->t_infomask & HEAP_XMAX_IS_MULTI) || !TransactionIdEquals(HeapTupleHeaderGetXmax(tp.t_data), xwait)) goto l1; /* Otherwise check if it committed or aborted */ UpdateXmaxHintBits(tp.t_data, buffer, xwait); } /* * We may overwrite if previous xmax aborted, or if it committed but * only locked the tuple without updating it. */ if (tp.t_data->t_infomask & (HEAP_XMAX_INVALID | HEAP_IS_LOCKED)) result = HeapTupleMayBeUpdated; else result = HeapTupleUpdated; } if (crosscheck != InvalidSnapshot && result == HeapTupleMayBeUpdated) { /* Perform additional check for serializable RI updates */ if (!HeapTupleSatisfiesVisibility(&tp, crosscheck, buffer)) result = HeapTupleUpdated; } if (result != HeapTupleMayBeUpdated) { Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated || result == HeapTupleBeingUpdated); Assert(!(tp.t_data->t_infomask & HEAP_XMAX_INVALID)); *ctid = tp.t_data->t_ctid; *update_xmax = HeapTupleHeaderGetXmax(tp.t_data); UnlockReleaseBuffer(buffer); if (have_tuple_lock) UnlockTuple(relation, &(tp.t_self), ExclusiveLock); return result; } /* replace cid with a combo cid if necessary */ HeapTupleHeaderAdjustCmax(tp.t_data, &cid, &iscombo); START_CRIT_SECTION(); /* * If this transaction commits, the tuple will become DEAD sooner or * later. Set flag that this page is a candidate for pruning once our xid * falls below the OldestXmin horizon. If the transaction finally aborts, * the subsequent page pruning will be a no-op and the hint will be * cleared. */ PageSetPrunable(dp, xid); /* store transaction information of xact deleting the tuple */ tp.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED | HEAP_XMAX_INVALID | HEAP_XMAX_IS_MULTI | HEAP_IS_LOCKED | HEAP_MOVED); HeapTupleHeaderClearHotUpdated(tp.t_data); HeapTupleHeaderSetXmax(tp.t_data, xid); HeapTupleHeaderSetCmax(tp.t_data, cid, iscombo); /* Make sure there is no forward chain link in t_ctid */ tp.t_data->t_ctid = tp.t_self; MarkBufferDirty(buffer); /* XLOG stuff */ if (!relation->rd_istemp) { xl_heap_delete xlrec; XLogRecPtr recptr; XLogRecData rdata[2]; xlrec.target.node = relation->rd_node; xlrec.target.tid = tp.t_self; rdata[0].data = (char *) &xlrec; rdata[0].len = SizeOfHeapDelete; rdata[0].buffer = InvalidBuffer; rdata[0].next = &(rdata[1]); rdata[1].data = NULL; rdata[1].len = 0; rdata[1].buffer = buffer; rdata[1].buffer_std = true; rdata[1].next = NULL; recptr = XLogInsert(RM_HEAP_ID, XLOG_HEAP_DELETE, rdata); PageSetLSN(dp, recptr); PageSetTLI(dp, ThisTimeLineID); } END_CRIT_SECTION(); LockBuffer(buffer, BUFFER_LOCK_UNLOCK); /* * If the tuple has toasted out-of-line attributes, we need to delete * those items too. We have to do this before releasing the buffer * because we need to look at the contents of the tuple, but it's OK to * release the content lock on the buffer first. */ if (relation->rd_rel->relkind != RELKIND_RELATION) { /* toast table entries should never be recursively toasted */ Assert(!HeapTupleHasExternal(&tp)); } else if (HeapTupleHasExternal(&tp)) toast_delete(relation, &tp); /* * Mark tuple for invalidation from system caches at next command * boundary. We have to do this before releasing the buffer because we * need to look at the contents of the tuple. */ CacheInvalidateHeapTuple(relation, &tp); /* Now we can release the buffer */ ReleaseBuffer(buffer); /* * Release the lmgr tuple lock, if we had it. */ if (have_tuple_lock) UnlockTuple(relation, &(tp.t_self), ExclusiveLock); pgstat_count_heap_delete(relation); return HeapTupleMayBeUpdated;}/* * simple_heap_delete - delete a tuple * * This routine may be used to delete a tuple when concurrent updates of * the target tuple are not expected (for example, because we have a lock * on the relation associated with the tuple). Any failure is reported * via ereport(). */voidsimple_heap_delete(Relation relation, ItemPointer tid){ HTSU_Result result; ItemPointerData update_ctid; TransactionId update_xmax; result = heap_delete(relation, tid, &update_ctid, &update_xmax, GetCurrentCommandId(true), InvalidSnapshot, true /* wait for commit */ ); switch (result) { case HeapTupleSelfUpdated: /* Tuple was already updated in current command? */ elog(ERROR, "tuple already updated by self"); break; case HeapTupleMayBeUpdated: /* done successfully */ break; case HeapTupleUpdated: elog(ERROR, "tuple concurrently updated"); break; default: elog(ERROR, "unrecognized heap_delete status: %u", result); break; }}/* * heap_update - replace a tuple * * NB: do not call this directly unless you are prepared to deal with * concurrent-update conditions. Use simple_heap_update instead. * * relation - table to be modified (caller must hold suitable lock) * otid - TID of old tuple to be replaced * newtup - newly constructed tuple data to store * ctid - output parameter, used only for failure case (see below) * update_xmax - output parameter, used only for failure case (see below) * cid - update command ID (used for visibility test, and stored into * cmax/cmin if successful) * crosscheck - if not InvalidSnapshot, also check old 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* update it. Failure return codes are * HeapTupleSelfUpdated, HeapTupleUpdated, or HeapTupleBeingUpdated * (the last only possible if wait == false). * * On success, the header fields of *newtup are updated to match the new * stored tuple; in particular, newtup->t_self is set to the TID where the * new tuple was inserted, and its HEAP_ONLY_TUPLE flag is set iff a HOT * update was done. However, any TOAST changes in the new tuple's * data are not reflected into *newtup. * * In the failure cases, the routine returns the tuple's t_ctid and t_xmax. * If t_ctid is the same as otid, 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_update(Relation relation, ItemPointer otid, HeapTuple newtup, ItemPointer ctid, TransactionId *update_xmax, CommandId cid, Snapshot crosscheck, bool wait){ HTSU_Result result; TransactionId xid = GetCurrentTransactionId(); Bitmapset *hot_attrs; ItemId lp; HeapTupleData oldtup; HeapTuple heaptup; PageHeader dp; Buffer buffer, newbuf; bool need_toast, already_marked; Size newtupsize, pagefree; bool have_tuple_lock = false; bool iscombo; bool use_hot_update = false; Assert(ItemPointerIsValid(otid)); /* * Fetch the list of attributes to be checked for HOT update. This is * wasted effort if we fail to update or have to put the new tuple on a * different page. But we must compute the list before obtaining buffer * lock --- in the worst case, if we are doing an update on one of the * relevant system catalogs, we could deadlock if we try to fetch the list * later. In any case, the relcache caches the data so this is usually * pretty cheap. * * Note that we get a copy here, so we need not worry about relcache flush * happening midway through. */ hot_attrs = RelationGetIndexAttrBitmap(relation); buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(otid)); LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE); dp = (PageHeader) BufferGetPage(buffer); lp = PageGe
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?