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