1 /*-------------------------------------------------------------------------
4 * top level transaction system support routines
6 * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7 * Portions Copyright (c) 1994, Regents of the University of California
11 * $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.73 2000/10/20 11:01:04 vadim Exp $
14 * Transaction aborts can now occur two ways:
16 * 1) system dies from some internal cause (Assert, etc..)
19 * These two cases used to be treated identically, but now
20 * we need to distinguish them. Why? consider the following
25 * 1) user types BEGIN 1) user types BEGIN
26 * 2) user does something 2) user does something
27 * 3) user does not like what 3) system aborts for some reason
28 * she sees and types ABORT
30 * In case 1, we want to abort the transaction and return to the
31 * default state. In case 2, there may be more commands coming
32 * our way which are part of the same transaction block and we have
33 * to ignore these commands until we see an END transaction.
34 * (or an ABORT! --djm)
36 * Internal aborts are now handled by AbortTransactionBlock(), just as
37 * they always have been, and user aborts are now handled by
38 * UserAbortTransactionBlock(). Both of them rely on AbortTransaction()
39 * to do all the real work. The only difference is what state we
40 * enter after AbortTransaction() does its work:
42 * * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
43 * * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
45 * Low-level transaction abort handling is divided into two phases:
46 * * AbortTransaction() executes as soon as we realize the transaction
47 * has failed. It should release all shared resources (locks etc)
48 * so that we do not delay other backends unnecessarily.
49 * * CleanupTransaction() executes when we finally see a user COMMIT
50 * or ROLLBACK command; it cleans things up and gets us out of
51 * the transaction internally. In particular, we mustn't destroy
52 * TransactionCommandContext until this point.
55 * This file is an attempt at a redesign of the upper layer
56 * of the V1 transaction system which was too poorly thought
57 * out to describe. This new system hopes to be both simpler
58 * in design, simpler to extend and needs to contain added
59 * functionality to solve problems beyond the scope of the V1
60 * system. (In particuler, communication of transaction
61 * information between parallel backends has to be supported)
63 * The essential aspects of the transaction system are:
65 * o transaction id generation
66 * o transaction log updating
68 * o cache invalidation
71 * Hence, the functional division of the transaction code is
72 * based on what of the above things need to be done during
73 * a start/commit/abort transaction. For instance, the
74 * routine AtCommit_Memory() takes care of all the memory
75 * cleanup stuff done at commit time.
77 * The code is layered as follows:
84 * are provided to do the lower level work like recording
85 * the transaction status in the log and doing memory cleanup.
86 * above these routines are another set of functions:
88 * StartTransactionCommand
89 * CommitTransactionCommand
90 * AbortCurrentTransaction
92 * These are the routines used in the postgres main processing
93 * loop. They are sensitive to the current transaction block state
94 * and make calls to the lower level routines appropriately.
96 * Support for transaction blocks is provided via the functions:
98 * StartTransactionBlock
99 * CommitTransactionBlock
100 * AbortTransactionBlock
102 * These are invoked only in responce to a user "BEGIN", "END",
103 * or "ABORT" command. The tricky part about these functions
104 * is that they are called within the postgres main loop, in between
105 * the StartTransactionCommand() and CommitTransactionCommand().
107 * For example, consider the following sequence of user commands:
110 * 2) retrieve (foo.all)
111 * 3) append foo (bar = baz)
114 * in the main processing loop, this results in the following
115 * transaction sequence:
117 * / StartTransactionCommand();
118 * 1) / ProcessUtility(); << begin
119 * \ StartTransactionBlock();
120 * \ CommitTransactionCommand();
122 * / StartTransactionCommand();
123 * 2) < ProcessQuery(); << retrieve (foo.all)
124 * \ CommitTransactionCommand();
126 * / StartTransactionCommand();
127 * 3) < ProcessQuery(); << append foo (bar = baz)
128 * \ CommitTransactionCommand();
130 * / StartTransactionCommand();
131 * 4) / ProcessUtility(); << end
132 * \ CommitTransactionBlock();
133 * \ CommitTransactionCommand();
135 * The point of this example is to demonstrate the need for
136 * StartTransactionCommand() and CommitTransactionCommand() to
137 * be state smart -- they should do nothing in between the calls
138 * to StartTransactionBlock() and EndTransactionBlock() and
139 * outside these calls they need to do normal start/commit
142 * Furthermore, suppose the "retrieve (foo.all)" caused an abort
143 * condition. We would then want to abort the transaction and
144 * ignore all subsequent commands up to the "end".
147 *-------------------------------------------------------------------------
151 * Large object clean up added in CommitTransaction() to prevent buffer leaks.
153 * [PA] is Pascal André <andre@via.ecp.fr>
155 #include "postgres.h"
157 #include <sys/time.h>
159 #include "access/nbtree.h"
160 #include "catalog/heap.h"
161 #include "catalog/index.h"
162 #include "commands/async.h"
163 #include "commands/sequence.h"
164 #include "commands/trigger.h"
165 #include "executor/spi.h"
166 #include "libpq/be-fsstubs.h"
167 #include "miscadmin.h"
168 #include "storage/proc.h"
169 #include "storage/sinval.h"
170 #include "utils/inval.h"
171 #include "utils/memutils.h"
172 #include "utils/portal.h"
173 #include "utils/catcache.h"
174 #include "utils/relcache.h"
175 #include "utils/temprel.h"
177 extern bool SharedBufferChanged;
179 static void AbortTransaction(void);
180 static void AtAbort_Cache(void);
181 static void AtAbort_Locks(void);
182 static void AtAbort_Memory(void);
183 static void AtCleanup_Memory(void);
184 static void AtCommit_Cache(void);
185 static void AtCommit_LocalCache(void);
186 static void AtCommit_Locks(void);
187 static void AtCommit_Memory(void);
188 static void AtStart_Cache(void);
189 static void AtStart_Locks(void);
190 static void AtStart_Memory(void);
191 static void CleanupTransaction(void);
192 static void CommitTransaction(void);
193 static void RecordTransactionAbort(void);
194 static void RecordTransactionCommit(void);
195 static void StartTransaction(void);
198 * global variables holding the current transaction state.
200 * Note: when we are running several slave processes, the
201 * current transaction state data is copied into shared memory
202 * and the CurrentTransactionState pointer changed to
203 * point to the shared copy. All this occurrs in slaves.c
206 TransactionStateData CurrentTransactionStateData = {
207 0, /* transaction id */
208 FirstCommandId, /* command id */
209 0, /* scan command id */
210 0x0, /* start time */
211 TRANS_DEFAULT, /* transaction state */
212 TBLOCK_DEFAULT /* transaction block state */
215 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
217 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
221 #include "access/xlogutils.h"
225 void xact_redo(XLogRecPtr lsn, XLogRecord *record);
226 void xact_undo(XLogRecPtr lsn, XLogRecord *record);
228 static void (*_RollbackFunc)(void*) = NULL;
229 static void *_RollbackData = NULL;
234 * info returned when the system is disabled
236 * Apparently a lot of this code is inherited from other prototype systems.
237 * For DisabledStartTime, use a symbolic value to make the relationships clearer.
238 * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
239 * every day. It appears that if we return a value guaranteed larger than
240 * any real time associated with a transaction then comparisons in other
241 * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
243 * Note: I have no idea what the significance of the
244 * 1073741823 in DisabledStartTime.. I just carried
245 * this over when converting things from the old
246 * V1 transaction system. -cim 3/18/90
249 TransactionId DisabledTransactionId = (TransactionId) -1;
251 CommandId DisabledCommandId = (CommandId) -1;
253 AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
259 bool CommandIdCounterOverflowFlag;
262 * catalog creation transaction bootstrapping flag.
263 * This should be eliminated and added to the transaction
264 * state stuff. -cim 3/19/90
267 bool AMI_OVERRIDE = false;
269 /* ----------------------------------------------------------------
270 * transaction state accessors
271 * ----------------------------------------------------------------
274 /* --------------------------------
275 * TranactionFlushEnabled()
276 * SetTransactionFlushEnabled()
278 * These are used to test and set the "TransactionFlushState"
279 * varable. If this variable is true (the default), then
280 * the system will flush all dirty buffers to disk at the end
281 * of each transaction. If false then we are assuming the
282 * buffer pool resides in stable main memory, in which case we
283 * only do writes as necessary.
284 * --------------------------------
286 static int TransactionFlushState = 1;
289 TransactionFlushEnabled(void)
291 return TransactionFlushState;
296 SetTransactionFlushEnabled(bool state)
298 TransactionFlushState = (state == true);
302 /* --------------------------------
305 * This returns true if we are currently running a query
306 * within an executing transaction.
307 * --------------------------------
310 IsTransactionState(void)
312 TransactionState s = CurrentTransactionState;
320 case TRANS_INPROGRESS:
331 * Shouldn't get here, but lint is not happy with this...
338 /* --------------------------------
339 * IsAbortedTransactionBlockState
341 * This returns true if we are currently running a query
342 * within an aborted transaction block.
343 * --------------------------------
346 IsAbortedTransactionBlockState()
348 TransactionState s = CurrentTransactionState;
350 if (s->blockState == TBLOCK_ABORT)
356 /* --------------------------------
357 * OverrideTransactionSystem
359 * This is used to temporarily disable the transaction
360 * processing system in order to do initialization of
361 * the transaction system data structures and relations
363 * --------------------------------
365 int SavedTransactionState;
368 OverrideTransactionSystem(bool flag)
370 TransactionState s = CurrentTransactionState;
374 if (s->state == TRANS_DISABLED)
377 SavedTransactionState = s->state;
378 s->state = TRANS_DISABLED;
382 if (s->state != TRANS_DISABLED)
385 s->state = SavedTransactionState;
389 /* --------------------------------
390 * GetCurrentTransactionId
392 * This returns the id of the current transaction, or
393 * the id of the "disabled" transaction.
394 * --------------------------------
397 GetCurrentTransactionId()
399 TransactionState s = CurrentTransactionState;
402 * if the transaction system is disabled, we return
403 * the special "disabled" transaction id.
406 if (s->state == TRANS_DISABLED)
407 return (TransactionId) DisabledTransactionId;
410 * otherwise return the current transaction id.
413 return (TransactionId) s->transactionIdData;
417 /* --------------------------------
418 * GetCurrentCommandId
419 * --------------------------------
422 GetCurrentCommandId()
424 TransactionState s = CurrentTransactionState;
427 * if the transaction system is disabled, we return
428 * the special "disabled" command id.
431 if (s->state == TRANS_DISABLED)
432 return (CommandId) DisabledCommandId;
440 TransactionState s = CurrentTransactionState;
443 * if the transaction system is disabled, we return
444 * the special "disabled" command id.
447 if (s->state == TRANS_DISABLED)
448 return (CommandId) DisabledCommandId;
450 return s->scanCommandId;
454 /* --------------------------------
455 * GetCurrentTransactionStartTime
456 * --------------------------------
459 GetCurrentTransactionStartTime()
461 TransactionState s = CurrentTransactionState;
464 * if the transaction system is disabled, we return
465 * the special "disabled" starting time.
468 if (s->state == TRANS_DISABLED)
469 return (AbsoluteTime) DisabledStartTime;
475 /* --------------------------------
476 * TransactionIdIsCurrentTransactionId
477 * --------------------------------
480 TransactionIdIsCurrentTransactionId(TransactionId xid)
482 TransactionState s = CurrentTransactionState;
488 TransactionIdEquals(xid, s->transactionIdData);
492 /* --------------------------------
493 * CommandIdIsCurrentCommandId
494 * --------------------------------
497 CommandIdIsCurrentCommandId(CommandId cid)
499 TransactionState s = CurrentTransactionState;
504 return (cid == s->commandId) ? true : false;
508 CommandIdGEScanCommandId(CommandId cid)
510 TransactionState s = CurrentTransactionState;
515 return (cid >= s->scanCommandId) ? true : false;
519 /* --------------------------------
520 * ClearCommandIdCounterOverflowFlag
521 * --------------------------------
525 ClearCommandIdCounterOverflowFlag()
527 CommandIdCounterOverflowFlag = false;
532 /* --------------------------------
533 * CommandCounterIncrement
534 * --------------------------------
537 CommandCounterIncrement()
539 CurrentTransactionStateData.commandId += 1;
540 if (CurrentTransactionStateData.commandId == FirstCommandId)
542 CommandIdCounterOverflowFlag = true;
543 elog(ERROR, "You may only have 2^32-1 commands per transaction");
546 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
549 * make cache changes visible to me. AtCommit_LocalCache() instead of
550 * AtCommit_Cache() is called here.
552 AtCommit_LocalCache();
558 SetScanCommandId(CommandId savedId)
561 CurrentTransactionStateData.scanCommandId = savedId;
565 /* ----------------------------------------------------------------
566 * initialization stuff
567 * ----------------------------------------------------------------
570 InitializeTransactionSystem()
572 InitializeTransactionLog();
575 /* ----------------------------------------------------------------
576 * StartTransaction stuff
577 * ----------------------------------------------------------------
580 /* --------------------------------
582 * --------------------------------
590 /* --------------------------------
592 * --------------------------------
599 * at present, it is unknown to me what belongs here -cim 3/18/90
601 * There isn't anything to do at the start of a xact for locks. -mer
606 /* --------------------------------
608 * --------------------------------
614 * We shouldn't have any transaction contexts already.
617 Assert(TopTransactionContext == NULL);
618 Assert(TransactionCommandContext == NULL);
621 * Create a toplevel context for the transaction.
624 TopTransactionContext =
625 AllocSetContextCreate(TopMemoryContext,
626 "TopTransactionContext",
627 ALLOCSET_DEFAULT_MINSIZE,
628 ALLOCSET_DEFAULT_INITSIZE,
629 ALLOCSET_DEFAULT_MAXSIZE);
632 * Create a statement-level context and make it active.
635 TransactionCommandContext =
636 AllocSetContextCreate(TopTransactionContext,
637 "TransactionCommandContext",
638 ALLOCSET_DEFAULT_MINSIZE,
639 ALLOCSET_DEFAULT_INITSIZE,
640 ALLOCSET_DEFAULT_MAXSIZE);
641 MemoryContextSwitchTo(TransactionCommandContext);
645 /* ----------------------------------------------------------------
646 * CommitTransaction stuff
647 * ----------------------------------------------------------------
650 /* --------------------------------
651 * RecordTransactionCommit
653 * Note: the two calls to BufferManagerFlush() exist to ensure
654 * that data pages are written before log pages. These
655 * explicit calls should be replaced by a more efficient
656 * ordered page write scheme in the buffer manager
658 * --------------------------------
661 RecordTransactionCommit()
667 * get the current transaction id
670 xid = GetCurrentTransactionId();
673 * flush the buffer manager pages. Note: if we have stable main
674 * memory, dirty shared buffers are not flushed plai 8/7/90
676 leak = BufferPoolCheckLeak();
679 * If no one shared buffer was changed by this transaction then we
680 * don't flush shared buffers and don't record commit status.
682 if (SharedBufferChanged)
686 ResetBufferPool(true);
689 * have the transaction access methods record the status of this
690 * transaction id in the pg_log relation.
692 TransactionIdCommit(xid);
696 xl_xact_commit xlrec;
697 struct timeval delay;
700 xlrec.xtime = time(NULL);
702 * MUST SAVE ARRAY OF RELFILENODE-s TO DROP
704 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT,
705 (char*) &xlrec, SizeOfXactCommit, NULL, 0);
708 * Sleep before commit! So we can flush more than one
709 * commit records per single fsync.
712 delay.tv_usec = CommitDelay;
713 (void) select(0, NULL, NULL, NULL, &delay);
717 * Now write the log info to the disk too.
719 leak = BufferPoolCheckLeak();
724 ResetBufferPool(true);
728 /* --------------------------------
730 * --------------------------------
736 * Make catalog changes visible to all backend.
739 RegisterInvalid(true);
742 /* --------------------------------
743 * AtCommit_LocalCache
744 * --------------------------------
747 AtCommit_LocalCache()
750 * Make catalog changes visible to me for the next command.
753 ImmediateLocalInvalidation(true);
756 /* --------------------------------
758 * --------------------------------
764 * XXX What if ProcReleaseLocks fails? (race condition?)
766 * Then you're up a creek! -mer 5/24/92
772 /* --------------------------------
774 * --------------------------------
780 * Now that we're "out" of a transaction, have the
781 * system allocate things in the top memory context instead
782 * of per-transaction contexts.
785 MemoryContextSwitchTo(TopMemoryContext);
788 * Release all transaction-local memory.
791 Assert(TopTransactionContext != NULL);
792 MemoryContextDelete(TopTransactionContext);
793 TopTransactionContext = NULL;
794 TransactionCommandContext = NULL;
797 /* ----------------------------------------------------------------
798 * AbortTransaction stuff
799 * ----------------------------------------------------------------
802 /* --------------------------------
803 * RecordTransactionAbort
804 * --------------------------------
807 RecordTransactionAbort()
812 * get the current transaction id
815 xid = GetCurrentTransactionId();
818 * Have the transaction access methods record the status of this
819 * transaction id in the pg_log relation. We skip it if no one shared
820 * buffer was changed by this transaction.
822 if (SharedBufferChanged && !TransactionIdDidCommit(xid))
823 TransactionIdAbort(xid);
826 if (SharedBufferChanged)
831 xlrec.xtime = time(NULL);
832 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT,
833 (char*) &xlrec, SizeOfXactAbort, NULL, 0);
838 * Tell bufmgr and smgr to release resources.
840 ResetBufferPool(false); /* false -> is abort */
843 /* --------------------------------
845 * --------------------------------
850 RelationCacheAbort();
852 RegisterInvalid(false);
855 /* --------------------------------
857 * --------------------------------
863 * XXX What if ProcReleaseLocks() fails? (race condition?)
865 * Then you're up a creek without a paddle! -mer
872 /* --------------------------------
874 * --------------------------------
880 * Make sure we are in a valid context (not a child of
881 * TransactionCommandContext...). Note that it is possible
882 * for this code to be called when we aren't in a transaction
883 * at all; go directly to TopMemoryContext in that case.
886 if (TransactionCommandContext != NULL)
888 MemoryContextSwitchTo(TransactionCommandContext);
891 * We do not want to destroy transaction contexts yet,
892 * but it should be OK to delete any command-local memory.
895 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
899 MemoryContextSwitchTo(TopMemoryContext);
904 /* ----------------------------------------------------------------
905 * CleanupTransaction stuff
906 * ----------------------------------------------------------------
909 /* --------------------------------
911 * --------------------------------
917 * Now that we're "out" of a transaction, have the
918 * system allocate things in the top memory context instead
919 * of per-transaction contexts.
922 MemoryContextSwitchTo(TopMemoryContext);
925 * Release all transaction-local memory.
928 if (TopTransactionContext != NULL)
929 MemoryContextDelete(TopTransactionContext);
930 TopTransactionContext = NULL;
931 TransactionCommandContext = NULL;
935 /* ----------------------------------------------------------------
937 * ----------------------------------------------------------------
940 /* --------------------------------
943 * --------------------------------
948 TransactionState s = CurrentTransactionState;
951 XactIsoLevel = DefaultXactIsoLevel;
954 * Check the current transaction state. If the transaction system
955 * is switched off, or if we're already in a transaction, do nothing.
956 * We're already in a transaction when the monitor sends a null
957 * command to the backend to flush the comm channel. This is a
958 * hacky fix to a communications problem, and we keep having to
959 * deal with it here. We should fix the comm channel code. mao 080891
962 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
966 * set the current transaction state information
967 * appropriately during start processing
970 s->state = TRANS_START;
972 SetReindexProcessing(false);
975 * generate a new transaction id
978 GetNewTransactionId(&(s->transactionIdData));
980 XactLockTableInsert(s->transactionIdData);
983 * initialize current transaction state fields
986 s->commandId = FirstCommandId;
987 s->scanCommandId = FirstCommandId;
988 s->startTime = GetCurrentAbsoluteTime();
991 * initialize the various transaction subsystems
999 * Tell the trigger manager to we're starting a transaction
1002 DeferredTriggerBeginXact();
1005 * done with start processing, set current transaction
1006 * state to "in progress"
1009 s->state = TRANS_INPROGRESS;
1015 * Tell me if we are currently in progress
1019 CurrentXactInProgress()
1021 return CurrentTransactionState->state == TRANS_INPROGRESS;
1025 /* --------------------------------
1028 * --------------------------------
1033 TransactionState s = CurrentTransactionState;
1036 * check the current transaction state
1039 if (s->state == TRANS_DISABLED)
1042 if (s->state != TRANS_INPROGRESS)
1043 elog(NOTICE, "CommitTransaction and not in in-progress state ");
1046 * Tell the trigger manager that this transaction is about to be
1047 * committed. He'll invoke all trigger deferred until XACT before
1048 * we really start on committing the transaction.
1051 DeferredTriggerEndXact();
1054 * set the current transaction state information
1055 * appropriately during the abort processing
1058 s->state = TRANS_COMMIT;
1061 * do commit processing
1065 /* handle commit for large objects [ PA, 7/17/98 ] */
1068 /* NOTIFY commit must also come before lower-level cleanup */
1073 RecordTransactionCommit();
1076 * Let others know about no transaction in progress by me. Note that
1077 * this must be done _before_ releasing locks we hold and
1078 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1079 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1080 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1081 * xid 0 as running as well or it will see two tuple versions - one
1082 * deleted by xid 1 and one inserted by xid 0.
1084 if (MyProc != (PROC *) NULL)
1086 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1087 SpinAcquire(SInvalLock);
1088 MyProc->xid = InvalidTransactionId;
1089 MyProc->xmin = InvalidTransactionId;
1090 SpinRelease(SInvalLock);
1093 RelationPurgeLocalRelation(true);
1101 SharedBufferChanged = false; /* safest place to do it */
1104 * done with commit processing, set current transaction
1105 * state back to default
1108 s->state = TRANS_DEFAULT;
1111 /* --------------------------------
1114 * --------------------------------
1119 TransactionState s = CurrentTransactionState;
1122 * Let others to know about no transaction in progress - vadim
1125 if (MyProc != (PROC *) NULL)
1127 MyProc->xid = InvalidTransactionId;
1128 MyProc->xmin = InvalidTransactionId;
1132 * check the current transaction state
1135 if (s->state == TRANS_DISABLED)
1138 if (s->state != TRANS_INPROGRESS)
1139 elog(NOTICE, "AbortTransaction and not in in-progress state");
1142 * Reset user id which might have been changed transiently
1144 SetUserId(GetSessionUserId());
1147 * Tell the trigger manager that this transaction is about to be
1151 DeferredTriggerAbortXact();
1154 * set the current transaction state information
1155 * appropriately during the abort processing
1158 s->state = TRANS_ABORT;
1161 * do abort processing
1164 lo_commit(false); /* 'false' means it's abort */
1169 RecordTransactionAbort();
1170 RelationPurgeLocalRelation(false);
1171 remove_temp_rel_in_myxid();
1178 /* Here we'll rollback xaction changes */
1182 SharedBufferChanged = false; /* safest place to do it */
1185 * State remains TRANS_ABORT until CleanupTransaction().
1190 /* --------------------------------
1191 * CleanupTransaction
1193 * --------------------------------
1196 CleanupTransaction()
1198 TransactionState s = CurrentTransactionState;
1200 if (s->state == TRANS_DISABLED)
1204 * State should still be TRANS_ABORT from AbortTransaction().
1207 if (s->state != TRANS_ABORT)
1208 elog(FATAL, "CleanupTransaction and not in abort state");
1211 * do abort cleanup processing
1217 * done with abort processing, set current transaction
1218 * state back to default
1221 s->state = TRANS_DEFAULT;
1224 /* --------------------------------
1225 * StartTransactionCommand
1226 * --------------------------------
1229 StartTransactionCommand()
1231 TransactionState s = CurrentTransactionState;
1233 switch (s->blockState)
1236 * if we aren't in a transaction block, we
1237 * just do our usual start transaction.
1240 case TBLOCK_DEFAULT:
1245 * We should never experience this -- if we do it
1246 * means the BEGIN state was not changed in the previous
1247 * CommitTransactionCommand(). If we get it, we print
1248 * a warning and change to the in-progress state.
1252 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1253 s->blockState = TBLOCK_INPROGRESS;
1257 * This is the case when are somewhere in a transaction
1258 * block and about to start a new command. For now we
1259 * do nothing but someday we may do command-local resource
1263 case TBLOCK_INPROGRESS:
1267 * As with BEGIN, we should never experience this
1268 * if we do it means the END state was not changed in the
1269 * previous CommitTransactionCommand(). If we get it, we
1270 * print a warning, commit the transaction, start a new
1271 * transaction and change to the default state.
1275 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1276 s->blockState = TBLOCK_DEFAULT;
1277 CommitTransaction();
1282 * Here we are in the middle of a transaction block but
1283 * one of the commands caused an abort so we do nothing
1284 * but remain in the abort state. Eventually we will get
1285 * to the "END TRANSACTION" which will set things straight.
1292 * This means we somehow aborted and the last call to
1293 * CommitTransactionCommand() didn't clear the state so
1294 * we remain in the ENDABORT state and maybe next time
1295 * we get to CommitTransactionCommand() the state will
1296 * get reset to default.
1299 case TBLOCK_ENDABORT:
1300 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1305 * We must switch to TransactionCommandContext before returning.
1306 * This is already done if we called StartTransaction, otherwise not.
1308 Assert(TransactionCommandContext != NULL);
1309 MemoryContextSwitchTo(TransactionCommandContext);
1312 /* --------------------------------
1313 * CommitTransactionCommand
1314 * --------------------------------
1317 CommitTransactionCommand()
1319 TransactionState s = CurrentTransactionState;
1321 switch (s->blockState)
1324 * if we aren't in a transaction block, we
1325 * just do our usual transaction commit
1328 case TBLOCK_DEFAULT:
1329 CommitTransaction();
1333 * This is the case right after we get a "BEGIN TRANSACTION"
1334 * command, but the user hasn't done anything else yet, so
1335 * we change to the "transaction block in progress" state
1340 s->blockState = TBLOCK_INPROGRESS;
1344 * This is the case when we have finished executing a command
1345 * someplace within a transaction block. We increment the
1346 * command counter and return. Someday we may free resources
1347 * local to the command.
1349 * That someday is today, at least for memory allocated in
1350 * TransactionCommandContext.
1354 case TBLOCK_INPROGRESS:
1355 CommandCounterIncrement();
1356 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1360 * This is the case when we just got the "END TRANSACTION"
1361 * statement, so we commit the transaction and go back to
1362 * the default state.
1366 CommitTransaction();
1367 s->blockState = TBLOCK_DEFAULT;
1371 * Here we are in the middle of a transaction block but
1372 * one of the commands caused an abort so we do nothing
1373 * but remain in the abort state. Eventually we will get
1374 * to the "END TRANSACTION" which will set things straight.
1381 * Here we were in an aborted transaction block which
1382 * just processed the "END TRANSACTION" command from the
1383 * user, so clean up and return to the default state.
1386 case TBLOCK_ENDABORT:
1387 CleanupTransaction();
1388 s->blockState = TBLOCK_DEFAULT;
1393 /* --------------------------------
1394 * AbortCurrentTransaction
1395 * --------------------------------
1398 AbortCurrentTransaction()
1400 TransactionState s = CurrentTransactionState;
1402 switch (s->blockState)
1405 * if we aren't in a transaction block, we
1406 * just do the basic abort & cleanup transaction.
1409 case TBLOCK_DEFAULT:
1411 CleanupTransaction();
1415 * If we are in the TBLOCK_BEGIN it means something
1416 * screwed up right after reading "BEGIN TRANSACTION"
1417 * so we enter the abort state. Eventually an "END
1418 * TRANSACTION" will fix things.
1422 s->blockState = TBLOCK_ABORT;
1424 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1428 * This is the case when are somewhere in a transaction
1429 * block which aborted so we abort the transaction and
1430 * set the ABORT state. Eventually an "END TRANSACTION"
1431 * will fix things and restore us to a normal state.
1434 case TBLOCK_INPROGRESS:
1435 s->blockState = TBLOCK_ABORT;
1437 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1441 * Here, the system was fouled up just after the
1442 * user wanted to end the transaction block so we
1443 * abort the transaction and put us back into the
1448 s->blockState = TBLOCK_DEFAULT;
1450 CleanupTransaction();
1454 * Here, we are already in an aborted transaction
1455 * state and are waiting for an "END TRANSACTION" to
1456 * come along and lo and behold, we abort again!
1457 * So we just remain in the abort state.
1464 * Here we were in an aborted transaction block which
1465 * just processed the "END TRANSACTION" command but somehow
1466 * aborted again.. since we must have done the abort
1467 * processing, we clean up and return to the default state.
1470 case TBLOCK_ENDABORT:
1471 CleanupTransaction();
1472 s->blockState = TBLOCK_DEFAULT;
1477 /* ----------------------------------------------------------------
1478 * transaction block support
1479 * ----------------------------------------------------------------
1481 /* --------------------------------
1482 * BeginTransactionBlock
1483 * --------------------------------
1486 BeginTransactionBlock(void)
1488 TransactionState s = CurrentTransactionState;
1491 * check the current transaction state
1494 if (s->state == TRANS_DISABLED)
1497 if (s->blockState != TBLOCK_DEFAULT)
1498 elog(NOTICE, "BEGIN: already a transaction in progress");
1501 * set the current transaction block state information
1502 * appropriately during begin processing
1505 s->blockState = TBLOCK_BEGIN;
1508 * do begin processing
1513 * done with begin processing, set block state to inprogress
1516 s->blockState = TBLOCK_INPROGRESS;
1519 /* --------------------------------
1520 * EndTransactionBlock
1521 * --------------------------------
1524 EndTransactionBlock(void)
1526 TransactionState s = CurrentTransactionState;
1529 * check the current transaction state
1532 if (s->state == TRANS_DISABLED)
1535 if (s->blockState == TBLOCK_INPROGRESS)
1538 * here we are in a transaction block which should commit
1539 * when we get to the upcoming CommitTransactionCommand()
1540 * so we set the state to "END". CommitTransactionCommand()
1541 * will recognize this and commit the transaction and return
1542 * us to the default state
1545 s->blockState = TBLOCK_END;
1549 if (s->blockState == TBLOCK_ABORT)
1552 * here, we are in a transaction block which aborted
1553 * and since the AbortTransaction() was already done,
1554 * we do whatever is needed and change to the special
1555 * "END ABORT" state. The upcoming CommitTransactionCommand()
1556 * will recognise this and then put us back in the default
1560 s->blockState = TBLOCK_ENDABORT;
1565 * here, the user issued COMMIT when not inside a transaction.
1566 * Issue a notice and go to abort state. The upcoming call to
1567 * CommitTransactionCommand() will then put us back into the
1571 elog(NOTICE, "COMMIT: no transaction in progress");
1573 s->blockState = TBLOCK_ENDABORT;
1576 /* --------------------------------
1577 * AbortTransactionBlock
1578 * --------------------------------
1582 AbortTransactionBlock(void)
1584 TransactionState s = CurrentTransactionState;
1587 * check the current transaction state
1590 if (s->state == TRANS_DISABLED)
1593 if (s->blockState == TBLOCK_INPROGRESS)
1596 * here we were inside a transaction block something
1597 * screwed up inside the system so we enter the abort state,
1598 * do the abort processing and then return.
1599 * We remain in the abort state until we see an
1600 * END TRANSACTION command.
1603 s->blockState = TBLOCK_ABORT;
1609 * here, the user issued ABORT when not inside a transaction.
1610 * Issue a notice and go to abort state. The upcoming call to
1611 * CommitTransactionCommand() will then put us back into the
1615 elog(NOTICE, "ROLLBACK: no transaction in progress");
1617 s->blockState = TBLOCK_ENDABORT;
1622 /* --------------------------------
1623 * UserAbortTransactionBlock
1624 * --------------------------------
1627 UserAbortTransactionBlock()
1629 TransactionState s = CurrentTransactionState;
1632 * check the current transaction state
1635 if (s->state == TRANS_DISABLED)
1639 * if the transaction has already been automatically aborted with an
1640 * error, and the user subsequently types 'abort', allow it. (the
1641 * behavior is the same as if they had typed 'end'.)
1643 if (s->blockState == TBLOCK_ABORT)
1645 s->blockState = TBLOCK_ENDABORT;
1649 if (s->blockState == TBLOCK_INPROGRESS)
1652 * here we were inside a transaction block and we
1653 * got an abort command from the user, so we move to
1654 * the abort state, do the abort processing and
1655 * then change to the ENDABORT state so we will end up
1656 * in the default state after the upcoming
1657 * CommitTransactionCommand().
1660 s->blockState = TBLOCK_ABORT;
1662 s->blockState = TBLOCK_ENDABORT;
1667 * here, the user issued ABORT when not inside a transaction.
1668 * Issue a notice and go to abort state. The upcoming call to
1669 * CommitTransactionCommand() will then put us back into the
1673 elog(NOTICE, "ROLLBACK: no transaction in progress");
1675 s->blockState = TBLOCK_ENDABORT;
1678 /* --------------------------------
1679 * AbortOutOfAnyTransaction
1681 * This routine is provided for error recovery purposes. It aborts any
1682 * active transaction or transaction block, leaving the system in a known
1684 * --------------------------------
1687 AbortOutOfAnyTransaction()
1689 TransactionState s = CurrentTransactionState;
1692 * Get out of any low-level transaction
1694 if (s->state != TRANS_DEFAULT)
1697 CleanupTransaction();
1701 * Now reset the high-level state
1703 s->blockState = TBLOCK_DEFAULT;
1707 IsTransactionBlock()
1709 TransactionState s = CurrentTransactionState;
1711 if (s->blockState == TBLOCK_INPROGRESS
1712 || s->blockState == TBLOCK_ABORT
1713 || s->blockState == TBLOCK_ENDABORT)
1722 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1724 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1726 if (info == XLOG_XACT_COMMIT)
1728 xl_xact_commit *xlrec = (xl_xact_commit*) XLogRecGetData(record);
1730 XLogMarkCommitted(record->xl_xid);
1731 /* MUST REMOVE FILES OF ALL DROPPED RELATIONS */
1733 else if (info == XLOG_XACT_ABORT)
1735 XLogMarkAborted(record->xl_xid);
1738 elog(STOP, "xact_redo: unknown op code %u", info);
1742 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1744 uint8 info = record->xl_info & ~XLR_INFO_MASK;
1746 if (info == XLOG_XACT_COMMIT) /* shouldn't be called by XLOG */
1747 elog(STOP, "xact_undo: can't undo committed xaction");
1748 else if (info != XLOG_XACT_ABORT)
1749 elog(STOP, "xact_redo: unknown op code %u", info);
1753 XactPushRollback(void (*func) (void *), void* data)
1755 if (_RollbackFunc != NULL)
1756 elog(STOP, "XactPushRollback: already installed");
1758 _RollbackFunc = func;
1759 _RollbackData = data;
1763 XactPopRollback(void)
1765 _RollbackFunc = NULL;