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