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