transam.c 11.9 KB
Newer Older
1 2
/*-------------------------------------------------------------------------
 *
3
 * transam.c
4
 *	  postgres transaction log interface routines
5
 *
6
 * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
B
Add:  
Bruce Momjian 已提交
7
 * Portions Copyright (c) 1994, Regents of the University of California
8 9 10
 *
 *
 * IDENTIFICATION
11
 *	  $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.70 2007/08/01 22:45:07 tgl Exp $
12 13
 *
 * NOTES
14 15 16
 *	  This file contains the high level access-method interface to the
 *	  transaction system.
 *
17 18
 *-------------------------------------------------------------------------
 */
M
-Wall'd  
Marc G. Fournier 已提交
19

20
#include "postgres.h"
21

22
#include "access/clog.h"
23
#include "access/subtrans.h"
24
#include "access/transam.h"
25
#include "utils/tqual.h"
26

27

28
static XidStatus TransactionLogFetch(TransactionId transactionId);
29
static void TransactionLogUpdate(TransactionId transactionId,
30
					 XidStatus status, XLogRecPtr lsn);
31

32 33
/*
 * Single-item cache for results of TransactionLogFetch.
34
 */
35 36
static TransactionId cachedFetchXid = InvalidTransactionId;
static XidStatus cachedFetchXidStatus;
37 38 39 40
static XLogRecPtr cachedCommitLSN;

/* Handy constant for an invalid xlog recptr */
static const XLogRecPtr InvalidXLogRecPtr = {0, 0};
41 42 43


/* ----------------------------------------------------------------
V
Vadim B. Mikheev 已提交
44
 *		postgres log access method interface
45
 *
46
 *		TransactionLogFetch
47
 *		TransactionLogUpdate
48 49 50
 * ----------------------------------------------------------------
 */

51 52
/*
 * TransactionLogFetch --- fetch commit status of specified transaction id
53
 */
54 55
static XidStatus
TransactionLogFetch(TransactionId transactionId)
56
{
57
	XidStatus	xidstatus;
58
	XLogRecPtr	xidlsn;
59

60
	/*
B
Bruce Momjian 已提交
61 62
	 * Before going to the commit log manager, check our single item cache to
	 * see if we didn't just check the transaction status a moment ago.
63
	 */
64 65
	if (TransactionIdEquals(transactionId, cachedFetchXid))
		return cachedFetchXidStatus;
66

67
	/*
68
	 * Also, check to see if the transaction ID is a permanent one.
69
	 */
70
	if (!TransactionIdIsNormal(transactionId))
71
	{
72
		if (TransactionIdEquals(transactionId, BootstrapTransactionId))
73
			return TRANSACTION_STATUS_COMMITTED;
74
		if (TransactionIdEquals(transactionId, FrozenTransactionId))
75 76
			return TRANSACTION_STATUS_COMMITTED;
		return TRANSACTION_STATUS_ABORTED;
77 78
	}

79
	/*
80
	 * Get the transaction status.
81
	 */
82
	xidstatus = TransactionIdGetStatus(transactionId, &xidlsn);
83 84

	/*
B
Bruce Momjian 已提交
85 86
	 * DO NOT cache status for unfinished or sub-committed transactions! We
	 * only cache status that is guaranteed not to change.
87
	 */
88 89
	if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
		xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
90
	{
91
		cachedFetchXid = transactionId;
92
		cachedFetchXidStatus = xidstatus;
93
		cachedCommitLSN = xidlsn;
94 95
	}

96
	return xidstatus;
97 98 99
}

/* --------------------------------
100
 *		TransactionLogUpdate
101 102 103
 *
 * Store the new status of a transaction.  The commit record LSN must be
 * passed when recording an async commit; else it should be InvalidXLogRecPtr.
104 105
 * --------------------------------
 */
106 107 108
static inline void
TransactionLogUpdate(TransactionId transactionId,
					 XidStatus status, XLogRecPtr lsn)
109
{
110
	/*
111
	 * update the commit log
112
	 */
113
	TransactionIdSetStatus(transactionId, status, lsn);
114
}
115

116 117 118 119 120 121
/*
 * TransactionLogMultiUpdate
 *
 * Update multiple transaction identifiers to a given status.
 * Don't depend on this being atomic; it's not.
 */
122 123 124
static inline void
TransactionLogMultiUpdate(int nxids, TransactionId *xids,
						  XidStatus status, XLogRecPtr lsn)
125
{
B
Bruce Momjian 已提交
126
	int			i;
127 128 129 130

	Assert(nxids != 0);

	for (i = 0; i < nxids; i++)
131
		TransactionIdSetStatus(xids[i], status, lsn);
132 133 134
}

