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