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