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.70 2000/08/06 04:17:47 tgl 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 "access/nbtree.h"
158 #include "catalog/heap.h"
159 #include "catalog/index.h"
160 #include "commands/async.h"
161 #include "commands/sequence.h"
162 #include "commands/trigger.h"
163 #include "executor/spi.h"
164 #include "libpq/be-fsstubs.h"
165 #include "storage/proc.h"
166 #include "storage/sinval.h"
167 #include "utils/inval.h"
168 #include "utils/memutils.h"
169 #include "utils/portal.h"
170 #include "utils/catcache.h"
171 #include "utils/relcache.h"
172 #include "utils/temprel.h"
174 extern bool SharedBufferChanged;
176 static void AbortTransaction(void);
177 static void AtAbort_Cache(void);
178 static void AtAbort_Locks(void);
179 static void AtAbort_Memory(void);
180 static void AtCleanup_Memory(void);
181 static void AtCommit_Cache(void);
182 static void AtCommit_LocalCache(void);
183 static void AtCommit_Locks(void);
184 static void AtCommit_Memory(void);
185 static void AtStart_Cache(void);
186 static void AtStart_Locks(void);
187 static void AtStart_Memory(void);
188 static void CleanupTransaction(void);
189 static void CommitTransaction(void);
190 static void RecordTransactionAbort(void);
191 static void RecordTransactionCommit(void);
192 static void StartTransaction(void);
195 * global variables holding the current transaction state.
197 * Note: when we are running several slave processes, the
198 * current transaction state data is copied into shared memory
199 * and the CurrentTransactionState pointer changed to
200 * point to the shared copy. All this occurrs in slaves.c
203 TransactionStateData CurrentTransactionStateData = {
204 0, /* transaction id */
205 FirstCommandId, /* command id */
206 0, /* scan command id */
207 0x0, /* start time */
208 TRANS_DEFAULT, /* transaction state */
209 TBLOCK_DEFAULT /* transaction block state */
212 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
214 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
218 * info returned when the system is disabled
220 * Apparently a lot of this code is inherited from other prototype systems.
221 * For DisabledStartTime, use a symbolic value to make the relationships clearer.
222 * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
223 * every day. It appears that if we return a value guaranteed larger than
224 * any real time associated with a transaction then comparisons in other
225 * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
227 * Note: I have no idea what the significance of the
228 * 1073741823 in DisabledStartTime.. I just carried
229 * this over when converting things from the old
230 * V1 transaction system. -cim 3/18/90
233 TransactionId DisabledTransactionId = (TransactionId) -1;
235 CommandId DisabledCommandId = (CommandId) -1;
237 AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
243 bool CommandIdCounterOverflowFlag;
246 * catalog creation transaction bootstrapping flag.
247 * This should be eliminated and added to the transaction
248 * state stuff. -cim 3/19/90
251 bool AMI_OVERRIDE = false;
253 /* ----------------------------------------------------------------
254 * transaction state accessors
255 * ----------------------------------------------------------------
258 /* --------------------------------
259 * TranactionFlushEnabled()
260 * SetTransactionFlushEnabled()
262 * These are used to test and set the "TransactionFlushState"
263 * varable. If this variable is true (the default), then
264 * the system will flush all dirty buffers to disk at the end
265 * of each transaction. If false then we are assuming the
266 * buffer pool resides in stable main memory, in which case we
267 * only do writes as necessary.
268 * --------------------------------
270 static int TransactionFlushState = 1;
273 TransactionFlushEnabled(void)
275 return TransactionFlushState;
280 SetTransactionFlushEnabled(bool state)
282 TransactionFlushState = (state == true);
286 /* --------------------------------
289 * This returns true if we are currently running a query
290 * within an executing transaction.
291 * --------------------------------
294 IsTransactionState(void)
296 TransactionState s = CurrentTransactionState;
304 case TRANS_INPROGRESS:
315 * Shouldn't get here, but lint is not happy with this...
322 /* --------------------------------
323 * IsAbortedTransactionBlockState
325 * This returns true if we are currently running a query
326 * within an aborted transaction block.
327 * --------------------------------
330 IsAbortedTransactionBlockState()
332 TransactionState s = CurrentTransactionState;
334 if (s->blockState == TBLOCK_ABORT)
340 /* --------------------------------
341 * OverrideTransactionSystem
343 * This is used to temporarily disable the transaction
344 * processing system in order to do initialization of
345 * the transaction system data structures and relations
347 * --------------------------------
349 int SavedTransactionState;
352 OverrideTransactionSystem(bool flag)
354 TransactionState s = CurrentTransactionState;
358 if (s->state == TRANS_DISABLED)
361 SavedTransactionState = s->state;
362 s->state = TRANS_DISABLED;
366 if (s->state != TRANS_DISABLED)
369 s->state = SavedTransactionState;
373 /* --------------------------------
374 * GetCurrentTransactionId
376 * This returns the id of the current transaction, or
377 * the id of the "disabled" transaction.
378 * --------------------------------
381 GetCurrentTransactionId()
383 TransactionState s = CurrentTransactionState;
386 * if the transaction system is disabled, we return
387 * the special "disabled" transaction id.
390 if (s->state == TRANS_DISABLED)
391 return (TransactionId) DisabledTransactionId;
394 * otherwise return the current transaction id.
397 return (TransactionId) s->transactionIdData;
401 /* --------------------------------
402 * GetCurrentCommandId
403 * --------------------------------
406 GetCurrentCommandId()
408 TransactionState s = CurrentTransactionState;
411 * if the transaction system is disabled, we return
412 * the special "disabled" command id.
415 if (s->state == TRANS_DISABLED)
416 return (CommandId) DisabledCommandId;
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;
434 return s->scanCommandId;
438 /* --------------------------------
439 * GetCurrentTransactionStartTime
440 * --------------------------------
443 GetCurrentTransactionStartTime()
445 TransactionState s = CurrentTransactionState;
448 * if the transaction system is disabled, we return
449 * the special "disabled" starting time.
452 if (s->state == TRANS_DISABLED)
453 return (AbsoluteTime) DisabledStartTime;
459 /* --------------------------------
460 * TransactionIdIsCurrentTransactionId
461 * --------------------------------
464 TransactionIdIsCurrentTransactionId(TransactionId xid)
466 TransactionState s = CurrentTransactionState;
472 TransactionIdEquals(xid, s->transactionIdData);
476 /* --------------------------------
477 * CommandIdIsCurrentCommandId
478 * --------------------------------
481 CommandIdIsCurrentCommandId(CommandId cid)
483 TransactionState s = CurrentTransactionState;
488 return (cid == s->commandId) ? true : false;
492 CommandIdGEScanCommandId(CommandId cid)
494 TransactionState s = CurrentTransactionState;
499 return (cid >= s->scanCommandId) ? true : false;
503 /* --------------------------------
504 * ClearCommandIdCounterOverflowFlag
505 * --------------------------------
509 ClearCommandIdCounterOverflowFlag()
511 CommandIdCounterOverflowFlag = false;
516 /* --------------------------------
517 * CommandCounterIncrement
518 * --------------------------------
521 CommandCounterIncrement()
523 CurrentTransactionStateData.commandId += 1;
524 if (CurrentTransactionStateData.commandId == FirstCommandId)
526 CommandIdCounterOverflowFlag = true;
527 elog(ERROR, "You may only have 2^32-1 commands per transaction");
530 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
533 * make cache changes visible to me. AtCommit_LocalCache() instead of
534 * AtCommit_Cache() is called here.
536 AtCommit_LocalCache();
542 SetScanCommandId(CommandId savedId)
545 CurrentTransactionStateData.scanCommandId = savedId;
549 /* ----------------------------------------------------------------
550 * initialization stuff
551 * ----------------------------------------------------------------
554 InitializeTransactionSystem()
556 InitializeTransactionLog();
559 /* ----------------------------------------------------------------
560 * StartTransaction stuff
561 * ----------------------------------------------------------------
564 /* --------------------------------
566 * --------------------------------
574 /* --------------------------------
576 * --------------------------------
583 * at present, it is unknown to me what belongs here -cim 3/18/90
585 * There isn't anything to do at the start of a xact for locks. -mer
590 /* --------------------------------
592 * --------------------------------
598 * We shouldn't have any transaction contexts already.
601 Assert(TopTransactionContext == NULL);
602 Assert(TransactionCommandContext == NULL);
605 * Create a toplevel context for the transaction.
608 TopTransactionContext =
609 AllocSetContextCreate(TopMemoryContext,
610 "TopTransactionContext",
611 ALLOCSET_DEFAULT_MINSIZE,
612 ALLOCSET_DEFAULT_INITSIZE,
613 ALLOCSET_DEFAULT_MAXSIZE);
616 * Create a statement-level context and make it active.
619 TransactionCommandContext =
620 AllocSetContextCreate(TopTransactionContext,
621 "TransactionCommandContext",
622 ALLOCSET_DEFAULT_MINSIZE,
623 ALLOCSET_DEFAULT_INITSIZE,
624 ALLOCSET_DEFAULT_MAXSIZE);
625 MemoryContextSwitchTo(TransactionCommandContext);
629 /* ----------------------------------------------------------------
630 * CommitTransaction stuff
631 * ----------------------------------------------------------------
634 /* --------------------------------
635 * RecordTransactionCommit
637 * Note: the two calls to BufferManagerFlush() exist to ensure
638 * that data pages are written before log pages. These
639 * explicit calls should be replaced by a more efficient
640 * ordered page write scheme in the buffer manager
642 * --------------------------------
645 RecordTransactionCommit()
651 * get the current transaction id
654 xid = GetCurrentTransactionId();
657 * flush the buffer manager pages. Note: if we have stable main
658 * memory, dirty shared buffers are not flushed plai 8/7/90
660 leak = BufferPoolCheckLeak();
663 * If no one shared buffer was changed by this transaction then we
664 * don't flush shared buffers and don't record commit status.
666 if (SharedBufferChanged)
670 ResetBufferPool(true);
673 * have the transaction access methods record the status of this
674 * transaction id in the pg_log relation.
676 TransactionIdCommit(xid);
679 * Now write the log info to the disk too.
681 leak = BufferPoolCheckLeak();
686 ResetBufferPool(true);
690 /* --------------------------------
692 * --------------------------------
698 * Make catalog changes visible to all backend.
701 RegisterInvalid(true);
704 /* --------------------------------
705 * AtCommit_LocalCache
706 * --------------------------------
709 AtCommit_LocalCache()
712 * Make catalog changes visible to me for the next command.
715 ImmediateLocalInvalidation(true);
718 /* --------------------------------
720 * --------------------------------
726 * XXX What if ProcReleaseLocks fails? (race condition?)
728 * Then you're up a creek! -mer 5/24/92
734 /* --------------------------------
736 * --------------------------------
742 * Now that we're "out" of a transaction, have the
743 * system allocate things in the top memory context instead
744 * of per-transaction contexts.
747 MemoryContextSwitchTo(TopMemoryContext);
750 * Release all transaction-local memory.
753 Assert(TopTransactionContext != NULL);
754 MemoryContextDelete(TopTransactionContext);
755 TopTransactionContext = NULL;
756 TransactionCommandContext = NULL;
759 /* ----------------------------------------------------------------
760 * AbortTransaction stuff
761 * ----------------------------------------------------------------
764 /* --------------------------------
765 * RecordTransactionAbort
766 * --------------------------------
769 RecordTransactionAbort()
774 * get the current transaction id
777 xid = GetCurrentTransactionId();
780 * Have the transaction access methods record the status of this
781 * transaction id in the pg_log relation. We skip it if no one shared
782 * buffer was changed by this transaction.
784 if (SharedBufferChanged && !TransactionIdDidCommit(xid))
785 TransactionIdAbort(xid);
788 * Tell bufmgr and smgr to release resources.
790 ResetBufferPool(false); /* false -> is abort */
793 /* --------------------------------
795 * --------------------------------
800 RelationCacheAbort();
802 RegisterInvalid(false);
805 /* --------------------------------
807 * --------------------------------
813 * XXX What if ProcReleaseLocks() fails? (race condition?)
815 * Then you're up a creek without a paddle! -mer
822 /* --------------------------------
824 * --------------------------------
830 * Make sure we are in a valid context (not a child of
831 * TransactionCommandContext...). Note that it is possible
832 * for this code to be called when we aren't in a transaction
833 * at all; go directly to TopMemoryContext in that case.
836 if (TransactionCommandContext != NULL)
838 MemoryContextSwitchTo(TransactionCommandContext);
841 * We do not want to destroy transaction contexts yet,
842 * but it should be OK to delete any command-local memory.
845 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
849 MemoryContextSwitchTo(TopMemoryContext);
854 /* ----------------------------------------------------------------
855 * CleanupTransaction stuff
856 * ----------------------------------------------------------------
859 /* --------------------------------
861 * --------------------------------
867 * Now that we're "out" of a transaction, have the
868 * system allocate things in the top memory context instead
869 * of per-transaction contexts.
872 MemoryContextSwitchTo(TopMemoryContext);
875 * Release all transaction-local memory.
878 if (TopTransactionContext != NULL)
879 MemoryContextDelete(TopTransactionContext);
880 TopTransactionContext = NULL;
881 TransactionCommandContext = NULL;
885 /* ----------------------------------------------------------------
887 * ----------------------------------------------------------------
890 /* --------------------------------
893 * --------------------------------
898 TransactionState s = CurrentTransactionState;
901 XactIsoLevel = DefaultXactIsoLevel;
904 * Check the current transaction state. If the transaction system
905 * is switched off, or if we're already in a transaction, do nothing.
906 * We're already in a transaction when the monitor sends a null
907 * command to the backend to flush the comm channel. This is a
908 * hacky fix to a communications problem, and we keep having to
909 * deal with it here. We should fix the comm channel code. mao 080891
912 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
916 * set the current transaction state information
917 * appropriately during start processing
920 s->state = TRANS_START;
922 SetReindexProcessing(false);
925 * generate a new transaction id
928 GetNewTransactionId(&(s->transactionIdData));
930 XactLockTableInsert(s->transactionIdData);
933 * initialize current transaction state fields
936 s->commandId = FirstCommandId;
937 s->scanCommandId = FirstCommandId;
938 s->startTime = GetCurrentAbsoluteTime();
941 * initialize the various transaction subsystems
949 * Tell the trigger manager to we're starting a transaction
952 DeferredTriggerBeginXact();
955 * done with start processing, set current transaction
956 * state to "in progress"
959 s->state = TRANS_INPROGRESS;
965 * Tell me if we are currently in progress
969 CurrentXactInProgress()
971 return CurrentTransactionState->state == TRANS_INPROGRESS;
975 /* --------------------------------
978 * --------------------------------
983 TransactionState s = CurrentTransactionState;
986 * check the current transaction state
989 if (s->state == TRANS_DISABLED)
992 if (s->state != TRANS_INPROGRESS)
993 elog(NOTICE, "CommitTransaction and not in in-progress state ");
996 * Tell the trigger manager that this transaction is about to be
997 * committed. He'll invoke all trigger deferred until XACT before
998 * we really start on committing the transaction.
1001 DeferredTriggerEndXact();
1004 * set the current transaction state information
1005 * appropriately during the abort processing
1008 s->state = TRANS_COMMIT;
1011 * do commit processing
1015 /* handle commit for large objects [ PA, 7/17/98 ] */
1018 /* NOTIFY commit must also come before lower-level cleanup */
1023 RecordTransactionCommit();
1026 * Let others know about no transaction in progress by me. Note that
1027 * this must be done _before_ releasing locks we hold and
1028 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1029 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1030 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1031 * xid 0 as running as well or it will see two tuple versions - one
1032 * deleted by xid 1 and one inserted by xid 0.
1034 if (MyProc != (PROC *) NULL)
1036 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1037 SpinAcquire(SInvalLock);
1038 MyProc->xid = InvalidTransactionId;
1039 MyProc->xmin = InvalidTransactionId;
1040 SpinRelease(SInvalLock);
1043 RelationPurgeLocalRelation(true);
1051 SharedBufferChanged = false; /* safest place to do it */
1054 * done with commit processing, set current transaction
1055 * state back to default
1058 s->state = TRANS_DEFAULT;
1061 /* --------------------------------
1064 * --------------------------------
1069 TransactionState s = CurrentTransactionState;
1072 * Let others to know about no transaction in progress - vadim
1075 if (MyProc != (PROC *) NULL)
1077 MyProc->xid = InvalidTransactionId;
1078 MyProc->xmin = InvalidTransactionId;
1082 * check the current transaction state
1085 if (s->state == TRANS_DISABLED)
1088 if (s->state != TRANS_INPROGRESS)
1089 elog(NOTICE, "AbortTransaction and not in in-progress state");
1092 * Tell the trigger manager that this transaction is about to be
1096 DeferredTriggerAbortXact();
1099 * set the current transaction state information
1100 * appropriately during the abort processing
1103 s->state = TRANS_ABORT;
1106 * do abort processing
1109 lo_commit(false); /* 'false' means it's abort */
1114 RecordTransactionAbort();
1115 RelationPurgeLocalRelation(false);
1116 invalidate_temp_relations();
1124 SharedBufferChanged = false; /* safest place to do it */
1127 * State remains TRANS_ABORT until CleanupTransaction().
1132 /* --------------------------------
1133 * CleanupTransaction
1135 * --------------------------------
1138 CleanupTransaction()
1140 TransactionState s = CurrentTransactionState;
1142 if (s->state == TRANS_DISABLED)
1146 * State should still be TRANS_ABORT from AbortTransaction().
1149 if (s->state != TRANS_ABORT)
1150 elog(FATAL, "CleanupTransaction and not in abort state");
1153 * do abort cleanup processing
1159 * done with abort processing, set current transaction
1160 * state back to default
1163 s->state = TRANS_DEFAULT;
1166 /* --------------------------------
1167 * StartTransactionCommand
1168 * --------------------------------
1171 StartTransactionCommand()
1173 TransactionState s = CurrentTransactionState;
1175 switch (s->blockState)
1178 * if we aren't in a transaction block, we
1179 * just do our usual start transaction.
1182 case TBLOCK_DEFAULT:
1187 * We should never experience this -- if we do it
1188 * means the BEGIN state was not changed in the previous
1189 * CommitTransactionCommand(). If we get it, we print
1190 * a warning and change to the in-progress state.
1194 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1195 s->blockState = TBLOCK_INPROGRESS;
1199 * This is the case when are somewhere in a transaction
1200 * block and about to start a new command. For now we
1201 * do nothing but someday we may do command-local resource
1205 case TBLOCK_INPROGRESS:
1209 * As with BEGIN, we should never experience this
1210 * if we do it means the END state was not changed in the
1211 * previous CommitTransactionCommand(). If we get it, we
1212 * print a warning, commit the transaction, start a new
1213 * transaction and change to the default state.
1217 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1218 s->blockState = TBLOCK_DEFAULT;
1219 CommitTransaction();
1224 * Here we are in the middle of a transaction block but
1225 * one of the commands caused an abort so we do nothing
1226 * but remain in the abort state. Eventually we will get
1227 * to the "END TRANSACTION" which will set things straight.
1234 * This means we somehow aborted and the last call to
1235 * CommitTransactionCommand() didn't clear the state so
1236 * we remain in the ENDABORT state and maybe next time
1237 * we get to CommitTransactionCommand() the state will
1238 * get reset to default.
1241 case TBLOCK_ENDABORT:
1242 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1247 * We must switch to TransactionCommandContext before returning.
1248 * This is already done if we called StartTransaction, otherwise not.
1250 Assert(TransactionCommandContext != NULL);
1251 MemoryContextSwitchTo(TransactionCommandContext);
1254 /* --------------------------------
1255 * CommitTransactionCommand
1256 * --------------------------------
1259 CommitTransactionCommand()
1261 TransactionState s = CurrentTransactionState;
1263 switch (s->blockState)
1266 * if we aren't in a transaction block, we
1267 * just do our usual transaction commit
1270 case TBLOCK_DEFAULT:
1271 CommitTransaction();
1275 * This is the case right after we get a "BEGIN TRANSACTION"
1276 * command, but the user hasn't done anything else yet, so
1277 * we change to the "transaction block in progress" state
1282 s->blockState = TBLOCK_INPROGRESS;
1286 * This is the case when we have finished executing a command
1287 * someplace within a transaction block. We increment the
1288 * command counter and return. Someday we may free resources
1289 * local to the command.
1291 * That someday is today, at least for memory allocated in
1292 * TransactionCommandContext.
1296 case TBLOCK_INPROGRESS:
1297 CommandCounterIncrement();
1298 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1302 * This is the case when we just got the "END TRANSACTION"
1303 * statement, so we commit the transaction and go back to
1304 * the default state.
1308 CommitTransaction();
1309 s->blockState = TBLOCK_DEFAULT;
1313 * Here we are in the middle of a transaction block but
1314 * one of the commands caused an abort so we do nothing
1315 * but remain in the abort state. Eventually we will get
1316 * to the "END TRANSACTION" which will set things straight.
1323 * Here we were in an aborted transaction block which
1324 * just processed the "END TRANSACTION" command from the
1325 * user, so clean up and return to the default state.
1328 case TBLOCK_ENDABORT:
1329 CleanupTransaction();
1330 s->blockState = TBLOCK_DEFAULT;
1335 /* --------------------------------
1336 * AbortCurrentTransaction
1337 * --------------------------------
1340 AbortCurrentTransaction()
1342 TransactionState s = CurrentTransactionState;
1344 switch (s->blockState)
1347 * if we aren't in a transaction block, we
1348 * just do the basic abort & cleanup transaction.
1351 case TBLOCK_DEFAULT:
1353 CleanupTransaction();
1357 * If we are in the TBLOCK_BEGIN it means something
1358 * screwed up right after reading "BEGIN TRANSACTION"
1359 * so we enter the abort state. Eventually an "END
1360 * TRANSACTION" will fix things.
1364 s->blockState = TBLOCK_ABORT;
1366 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1370 * This is the case when are somewhere in a transaction
1371 * block which aborted so we abort the transaction and
1372 * set the ABORT state. Eventually an "END TRANSACTION"
1373 * will fix things and restore us to a normal state.
1376 case TBLOCK_INPROGRESS:
1377 s->blockState = TBLOCK_ABORT;
1379 /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1383 * Here, the system was fouled up just after the
1384 * user wanted to end the transaction block so we
1385 * abort the transaction and put us back into the
1390 s->blockState = TBLOCK_DEFAULT;
1392 CleanupTransaction();
1396 * Here, we are already in an aborted transaction
1397 * state and are waiting for an "END TRANSACTION" to
1398 * come along and lo and behold, we abort again!
1399 * So we just remain in the abort state.
1406 * Here we were in an aborted transaction block which
1407 * just processed the "END TRANSACTION" command but somehow
1408 * aborted again.. since we must have done the abort
1409 * processing, we clean up and return to the default state.
1412 case TBLOCK_ENDABORT:
1413 CleanupTransaction();
1414 s->blockState = TBLOCK_DEFAULT;
1419 /* ----------------------------------------------------------------
1420 * transaction block support
1421 * ----------------------------------------------------------------
1423 /* --------------------------------
1424 * BeginTransactionBlock
1425 * --------------------------------
1428 BeginTransactionBlock(void)
1430 TransactionState s = CurrentTransactionState;
1433 * check the current transaction state
1436 if (s->state == TRANS_DISABLED)
1439 if (s->blockState != TBLOCK_DEFAULT)
1440 elog(NOTICE, "BEGIN: already a transaction in progress");
1443 * set the current transaction block state information
1444 * appropriately during begin processing
1447 s->blockState = TBLOCK_BEGIN;
1450 * do begin processing
1455 * done with begin processing, set block state to inprogress
1458 s->blockState = TBLOCK_INPROGRESS;
1461 /* --------------------------------
1462 * EndTransactionBlock
1463 * --------------------------------
1466 EndTransactionBlock(void)
1468 TransactionState s = CurrentTransactionState;
1471 * check the current transaction state
1474 if (s->state == TRANS_DISABLED)
1477 if (s->blockState == TBLOCK_INPROGRESS)
1480 * here we are in a transaction block which should commit
1481 * when we get to the upcoming CommitTransactionCommand()
1482 * so we set the state to "END". CommitTransactionCommand()
1483 * will recognize this and commit the transaction and return
1484 * us to the default state
1487 s->blockState = TBLOCK_END;
1491 if (s->blockState == TBLOCK_ABORT)
1494 * here, we are in a transaction block which aborted
1495 * and since the AbortTransaction() was already done,
1496 * we do whatever is needed and change to the special
1497 * "END ABORT" state. The upcoming CommitTransactionCommand()
1498 * will recognise this and then put us back in the default
1502 s->blockState = TBLOCK_ENDABORT;
1507 * here, the user issued COMMIT when not inside a transaction.
1508 * Issue a notice and go to abort state. The upcoming call to
1509 * CommitTransactionCommand() will then put us back into the
1513 elog(NOTICE, "COMMIT: no transaction in progress");
1515 s->blockState = TBLOCK_ENDABORT;
1518 /* --------------------------------
1519 * AbortTransactionBlock
1520 * --------------------------------
1524 AbortTransactionBlock(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 were inside a transaction block something
1539 * screwed up inside the system so we enter the abort state,
1540 * do the abort processing and then return.
1541 * We remain in the abort state until we see an
1542 * END TRANSACTION command.
1545 s->blockState = TBLOCK_ABORT;
1551 * here, the user issued ABORT when not inside a transaction.
1552 * Issue a notice and go to abort state. The upcoming call to
1553 * CommitTransactionCommand() will then put us back into the
1557 elog(NOTICE, "ROLLBACK: no transaction in progress");
1559 s->blockState = TBLOCK_ENDABORT;
1564 /* --------------------------------
1565 * UserAbortTransactionBlock
1566 * --------------------------------
1569 UserAbortTransactionBlock()
1571 TransactionState s = CurrentTransactionState;
1574 * check the current transaction state
1577 if (s->state == TRANS_DISABLED)
1581 * if the transaction has already been automatically aborted with an
1582 * error, and the user subsequently types 'abort', allow it. (the
1583 * behavior is the same as if they had typed 'end'.)
1585 if (s->blockState == TBLOCK_ABORT)
1587 s->blockState = TBLOCK_ENDABORT;
1591 if (s->blockState == TBLOCK_INPROGRESS)
1594 * here we were inside a transaction block and we
1595 * got an abort command from the user, so we move to
1596 * the abort state, do the abort processing and
1597 * then change to the ENDABORT state so we will end up
1598 * in the default state after the upcoming
1599 * CommitTransactionCommand().
1602 s->blockState = TBLOCK_ABORT;
1604 s->blockState = TBLOCK_ENDABORT;
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;
1620 /* --------------------------------
1621 * AbortOutOfAnyTransaction
1623 * This routine is provided for error recovery purposes. It aborts any
1624 * active transaction or transaction block, leaving the system in a known
1626 * --------------------------------
1629 AbortOutOfAnyTransaction()
1631 TransactionState s = CurrentTransactionState;
1634 * Get out of any low-level transaction
1636 if (s->state != TRANS_DEFAULT)
1639 CleanupTransaction();
1643 * Now reset the high-level state
1645 s->blockState = TBLOCK_DEFAULT;
1649 IsTransactionBlock()
1651 TransactionState s = CurrentTransactionState;
1653 if (s->blockState == TBLOCK_INPROGRESS
1654 || s->blockState == TBLOCK_ABORT
1655 || s->blockState == TBLOCK_ENDABORT)