/* ----------------------------------------------------------------
135 136 137 138 139 140 141 142 143 144 145 146 147
 *						Interface functions
 *
 *		TransactionId DidCommit
 *		TransactionId DidAbort
 *		TransactionId IsInProgress
 *		========
 *		   these functions test the transaction status of
 *		   a specified transaction id.
 *
 *		TransactionId Commit
 *		TransactionId Abort
 *		========
 *		   these functions set the transaction status
148
 *		   of the specified xid.
149 150 151 152 153
 *
 * ----------------------------------------------------------------
 */

/* --------------------------------
154 155 156
 *		TransactionId DidCommit
 *		TransactionId DidAbort
 *		TransactionId IsInProgress
157 158 159 160
 * --------------------------------
 */

/*
B
Bruce Momjian 已提交
161
 * TransactionIdDidCommit
162
 *		True iff transaction associated with the identifier did commit.
163 164
 *
 * Note:
165
 *		Assumes transaction identifier is valid.
166
 */
167
bool							/* true if given transaction committed */
168 169
TransactionIdDidCommit(TransactionId transactionId)
{
170 171 172 173 174 175 176 177 178 179 180
	XidStatus	xidstatus;

	xidstatus = TransactionLogFetch(transactionId);

	/*
	 * If it's marked committed, it's committed.
	 */
	if (xidstatus == TRANSACTION_STATUS_COMMITTED)
		return true;

	/*
B
Bruce Momjian 已提交
181 182 183 184
	 * If it's marked subcommitted, we have to check the parent recursively.
	 * However, if it's older than TransactionXmin, we can't look at
	 * pg_subtrans; instead assume that the parent crashed without cleaning up
	 * its children.
185
	 *
186 187 188
	 * Originally we Assert'ed that the result of SubTransGetParent was not
	 * zero. However with the introduction of prepared transactions, there can
	 * be a window just after database startup where we do not have complete
B
Bruce Momjian 已提交
189 190 191 192
	 * knowledge in pg_subtrans of the transactions after TransactionXmin.
	 * StartupSUBTRANS() has ensured that any missing information will be
	 * zeroed.	Since this case should not happen under normal conditions, it
	 * seems reasonable to emit a WARNING for it.
193 194 195 196
	 */
	if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
	{
		TransactionId parentXid;
197

198
		if (TransactionIdPrecedes(transactionId, TransactionXmin))
199
			return false;
200
		parentXid = SubTransGetParent(transactionId);
201 202 203 204 205 206
		if (!TransactionIdIsValid(parentXid))
		{
			elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
				 transactionId);
			return false;
		}
207 208 209
		return TransactionIdDidCommit(parentXid);
	}

B
Bruce Momjian 已提交
210
	/*
211 212 213
	 * It's not committed.
	 */
	return false;
214 215 216
}

/*
217
 * TransactionIdDidAbort
218
 *		True iff transaction associated with the identifier did abort.
219 220
 *
 * Note:
221
 *		Assumes transaction identifier is valid.
222
 */
223
bool							/* true if given transaction aborted */
224 225
TransactionIdDidAbort(TransactionId transactionId)
{
226 227 228
	XidStatus	xidstatus;

	xidstatus = TransactionLogFetch(transactionId);
229

230 231 232 233 234 235 236
	/*
	 * If it's marked aborted, it's aborted.
	 */
	if (xidstatus == TRANSACTION_STATUS_ABORTED)
		return true;

	/*
B
Bruce Momjian 已提交
237 238 239 240
	 * If it's marked subcommitted, we have to check the parent recursively.
	 * However, if it's older than TransactionXmin, we can't look at
	 * pg_subtrans; instead assume that the parent crashed without cleaning up
	 * its children.
241 242
	 */
	if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
243
	{
244 245
		TransactionId parentXid;

246
		if (TransactionIdPrecedes(transactionId, TransactionXmin))
247 248
			return true;
		parentXid = SubTransGetParent(transactionId);
249 250 251 252 253 254 255
		if (!TransactionIdIsValid(parentXid))
		{
			/* see notes in TransactionIdDidCommit */
			elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
				 transactionId);
			return true;
		}
256
		return TransactionIdDidAbort(parentXid);
257
	}
258

259 260 261 262
	/*
	 * It's not aborted.
	 */
	return false;
263 264 265
}

/* --------------------------------
266 267
 *		TransactionId Commit
 *		TransactionId Abort
268 269 270 271
 * --------------------------------
 */

/*
B
Bruce Momjian 已提交
272
 * TransactionIdCommit
273
 *		Commits the transaction associated with the identifier.
274 275
 *
 * Note:
276
 *		Assumes transaction identifier is valid.
277 278 279 280
 */
void
TransactionIdCommit(TransactionId transactionId)
{
281 282 283 284 285 286 287 288 289 290 291 292
	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED,
						 InvalidXLogRecPtr);
}

/*
 * TransactionIdAsyncCommit
 *		Same as above, but for async commits.  The commit record LSN is needed.
 */
void
TransactionIdAsyncCommit(TransactionId transactionId, XLogRecPtr lsn)
{
	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_COMMITTED, lsn);
293 294
}

295

