⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 heapam.c

📁 PostgreSQL 8.1.4的源码 适用于Linux下的开源数据库系统
💻 C
📖 第 1 页 / 共 5 页
字号:
									   HEAP_IS_LOCKED |									   HEAP_MOVED);		HeapTupleHeaderSetXmax(oldtup.t_data, xid);		HeapTupleHeaderSetCmax(oldtup.t_data, cid);		/* temporarily make it look not-updated */		oldtup.t_data->t_ctid = oldtup.t_self;		already_marked = true;		LockBuffer(buffer, BUFFER_LOCK_UNLOCK);		/*		 * Let the toaster do its thing, if needed.		 *		 * Note: below this point, heaptup is the data we actually intend to		 * store into the relation; newtup is the caller's original untoasted		 * data.		 */		if (need_toast)		{			heaptup = toast_insert_or_update(relation, newtup, &oldtup);			newtupsize = MAXALIGN(heaptup->t_len);		}		else			heaptup = newtup;		/*		 * Now, do we need a new page for the tuple, or not?  This is a bit		 * tricky since someone else could have added tuples to the page while		 * we weren't looking.  We have to recheck the available space after		 * reacquiring the buffer lock.  But don't bother to do that if the		 * former amount of free space is still not enough; it's unlikely		 * there's more free now than before.		 *		 * What's more, if we need to get a new page, we will need to acquire		 * buffer locks on both old and new pages.	To avoid deadlock against		 * some other backend trying to get the same two locks in the other		 * order, we must be consistent about the order we get the locks in.		 * We use the rule "lock the lower-numbered page of the relation		 * first".  To implement this, we must do RelationGetBufferForTuple		 * while not holding the lock on the old page, and we must rely on it		 * to get the locks on both pages in the correct order.		 */		if (newtupsize > pagefree)		{			/* Assume there's no chance to put heaptup on same page. */			newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,											   buffer, true);		}		else		{			/* Re-acquire the lock on the old tuple's page. */			LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);			/* Re-check using the up-to-date free space */			pagefree = PageGetFreeSpace((Page) dp);			if (newtupsize > pagefree)			{				/*				 * Rats, it doesn't fit anymore.  We must now unlock and				 * relock to avoid deadlock.  Fortunately, this path should				 * seldom be taken.				 */				LockBuffer(buffer, BUFFER_LOCK_UNLOCK);				newbuf = RelationGetBufferForTuple(relation, heaptup->t_len,												   buffer, true);			}			else			{				/* OK, it fits here, so we're done. */				newbuf = buffer;			}		}	}	else	{		/* No TOAST work needed, and it'll fit on same page */		already_marked = false;		newbuf = buffer;		heaptup = newtup;	}	/*	 * At this point newbuf and buffer are both pinned and locked, and newbuf	 * has enough space for the new tuple.	If they are the same buffer, only	 * one pin is held.	 */	/* NO EREPORT(ERROR) from here till changes are logged */	START_CRIT_SECTION();	RelationPutHeapTuple(relation, newbuf, heaptup);	/* insert new tuple */	if (!already_marked)	{		oldtup.t_data->t_infomask &= ~(HEAP_XMAX_COMMITTED |									   HEAP_XMAX_INVALID |									   HEAP_XMAX_IS_MULTI |									   HEAP_IS_LOCKED |									   HEAP_MOVED);		HeapTupleHeaderSetXmax(oldtup.t_data, xid);		HeapTupleHeaderSetCmax(oldtup.t_data, cid);	}	/* record address of new tuple in t_ctid of old one */	oldtup.t_data->t_ctid = heaptup->t_self;	/* XLOG stuff */	if (!relation->rd_istemp)	{		XLogRecPtr	recptr = log_heap_update(relation, buffer, oldtup.t_self,											 newbuf, heaptup, false);		if (newbuf != buffer)		{			PageSetLSN(BufferGetPage(newbuf), recptr);			PageSetTLI(BufferGetPage(newbuf), ThisTimeLineID);		}		PageSetLSN(BufferGetPage(buffer), recptr);		PageSetTLI(BufferGetPage(buffer), ThisTimeLineID);	}	else	{		/* No XLOG record, but still need to flag that XID exists on disk */		MyXactMadeTempRelUpdate = true;	}	END_CRIT_SECTION();	if (newbuf != buffer)		LockBuffer(newbuf, BUFFER_LOCK_UNLOCK);	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);	/*	 * Mark old tuple for invalidation from system caches at next command	 * boundary. We have to do this before WriteBuffer because we need to look	 * at the contents of the tuple, so we need to hold our refcount.	 */	CacheInvalidateHeapTuple(relation, &oldtup);	if (newbuf != buffer)		WriteBuffer(newbuf);	WriteBuffer(buffer);	/*	 * If new 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);	/*	 * Release the lmgr tuple lock, if we had it.	 */	if (have_tuple_lock)		UnlockTuple(relation, &(oldtup.t_self), ExclusiveLock);	pgstat_count_heap_update(&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 != newtup)	{		newtup->t_self = heaptup->t_self;		heap_freetuple(heaptup);	}	return HeapTupleMayBeUpdated;}/* *	simple_heap_update - replace a tuple * * This routine may be used to update 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_update(Relation relation, ItemPointer otid, HeapTuple tup){	HTSU_Result result;	ItemPointerData update_ctid;	TransactionId update_xmax;	result = heap_update(relation, otid, tup,						 &update_ctid, &update_xmax,						 GetCurrentCommandId(), 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_update status: %u", result);			break;	}}/* *	heap_lock_tuple - lock a tuple in shared or exclusive mode * * Note that this acquires a buffer pin, which the caller must release. * * Input parameters: *	relation: relation containing tuple (caller must hold suitable lock) *	tuple->t_self: TID of tuple to lock (rest of struct need not be valid) *	cid: current command ID (used for visibility test, and stored into *		tuple's cmax if lock is successful) *	mode: indicates if shared or exclusive tuple lock is desired *	nowait: if true, ereport rather than blocking if lock not available * * Output parameters: *	*tuple: all fields filled in *	*buffer: set to buffer holding tuple (pinned but not locked at exit) *	*ctid: set to tuple's t_ctid, but only in failure cases *	*update_xmax: set to tuple's xmax, but only in failure cases * * Function result may be: *	HeapTupleMayBeUpdated: lock was successfully acquired *	HeapTupleSelfUpdated: lock failed because tuple updated by self *	HeapTupleUpdated: lock failed because tuple updated by other xact * * In the failure cases, the routine returns the tuple's t_ctid and t_xmax. * If t_ctid is the same as t_self, 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.) * * * NOTES: because the shared-memory lock table is of finite size, but users * could reasonably want to lock large numbers of tuples, we do not rely on * the standard lock manager to store tuple-level locks over the long term. * Instead, a tuple is marked as locked by setting the current transaction's * XID as its XMAX, and setting additional infomask bits to distinguish this * usage from the more normal case of having deleted the tuple.  When * multiple transactions concurrently share-lock a tuple, the first locker's * XID is replaced in XMAX with a MultiTransactionId representing the set of * XIDs currently holding share-locks. * * When it is necessary to wait for a tuple-level lock to be released, the * basic delay is provided by XactLockTableWait or MultiXactIdWait on the * contents of the tuple's XMAX.  However, that mechanism will release all * waiters concurrently, so there would be a race condition as to which * waiter gets the tuple, potentially leading to indefinite starvation of * some waiters.  The possibility of share-locking makes the problem much * worse --- a steady stream of share-lockers can easily block an exclusive * locker forever.	To provide more reliable semantics about who gets a * tuple-level lock first, we use the standard lock manager.  The protocol * for waiting for a tuple-level lock is really *		LockTuple() *		XactLockTableWait() *		mark tuple as locked by me *		UnlockTuple() * When there are multiple waiters, arbitration of who is to get the lock next * is provided by LockTuple().	However, at most one tuple-level lock will * be held or awaited per backend at any time, so we don't risk overflow * of the lock table.  Note that incoming share-lockers are required to * do LockTuple as well, if there is any conflict, to ensure that they don't * starve out waiting exclusive-lockers.  However, if there is not any active * conflict for a tuple, we don't incur any extra overhead. */HTSU_Resultheap_lock_tuple(Relation relation, HeapTuple tuple, Buffer *buffer,				ItemPointer ctid, TransactionId *update_xmax,				CommandId cid, LockTupleMode mode, bool nowait){	HTSU_Result result;	ItemPointer tid = &(tuple->t_self);	ItemId		lp;	PageHeader	dp;	TransactionId xid;	uint16		new_infomask;	LOCKMODE	tuple_lock_type;	bool		have_tuple_lock = false;	tuple_lock_type = (mode == LockTupleShared) ? ShareLock : ExclusiveLock;	*buffer = ReadBuffer(relation, ItemPointerGetBlockNumber(tid));	LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);	dp = (PageHeader) BufferGetPage(*buffer);	lp = PageGetItemId(dp, ItemPointerGetOffsetNumber(tid));	Assert(ItemIdIsUsed(lp));	tuple->t_datamcxt = NULL;	tuple->t_data = (HeapTupleHeader) PageGetItem((Page) dp, lp);	tuple->t_len = ItemIdGetLength(lp);	tuple->t_tableOid = RelationGetRelid(relation);l3:	result = HeapTupleSatisfiesUpdate(tuple->t_data, cid, *buffer);	if (result == HeapTupleInvisible)	{		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);		ReleaseBuffer(*buffer);		elog(ERROR, "attempted to lock invisible tuple");	}	else if (result == HeapTupleBeingUpdated)	{		TransactionId xwait;		uint16		infomask;		/* must copy state data before unlocking buffer */		xwait = HeapTupleHeaderGetXmax(tuple->t_data);		infomask = tuple->t_data->t_infomask;		LockBuffer(*buffer, BUFFER_LOCK_UNLOCK);		/*		 * Acquire tuple lock to establish our priority for the tuple.		 * LockTuple will release us when we are next-in-line for the tuple.		 * We must do this even if we are share-locking.		 *		 * 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)		{			if (nowait)			{				if (!ConditionalLockTuple(relation, tid, tuple_lock_type))					ereport(ERROR,							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),					errmsg("could not obtain lock on row in relation \"%s\"",						   RelationGetRelationName(relation))));			}			else				LockTuple(relation, tid, tuple_lock_type);			have_tuple_lock = true;		}		if (mode == LockTupleShared && (infomask & HEAP_XMAX_SHARED_LOCK))		{			/*			 * Acquiring sharelock when there's at least one sharelocker			 * already.  We need not wait for him/them to complete.			 */			LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);			/*			 * Make sure it's still a shared lock, else start over.  (It's OK			 * if the ownership of the shared lock has changed, though.)			 */			if (!(tuple->t_data->t_infomask & HEAP_XMAX_SHARED_LOCK))				goto l3;		}		else if (infomask & HEAP_XMAX_IS_MULTI)		{			/* wait for multixact to end */			if (nowait)			{				if (!ConditionalMultiXactIdWait((MultiXactId) xwait))					ereport(ERROR,							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),					errmsg("could not obtain lock on row in relation \"%s\"",						   RelationGetRelationName(relation))));			}			else				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 (!(tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||				!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),									 xwait))				goto l3;			/*			 * 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 lock the			 * tuple in either case, however.  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 */			if (nowait)			{				if (!ConditionalXactLockTableWait(xwait))					ereport(ERROR,							(errcode(ERRCODE_LOCK_NOT_AVAILABLE),					errmsg("could not obtain lock on row in relation \"%s\"",						   RelationGetRelationName(relation))));			}			else				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 ((tuple->t_data->t_infomask & HEAP_XMAX_IS_MULTI) ||				!TransactionIdEquals(HeapTupleHeaderGetXmax(tuple->t_data),									 xwait))				goto l3;			/* Otherwise we can mark it committed or aborted */			if (!(tuple->t_data->t_infomask & (HEAP_XMAX_COMMITTED |											   HEAP_XMAX_INVALID)))			{				if (TransactionIdDidCommit(xwait))					tuple->t_data->t_infomask |= HEAP_XMAX_COMMITTED;				else					tuple->t_data->t_infomask |= HEAP_XMAX_INVALID;				SetBufferCommitInfoNeedsSave(*buffer);			}		}		/*		 * We may lock if previous xmax aborted, or if it committed but only		 * locked the tuple without updating it.  The case where we didn't		 * wait because we are joining an existing shared lock is correctly		 * handled, too.		 */		if (tuple->t_data->t_infomask & (HEAP_XMAX_INVALID |										 HEAP_IS_LOCKED))			result = HeapTupleMayBeUpdated;		else			result = HeapTupleUpdated;	}	if (result != HeapTupleMayBeUpdated)	{		Assert(result == HeapTupleSelfUpdated || result == HeapTupleUpdated);		Assert(!(tuple->t_data->t_infomask & HEAP_XMAX_INVAL

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -