]> granicus.if.org Git - postgresql/blob - src/backend/access/transam/xact.c
Improve documentation about reasoning behind the order of operations
[postgresql] / src / backend / access / transam / xact.c
1 /*-------------------------------------------------------------------------
2  *
3  * xact.c
4  *        top level transaction system support routines
5  *
6  * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/access/transam/xact.c,v 1.108 2001/07/16 22:43:33 tgl Exp $
12  *
13  * NOTES
14  *              Transaction aborts can now occur two ways:
15  *
16  *              1)      system dies from some internal cause  (Assert, etc..)
17  *              2)      user types abort
18  *
19  *              These two cases used to be treated identically, but now
20  *              we need to distinguish them.  Why?      consider the following
21  *              two situations:
22  *
23  *                              case 1                                                  case 2
24  *                              ------                                                  ------
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
29  *
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)
35  *
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:
41  *
42  *              * AbortTransactionBlock() leaves us in TBLOCK_ABORT and
43  *              * UserAbortTransactionBlock() leaves us in TBLOCK_ENDABORT
44  *
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.
53  *
54  *       NOTES
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)
62  *
63  *              The essential aspects of the transaction system are:
64  *
65  *                              o  transaction id generation
66  *                              o  transaction log updating
67  *                              o  memory cleanup
68  *                              o  cache invalidation
69  *                              o  lock cleanup
70  *
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.
76  *
77  *              The code is layered as follows:
78  *
79  *                              StartTransaction
80  *                              CommitTransaction
81  *                              AbortTransaction
82  *                              CleanupTransaction
83  *
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:
87  *
88  *                              StartTransactionCommand
89  *                              CommitTransactionCommand
90  *                              AbortCurrentTransaction
91  *
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.
95  *
96  *              Support for transaction blocks is provided via the functions:
97  *
98  *                              StartTransactionBlock
99  *                              CommitTransactionBlock
100  *                              AbortTransactionBlock
101  *
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().
106  *
107  *              For example, consider the following sequence of user commands:
108  *
109  *              1)              begin
110  *              2)              retrieve (foo.all)
111  *              3)              append foo (bar = baz)
112  *              4)              end
113  *
114  *              in the main processing loop, this results in the following
115  *              transaction sequence:
116  *
117  *                      /       StartTransactionCommand();
118  *              1) /    ProcessUtility();                               << begin
119  *                 \            StartTransactionBlock();
120  *                      \       CommitTransactionCommand();
121  *
122  *                      /       StartTransactionCommand();
123  *              2) <    ProcessQuery();                                 << retrieve (foo.all)
124  *                      \       CommitTransactionCommand();
125  *
126  *                      /       StartTransactionCommand();
127  *              3) <    ProcessQuery();                                 << append foo (bar = baz)
128  *                      \       CommitTransactionCommand();
129  *
130  *                      /       StartTransactionCommand();
131  *              4) /    ProcessUtility();                               << end
132  *                 \            CommitTransactionBlock();
133  *                      \       CommitTransactionCommand();
134  *
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
140  *              processing.
141  *
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".
145  *              -cim 3/23/90
146  *
147  *-------------------------------------------------------------------------
148  */
149
150 /*
151  * Large object clean up added in CommitTransaction() to prevent buffer leaks.
152  * [PA, 7/17/98]
153  * [PA] is Pascal AndrĂ© <andre@via.ecp.fr>
154  */
155 #include "postgres.h"
156
157 #include <sys/time.h>
158
159 #include "access/gistscan.h"
160 #include "access/hash.h"
161 #include "access/nbtree.h"
162 #include "access/rtree.h"
163 #include "access/xact.h"
164 #include "catalog/heap.h"
165 #include "catalog/index.h"
166 #include "commands/async.h"
167 #include "commands/sequence.h"
168 #include "commands/trigger.h"
169 #include "executor/spi.h"
170 #include "libpq/be-fsstubs.h"
171 #include "miscadmin.h"
172 #include "storage/proc.h"
173 #include "storage/sinval.h"
174 #include "storage/smgr.h"
175 #include "utils/inval.h"
176 #include "utils/memutils.h"
177 #include "utils/portal.h"
178 #include "utils/catcache.h"
179 #include "utils/relcache.h"
180 #include "utils/temprel.h"
181
182 #include "pgstat.h"
183
184 extern bool SharedBufferChanged;
185
186 static void AbortTransaction(void);
187 static void AtAbort_Cache(void);
188 static void AtAbort_Locks(void);
189 static void AtAbort_Memory(void);
190 static void AtCleanup_Memory(void);
191 static void AtCommit_Cache(void);
192 static void AtCommit_LocalCache(void);
193 static void AtCommit_Locks(void);
194 static void AtCommit_Memory(void);
195 static void AtStart_Cache(void);
196 static void AtStart_Locks(void);
197 static void AtStart_Memory(void);
198 static void CleanupTransaction(void);
199 static void CommitTransaction(void);
200 static void RecordTransactionAbort(void);
201 static void StartTransaction(void);
202
203 /* ----------------
204  *              global variables holding the current transaction state.
205  * ----------------
206  */
207 static TransactionStateData CurrentTransactionStateData = {
208         0,                                                      /* transaction id */
209         FirstCommandId,                         /* command id */
210         0,                                                      /* scan command id */
211         0x0,                                            /* start time */
212         TRANS_DEFAULT,                          /* transaction state */
213         TBLOCK_DEFAULT                          /* transaction block state */
214 };
215
216 TransactionState CurrentTransactionState = &CurrentTransactionStateData;
217
218 /*
219  * User-tweakable parameters
220  */
221 int                     DefaultXactIsoLevel = XACT_READ_COMMITTED;
222 int                     XactIsoLevel;
223
224 int                     CommitDelay = 0;        /* precommit delay in microseconds */
225 int                     CommitSiblings = 5; /* number of concurrent xacts needed to
226                                                                  * sleep */
227
228 static void (*_RollbackFunc) (void *) = NULL;
229 static void *_RollbackData = NULL;
230
231 /* ----------------
232  *              info returned when the system is disabled
233  *
234  * Apparently a lot of this code is inherited from other prototype systems.
235  *
236  * For DisabledStartTime, use a symbolic value to make the relationships clearer.
237  * The old value of 1073741823 corresponds to a date in y2004, which is coming closer
238  *      every day. It appears that if we return a value guaranteed larger than
239  *      any real time associated with a transaction then comparisons in other
240  *      modules will still be correct. Let's use BIG_ABSTIME for this. tgl 2/14/97
241  * ----------------
242  */
243 static CommandId        DisabledCommandId = (CommandId) -1;
244
245 static AbsoluteTime DisabledStartTime = (AbsoluteTime) BIG_ABSTIME;
246
247 /* ----------------
248  *              catalog creation transaction bootstrapping flag.
249  *              This should be eliminated and added to the transaction
250  *              state stuff.  -cim 3/19/90
251  * ----------------
252  */
253 bool            AMI_OVERRIDE = false;
254
255 /* ----------------------------------------------------------------
256  *                                       transaction state accessors
257  * ----------------------------------------------------------------
258  */
259
260 /* --------------------------------
261  *              TranactionFlushEnabled()
262  *              SetTransactionFlushEnabled()
263  *
264  *              These are used to test and set the "TransactionFlushState"
265  *              varable.  If this variable is true (the default), then
266  *              the system will flush all dirty buffers to disk at the end
267  *              of each transaction.   If false then we are assuming the
268  *              buffer pool resides in stable main memory, in which case we
269  *              only do writes as necessary.
270  * --------------------------------
271  */
272 static int      TransactionFlushState = 1;
273
274 int
275 TransactionFlushEnabled(void)
276 {
277         return TransactionFlushState;
278 }
279
280 #ifdef NOT_USED
281 void
282 SetTransactionFlushEnabled(bool state)
283 {
284         TransactionFlushState = (state == true);
285 }
286
287
288 /* --------------------------------
289  *              IsTransactionState
290  *
291  *              This returns true if we are currently running a query
292  *              within an executing transaction.
293  * --------------------------------
294  */
295 bool
296 IsTransactionState(void)
297 {
298         TransactionState s = CurrentTransactionState;
299
300         switch (s->state)
301         {
302                 case TRANS_DEFAULT:
303                         return false;
304                 case TRANS_START:
305                         return true;
306                 case TRANS_INPROGRESS:
307                         return true;
308                 case TRANS_COMMIT:
309                         return true;
310                 case TRANS_ABORT:
311                         return true;
312                 case TRANS_DISABLED:
313                         return false;
314         }
315
316         /*
317          * Shouldn't get here, but lint is not happy with this...
318          */
319         return false;
320 }
321
322 #endif
323
324 /* --------------------------------
325  *              IsAbortedTransactionBlockState
326  *
327  *              This returns true if we are currently running a query
328  *              within an aborted transaction block.
329  * --------------------------------
330  */
331 bool
332 IsAbortedTransactionBlockState(void)
333 {
334         TransactionState s = CurrentTransactionState;
335
336         if (s->blockState == TBLOCK_ABORT)
337                 return true;
338
339         return false;
340 }
341
342 /* --------------------------------
343  *              OverrideTransactionSystem
344  *
345  *              This is used to temporarily disable the transaction
346  *              processing system in order to do initialization of
347  *              the transaction system data structures and relations
348  *              themselves.
349  * --------------------------------
350  */
351 static int              SavedTransactionState;
352
353 void
354 OverrideTransactionSystem(bool flag)
355 {
356         TransactionState s = CurrentTransactionState;
357
358         if (flag == true)
359         {
360                 if (s->state == TRANS_DISABLED)
361                         return;
362
363                 SavedTransactionState = s->state;
364                 s->state = TRANS_DISABLED;
365         }
366         else
367         {
368                 if (s->state != TRANS_DISABLED)
369                         return;
370
371                 s->state = SavedTransactionState;
372         }
373 }
374
375 /* --------------------------------
376  *              GetCurrentTransactionId
377  *
378  *              This returns the id of the current transaction, or
379  *              the id of the "disabled" transaction.
380  * --------------------------------
381  */
382 TransactionId
383 GetCurrentTransactionId(void)
384 {
385         TransactionState s = CurrentTransactionState;
386
387         /*
388          * if the transaction system is disabled, we return the special
389          * "disabled" transaction id.
390          */
391         if (s->state == TRANS_DISABLED)
392                 return DisabledTransactionId;
393
394         /*
395          * otherwise return the current transaction id.
396          */
397         return s->transactionIdData;
398 }
399
400
401 /* --------------------------------
402  *              GetCurrentCommandId
403  * --------------------------------
404  */
405 CommandId
406 GetCurrentCommandId(void)
407 {
408         TransactionState s = CurrentTransactionState;
409
410         /*
411          * if the transaction system is disabled, we return the special
412          * "disabled" command id.
413          */
414         if (s->state == TRANS_DISABLED)
415                 return DisabledCommandId;
416
417         return s->commandId;
418 }
419
420 CommandId
421 GetScanCommandId(void)
422 {
423         TransactionState s = CurrentTransactionState;
424
425         /*
426          * if the transaction system is disabled, we return the special
427          * "disabled" command id.
428          */
429         if (s->state == TRANS_DISABLED)
430                 return DisabledCommandId;
431
432         return s->scanCommandId;
433 }
434
435
436 /* --------------------------------
437  *              GetCurrentTransactionStartTime
438  * --------------------------------
439  */
440 AbsoluteTime
441 GetCurrentTransactionStartTime(void)
442 {
443         TransactionState s = CurrentTransactionState;
444
445         /*
446          * if the transaction system is disabled, we return the special
447          * "disabled" starting time.
448          */
449         if (s->state == TRANS_DISABLED)
450                 return DisabledStartTime;
451
452         return s->startTime;
453 }
454
455
456 /* --------------------------------
457  *              TransactionIdIsCurrentTransactionId
458  * --------------------------------
459  */
460 bool
461 TransactionIdIsCurrentTransactionId(TransactionId xid)
462 {
463         TransactionState s = CurrentTransactionState;
464
465         if (AMI_OVERRIDE)
466                 return false;
467
468         return TransactionIdEquals(xid, s->transactionIdData);
469 }
470
471
472 /* --------------------------------
473  *              CommandIdIsCurrentCommandId
474  * --------------------------------
475  */
476 bool
477 CommandIdIsCurrentCommandId(CommandId cid)
478 {
479         TransactionState s = CurrentTransactionState;
480
481         if (AMI_OVERRIDE)
482                 return false;
483
484         return (cid == s->commandId) ? true : false;
485 }
486
487 bool
488 CommandIdGEScanCommandId(CommandId cid)
489 {
490         TransactionState s = CurrentTransactionState;
491
492         if (AMI_OVERRIDE)
493                 return false;
494
495         return (cid >= s->scanCommandId) ? true : false;
496 }
497
498
499 /* --------------------------------
500  *              CommandCounterIncrement
501  * --------------------------------
502  */
503 void
504 CommandCounterIncrement(void)
505 {
506         CurrentTransactionStateData.commandId += 1;
507         if (CurrentTransactionStateData.commandId == FirstCommandId)
508                 elog(ERROR, "You may only have 2^32-1 commands per transaction");
509
510         CurrentTransactionStateData.scanCommandId = CurrentTransactionStateData.commandId;
511
512         /*
513          * make cache changes visible to me.  AtCommit_LocalCache() instead of
514          * AtCommit_Cache() is called here.
515          */
516         AtCommit_LocalCache();
517         AtStart_Cache();
518 }
519
520 void
521 SetScanCommandId(CommandId savedId)
522 {
523         CurrentTransactionStateData.scanCommandId = savedId;
524 }
525
526 /* ----------------------------------------------------------------
527  *                                              initialization stuff
528  * ----------------------------------------------------------------
529  */
530 void
531 InitializeTransactionSystem(void)
532 {
533         InitializeTransactionLog();
534 }
535
536 /* ----------------------------------------------------------------
537  *                                              StartTransaction stuff
538  * ----------------------------------------------------------------
539  */
540
541 /* --------------------------------
542  *              AtStart_Cache
543  * --------------------------------
544  */
545 static void
546 AtStart_Cache(void)
547 {
548         AcceptInvalidationMessages();
549 }
550
551 /* --------------------------------
552  *              AtStart_Locks
553  * --------------------------------
554  */
555 static void
556 AtStart_Locks(void)
557 {
558
559         /*
560          * at present, it is unknown to me what belongs here -cim 3/18/90
561          *
562          * There isn't anything to do at the start of a xact for locks. -mer
563          * 5/24/92
564          */
565 }
566
567 /* --------------------------------
568  *              AtStart_Memory
569  * --------------------------------
570  */
571 static void
572 AtStart_Memory(void)
573 {
574
575         /*
576          * We shouldn't have any transaction contexts already.
577          */
578         Assert(TopTransactionContext == NULL);
579         Assert(TransactionCommandContext == NULL);
580
581         /*
582          * Create a toplevel context for the transaction.
583          */
584         TopTransactionContext =
585                 AllocSetContextCreate(TopMemoryContext,
586                                                           "TopTransactionContext",
587                                                           ALLOCSET_DEFAULT_MINSIZE,
588                                                           ALLOCSET_DEFAULT_INITSIZE,
589                                                           ALLOCSET_DEFAULT_MAXSIZE);
590
591         /*
592          * Create a statement-level context and make it active.
593          */
594         TransactionCommandContext =
595                 AllocSetContextCreate(TopTransactionContext,
596                                                           "TransactionCommandContext",
597                                                           ALLOCSET_DEFAULT_MINSIZE,
598                                                           ALLOCSET_DEFAULT_INITSIZE,
599                                                           ALLOCSET_DEFAULT_MAXSIZE);
600         MemoryContextSwitchTo(TransactionCommandContext);
601 }
602
603
604 /* ----------------------------------------------------------------
605  *                                              CommitTransaction stuff
606  * ----------------------------------------------------------------
607  */
608
609 /* --------------------------------
610  *              RecordTransactionCommit
611  *
612  *              Note: the two calls to BufferManagerFlush() exist to ensure
613  *                        that data pages are written before log pages.  These
614  *                        explicit calls should be replaced by a more efficient
615  *                        ordered page write scheme in the buffer manager
616  *                        -cim 3/18/90
617  * --------------------------------
618  */
619 void
620 RecordTransactionCommit()
621 {
622         TransactionId xid;
623         bool            leak;
624
625         xid = GetCurrentTransactionId();
626
627         leak = BufferPoolCheckLeak();
628
629         if (MyLastRecPtr.xrecoff != 0)
630         {
631                 XLogRecData rdata;
632                 xl_xact_commit xlrec;
633                 XLogRecPtr      recptr;
634
635                 BufmgrCommit();
636
637                 xlrec.xtime = time(NULL);
638                 rdata.buffer = InvalidBuffer;
639                 rdata.data = (char *) (&xlrec);
640                 rdata.len = SizeOfXactCommit;
641                 rdata.next = NULL;
642
643                 START_CRIT_SECTION();
644
645                 /*
646                  * SHOULD SAVE ARRAY OF RELFILENODE-s TO DROP
647                  */
648                 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_COMMIT, &rdata);
649
650                 /*
651                  * Sleep before commit! So we can flush more than one commit
652                  * records per single fsync.  (The idea is some other backend may
653                  * do the XLogFlush while we're sleeping.  This needs work still,
654                  * because on most Unixen, the minimum select() delay is 10msec or
655                  * more, which is way too long.)
656                  *
657                  * We do not sleep if enableFsync is not turned on, nor if there are
658                  * fewer than CommitSiblings other backends with active
659                  * transactions.
660                  */
661                 if (CommitDelay > 0 && enableFsync &&
662                         CountActiveBackends() >= CommitSiblings)
663                 {
664                         struct timeval delay;
665
666                         delay.tv_sec = 0;
667                         delay.tv_usec = CommitDelay;
668                         (void) select(0, NULL, NULL, NULL, &delay);
669                 }
670
671                 XLogFlush(recptr);
672
673                 /* Break the chain of back-links in the XLOG records I output */
674                 MyLastRecPtr.xrecoff = 0;
675
676                 TransactionIdCommit(xid);
677
678                 END_CRIT_SECTION();
679         }
680
681         /* Show myself as out of the transaction in PROC array */
682         MyProc->logRec.xrecoff = 0;
683
684         if (leak)
685                 ResetBufferPool(true);
686 }
687
688
689 /* --------------------------------
690  *              AtCommit_Cache
691  * --------------------------------
692  */
693 static void
694 AtCommit_Cache(void)
695 {
696         /*
697          * Make catalog changes visible to all backends.
698          */
699         AtEOXactInvalidationMessages(true);
700 }
701
702 /* --------------------------------
703  *              AtCommit_LocalCache
704  * --------------------------------
705  */
706 static void
707 AtCommit_LocalCache(void)
708 {
709         /*
710          * Make catalog changes visible to me for the next command.
711          */
712         CommandEndInvalidationMessages(true);
713 }
714
715 /* --------------------------------
716  *              AtCommit_Locks
717  * --------------------------------
718  */
719 static void
720 AtCommit_Locks(void)
721 {
722         /*
723          * XXX What if ProcReleaseLocks fails?  (race condition?)
724          *
725          * Then you're up a creek! -mer 5/24/92
726          */
727         ProcReleaseLocks(true);
728 }
729
730 /* --------------------------------
731  *              AtCommit_Memory
732  * --------------------------------
733  */
734 static void
735 AtCommit_Memory(void)
736 {
737         /*
738          * Now that we're "out" of a transaction, have the system allocate
739          * things in the top memory context instead of per-transaction
740          * contexts.
741          */
742         MemoryContextSwitchTo(TopMemoryContext);
743
744         /*
745          * Release all transaction-local memory.
746          */
747         Assert(TopTransactionContext != NULL);
748         MemoryContextDelete(TopTransactionContext);
749         TopTransactionContext = NULL;
750         TransactionCommandContext = NULL;
751 }
752
753 /* ----------------------------------------------------------------
754  *                                              AbortTransaction stuff
755  * ----------------------------------------------------------------
756  */
757
758 /* --------------------------------
759  *              RecordTransactionAbort
760  * --------------------------------
761  */
762 static void
763 RecordTransactionAbort(void)
764 {
765         TransactionId xid = GetCurrentTransactionId();
766
767         /*
768          * Double check here is to catch case that we aborted partway through
769          * RecordTransactionCommit ...
770          */
771         if (MyLastRecPtr.xrecoff != 0 && !TransactionIdDidCommit(xid))
772         {
773                 XLogRecData rdata;
774                 xl_xact_abort xlrec;
775                 XLogRecPtr      recptr;
776
777                 xlrec.xtime = time(NULL);
778                 rdata.buffer = InvalidBuffer;
779                 rdata.data = (char *) (&xlrec);
780                 rdata.len = SizeOfXactAbort;
781                 rdata.next = NULL;
782
783                 START_CRIT_SECTION();
784
785                 recptr = XLogInsert(RM_XACT_ID, XLOG_XACT_ABORT, &rdata);
786
787                 TransactionIdAbort(xid);
788
789                 END_CRIT_SECTION();
790         }
791
792         /* Break the chain of back-links in the XLOG records I output */
793         MyLastRecPtr.xrecoff = 0;
794         /* Show myself as out of the transaction in PROC array */
795         MyProc->logRec.xrecoff = 0;
796
797         /*
798          * Tell bufmgr and smgr to release resources.
799          */
800         ResetBufferPool(false);         /* false -> is abort */
801 }
802
803 /* --------------------------------
804  *              AtAbort_Cache
805  * --------------------------------
806  */
807 static void
808 AtAbort_Cache(void)
809 {
810         RelationCacheAbort();
811         AtEOXactInvalidationMessages(false);
812 }
813
814 /* --------------------------------
815  *              AtAbort_Locks
816  * --------------------------------
817  */
818 static void
819 AtAbort_Locks(void)
820 {
821
822         /*
823          * XXX What if ProcReleaseLocks() fails?  (race condition?)
824          *
825          * Then you're up a creek without a paddle! -mer
826          */
827         ProcReleaseLocks(false);
828 }
829
830
831 /* --------------------------------
832  *              AtAbort_Memory
833  * --------------------------------
834  */
835 static void
836 AtAbort_Memory(void)
837 {
838
839         /*
840          * Make sure we are in a valid context (not a child of
841          * TransactionCommandContext...).  Note that it is possible for this
842          * code to be called when we aren't in a transaction at all; go
843          * directly to TopMemoryContext in that case.
844          */
845         if (TransactionCommandContext != NULL)
846         {
847                 MemoryContextSwitchTo(TransactionCommandContext);
848
849                 /*
850                  * We do not want to destroy transaction contexts yet, but it
851                  * should be OK to delete any command-local memory.
852                  */
853                 MemoryContextResetAndDeleteChildren(TransactionCommandContext);
854         }
855         else
856                 MemoryContextSwitchTo(TopMemoryContext);
857 }
858
859
860 /* ----------------------------------------------------------------
861  *                                              CleanupTransaction stuff
862  * ----------------------------------------------------------------
863  */
864
865 /* --------------------------------
866  *              AtCleanup_Memory
867  * --------------------------------
868  */
869 static void
870 AtCleanup_Memory(void)
871 {
872
873         /*
874          * Now that we're "out" of a transaction, have the system allocate
875          * things in the top memory context instead of per-transaction
876          * contexts.
877          */
878         MemoryContextSwitchTo(TopMemoryContext);
879
880         /*
881          * Release all transaction-local memory.
882          */
883         if (TopTransactionContext != NULL)
884                 MemoryContextDelete(TopTransactionContext);
885         TopTransactionContext = NULL;
886         TransactionCommandContext = NULL;
887 }
888
889
890 /* ----------------------------------------------------------------
891  *                                              interface routines
892  * ----------------------------------------------------------------
893  */
894
895 /* --------------------------------
896  *              StartTransaction
897  *
898  * --------------------------------
899  */
900 static void
901 StartTransaction(void)
902 {
903         TransactionState s = CurrentTransactionState;
904
905         FreeXactSnapshot();
906         XactIsoLevel = DefaultXactIsoLevel;
907
908         /*
909          * Check the current transaction state.  If the transaction system is
910          * switched off, or if we're already in a transaction, do nothing.
911          * We're already in a transaction when the monitor sends a null
912          * command to the backend to flush the comm channel.  This is a hacky
913          * fix to a communications problem, and we keep having to deal with it
914          * here.  We should fix the comm channel code.  mao 080891
915          */
916         if (s->state == TRANS_DISABLED || s->state == TRANS_INPROGRESS)
917                 return;
918
919         /*
920          * set the current transaction state information appropriately during
921          * start processing
922          */
923         s->state = TRANS_START;
924
925         SetReindexProcessing(false);
926
927         /*
928          * generate a new transaction id
929          */
930         GetNewTransactionId(&(s->transactionIdData));
931
932         XactLockTableInsert(s->transactionIdData);
933
934         /*
935          * initialize current transaction state fields
936          */
937         s->commandId = FirstCommandId;
938         s->scanCommandId = FirstCommandId;
939         s->startTime = GetCurrentAbsoluteTime();
940
941         /*
942          * initialize the various transaction subsystems
943          */
944         AtStart_Memory();
945         AtStart_Cache();
946         AtStart_Locks();
947
948         /*
949          * Tell the trigger manager to we're starting a transaction
950          */
951         DeferredTriggerBeginXact();
952
953         /*
954          * done with start processing, set current transaction state to "in
955          * progress"
956          */
957         s->state = TRANS_INPROGRESS;
958
959 }
960
961 #ifdef NOT_USED
962 /* ---------------
963  * Tell me if we are currently in progress
964  * ---------------
965  */
966 bool
967 CurrentXactInProgress(void)
968 {
969         return CurrentTransactionState->state == TRANS_INPROGRESS;
970 }
971
972 #endif
973
974 /* --------------------------------
975  *              CommitTransaction
976  *
977  * --------------------------------
978  */
979 static void
980 CommitTransaction(void)
981 {
982         TransactionState s = CurrentTransactionState;
983
984         /*
985          * check the current transaction state
986          */
987         if (s->state == TRANS_DISABLED)
988                 return;
989
990         if (s->state != TRANS_INPROGRESS)
991                 elog(NOTICE, "CommitTransaction and not in in-progress state ");
992
993         /*
994          * Tell the trigger manager that this transaction is about to be
995          * committed. He'll invoke all trigger deferred until XACT before we
996          * really start on committing the transaction.
997          */
998         DeferredTriggerEndXact();
999
1000         /* Prevent cancel/die interrupt while cleaning up */
1001         HOLD_INTERRUPTS();
1002
1003         /*
1004          * set the current transaction state information appropriately during
1005          * the abort processing
1006          */
1007         s->state = TRANS_COMMIT;
1008
1009         /*
1010          * do commit processing
1011          */
1012
1013         /* handle commit for large objects [ PA, 7/17/98 ] */
1014         lo_commit(true);
1015
1016         /* NOTIFY commit must also come before lower-level cleanup */
1017         AtCommit_Notify();
1018
1019         CloseSequences();
1020         AtEOXact_portals();
1021
1022         /* Here is where we really truly commit. */
1023         RecordTransactionCommit();
1024
1025         /*
1026          * Let others know about no transaction in progress by me. Note that
1027          * this must be done _before_ releasing locks we hold and _after_
1028          * RecordTransactionCommit.
1029          *
1030          * SpinAcquire(SInvalLock) is required: UPDATE with xid 0 is blocked
1031          * by xid 1' UPDATE, xid 1 is doing commit while xid 2 gets snapshot -
1032          * if xid 2' GetSnapshotData sees xid 1 as running then it must see
1033          * xid 0 as running as well or it will see two tuple versions - one
1034          * deleted by xid 1 and one inserted by xid 0.  See notes in
1035          * GetSnapshotData.
1036          */
1037         if (MyProc != (PROC *) NULL)
1038         {
1039                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1040                 SpinAcquire(SInvalLock);
1041                 MyProc->xid = InvalidTransactionId;
1042                 MyProc->xmin = InvalidTransactionId;
1043                 SpinRelease(SInvalLock);
1044         }
1045
1046         /*
1047          * This is all post-commit cleanup.  Note that if an error is raised
1048          * here, it's too late to abort the transaction.  This should be just
1049          * noncritical resource releasing.
1050          */
1051
1052         RelationPurgeLocalRelation(true);
1053         AtEOXact_temp_relations(true);
1054         smgrDoPendingDeletes(true);
1055
1056         AtEOXact_SPI();
1057         AtEOXact_gist();
1058         AtEOXact_hash();
1059         AtEOXact_nbtree();
1060         AtEOXact_rtree();
1061         AtCommit_Cache();
1062         AtCommit_Locks();
1063         AtEOXact_CatCache(true);
1064         AtCommit_Memory();
1065         AtEOXact_Files();
1066
1067         SharedBufferChanged = false;/* safest place to do it */
1068
1069         /* Count transaction commit in statistics collector */
1070         pgstat_count_xact_commit();
1071
1072         /*
1073          * done with commit processing, set current transaction state back to
1074          * default
1075          */
1076         s->state = TRANS_DEFAULT;
1077
1078         RESUME_INTERRUPTS();
1079 }
1080
1081 /* --------------------------------
1082  *              AbortTransaction
1083  *
1084  * --------------------------------
1085  */
1086 static void
1087 AbortTransaction(void)
1088 {
1089         TransactionState s = CurrentTransactionState;
1090
1091         /* Prevent cancel/die interrupt while cleaning up */
1092         HOLD_INTERRUPTS();
1093
1094         /*
1095          * Release any spinlocks or buffer context locks we might be holding
1096          * as quickly as possible.      (Real locks, however, must be held till we
1097          * finish aborting.)  Releasing spinlocks is critical since we might
1098          * try to grab them again while cleaning up!
1099          */
1100         ProcReleaseSpins(NULL);
1101         UnlockBuffers();
1102
1103         /*
1104          * Also clean up any open wait for lock, since the lock manager will
1105          * choke if we try to wait for another lock before doing this.
1106          */
1107         LockWaitCancel();
1108
1109         /*
1110          * check the current transaction state
1111          */
1112         if (s->state == TRANS_DISABLED)
1113         {
1114                 RESUME_INTERRUPTS();
1115                 return;
1116         }
1117
1118         if (s->state != TRANS_INPROGRESS)
1119                 elog(NOTICE, "AbortTransaction and not in in-progress state");
1120
1121         /*
1122          * set the current transaction state information appropriately during
1123          * the abort processing
1124          */
1125         s->state = TRANS_ABORT;
1126
1127         /*
1128          * Reset user id which might have been changed transiently
1129          */
1130         SetUserId(GetSessionUserId());
1131
1132         /*
1133          * do abort processing
1134          */
1135         DeferredTriggerAbortXact();
1136         lo_commit(false);                       /* 'false' means it's abort */
1137         AtAbort_Notify();
1138         CloseSequences();
1139         AtEOXact_portals();
1140
1141         /* Advertise the fact that we aborted in pg_log. */
1142         RecordTransactionAbort();
1143
1144         /*
1145          * Let others know about no transaction in progress by me. Note that
1146          * this must be done _before_ releasing locks we hold and _after_
1147          * RecordTransactionAbort.
1148          */
1149         if (MyProc != (PROC *) NULL)
1150         {
1151                 /* Lock SInvalLock because that's what GetSnapshotData uses. */
1152                 SpinAcquire(SInvalLock);
1153                 MyProc->xid = InvalidTransactionId;
1154                 MyProc->xmin = InvalidTransactionId;
1155                 SpinRelease(SInvalLock);
1156         }
1157
1158         RelationPurgeLocalRelation(false);
1159         AtEOXact_temp_relations(false);
1160         smgrDoPendingDeletes(false);
1161
1162         AtEOXact_SPI();
1163         AtEOXact_gist();
1164         AtEOXact_hash();
1165         AtEOXact_nbtree();
1166         AtEOXact_rtree();
1167         AtAbort_Cache();
1168         AtEOXact_CatCache(false);
1169         AtAbort_Memory();
1170         AtEOXact_Files();
1171         AtAbort_Locks();
1172
1173         SharedBufferChanged = false;/* safest place to do it */
1174
1175         /* Count transaction abort in statistics collector */
1176         pgstat_count_xact_rollback();
1177
1178         /*
1179          * State remains TRANS_ABORT until CleanupTransaction().
1180          */
1181         RESUME_INTERRUPTS();
1182 }
1183
1184 /* --------------------------------
1185  *              CleanupTransaction
1186  *
1187  * --------------------------------
1188  */
1189 static void
1190 CleanupTransaction(void)
1191 {
1192         TransactionState s = CurrentTransactionState;
1193
1194         if (s->state == TRANS_DISABLED)
1195                 return;
1196
1197         /*
1198          * State should still be TRANS_ABORT from AbortTransaction().
1199          */
1200         if (s->state != TRANS_ABORT)
1201                 elog(FATAL, "CleanupTransaction and not in abort state");
1202
1203         /*
1204          * do abort cleanup processing
1205          */
1206         AtCleanup_Memory();
1207
1208         /*
1209          * done with abort processing, set current transaction state back to
1210          * default
1211          */
1212         s->state = TRANS_DEFAULT;
1213 }
1214
1215 /* --------------------------------
1216  *              StartTransactionCommand
1217  * --------------------------------
1218  */
1219 void
1220 StartTransactionCommand(void)
1221 {
1222         TransactionState s = CurrentTransactionState;
1223
1224         switch (s->blockState)
1225         {
1226
1227                         /*
1228                          * if we aren't in a transaction block, we just do our usual
1229                          * start transaction.
1230                          */
1231                 case TBLOCK_DEFAULT:
1232                         StartTransaction();
1233                         break;
1234
1235                         /*
1236                          * We should never experience this -- if we do it means the
1237                          * BEGIN state was not changed in the previous
1238                          * CommitTransactionCommand().  If we get it, we print a
1239                          * warning and change to the in-progress state.
1240                          */
1241                 case TBLOCK_BEGIN:
1242                         elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_BEGIN");
1243                         s->blockState = TBLOCK_INPROGRESS;
1244                         break;
1245
1246                         /*
1247                          * This is the case when are somewhere in a transaction block
1248                          * and about to start a new command.  For now we do nothing
1249                          * but someday we may do command-local resource
1250                          * initialization.
1251                          */
1252                 case TBLOCK_INPROGRESS:
1253                         break;
1254
1255                         /*
1256                          * As with BEGIN, we should never experience this if we do it
1257                          * means the END state was not changed in the previous
1258                          * CommitTransactionCommand().  If we get it, we print a
1259                          * warning, commit the transaction, start a new transaction
1260                          * and change to the default state.
1261                          */
1262                 case TBLOCK_END:
1263                         elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_END");
1264                         s->blockState = TBLOCK_DEFAULT;
1265                         CommitTransaction();
1266                         StartTransaction();
1267                         break;
1268
1269                         /*
1270                          * Here we are in the middle of a transaction block but one of
1271                          * the commands caused an abort so we do nothing but remain in
1272                          * the abort state.  Eventually we will get to the "END
1273                          * TRANSACTION" which will set things straight.
1274                          */
1275                 case TBLOCK_ABORT:
1276                         break;
1277
1278                         /*
1279                          * This means we somehow aborted and the last call to
1280                          * CommitTransactionCommand() didn't clear the state so we
1281                          * remain in the ENDABORT state and maybe next time we get to
1282                          * CommitTransactionCommand() the state will get reset to
1283                          * default.
1284                          */
1285                 case TBLOCK_ENDABORT:
1286                         elog(NOTICE, "StartTransactionCommand: unexpected TBLOCK_ENDABORT");
1287                         break;
1288         }
1289
1290         /*
1291          * We must switch to TransactionCommandContext before returning. This
1292          * is already done if we called StartTransaction, otherwise not.
1293          */
1294         Assert(TransactionCommandContext != NULL);
1295         MemoryContextSwitchTo(TransactionCommandContext);
1296 }
1297
1298 /* --------------------------------
1299  *              CommitTransactionCommand
1300  * --------------------------------
1301  */
1302 void
1303 CommitTransactionCommand(void)
1304 {
1305         TransactionState s = CurrentTransactionState;
1306
1307         switch (s->blockState)
1308         {
1309
1310                         /*
1311                          * if we aren't in a transaction block, we just do our usual
1312                          * transaction commit
1313                          */
1314                 case TBLOCK_DEFAULT:
1315                         CommitTransaction();
1316                         break;
1317
1318                         /*
1319                          * This is the case right after we get a "BEGIN TRANSACTION"
1320                          * command, but the user hasn't done anything else yet, so we
1321                          * change to the "transaction block in progress" state and
1322                          * return.
1323                          */
1324                 case TBLOCK_BEGIN:
1325                         s->blockState = TBLOCK_INPROGRESS;
1326                         break;
1327
1328                         /*
1329                          * This is the case when we have finished executing a command
1330                          * someplace within a transaction block.  We increment the
1331                          * command counter and return.  Someday we may free resources
1332                          * local to the command.
1333                          *
1334                          * That someday is today, at least for memory allocated in
1335                          * TransactionCommandContext. - vadim 03/25/97
1336                          */
1337                 case TBLOCK_INPROGRESS:
1338                         CommandCounterIncrement();
1339                         MemoryContextResetAndDeleteChildren(TransactionCommandContext);
1340                         break;
1341
1342                         /*
1343                          * This is the case when we just got the "END TRANSACTION"
1344                          * statement, so we commit the transaction and go back to the
1345                          * default state.
1346                          */
1347                 case TBLOCK_END:
1348                         CommitTransaction();
1349                         s->blockState = TBLOCK_DEFAULT;
1350                         break;
1351
1352                         /*
1353                          * Here we are in the middle of a transaction block but one of
1354                          * the commands caused an abort so we do nothing but remain in
1355                          * the abort state.  Eventually we will get to the "END
1356                          * TRANSACTION" which will set things straight.
1357                          */
1358                 case TBLOCK_ABORT:
1359                         break;
1360
1361                         /*
1362                          * Here we were in an aborted transaction block which just
1363                          * processed the "END TRANSACTION" command from the user, so
1364                          * clean up and return to the default state.
1365                          */
1366                 case TBLOCK_ENDABORT:
1367                         CleanupTransaction();
1368                         s->blockState = TBLOCK_DEFAULT;
1369                         break;
1370         }
1371 }
1372
1373 /* --------------------------------
1374  *              AbortCurrentTransaction
1375  * --------------------------------
1376  */
1377 void
1378 AbortCurrentTransaction(void)
1379 {
1380         TransactionState s = CurrentTransactionState;
1381
1382         switch (s->blockState)
1383         {
1384
1385                         /*
1386                          * if we aren't in a transaction block, we just do the basic
1387                          * abort & cleanup transaction.
1388                          */
1389                 case TBLOCK_DEFAULT:
1390                         AbortTransaction();
1391                         CleanupTransaction();
1392                         break;
1393
1394                         /*
1395                          * If we are in the TBLOCK_BEGIN it means something screwed up
1396                          * right after reading "BEGIN TRANSACTION" so we enter the
1397                          * abort state.  Eventually an "END TRANSACTION" will fix
1398                          * things.
1399                          */
1400                 case TBLOCK_BEGIN:
1401                         s->blockState = TBLOCK_ABORT;
1402                         AbortTransaction();
1403                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1404                         break;
1405
1406                         /*
1407                          * This is the case when are somewhere in a transaction block
1408                          * which aborted so we abort the transaction and set the ABORT
1409                          * state.  Eventually an "END TRANSACTION" will fix things and
1410                          * restore us to a normal state.
1411                          */
1412                 case TBLOCK_INPROGRESS:
1413                         s->blockState = TBLOCK_ABORT;
1414                         AbortTransaction();
1415                         /* CleanupTransaction happens when we exit TBLOCK_ABORT */
1416                         break;
1417
1418                         /*
1419                          * Here, the system was fouled up just after the user wanted
1420                          * to end the transaction block so we abort the transaction
1421                          * and put us back into the default state.
1422                          */
1423                 case TBLOCK_END:
1424                         s->blockState = TBLOCK_DEFAULT;
1425                         AbortTransaction();
1426                         CleanupTransaction();
1427                         break;
1428
1429                         /*
1430                          * Here, we are already in an aborted transaction state and
1431                          * are waiting for an "END TRANSACTION" to come along and lo
1432                          * and behold, we abort again! So we just remain in the abort
1433                          * state.
1434                          */
1435                 case TBLOCK_ABORT:
1436                         break;
1437
1438                         /*
1439                          * Here we were in an aborted transaction block which just
1440                          * processed the "END TRANSACTION" command but somehow aborted
1441                          * again.. since we must have done the abort processing, we
1442                          * clean up and return to the default state.
1443                          */
1444                 case TBLOCK_ENDABORT:
1445                         CleanupTransaction();
1446                         s->blockState = TBLOCK_DEFAULT;
1447                         break;
1448         }
1449 }
1450
1451 /* ----------------------------------------------------------------
1452  *                                         transaction block support
1453  * ----------------------------------------------------------------
1454  */
1455 /* --------------------------------
1456  *              BeginTransactionBlock
1457  * --------------------------------
1458  */
1459 void
1460 BeginTransactionBlock(void)
1461 {
1462         TransactionState s = CurrentTransactionState;
1463
1464         /*
1465          * check the current transaction state
1466          */
1467         if (s->state == TRANS_DISABLED)
1468                 return;
1469
1470         if (s->blockState != TBLOCK_DEFAULT)
1471                 elog(NOTICE, "BEGIN: already a transaction in progress");
1472
1473         /*
1474          * set the current transaction block state information appropriately
1475          * during begin processing
1476          */
1477         s->blockState = TBLOCK_BEGIN;
1478
1479         /*
1480          * do begin processing
1481          */
1482
1483         /*
1484          * done with begin processing, set block state to inprogress
1485          */
1486         s->blockState = TBLOCK_INPROGRESS;
1487 }
1488
1489 /* --------------------------------
1490  *              EndTransactionBlock
1491  * --------------------------------
1492  */
1493 void
1494 EndTransactionBlock(void)
1495 {
1496         TransactionState s = CurrentTransactionState;
1497
1498         /*
1499          * check the current transaction state
1500          */
1501         if (s->state == TRANS_DISABLED)
1502                 return;
1503
1504         if (s->blockState == TBLOCK_INPROGRESS)
1505         {
1506
1507                 /*
1508                  * here we are in a transaction block which should commit when we
1509                  * get to the upcoming CommitTransactionCommand() so we set the
1510                  * state to "END".      CommitTransactionCommand() will recognize this
1511                  * and commit the transaction and return us to the default state
1512                  */
1513                 s->blockState = TBLOCK_END;
1514                 return;
1515         }
1516
1517         if (s->blockState == TBLOCK_ABORT)
1518         {
1519
1520                 /*
1521                  * here, we are in a transaction block which aborted and since the
1522                  * AbortTransaction() was already done, we do whatever is needed
1523                  * and change to the special "END ABORT" state.  The upcoming
1524                  * CommitTransactionCommand() will recognise this and then put us
1525                  * back in the default state.
1526                  */
1527                 s->blockState = TBLOCK_ENDABORT;
1528                 return;
1529         }
1530
1531         /*
1532          * here, the user issued COMMIT when not inside a transaction. Issue a
1533          * notice and go to abort state.  The upcoming call to
1534          * CommitTransactionCommand() will then put us back into the default
1535          * state.
1536          */
1537         elog(NOTICE, "COMMIT: no transaction in progress");
1538         AbortTransaction();
1539         s->blockState = TBLOCK_ENDABORT;
1540 }
1541
1542 /* --------------------------------
1543  *              AbortTransactionBlock
1544  * --------------------------------
1545  */
1546 #ifdef NOT_USED
1547 static void
1548 AbortTransactionBlock(void)
1549 {
1550         TransactionState s = CurrentTransactionState;
1551
1552         /*
1553          * check the current transaction state
1554          */
1555         if (s->state == TRANS_DISABLED)
1556                 return;
1557
1558         if (s->blockState == TBLOCK_INPROGRESS)
1559         {
1560
1561                 /*
1562                  * here we were inside a transaction block something screwed up
1563                  * inside the system so we enter the abort state, do the abort
1564                  * processing and then return. We remain in the abort state until
1565                  * we see an END TRANSACTION command.
1566                  */
1567                 s->blockState = TBLOCK_ABORT;
1568                 AbortTransaction();
1569                 return;
1570         }
1571
1572         /*
1573          * here, the user issued ABORT when not inside a transaction. Issue a
1574          * notice and go to abort state.  The upcoming call to
1575          * CommitTransactionCommand() will then put us back into the default
1576          * state.
1577          */
1578         elog(NOTICE, "ROLLBACK: no transaction in progress");
1579         AbortTransaction();
1580         s->blockState = TBLOCK_ENDABORT;
1581 }
1582
1583 #endif
1584
1585 /* --------------------------------
1586  *              UserAbortTransactionBlock
1587  * --------------------------------
1588  */
1589 void
1590 UserAbortTransactionBlock(void)
1591 {
1592         TransactionState s = CurrentTransactionState;
1593
1594         /*
1595          * check the current transaction state
1596          */
1597         if (s->state == TRANS_DISABLED)
1598                 return;
1599
1600         /*
1601          * if the transaction has already been automatically aborted with an
1602          * error, and the user subsequently types 'abort', allow it.  (the
1603          * behavior is the same as if they had typed 'end'.)
1604          */
1605         if (s->blockState == TBLOCK_ABORT)
1606         {
1607                 s->blockState = TBLOCK_ENDABORT;
1608                 return;
1609         }
1610
1611         if (s->blockState == TBLOCK_INPROGRESS)
1612         {
1613
1614                 /*
1615                  * here we were inside a transaction block and we got an abort
1616                  * command from the user, so we move to the abort state, do the
1617                  * abort processing and then change to the ENDABORT state so we
1618                  * will end up in the default state after the upcoming
1619                  * CommitTransactionCommand().
1620                  */
1621                 s->blockState = TBLOCK_ABORT;
1622                 AbortTransaction();
1623                 s->blockState = TBLOCK_ENDABORT;
1624                 return;
1625         }
1626
1627         /*
1628          * here, the user issued ABORT when not inside a transaction. Issue a
1629          * notice and go to abort state.  The upcoming call to
1630          * CommitTransactionCommand() will then put us back into the default
1631          * state.
1632          */
1633         elog(NOTICE, "ROLLBACK: no transaction in progress");
1634         AbortTransaction();
1635         s->blockState = TBLOCK_ENDABORT;
1636 }
1637
1638 /* --------------------------------
1639  *              AbortOutOfAnyTransaction
1640  *
1641  * This routine is provided for error recovery purposes.  It aborts any
1642  * active transaction or transaction block, leaving the system in a known
1643  * idle state.
1644  * --------------------------------
1645  */
1646 void
1647 AbortOutOfAnyTransaction(void)
1648 {
1649         TransactionState s = CurrentTransactionState;
1650
1651         /*
1652          * Get out of any low-level transaction
1653          */
1654         switch (s->state)
1655         {
1656                 case TRANS_START:
1657                 case TRANS_INPROGRESS:
1658                 case TRANS_COMMIT:
1659                         /* In a transaction, so clean up */
1660                         AbortTransaction();
1661                         CleanupTransaction();
1662                         break;
1663                 case TRANS_ABORT:
1664                         /* AbortTransaction already done, still need Cleanup */
1665                         CleanupTransaction();
1666                         break;
1667                 case TRANS_DEFAULT:
1668                 case TRANS_DISABLED:
1669                         /* Not in a transaction, do nothing */
1670                         break;
1671         }
1672
1673         /*
1674          * Now reset the high-level state
1675          */
1676         s->blockState = TBLOCK_DEFAULT;
1677 }
1678
1679 bool
1680 IsTransactionBlock(void)
1681 {
1682         TransactionState s = CurrentTransactionState;
1683
1684         if (s->blockState == TBLOCK_INPROGRESS
1685                 || s->blockState == TBLOCK_ABORT
1686                 || s->blockState == TBLOCK_ENDABORT)
1687                 return true;
1688
1689         return false;
1690 }
1691
1692 void
1693 xact_redo(XLogRecPtr lsn, XLogRecord *record)
1694 {
1695         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1696
1697         if (info == XLOG_XACT_COMMIT)
1698         {
1699                 TransactionIdCommit(record->xl_xid);
1700                 /* SHOULD REMOVE FILES OF ALL DROPPED RELATIONS */
1701         }
1702         else if (info == XLOG_XACT_ABORT)
1703                 TransactionIdAbort(record->xl_xid);
1704         else
1705                 elog(STOP, "xact_redo: unknown op code %u", info);
1706 }
1707
1708 void
1709 xact_undo(XLogRecPtr lsn, XLogRecord *record)
1710 {
1711         uint8           info = record->xl_info & ~XLR_INFO_MASK;
1712
1713         if (info == XLOG_XACT_COMMIT)           /* shouldn't be called by XLOG */
1714                 elog(STOP, "xact_undo: can't undo committed xaction");
1715         else if (info != XLOG_XACT_ABORT)
1716                 elog(STOP, "xact_redo: unknown op code %u", info);
1717 }
1718
1719 void
1720 xact_desc(char *buf, uint8 xl_info, char *rec)
1721 {
1722         uint8           info = xl_info & ~XLR_INFO_MASK;
1723
1724         if (info == XLOG_XACT_COMMIT)
1725         {
1726                 xl_xact_commit *xlrec = (xl_xact_commit *) rec;
1727                 struct tm  *tm = localtime(&xlrec->xtime);
1728
1729                 sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
1730                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1731                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1732         }
1733         else if (info == XLOG_XACT_ABORT)
1734         {
1735                 xl_xact_abort *xlrec = (xl_xact_abort *) rec;
1736                 struct tm  *tm = localtime(&xlrec->xtime);
1737
1738                 sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
1739                                 tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
1740                                 tm->tm_hour, tm->tm_min, tm->tm_sec);
1741         }
1742         else
1743                 strcat(buf, "UNKNOWN");
1744 }
1745
1746 void
1747                         XactPushRollback(void (*func) (void *), void *data)
1748 {
1749 #ifdef XLOG_II
1750         if (_RollbackFunc != NULL)
1751                 elog(STOP, "XactPushRollback: already installed");
1752 #endif
1753
1754         _RollbackFunc = func;
1755         _RollbackData = data;
1756 }
1757
1758 void
1759 XactPopRollback(void)
1760 {
1761         _RollbackFunc = NULL;
1762 }