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 + -
显示快捷键?