296
/*
B
Bruce Momjian 已提交
297
 * TransactionIdAbort
298
 *		Aborts the transaction associated with the identifier.
299 300
 *
 * Note:
301
 *		Assumes transaction identifier is valid.
302
 *		No async version of this is needed.
303 304 305 306
 */
void
TransactionIdAbort(TransactionId transactionId)
{
307 308
	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED,
						 InvalidXLogRecPtr);
309
}
310

311 312 313 314
/*
 * TransactionIdSubCommit
 *		Marks the subtransaction associated with the identifier as
 *		sub-committed.
315 316 317
 *
 * Note:
 *		No async version of this is needed.
318 319 320 321
 */
void
TransactionIdSubCommit(TransactionId transactionId)
{
322 323
	TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED,
						 InvalidXLogRecPtr);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338
}

/*
 * TransactionIdCommitTree
 *		Marks all the given transaction ids as committed.
 *
 * The caller has to be sure that this is used only to mark subcommitted
 * subtransactions as committed, and only *after* marking the toplevel
 * parent as committed.  Otherwise there is a race condition against
 * TransactionIdDidCommit.
 */
void
TransactionIdCommitTree(int nxids, TransactionId *xids)
{
	if (nxids > 0)
339 340
		TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
								  InvalidXLogRecPtr);
341 342
}

343 344 345 346 347 348 349 350 351 352 353 354 355
/*
 * TransactionIdAsyncCommitTree
 *		Same as above, but for async commits.  The commit record LSN is needed.
 */
void
TransactionIdAsyncCommitTree(int nxids, TransactionId *xids, XLogRecPtr lsn)
{
	if (nxids > 0)
		TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED,
								  lsn);
}


356 357 358 359 360 361 362 363 364 365 366
/*
 * TransactionIdAbortTree
 *		Marks all the given transaction ids as aborted.
 *
 * We don't need to worry about the non-atomic behavior, since any onlookers
 * will consider all the xacts as not-yet-committed anyway.
 */
void
TransactionIdAbortTree(int nxids, TransactionId *xids)
{
	if (nxids > 0)
367 368
		TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_ABORTED,
								  InvalidXLogRecPtr);
369
}
370 371 372 373 374 375 376 377 378

/*
 * TransactionIdPrecedes --- is id1 logically < id2?
 */
bool
TransactionIdPrecedes(TransactionId id1, TransactionId id2)
{
	/*
	 * If either ID is a permanent XID then we can just do unsigned
379
	 * comparison.	If both are normal, do a modulo-2^31 comparison.
380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
	 */
	int32		diff;

	if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
		return (id1 < id2);

	diff = (int32) (id1 - id2);
	return (diff < 0);
}

/*
 * TransactionIdPrecedesOrEquals --- is id1 logically <= id2?
 */
bool
TransactionIdPrecedesOrEquals(TransactionId id1, TransactionId id2)
{
	int32		diff;

	if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
		return (id1 <= id2);

	diff = (int32) (id1 - id2);
	return (diff <= 0);
}

/*
 * TransactionIdFollows --- is id1 logically > id2?
 */
bool
TransactionIdFollows(TransactionId id1, TransactionId id2)
{
	int32		diff;

	if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
		return (id1 > id2);

	diff = (int32) (id1 - id2);
	return (diff > 0);
}

/*
 * TransactionIdFollowsOrEquals --- is id1 logically >= id2?
 */
bool
TransactionIdFollowsOrEquals(TransactionId id1, TransactionId id2)
{
	int32		diff;

	if (!TransactionIdIsNormal(id1) || !TransactionIdIsNormal(id2))
		return (id1 >= id2);

	diff = (int32) (id1 - id2);
	return (diff >= 0);
}
434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473

/*
 * TransactionIdGetCommitLSN
 *
 * This function returns an LSN that is late enough to be able
 * to guarantee that if we flush up to the LSN returned then we
 * will have flushed the transaction's commit record to disk.
 *
 * The result is not necessarily the exact LSN of the transaction's
 * commit record!  For example, for long-past transactions (those whose
 * clog pages already migrated to disk), we'll return InvalidXLogRecPtr.
 * Also, because we group transactions on the same clog page to conserve
 * storage, we might return the LSN of a later transaction that falls into
 * the same group.
 */
XLogRecPtr
TransactionIdGetCommitLSN(TransactionId xid)
{
	XLogRecPtr	result;

	/*
	 * Currently, all uses of this function are for xids that were just
	 * reported to be committed by TransactionLogFetch, so we expect that
	 * checking TransactionLogFetch's cache will usually succeed and avoid an
	 * extra trip to shared memory.
	 */
	if (TransactionIdEquals(xid, cachedFetchXid))
		return cachedCommitLSN;

	/* Special XIDs are always known committed */
	if (!TransactionIdIsNormal(xid))
		return InvalidXLogRecPtr;

	/*
	 * Get the transaction status.
	 */
	(void) TransactionIdGetStatus(xid, &result);

	return result;
}