tqual.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 1,436 行 · 第 1/3 页

C
1,436
字号
/*------------------------------------------------------------------------- * * tqual.c *	  POSTGRES "time qualification" code, ie, tuple visibility rules. * * NOTE: all the HeapTupleSatisfies routines will update the tuple's * "hint" status bits if we see that the inserting or deleting transaction * has now committed or aborted (and it is safe to set the hint bits). * If the hint bits are changed, SetBufferCommitInfoNeedsSave is called on * the passed-in buffer.  The caller must hold not only a pin, but at least * shared buffer content lock on the buffer containing the tuple. * * NOTE: must check TransactionIdIsInProgress (which looks in PGPROC array) * before TransactionIdDidCommit/TransactionIdDidAbort (which look in * pg_clog).  Otherwise we have a race condition: we might decide that a * just-committed transaction crashed, because none of the tests succeed. * xact.c is careful to record commit/abort in pg_clog before it unsets * MyProc->xid in PGPROC array.  That fixes that problem, but it also * means there is a window where TransactionIdIsInProgress and * TransactionIdDidCommit will both return true.  If we check only * TransactionIdDidCommit, we could consider a tuple committed when a * later GetSnapshotData call will still think the originating transaction * is in progress, which leads to application-level inconsistency.	The * upshot is that we gotta check TransactionIdIsInProgress first in all * code paths, except for a few cases where we are looking at * subtransactions of our own main transaction and so there can't be any * race condition. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/utils/time/tqual.c,v 1.109.2.1 2008/09/11 14:01:35 alvherre Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/multixact.h"#include "access/subtrans.h"#include "access/transam.h"#include "access/xact.h"#include "storage/bufmgr.h"#include "storage/procarray.h"#include "utils/tqual.h"/* Static variables representing various special snapshot semantics */SnapshotData SnapshotNowData = {HeapTupleSatisfiesNow};SnapshotData SnapshotSelfData = {HeapTupleSatisfiesSelf};SnapshotData SnapshotAnyData = {HeapTupleSatisfiesAny};SnapshotData SnapshotToastData = {HeapTupleSatisfiesToast};/* * These SnapshotData structs are static to simplify memory allocation * (see the hack in GetSnapshotData to avoid repeated malloc/free). */static SnapshotData SerializableSnapshotData = {HeapTupleSatisfiesMVCC};static SnapshotData LatestSnapshotData = {HeapTupleSatisfiesMVCC};/* Externally visible pointers to valid snapshots: */Snapshot	SerializableSnapshot = NULL;Snapshot	LatestSnapshot = NULL;/* * This pointer is not maintained by this module, but it's convenient * to declare it here anyway.  Callers typically assign a copy of * GetTransactionSnapshot's result to ActiveSnapshot. */Snapshot	ActiveSnapshot = NULL;/* * These are updated by GetSnapshotData.  We initialize them this way * for the convenience of TransactionIdIsInProgress: even in bootstrap * mode, we don't want it to say that BootstrapTransactionId is in progress. * * RecentGlobalXmin is initialized to InvalidTransactionId, to ensure that no * one tries to use a stale value.  Readers should ensure that it has been set * to something else before using it. */TransactionId TransactionXmin = FirstNormalTransactionId;TransactionId RecentXmin = FirstNormalTransactionId;TransactionId RecentGlobalXmin = InvalidTransactionId;/* local functions */static bool XidInMVCCSnapshot(TransactionId xid, Snapshot snapshot);/* * SetHintBits() * * Set commit/abort hint bits on a tuple, if appropriate at this time. * * It is only safe to set a transaction-committed hint bit if we know the * transaction's commit record has been flushed to disk.  We cannot change * the LSN of the page here because we may hold only a share lock on the * buffer, so we can't use the LSN to interlock this; we have to just refrain * from setting the hint bit until some future re-examination of the tuple. * * We can always set hint bits when marking a transaction aborted.	(Some * code in heapam.c relies on that!) * * Also, if we are cleaning up HEAP_MOVED_IN or HEAP_MOVED_OFF entries, then * we can always set the hint bits, since VACUUM FULL always uses synchronous * commits and doesn't move tuples that weren't previously hinted.	(This is * not known by this subroutine, but is applied by its callers.) * * Normal commits may be asynchronous, so for those we need to get the LSN * of the transaction and then check whether this is flushed. * * The caller should pass xid as the XID of the transaction to check, or * InvalidTransactionId if no check is needed. */static inline voidSetHintBits(HeapTupleHeader tuple, Buffer buffer,			uint16 infomask, TransactionId xid){	if (TransactionIdIsValid(xid))	{		/* NB: xid must be known committed here! */		XLogRecPtr	commitLSN = TransactionIdGetCommitLSN(xid);		if (XLogNeedsFlush(commitLSN))			return;				/* not flushed yet, so don't set hint */	}	tuple->t_infomask |= infomask;	SetBufferCommitInfoNeedsSave(buffer);}/* * HeapTupleSetHintBits --- exported version of SetHintBits() * * This must be separate because of C99's brain-dead notions about how to * implement inline functions. */voidHeapTupleSetHintBits(HeapTupleHeader tuple, Buffer buffer,					 uint16 infomask, TransactionId xid){	SetHintBits(tuple, buffer, infomask, xid);}/* * HeapTupleSatisfiesSelf *		True iff heap tuple is valid "for itself". * *	Here, we consider the effects of: *		all committed transactions (as of the current instant) *		previous commands of this transaction *		changes made by the current command * * Note: *		Assumes heap tuple is valid. * * The satisfaction of "itself" requires the following: * * ((Xmin == my-transaction &&				the row was updated by the current transaction, and *		(Xmax is null						it was not deleted *		 [|| Xmax != my-transaction)])			[or it was deleted by another transaction] * || * * (Xmin is committed &&					the row was modified by a committed transaction, and *		(Xmax is null ||					the row has not been deleted, or *			(Xmax != my-transaction &&			the row was deleted by another transaction *			 Xmax is not committed)))			that has not been committed */boolHeapTupleSatisfiesSelf(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer){	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))	{		if (tuple->t_infomask & HEAP_XMIN_INVALID)			return false;		if (tuple->t_infomask & HEAP_MOVED_OFF)		{			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);			if (TransactionIdIsCurrentTransactionId(xvac))				return false;			if (!TransactionIdIsInProgress(xvac))			{				if (TransactionIdDidCommit(xvac))				{					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,								InvalidTransactionId);					return false;				}				SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,							InvalidTransactionId);			}		}		else if (tuple->t_infomask & HEAP_MOVED_IN)		{			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);			if (!TransactionIdIsCurrentTransactionId(xvac))			{				if (TransactionIdIsInProgress(xvac))					return false;				if (TransactionIdDidCommit(xvac))					SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,								InvalidTransactionId);				else				{					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,								InvalidTransactionId);					return false;				}			}		}		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))		{			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */				return true;			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */				return true;			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))			{				/* deleting subtransaction must have aborted */				SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,							InvalidTransactionId);				return true;			}			return false;		}		else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))			return false;		else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))			SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,						HeapTupleHeaderGetXmin(tuple));		else		{			/* it must have aborted or crashed */			SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,						InvalidTransactionId);			return false;		}	}	/* by here, the inserting transaction has committed */	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */		return true;	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)	{		if (tuple->t_infomask & HEAP_IS_LOCKED)			return true;		return false;			/* updated by other */	}	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)	{		/* MultiXacts are currently only allowed to lock tuples */		Assert(tuple->t_infomask & HEAP_IS_LOCKED);		return true;	}	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))	{		if (tuple->t_infomask & HEAP_IS_LOCKED)			return true;		return false;	}	if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))		return true;	if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))	{		/* it must have aborted or crashed */		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,					InvalidTransactionId);		return true;	}	/* xmax transaction committed */	if (tuple->t_infomask & HEAP_IS_LOCKED)	{		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,					InvalidTransactionId);		return true;	}	SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,				HeapTupleHeaderGetXmax(tuple));	return false;}/* * HeapTupleSatisfiesNow *		True iff heap tuple is valid "now". * *	Here, we consider the effects of: *		all committed transactions (as of the current instant) *		previous commands of this transaction * * Note we do _not_ include changes made by the current command.  This * solves the "Halloween problem" wherein an UPDATE might try to re-update * its own output tuples. * * Note: *		Assumes heap tuple is valid. * * The satisfaction of "now" requires the following: * * ((Xmin == my-transaction &&				inserted by the current transaction *	 Cmin < my-command &&					before this command, and *	 (Xmax is null ||						the row has not been deleted, or *	  (Xmax == my-transaction &&			it was deleted by the current transaction *	   Cmax >= my-command)))				but not before this command, * ||										or *	(Xmin is committed &&					the row was inserted by a committed transaction, and *		(Xmax is null ||					the row has not been deleted, or *		 (Xmax == my-transaction &&			the row is being deleted by this transaction *		  Cmax >= my-command) ||			but it's not deleted "yet", or *		 (Xmax != my-transaction &&			the row was deleted by another transaction *		  Xmax is not committed))))			that has not been committed * *		mao says 17 march 1993:  the tests in this routine are correct; *		if you think they're not, you're wrong, and you should think *		about it again.  i know, it happened to me.  we don't need to *		check commit time against the start time of this transaction *		because 2ph locking protects us from doing the wrong thing. *		if you mess around here, you'll break serializability.  the only *		problem with this code is that it does the wrong thing for system *		catalog updates, because the catalogs aren't subject to 2ph, so *		the serializability guarantees we provide don't extend to xacts *		that do catalog accesses.  this is unfortunate, but not critical. */boolHeapTupleSatisfiesNow(HeapTupleHeader tuple, Snapshot snapshot, Buffer buffer){	if (!(tuple->t_infomask & HEAP_XMIN_COMMITTED))	{		if (tuple->t_infomask & HEAP_XMIN_INVALID)			return false;		if (tuple->t_infomask & HEAP_MOVED_OFF)		{			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);			if (TransactionIdIsCurrentTransactionId(xvac))				return false;			if (!TransactionIdIsInProgress(xvac))			{				if (TransactionIdDidCommit(xvac))				{					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,								InvalidTransactionId);					return false;				}				SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,							InvalidTransactionId);			}		}		else if (tuple->t_infomask & HEAP_MOVED_IN)		{			TransactionId xvac = HeapTupleHeaderGetXvac(tuple);			if (!TransactionIdIsCurrentTransactionId(xvac))			{				if (TransactionIdIsInProgress(xvac))					return false;				if (TransactionIdDidCommit(xvac))					SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,								InvalidTransactionId);				else				{					SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,								InvalidTransactionId);					return false;				}			}		}		else if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmin(tuple)))		{			if (HeapTupleHeaderGetCmin(tuple) >= GetCurrentCommandId(false))				return false;	/* inserted after scan started */			if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid */				return true;			if (tuple->t_infomask & HEAP_IS_LOCKED)		/* not deleter */				return true;			Assert(!(tuple->t_infomask & HEAP_XMAX_IS_MULTI));			if (!TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))			{				/* deleting subtransaction must have aborted */				SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,							InvalidTransactionId);				return true;			}			if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))				return true;	/* deleted after scan started */			else				return false;	/* deleted before scan started */		}		else if (TransactionIdIsInProgress(HeapTupleHeaderGetXmin(tuple)))			return false;		else if (TransactionIdDidCommit(HeapTupleHeaderGetXmin(tuple)))			SetHintBits(tuple, buffer, HEAP_XMIN_COMMITTED,						HeapTupleHeaderGetXmin(tuple));		else		{			/* it must have aborted or crashed */			SetHintBits(tuple, buffer, HEAP_XMIN_INVALID,						InvalidTransactionId);			return false;		}	}	/* by here, the inserting transaction has committed */	if (tuple->t_infomask & HEAP_XMAX_INVALID)	/* xid invalid or aborted */		return true;	if (tuple->t_infomask & HEAP_XMAX_COMMITTED)	{		if (tuple->t_infomask & HEAP_IS_LOCKED)			return true;		return false;	}	if (tuple->t_infomask & HEAP_XMAX_IS_MULTI)	{		/* MultiXacts are currently only allowed to lock tuples */		Assert(tuple->t_infomask & HEAP_IS_LOCKED);		return true;	}	if (TransactionIdIsCurrentTransactionId(HeapTupleHeaderGetXmax(tuple)))	{		if (tuple->t_infomask & HEAP_IS_LOCKED)			return true;		if (HeapTupleHeaderGetCmax(tuple) >= GetCurrentCommandId(false))			return true;		/* deleted after scan started */		else			return false;		/* deleted before scan started */	}	if (TransactionIdIsInProgress(HeapTupleHeaderGetXmax(tuple)))		return true;	if (!TransactionIdDidCommit(HeapTupleHeaderGetXmax(tuple)))	{		/* it must have aborted or crashed */		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,					InvalidTransactionId);		return true;	}	/* xmax transaction committed */	if (tuple->t_infomask & HEAP_IS_LOCKED)	{		SetHintBits(tuple, buffer, HEAP_XMAX_INVALID,					InvalidTransactionId);		return true;	}	SetHintBits(tuple, buffer, HEAP_XMAX_COMMITTED,				HeapTupleHeaderGetXmax(tuple));	return false;}

⌨️ 快捷键说明

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