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.64 2000/04/12 17:14:53 momjian 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 shes 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
46 * This file is an attempt at a redesign of the upper layer
47 * of the V1 transaction system which was too poorly thought
48 * out to describe. This new system hopes to be both simpler
49 * in design, simpler to extend and needs to contain added
50 * functionality to solve problems beyond the scope of the V1
51 * system. (In particuler, communication of transaction
52 * information between parallel backends has to be supported)
54 * The essential aspects of the transaction system are:
56 * o transaction id generation
57 * o transaction log updating
59 * o cache invalidation
62 * Hence, the functional division of the transaction code is
63 * based on what of the above things need to be done during
64 * a start/commit/abort transaction. For instance, the
65 * routine AtCommit_Memory() takes care of all the memory
66 * cleanup stuff done at commit time.
68 * The code is layered as follows:
73 * UserAbortTransaction
75 * are provided to do the lower level work like recording
76 * the transaction status in the log and doing memory cleanup.
77 * above these routines are another set of functions:
79 * StartTransactionCommand
80 * CommitTransactionCommand
81 * AbortCurrentTransaction
83 * These are the routines used in the postgres main processing
84 * loop. They are sensitive to the current transaction block state
85 * and make calls to the lower level routines appropriately.
87 * Support for transaction blocks is provided via the functions:
89 * StartTransactionBlock
90 * CommitTransactionBlock
91 * AbortTransactionBlock
93 * These are invoked only in responce to a user "BEGIN", "END",
94 * or "ABORT" command. The tricky part about these functions
95 * is that they are called within the postgres main loop, in between
96 * the StartTransactionCommand() and CommitTransactionCommand().
98 * For example, consider the following sequence of user commands:
101 * 2) retrieve (foo.all)
102 * 3) append foo (bar = baz)
105 * in the main processing loop, this results in the following
106 * transaction sequence:
108 * / StartTransactionCommand();
109 * 1) / ProcessUtility(); << begin
110 * \ StartTransactionBlock();
111 * \ CommitTransactionCommand();
113 * / StartTransactionCommand();
114 * 2) < ProcessQuery(); << retrieve (foo.all)
115 * \ CommitTransactionCommand();
117 * / StartTransactionCommand();
118 * 3) < ProcessQuery(); << append foo (bar = baz)
119 * \ CommitTransactionCommand();
121 * / StartTransactionCommand();
122 * 4) / ProcessUtility(); << end
123 * \ CommitTransactionBlock();
124 * \ CommitTransactionCommand();
126 * The point of this example is to demonstrate the need for
127 * StartTransactionCommand() and CommitTransactionCommand() to
128 * be state smart -- they should do nothing in between the calls
129 * to StartTransactionBlock() and EndTransactionBlock() and
130 * outside these calls they need to do normal start/commit
133 * Furthermore, suppose the "retrieve (foo.all)" caused an abort
134 * condition. We would then want to abort the transaction and
135 * ignore all subsequent commands up to the "end".
138 *-------------------------------------------------------------------------
142 * Large object clean up added in CommitTransaction() to prevent buffer leaks.
144 * [PA] is Pascal André <andre@via.ecp.fr>
146 #include "postgres.h"
148 #include "access/nbtree.h"
149 #include "catalog/heap.h"
150 #include "catalog/index.h"
151 #include "commands/async.h"
152 #include "commands/sequence.h"
153 #include "commands/vacuum.h"
154 #include "commands/trigger.h"
155 #include "libpq/be-fsstubs.h"
156 #include "storage/proc.h"
157 #include "storage/sinval.h"
158 #include "utils/temprel.h"
159 #include "utils/inval.h"
160 #include "utils/portal.h"
161 #include "utils/relcache.h"
163 extern bool SharedBufferChanged;
165 static void AbortTransaction(void);
166 static void AtAbort_Cache(void);
167 static void AtAbort_Locks(void);
168 static void AtAbort_Memory(void);
169 static void AtCommit_Cache(void);
170 static void AtCommit_LocalCache(void);
171 static void AtCommit_Locks(void);
172 static void AtCommit_Memory(void);
173 static void AtStart_Cache(void);
174 static void AtStart_Locks(void);
175 static void AtStart_Memory(void);
176 static void CommitTransaction(void);
177 static void RecordTransactionAbort(void);
178 static void RecordTransactionCommit(void);
179 static void StartTransaction(void);
182 * global variables holding the current transaction state.
184 * Note: when we are running several slave processes, the
185 * current transaction state data is copied into shared memory
186 * and the CurrentTransactionState pointer changed to
187 * point to the shared copy. All this occurrs in slaves.c
190 TransactionStateData CurrentTransactionStateData = {
191 0, /* transaction id */
192 FirstCommandId, /* command id */
193 0, /* scan command id */
194 0x0, /* start time */
195 TRANS_DEFAULT, /* transaction state */
196 TBLOCK_DEFAULT /* transaction block state */
199 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
201 int DefaultXactIsoLevel = XACT_READ_COMMITTED;
205 * info returned when the system is disabled
207 * Apparently a lot of this code is inherited from other prototype systems.
208 * For DisabledStartTime, use a symbolic value to make the relationships clearer.
209 * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
210 * every day. It appears that if we return a value guaranteed larger than
211 * any real time associated with a transaction then comparisons in other
212 * modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
214 * Note: I have no idea what the significance of the
215 * 1073741823 in DisabledStartTime.. I just carried
216 * this over when converting things from the old
217 * V1 transaction system. -cim 3/18/90
220 TransactionId DisabledTransactionId = (TransactionId) -1;
222 CommandId DisabledCommandId = (CommandId) -1;
224 AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME; /* 1073741823; */
230 bool CommandIdCounterOverflowFlag;
233 * catalog creation transaction bootstrapping flag.
234 * This should be eliminated and added to the transaction
235 * state stuff. -cim 3/19/90
238 bool AMI_OVERRIDE = false;
240 /* ----------------------------------------------------------------
241 * transaction state accessors
242 * ----------------------------------------------------------------
245 /* --------------------------------
246 * TranactionFlushEnabled()
247 * SetTranactionFlushEnabled()
249 * These are used to test and set the "TransactionFlushState"
250 * varable. If this variable is true (the default), then
251 * the system will flush all dirty buffers to disk at the end
252 * of each transaction. If false then we are assuming the
253 * buffer pool resides in stable main memory, in which case we
254 * only do writes as necessary.
255 * --------------------------------
257 static int TransactionFlushState = 1;
260 TransactionFlushEnabled(void)
262 return TransactionFlushState;
267 SetTransactionFlushEnabled(bool state)
269 TransactionFlushState = (state == true);
273 /* --------------------------------
276 * This returns true if we are currently running a query
277 * within an executing transaction.
278 * --------------------------------
281 IsTransactionState(void)
283 TransactionState s = CurrentTransactionState;
291 case TRANS_INPROGRESS:
302 * Shouldn't get here, but lint is not happy with this...
309 /* --------------------------------
310 * IsAbortedTransactionBlockState
312 * This returns true if we are currently running a query
313 * within an aborted transaction block.
314 * --------------------------------
317 IsAbortedTransactionBlockState()
319 TransactionState s = CurrentTransactionState;
321 if (s->blockState == TBLOCK_ABORT)
327 /* --------------------------------
328 * OverrideTransactionSystem
330 * This is used to temporarily disable the transaction
331 * processing system in order to do initialization of
332 * the transaction system data structures and relations
334 * --------------------------------
336 int SavedTransactionState;
339 OverrideTransactionSystem(bool flag)
341 TransactionState s = CurrentTransactionState;
345 if (s->state == TRANS_DISABLED)
348 SavedTransactionState = s->state;
349 s->state = TRANS_DISABLED;
353 if (s->state != TRANS_DISABLED)
356 s->state = SavedTransactionState;
360 /* --------------------------------
361 * GetCurrentTransactionId
363 * This returns the id of the current transaction, or
364 * the id of the "disabled" transaction.
365 * --------------------------------
368 GetCurrentTransactionId()
370 TransactionState s = CurrentTransactionState;
373 * if the transaction system is disabled, we return
374 * the special "disabled" transaction id.
377 if (s->state == TRANS_DISABLED)
378 return (TransactionId) DisabledTransactionId;
381 * otherwise return the current transaction id.
384 return (TransactionId) s->transactionIdData;
388 /* --------------------------------
389 * GetCurrentCommandId
390 * --------------------------------
393 GetCurrentCommandId()
395 TransactionState s = CurrentTransactionState;
398 * if the transaction system is disabled, we return
399 * the special "disabled" command id.
402 if (s->state == TRANS_DISABLED)
403 return (CommandId) DisabledCommandId;
411 TransactionState s = CurrentTransactionState;
414 * if the transaction system is disabled, we return
415 * the special "disabled" command id.
418 if (s->state == TRANS_DISABLED)
419 return (CommandId) DisabledCommandId;
421 return s->scanCommandId;
425 /* --------------------------------
426 * GetCurrentTransactionStartTime
427 * --------------------------------
430 GetCurrentTransactionStartTime()
432 TransactionState s = CurrentTransactionState;
435 * if the transaction system is disabled, we return
436 * the special "disabled" starting time.
439 if (s->state == TRANS_DISABLED)
440 return (AbsoluteTime) DisabledStartTime;
446 /* --------------------------------
447 * TransactionIdIsCurrentTransactionId
448 * --------------------------------
451 TransactionIdIsCurrentTransactionId(TransactionId xid)
453 TransactionState s = CurrentTransactionState;
459 TransactionIdEquals(xid, s->transactionIdData);
463 /* --------------------------------
464 * CommandIdIsCurrentCommandId
465 * --------------------------------
468 CommandIdIsCurrentCommandId(CommandId cid)
470 TransactionState s = CurrentTransactionState;
475 return (cid == s->commandId) ? true : false;
479 CommandIdGEScanCommandId(CommandId cid)
481 TransactionState s = CurrentTransactionState;
486 return (cid >= s->scanCommandId) ? true : false;
490 /* --------------------------------
491 * ClearCommandIdCounterOverflowFlag
492 * --------------------------------
496 ClearCommandIdCounterOverflowFlag()
498 CommandIdCounterOverflowFlag = false;
503 /* --------------------------------
504 * CommandCounterIncrement
505 * --------------------------------
508 CommandCounterIncrement()
510 CurrentTransactionStateData.commandId += 1;
511 if (CurrentTransactionStateData.commandId == FirstCommandId)
513 CommandIdCounterOverflowFlag = true;
514 elog(ERROR, "You may only have 2^32-1 commands per transaction");
517 CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
520 * make cache changes visible to me. AtCommit_LocalCache() instead of
521 * AtCommit_Cache() is called here.
523 AtCommit_LocalCache();
529 SetScanCommandId(CommandId savedId)
532 CurrentTransactionStateData.scanCommandId = savedId;
536 /* ----------------------------------------------------------------
537 * initialization stuff
538 * ----------------------------------------------------------------
541 InitializeTransactionSystem()
543 InitializeTransactionLog();
546 /* ----------------------------------------------------------------
547 * StartTransaction stuff
548 * ----------------------------------------------------------------
551 /* --------------------------------
553 * --------------------------------
561 /* --------------------------------
563 * --------------------------------
570 * at present, it is unknown to me what belongs here -cim 3/18/90
572 * There isn't anything to do at the start of a xact for locks. -mer
577 /* --------------------------------
579 * --------------------------------
585 MemoryContext portalContext;
588 * get the blank portal and its memory context
591 portal = GetPortalByName(NULL);
592 portalContext = (MemoryContext) PortalGetHeapMemory(portal);
595 * tell system to allocate in the blank portal context
598 MemoryContextSwitchTo(portalContext);
599 StartPortalAllocMode(DefaultAllocMode, 0);
603 /* ----------------------------------------------------------------
604 * CommitTransaction stuff
605 * ----------------------------------------------------------------
608 /* --------------------------------
609 * RecordTransactionCommit
611 * Note: the two calls to BufferManagerFlush() exist to ensure
612 * that data pages are written before log pages. These
613 * explicit calls should be replaced by a more efficient
614 * ordered page write scheme in the buffer manager
616 * --------------------------------
619 RecordTransactionCommit()
625 * get the current transaction id
628 xid = GetCurrentTransactionId();
631 * flush the buffer manager pages. Note: if we have stable main
632 * memory, dirty shared buffers are not flushed plai 8/7/90
634 leak = BufferPoolCheckLeak();
637 * If no one shared buffer was changed by this transaction then we
638 * don't flush shared buffers and don't record commit status.
640 if (SharedBufferChanged)
644 ResetBufferPool(true);
647 * have the transaction access methods record the status of this
648 * transaction id in the pg_log relation.
650 TransactionIdCommit(xid);
653 * Now write the log info to the disk too.
655 leak = BufferPoolCheckLeak();
660 ResetBufferPool(true);
664 /* --------------------------------
666 * --------------------------------
672 * Make catalog changes visible to all backend.
675 RegisterInvalid(true);
678 /* --------------------------------
679 * AtCommit_LocalCache
680 * --------------------------------
683 AtCommit_LocalCache()
686 * Make catalog changes visible to me for the next command.
689 ImmediateLocalInvalidation(true);
692 /* --------------------------------
694 * --------------------------------
700 * XXX What if ProcReleaseLocks fails? (race condition?)
702 * Then you're up a creek! -mer 5/24/92
708 /* --------------------------------
710 * --------------------------------
718 * Release all heap memory in the blank portal.
721 portal = GetPortalByName(NULL);
722 PortalResetHeapMemory(portal);
725 * Now that we're "out" of a transaction, have the
726 * system allocate things in the top memory context instead
727 * of the blank portal memory context.
730 MemoryContextSwitchTo(TopMemoryContext);
733 /* ----------------------------------------------------------------
734 * AbortTransaction stuff
735 * ----------------------------------------------------------------
738 /* --------------------------------
739 * RecordTransactionAbort
740 * --------------------------------
743 RecordTransactionAbort()
748 * get the current transaction id
751 xid = GetCurrentTransactionId();
754 * Have the transaction access methods record the status of this
755 * transaction id in the pg_log relation. We skip it if no one shared
756 * buffer was changed by this transaction.
758 if (SharedBufferChanged && !TransactionIdDidCommit(xid))
759 TransactionIdAbort(xid);
762 * Tell bufmgr and smgr to release resources.
764 ResetBufferPool(false); /* false -> is abort */
767 /* --------------------------------
769 * --------------------------------
774 RelationCacheAbort();
775 RegisterInvalid(false);
778 /* --------------------------------
780 * --------------------------------
786 * XXX What if ProcReleaseLocks() fails? (race condition?)
788 * Then you're up a creek without a paddle! -mer
795 /* --------------------------------
797 * --------------------------------
805 * Release all heap memory in the blank portal.
808 portal = GetPortalByName(NULL);
809 PortalResetHeapMemory(portal);
812 * Now that we're "out" of a transaction, have the
813 * system allocate things in the top memory context instead
814 * of the blank portal memory context.
817 MemoryContextSwitchTo(TopMemoryContext);
820 /* ----------------------------------------------------------------
822 * ----------------------------------------------------------------
825 /* --------------------------------
828 * --------------------------------
833 TransactionState s = CurrentTransactionState;
836 XactIsoLevel = DefaultXactIsoLevel;
839 * Check the current transaction state. If the transaction system
840 * is switched off, or if we're already in a transaction, do nothing.
841 * We're already in a transaction when the monitor sends a null
842 * command to the backend to flush the comm channel. This is a
843 * hacky fix to a communications problem, and we keep having to
844 * deal with it here. We should fix the comm channel code. mao 080891
847 if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
851 * set the current transaction state information
852 * appropriately during start processing
855 s->state = TRANS_START;
857 SetReindexProcessing(false);
859 * generate a new transaction id
862 GetNewTransactionId(&(s->transactionIdData));
864 XactLockTableInsert(s->transactionIdData);
867 * initialize current transaction state fields
870 s->commandId = FirstCommandId;
871 s->scanCommandId = FirstCommandId;
872 s->startTime = GetCurrentAbsoluteTime();
875 * initialize the various transaction subsystems
883 initialize temporary relations list
884 the tempRelList is a list of temporary relations that
885 are created in the course of the transactions
886 they need to be destroyed properly at the end of the transactions
891 * Tell the trigger manager to we're starting a transaction
894 DeferredTriggerBeginXact();
897 * done with start processing, set current transaction
898 * state to "in progress"
901 s->state = TRANS_INPROGRESS;
906 * Tell me if we are currently in progress
910 CurrentXactInProgress()
912 return CurrentTransactionState->state == TRANS_INPROGRESS;
915 /* --------------------------------
918 * --------------------------------
923 TransactionState s = CurrentTransactionState;
926 * check the current transaction state
929 if (s->state == TRANS_DISABLED)
932 if (s->state != TRANS_INPROGRESS)
933 elog(NOTICE, "CommitTransaction and not in in-progress state ");
936 * Tell the trigger manager that this transaction is about to be
937 * committed. He'll invoke all trigger deferred until XACT before
938 * we really start on committing the transaction.
941 DeferredTriggerEndXact();
944 * set the current transaction state information
945 * appropriately during the abort processing
948 s->state = TRANS_COMMIT;
951 * do commit processing
955 /* handle commit for large objects [ PA, 7/17/98 ] */
958 /* NOTIFY commit must also come before lower-level cleanup */
964 RecordTransactionCommit();
967 * Let others know about no transaction in progress by me. Note that
968 * this must be done _before_ releasing locks we hold and
969 * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
970 * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
971 * if xid 2' GetSnapshotData sees xid 1 as running then it must see
972 * xid 0 as running as well or it will see two tuple versions - one
973 * deleted by xid 1 and one inserted by xid 0.
975 if (MyProc != (PROC *) NULL)
977 /* Lock SInvalLock because that's what GetSnapshotData uses. */
978 SpinAcquire(SInvalLock);
979 MyProc->xid = InvalidTransactionId;
980 MyProc->xmin = InvalidTransactionId;
981 SpinRelease(SInvalLock);
984 RelationPurgeLocalRelation(true);
992 * done with commit processing, set current transaction
993 * state back to default
996 s->state = TRANS_DEFAULT;
997 SharedBufferChanged = false;/* safest place to do it */
1001 /* --------------------------------
1004 * --------------------------------
1009 TransactionState s = CurrentTransactionState;
1012 * Let others to know about no transaction in progress - vadim
1015 if (MyProc != (PROC *) NULL)
1017 MyProc->xid = InvalidTransactionId;
1018 MyProc->xmin = InvalidTransactionId;
1022 * check the current transaction state
1025 if (s->state == TRANS_DISABLED)
1028 if (s->state != TRANS_INPROGRESS)
1029 elog(NOTICE, "AbortTransaction and not in in-progress state ");
1032 * Tell the trigger manager that this transaction is about to be
1036 DeferredTriggerAbortXact();
1039 * set the current transaction state information
1040 * appropriately during the abort processing
1043 s->state = TRANS_ABORT;
1046 * do abort processing
1049 lo_commit(false); /* 'false' means it's abort */
1054 if (CommonSpecialPortalIsOpen())
1055 CommonSpecialPortalClose();
1056 RecordTransactionAbort();
1057 RelationPurgeLocalRelation(false);
1059 invalidate_temp_relations();
1067 * done with abort processing, set current transaction
1068 * state back to default
1071 s->state = TRANS_DEFAULT;
1072 SharedBufferChanged = false;/* safest place to do it */
1075 /* --------------------------------
1076 * StartTransactionCommand
1077 * --------------------------------
1080 StartTransactionCommand()
1082 TransactionState s = CurrentTransactionState;
1084 switch (s->blockState)
1087 * if we aren't in a transaction block, we
1088 * just do our usual start transaction.
1091 case TBLOCK_DEFAULT:
1096 * We should never experience this -- if we do it
1097 * means the BEGIN state was not changed in the previous
1098 * CommitTransactionCommand(). If we get it, we print
1099 * a warning and change to the in-progress state.
1103 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1104 s->blockState = TBLOCK_INPROGRESS;
1108 * This is the case when are somewhere in a transaction
1109 * block and about to start a new command. For now we
1110 * do nothing but someday we may do command-local resource
1114 case TBLOCK_INPROGRESS:
1118 * As with BEGIN, we should never experience this
1119 * if we do it means the END state was not changed in the
1120 * previous CommitTransactionCommand(). If we get it, we
1121 * print a warning, commit the transaction, start a new
1122 * transaction and change to the default state.
1126 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1127 s->blockState = TBLOCK_DEFAULT;
1128 CommitTransaction();
1133 * Here we are in the middle of a transaction block but
1134 * one of the commands caused an abort so we do nothing
1135 * but remain in the abort state. Eventually we will get
1136 * to the "END TRANSACTION" which will set things straight.
1143 * This means we somehow aborted and the last call to
1144 * CommitTransactionCommand() didn't clear the state so
1145 * we remain in the ENDABORT state and mabey next time
1146 * we get to CommitTransactionCommand() the state will
1147 * get reset to default.
1150 case TBLOCK_ENDABORT:
1151 elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1156 /* --------------------------------
1157 * CommitTransactionCommand
1158 * --------------------------------
1161 CommitTransactionCommand()
1163 TransactionState s = CurrentTransactionState;
1165 switch (s->blockState)
1168 * if we aren't in a transaction block, we
1169 * just do our usual transaction commit
1172 case TBLOCK_DEFAULT:
1173 CommitTransaction();
1177 * This is the case right after we get a "BEGIN TRANSACTION"
1178 * command, but the user hasn't done anything else yet, so
1179 * we change to the "transaction block in progress" state
1184 s->blockState = TBLOCK_INPROGRESS;
1188 * This is the case when we have finished executing a command
1189 * someplace within a transaction block. We increment the
1190 * command counter and return. Someday we may free resources
1191 * local to the command.
1193 * That someday is today, at least for memory allocated by
1194 * command in the BlankPortal' HeapMemory context.
1198 case TBLOCK_INPROGRESS:
1199 CommandCounterIncrement();
1200 #ifdef TBL_FREE_CMD_MEMORY
1201 EndPortalAllocMode();
1202 StartPortalAllocMode(DefaultAllocMode, 0);
1207 * This is the case when we just got the "END TRANSACTION"
1208 * statement, so we go back to the default state and
1209 * commit the transaction.
1213 s->blockState = TBLOCK_DEFAULT;
1214 CommitTransaction();
1218 * Here we are in the middle of a transaction block but
1219 * one of the commands caused an abort so we do nothing
1220 * but remain in the abort state. Eventually we will get
1221 * to the "END TRANSACTION" which will set things straight.
1228 * Here we were in an aborted transaction block which
1229 * just processed the "END TRANSACTION" command from the
1230 * user, so now we return the to default state.
1233 case TBLOCK_ENDABORT:
1234 s->blockState = TBLOCK_DEFAULT;
1239 /* --------------------------------
1240 * AbortCurrentTransaction
1241 * --------------------------------
1244 AbortCurrentTransaction()
1246 TransactionState s = CurrentTransactionState;
1248 switch (s->blockState)
1251 * if we aren't in a transaction block, we
1252 * just do our usual abort transaction.
1255 case TBLOCK_DEFAULT:
1260 * If we are in the TBLOCK_BEGIN it means something
1261 * screwed up right after reading "BEGIN TRANSACTION"
1262 * so we enter the abort state. Eventually an "END
1263 * TRANSACTION" will fix things.
1267 s->blockState = TBLOCK_ABORT;
1272 * This is the case when are somewhere in a transaction
1273 * block which aborted so we abort the transaction and
1274 * set the ABORT state. Eventually an "END TRANSACTION"
1275 * will fix things and restore us to a normal state.
1278 case TBLOCK_INPROGRESS:
1279 s->blockState = TBLOCK_ABORT;
1284 * Here, the system was fouled up just after the
1285 * user wanted to end the transaction block so we
1286 * abort the transaction and put us back into the
1291 s->blockState = TBLOCK_DEFAULT;
1296 * Here, we are already in an aborted transaction
1297 * state and are waiting for an "END TRANSACTION" to
1298 * come along and lo and behold, we abort again!
1299 * So we just remain in the abort state.
1306 * Here we were in an aborted transaction block which
1307 * just processed the "END TRANSACTION" command but somehow
1308 * aborted again.. since we must have done the abort
1309 * processing, we return to the default state.
1312 case TBLOCK_ENDABORT:
1313 s->blockState = TBLOCK_DEFAULT;
1318 /* ----------------------------------------------------------------
1319 * transaction block support
1320 * ----------------------------------------------------------------
1322 /* --------------------------------
1323 * BeginTransactionBlock
1324 * --------------------------------
1327 BeginTransactionBlock(void)
1329 TransactionState s = CurrentTransactionState;
1332 * check the current transaction state
1335 if (s->state == TRANS_DISABLED)
1338 if (s->blockState != TBLOCK_DEFAULT)
1339 elog(NOTICE, "BEGIN: already a transaction in progress");
1342 * set the current transaction block state information
1343 * appropriately during begin processing
1346 s->blockState = TBLOCK_BEGIN;
1349 * do begin processing
1354 * done with begin processing, set block state to inprogress
1357 s->blockState = TBLOCK_INPROGRESS;
1360 /* --------------------------------
1361 * EndTransactionBlock
1362 * --------------------------------
1365 EndTransactionBlock(void)
1367 TransactionState s = CurrentTransactionState;
1370 * check the current transaction state
1373 if (s->state == TRANS_DISABLED)
1376 if (s->blockState == TBLOCK_INPROGRESS)
1379 * here we are in a transaction block which should commit
1380 * when we get to the upcoming CommitTransactionCommand()
1381 * so we set the state to "END". CommitTransactionCommand()
1382 * will recognize this and commit the transaction and return
1383 * us to the default state
1386 s->blockState = TBLOCK_END;
1390 if (s->blockState == TBLOCK_ABORT)
1393 * here, we are in a transaction block which aborted
1394 * and since the AbortTransaction() was already done,
1395 * we do whatever is needed and change to the special
1396 * "END ABORT" state. The upcoming CommitTransactionCommand()
1397 * will recognise this and then put us back in the default
1401 s->blockState = TBLOCK_ENDABORT;
1406 * We should not get here, but if we do, we go to the ENDABORT
1407 * state after printing a warning. The upcoming call to
1408 * CommitTransactionCommand() will then put us back into the
1412 elog(NOTICE, "COMMIT: no transaction in progress");
1413 s->blockState = TBLOCK_ENDABORT;
1416 /* --------------------------------
1417 * AbortTransactionBlock
1418 * --------------------------------
1422 AbortTransactionBlock(void)
1424 TransactionState s = CurrentTransactionState;
1427 * check the current transaction state
1430 if (s->state == TRANS_DISABLED)
1433 if (s->blockState == TBLOCK_INPROGRESS)
1436 * here we were inside a transaction block something
1437 * screwed up inside the system so we enter the abort state,
1438 * do the abort processing and then return.
1439 * We remain in the abort state until we see the upcoming
1440 * END TRANSACTION command.
1443 s->blockState = TBLOCK_ABORT;
1446 * do abort processing and return
1454 * this case should not be possible, because it would mean
1455 * the user entered an "abort" from outside a transaction block.
1456 * So we print an error message, abort the transaction and
1457 * enter the "ENDABORT" state so we will end up in the default
1458 * state after the upcoming CommitTransactionCommand().
1461 elog(NOTICE, "AbortTransactionBlock and not in in-progress state");
1463 s->blockState = TBLOCK_ENDABORT;
1468 /* --------------------------------
1469 * UserAbortTransactionBlock
1470 * --------------------------------
1473 UserAbortTransactionBlock()
1475 TransactionState s = CurrentTransactionState;
1478 * check the current transaction state
1481 if (s->state == TRANS_DISABLED)
1485 * if the transaction has already been automatically aborted with an
1486 * error, and the user subsequently types 'abort', allow it. (the
1487 * behavior is the same as if they had typed 'end'.)
1489 if (s->blockState == TBLOCK_ABORT)
1491 s->blockState = TBLOCK_ENDABORT;
1495 if (s->blockState == TBLOCK_INPROGRESS)
1498 * here we were inside a transaction block and we
1499 * got an abort command from the user, so we move to
1500 * the abort state, do the abort processing and
1501 * then change to the ENDABORT state so we will end up
1502 * in the default state after the upcoming
1503 * CommitTransactionCommand().
1506 s->blockState = TBLOCK_ABORT;
1509 * do abort processing
1515 * change to the end abort state and return
1518 s->blockState = TBLOCK_ENDABORT;
1523 * this case should not be possible, because it would mean
1524 * the user entered a "rollback" from outside a transaction block.
1525 * So we print an error message, abort the transaction and
1526 * enter the "ENDABORT" state so we will end up in the default
1527 * state after the upcoming CommitTransactionCommand().
1530 elog(NOTICE, "ROLLBACK: no transaction in progress");
1532 s->blockState = TBLOCK_ENDABORT;
1535 /* --------------------------------
1536 * AbortOutOfAnyTransaction
1538 * This routine is provided for error recovery purposes. It aborts any
1539 * active transaction or transaction block, leaving the system in a known
1541 * --------------------------------
1544 AbortOutOfAnyTransaction()
1546 TransactionState s = CurrentTransactionState;
1549 * Get out of any low-level transaction
1551 if (s->state != TRANS_DEFAULT)
1555 * Now reset the high-level state
1557 s->blockState = TBLOCK_DEFAULT;
1561 IsTransactionBlock()
1563 TransactionState s = CurrentTransactionState;
1565 if (s->blockState == TBLOCK_INPROGRESS
1566 || s->blockState == TBLOCK_ABORT
1567 || s->blockState == TBLOCK_ENDABORT)