summaryrefslogtreecommitdiff
path: root/src/backend/access/transam/transsup.c
blob: d219f8b6841f75bd4d320b901e3a3fcdb9e29914 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
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
/*-------------------------------------------------------------------------
 *
 * transsup.c
 *	  postgres transaction access method support code
 *
 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/access/transam/Attic/transsup.c,v 1.25 2000/01/26 05:56:04 momjian Exp $
 *
 * NOTES
 *	  This file contains support functions for the high
 *	  level access method interface routines found in transam.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/xact.h"
#include "utils/bit.h"

static XidStatus TransBlockGetXidStatus(Block tblock,
					   TransactionId transactionId);
static void TransBlockSetXidStatus(Block tblock,
					   TransactionId transactionId, XidStatus xstatus);

/* ----------------------------------------------------------------
 *					  general support routines
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *		AmiTransactionOverride
 *
 *		This function is used to manipulate the bootstrap flag.
 * --------------------------------
 */
void
AmiTransactionOverride(bool flag)
{
	AMI_OVERRIDE = flag;
}

/* --------------------------------
 *		TransComputeBlockNumber
 * --------------------------------
 */
void
TransComputeBlockNumber(Relation relation,		/* relation to test */
						TransactionId transactionId,	/* transaction id to
														 * test */
						BlockNumber *blockNumberOutP)
{
	long		itemsPerBlock = 0;

	/* ----------------
	 *	we calculate the block number of our transaction
	 *	by dividing the transaction id by the number of
	 *	transaction things per block.
	 * ----------------
	 */
	if (relation == LogRelation)
		itemsPerBlock = TP_NumXidStatusPerBlock;
	else
		elog(ERROR, "TransComputeBlockNumber: unknown relation");

	/* ----------------
	 *	warning! if the transaction id's get too large
	 *	then a BlockNumber may not be large enough to hold the results
	 *	of our division.
	 *
	 *	XXX  this will all vanish soon when we implement an improved
	 *		 transaction id schema -cim 3/23/90
	 *
	 *	This has vanished now that xid's are 4 bytes (no longer 5).
	 *	-mer 5/24/92
	 * ----------------
	 */
	(*blockNumberOutP) = transactionId / itemsPerBlock;
}


/* ----------------------------------------------------------------
 *					 trans block support routines
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *		TransBlockGetLastTransactionIdStatus
 *
 *		This returns the status and transaction id of the last
 *		transaction information recorded on the given TransBlock.
 * --------------------------------
 */

#ifdef NOT_USED
static XidStatus
TransBlockGetLastTransactionIdStatus(Block tblock,
									 TransactionId baseXid,
									 TransactionId *returnXidP)
{
	Index		index;
	Index		maxIndex;
	bits8		bit1;
	bits8		bit2;
	BitIndex	offset;
	XidStatus	xstatus;

	/* ----------------
	 *	sanity check
	 * ----------------
	 */
	Assert((tblock != NULL));

	/* ----------------
	 *	search downward from the top of the block data, looking
	 *	for the first Non-in progress transaction status.  Since we
	 *	are scanning backward, this will be last recorded transaction
	 *	status on the block.
	 * ----------------
	 */
	maxIndex = TP_NumXidStatusPerBlock;
	for (index = maxIndex; index > 0; index--)
	{
		offset = BitIndexOf(index - 1);
		bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1;
		bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset);

		xstatus = (bit1 | bit2);

		/* ----------------
		 *	here we have the status of some transaction, so test
		 *	if the status is recorded as "in progress".  If so, then
		 *	we save the transaction id in the place specified by the caller.
		 * ----------------
		 */
		if (xstatus != XID_INPROGRESS)
		{
			if (returnXidP != NULL)
			{
				TransactionIdStore(baseXid, returnXidP);
				TransactionIdAdd(returnXidP, index - 1);
			}
			break;
		}
	}

	/* ----------------
	 *	if we get here and index is 0 it means we couldn't find
	 *	a non-inprogress transaction on the block.	For now we just
	 *	return this info to the user.  They can check if the return
	 *	status is "in progress" to know this condition has arisen.
	 * ----------------
	 */
	if (index == 0)
	{
		if (returnXidP != NULL)
			TransactionIdStore(baseXid, returnXidP);
	}

	/* ----------------
	 *	return the status to the user
	 * ----------------
	 */
	return xstatus;
}

#endif

/* --------------------------------
 *		TransBlockGetXidStatus
 *
 *		This returns the status of the desired transaction
 * --------------------------------
 */

static XidStatus
TransBlockGetXidStatus(Block tblock,
					   TransactionId transactionId)
{
	Index		index;
	bits8		bit1;
	bits8		bit2;
	BitIndex	offset;

	/* ----------------
	 *	calculate the index into the transaction data where
	 *	our transaction status is located
	 *
	 *	XXX this will be replaced soon when we move to the
	 *		new transaction id scheme -cim 3/23/90
	 *
	 *	The old system has now been replaced. -mer 5/24/92
	 * ----------------
	 */
	index = transactionId % TP_NumXidStatusPerBlock;

	/* ----------------
	 *	get the data at the specified index
	 * ----------------
	 */
	offset = BitIndexOf(index);
	bit1 = ((bits8) BitArrayBitIsSet((BitArray) tblock, offset++)) << 1;
	bit2 = (bits8) BitArrayBitIsSet((BitArray) tblock, offset);

	/* ----------------
	 *	return the transaction status to the caller
	 * ----------------
	 */
	return (XidStatus) (bit1 | bit2);
}

/* --------------------------------
 *		TransBlockSetXidStatus
 *
 *		This sets the status of the desired transaction
 * --------------------------------
 */
