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