static void
TransBlockSetXidStatus(Block tblock,
					   TransactionId transactionId,
					   XidStatus xstatus)
{
	Index		index;
	BitIndex	offset;

	/* ----------------
	 *	calculate the index into the transaction data where
	 *	we sould store our transaction status.
	 *
	 *	XXX this will be replaced soon when we move to the
	 *		new transaction id scheme -cim 3/23/90
	 *
	 *	The new scheme is here -mer 5/24/92
	 * ----------------
	 */
	index = transactionId % TP_NumXidStatusPerBlock;

	offset = BitIndexOf(index);

	/* ----------------
	 *	store the transaction value at the specified offset
	 * ----------------
	 */
	switch (xstatus)
	{
		case XID_COMMIT:		/* set 10 */
			BitArraySetBit((BitArray) tblock, offset);
			BitArrayClearBit((BitArray) tblock, offset + 1);
			break;
		case XID_ABORT: /* set 01 */
			BitArrayClearBit((BitArray) tblock, offset);
			BitArraySetBit((BitArray) tblock, offset + 1);
			break;
		case XID_INPROGRESS:	/* set 00 */
			BitArrayClearBit((BitArray) tblock, offset);
			BitArrayClearBit((BitArray) tblock, offset + 1);
			break;
		default:
			elog(NOTICE,
				 "TransBlockSetXidStatus: invalid status: %d (ignored)",
				 xstatus);
			break;
	}
}

/* ----------------------------------------------------------------
 *				   transam i/o support routines
 * ----------------------------------------------------------------
 */

/* --------------------------------
 *		TransBlockNumberGetXidStatus
 * --------------------------------
 */
XidStatus
TransBlockNumberGetXidStatus(Relation relation,
							 BlockNumber blockNumber,
							 TransactionId xid,
							 bool *failP)
{
	Buffer		buffer;			/* buffer associated with block */
	Block		block;			/* block containing xstatus */
	XidStatus	xstatus;		/* recorded status of xid */
	bool		localfail;		/* bool used if failP = NULL */

	/* ----------------
	 *	get the page containing the transaction information
	 * ----------------
	 */
	buffer = ReadBuffer(relation, blockNumber);
	LockBuffer(buffer, BUFFER_LOCK_SHARE);
	block = BufferGetBlock(buffer);

	/* ----------------
	 *	get the status from the block.	note, for now we always
	 *	return false in failP.
	 * ----------------
	 */
	if (failP == NULL)
		failP = &localfail;
	(*failP) = false;

	xstatus = TransBlockGetXidStatus(block, xid);

	/* ----------------
	 *	release the buffer and return the status
	 * ----------------
	 */
	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
	ReleaseBuffer(buffer);

	return xstatus;
}

/* --------------------------------
 *		TransBlockNumberSetXidStatus
 * --------------------------------
 */
void
TransBlockNumberSetXidStatus(Relation relation,
							 BlockNumber blockNumber,
							 TransactionId xid,
							 XidStatus xstatus,
							 bool *failP)
{
	Buffer		buffer;			/* buffer associated with block */
	Block		block;			/* block containing xstatus */
	bool		localfail;		/* bool used if failP = NULL */

	/* ----------------
	 *	get the block containing the transaction status
	 * ----------------
	 */
	buffer = ReadBuffer(relation, blockNumber);
	LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE);
	block = BufferGetBlock(buffer);

	/* ----------------
	 *	attempt to update the status of the transaction on the block.
	 *	if we are successful, write the block. otherwise release the buffer.
	 *	note, for now we always return false in failP.
	 * ----------------
	 */
	if (failP == NULL)
		failP = &localfail;
	(*failP) = false;

	TransBlockSetXidStatus(block, xid, xstatus);

	LockBuffer(buffer, BUFFER_LOCK_UNLOCK);
	if ((*failP) == false)
		WriteBuffer(buffer);
	else
		ReleaseBuffer(buffer);
}

/* --------------------------------
 *		TransGetLastRecordedTransaction
 * --------------------------------
 */
#ifdef NOT_USED
void
TransGetLastRecordedTransaction(Relation relation,
								TransactionId xid,		/* return: transaction
														 * id */
								bool *failP)
{
	BlockNumber blockNumber;	/* block number */
	Buffer		buffer;			/* buffer associated with block */
	Block		block;			/* block containing xid status */
	BlockNumber n;				/* number of blocks in the relation */
	TransactionId baseXid;

	(*failP) = false;

	/* ----------------
	 *	SOMEDAY gain exclusive access to the log relation
	 *
	 *	That someday is today 5 Aug. 1991 -mer
	 *	It looks to me like we only need to set a read lock here, despite
	 *	the above comment about exclusive access.  The block is never
	 *	actually written into, we only check status bits.
	 * ----------------
	 */
	RelationSetLockForRead(relation);

	/* ----------------
	 *	we assume the last block of the log contains the last
	 *	recorded transaction.  If the relation is empty we return
	 *	failure to the user.
	 * ----------------
	 */
	n = RelationGetNumberOfBlocks(relation);
	if (n == 0)
	{
		(*failP) = true;
		return;
	}

	/* ----------------
	 *	get the block containing the transaction information
	 * ----------------
	 */
	blockNumber = n - 1;
	buffer = ReadBuffer(relation, blockNumber);
	block = BufferGetBlock(buffer);

	/* ----------------
	 *	get the last xid on the block
	 * ----------------
	 */
	baseXid = blockNumber * TP_NumXidStatusPerBlock;

/* XXX ???? xid won't get returned! - AY '94 */
	TransBlockGetLastTransactionIdStatus(block, baseXid, &xid);

	ReleaseBuffer(buffer);

	/* ----------------
	 *	SOMEDAY release our lock on the log relation
	 * ----------------
	 */
	RelationUnsetLockForRead(relation);
}

#endif