]> granicus.if.org Git - postgresql/blobdiff - src/backend/access/transam/xact.c
Enable probes to work with Mac OS X Leopard and other OSes that will
[postgresql] / src / backend / access / transam / xact.c
index 13c3a0378b143a8b7d9e2044f49fc712b692df61..9af53a5953f09e2b2726fc7efa8ad4fcc1f2b9ab 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.257 2008/01/15 18:56:59 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.260 2008/03/17 19:44:41 petere Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -46,6 +46,7 @@
 #include "utils/memutils.h"
 #include "utils/relcache.h"
 #include "utils/xml.h"
+#include "pg_trace.h"
 
 
 /*
@@ -62,6 +63,13 @@ bool         XactSyncCommit = true;
 int                    CommitDelay = 0;        /* precommit delay in microseconds */
 int                    CommitSiblings = 5; /* # concurrent xacts needed to sleep */
 
+/*
+ * MyXactAccessedTempRel is set when a temporary relation is accessed.
+ * We don't allow PREPARE TRANSACTION in that case.  (This is global
+ * so that it can be set from heapam.c.)
+ */
+bool           MyXactAccessedTempRel = false;
+
 
 /*
  *     transaction states - transaction state from server perspective
@@ -123,7 +131,9 @@ typedef struct TransactionStateData
        int                     gucNestLevel;   /* GUC context nesting depth */
        MemoryContext curTransactionContext;            /* my xact-lifetime context */
        ResourceOwner curTransactionOwner;      /* my query resources */
-       List       *childXids;          /* subcommitted child XIDs */
+       TransactionId *childXids;       /* subcommitted child XIDs, in XID order */
+       int                     nChildXids;             /* # of subcommitted child XIDs */
+       int                     maxChildXids;   /* allocated size of childXids[] */
        Oid                     prevUser;               /* previous CurrentUserId setting */
        bool            prevSecDefCxt;  /* previous SecurityDefinerContext setting */
        bool            prevXactReadOnly;               /* entry-time xact r/o state */
@@ -149,7 +159,9 @@ static TransactionStateData TopTransactionStateData = {
        0,                                                      /* GUC context nesting depth */
        NULL,                                           /* cur transaction context */
        NULL,                                           /* cur transaction resource owner */
-       NIL,                                            /* subcommitted child Xids */
+       NULL,                                           /* subcommitted child Xids */
+       0,                                                      /* # of subcommitted child Xids */
+       0,                                                      /* allocated size of childXids[] */
        InvalidOid,                                     /* previous CurrentUserId setting */
        false,                                          /* previous SecurityDefinerContext setting */
        false,                                          /* entry-time xact r/o state */
@@ -552,7 +564,7 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
         */
        for (s = CurrentTransactionState; s != NULL; s = s->parent)
        {
-               ListCell   *cell;
+               int low, high;
 
                if (s->state == TRANS_ABORT)
                        continue;
@@ -560,10 +572,22 @@ TransactionIdIsCurrentTransactionId(TransactionId xid)
                        continue;                       /* it can't have any child XIDs either */
                if (TransactionIdEquals(xid, s->transactionId))
                        return true;
-               foreach(cell, s->childXids)
+               /* As the childXids array is ordered, we can use binary search */
+               low = 0;
+               high = s->nChildXids - 1;
+               while (low <= high)
                {
-                       if (TransactionIdEquals(xid, lfirst_xid(cell)))
+                       int                             middle;
+                       TransactionId   probe;
+
+                       middle = low + (high - low) / 2;
+                       probe = s->childXids[middle];
+                       if (TransactionIdEquals(probe, xid))
                                return true;
+                       else if (TransactionIdPrecedes(probe, xid))
+                               low = middle + 1;
+                       else
+                               high = middle - 1;
                }
        }
 
@@ -978,8 +1002,6 @@ cleanup:
        /* Clean up local data */
        if (rels)
                pfree(rels);
-       if (children)
-               pfree(children);
 
        return latestXid;
 }
@@ -1060,34 +1082,79 @@ static void
 AtSubCommit_childXids(void)
 {
        TransactionState s = CurrentTransactionState;
-       MemoryContext old_cxt;
+       int                     new_nChildXids;
 
        Assert(s->parent != NULL);
 
        /*
-        * We keep the child-XID lists in TopTransactionContext; this avoids
-        * setting up child-transaction contexts for what might be just a few
-        * bytes of grandchild XIDs.
+        * The parent childXids array will need to hold my XID and all my
+        * childXids, in addition to the XIDs already there.
         */
-       old_cxt = MemoryContextSwitchTo(TopTransactionContext);
-
-       s->parent->childXids = lappend_xid(s->parent->childXids,
-                                                                          s->transactionId);
+       new_nChildXids = s->parent->nChildXids + s->nChildXids + 1;
 
-       if (s->childXids != NIL)
+       /* Allocate or enlarge the parent array if necessary */
+       if (s->parent->maxChildXids < new_nChildXids)
        {
-               s->parent->childXids = list_concat(s->parent->childXids,
-                                                                                  s->childXids);
+               int                             new_maxChildXids;
+               TransactionId  *new_childXids;
 
                /*
-                * list_concat doesn't free the list header for the second list; do so
-                * here to avoid memory leakage (kluge)
+                * Make it 2x what's needed right now, to avoid having to enlarge it
+                * repeatedly. But we can't go above MaxAllocSize.  (The latter
+                * limit is what ensures that we don't need to worry about integer
+                * overflow here or in the calculation of new_nChildXids.)
                 */
-               pfree(s->childXids);
-               s->childXids = NIL;
+               new_maxChildXids = Min(new_nChildXids * 2,
+                                                          (int) (MaxAllocSize / sizeof(TransactionId)));
+
+               if (new_maxChildXids < new_nChildXids)
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
+                                        errmsg("maximum number of committed subtransactions (%d) exceeded",
+                                                       (int) (MaxAllocSize / sizeof(TransactionId)))));
+
+               /*
+                * We keep the child-XID arrays in TopTransactionContext; this avoids
+                * setting up child-transaction contexts for what might be just a few
+                * bytes of grandchild XIDs.
+                */
+               if (s->parent->childXids == NULL)
+                       new_childXids =
+                               MemoryContextAlloc(TopTransactionContext, 
+                                                                  new_maxChildXids * sizeof(TransactionId));
+               else
+                       new_childXids = repalloc(s->parent->childXids, 
+                                                                        new_maxChildXids * sizeof(TransactionId));
+
+               s->parent->childXids  = new_childXids;
+               s->parent->maxChildXids = new_maxChildXids;
        }
 
-       MemoryContextSwitchTo(old_cxt);
+       /*
+        * Copy all my XIDs to parent's array.
+        *
+        * Note: We rely on the fact that the XID of a child always follows that
+        * of its parent.  By copying the XID of this subtransaction before the
+        * XIDs of its children, we ensure that the array stays ordered.  Likewise,
+        * all XIDs already in the array belong to subtransactions started and
+        * subcommitted before us, so their XIDs must precede ours.
+        */
+       s->parent->childXids[s->parent->nChildXids] = s->transactionId;
+
+       if (s->nChildXids > 0)
+               memcpy(&s->parent->childXids[s->parent->nChildXids + 1],
+                          s->childXids,
+                          s->nChildXids * sizeof(TransactionId));
+
+       s->parent->nChildXids = new_nChildXids;
+
+       /* Release child's array to avoid leakage */
+       if (s->childXids != NULL)
+               pfree(s->childXids);
+       /* We must reset these to avoid double-free if fail later in commit */
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
 }
 
 /*
@@ -1252,8 +1319,6 @@ RecordTransactionAbort(bool isSubXact)
        /* And clean up local data */
        if (rels)
                pfree(rels);
-       if (children)
-               pfree(children);
 
        return latestXid;
 }
@@ -1325,12 +1390,15 @@ AtSubAbort_childXids(void)
        TransactionState s = CurrentTransactionState;
 
        /*
-        * We keep the child-XID lists in TopTransactionContext (see
-        * AtSubCommit_childXids).      This means we'd better free the list
+        * We keep the child-XID arrays in TopTransactionContext (see
+        * AtSubCommit_childXids).      This means we'd better free the array
         * explicitly at abort to avoid leakage.
         */
-       list_free(s->childXids);
-       s->childXids = NIL;
+       if (s->childXids != NULL)
+               pfree(s->childXids);
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
 }
 
 /* ----------------------------------------------------------------
@@ -1445,6 +1513,7 @@ StartTransaction(void)
        XactIsoLevel = DefaultXactIsoLevel;
        XactReadOnly = DefaultXactReadOnly;
        forceSyncCommit = false;
+       MyXactAccessedTempRel = false;
 
        /*
         * reinitialize within-transaction counters
@@ -1479,7 +1548,7 @@ StartTransaction(void)
        Assert(MyProc->backendId == vxid.backendId);
        MyProc->lxid = vxid.localTransactionId;
 
-       PG_TRACE1(transaction__start, vxid.localTransactionId);
+       TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
 
        /*
         * set transaction_timestamp() (a/k/a now()).  We want this to be the same
@@ -1498,7 +1567,9 @@ StartTransaction(void)
         */
        s->nestingLevel = 1;
        s->gucNestLevel = 1;
-       s->childXids = NIL;
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
        GetUserIdAndContext(&s->prevUser, &s->prevSecDefCxt);
        /* SecurityDefinerContext should never be set outside a transaction */
        Assert(!s->prevSecDefCxt);
@@ -1604,7 +1675,7 @@ CommitTransaction(void)
         */
        latestXid = RecordTransactionCommit();
 
-       PG_TRACE1(transaction__commit, MyProc->lxid);
+       TRACE_POSTGRESQL_TRANSACTION_COMMIT(MyProc->lxid);
 
        /*
         * Let others know about no transaction in progress by me. Note that this
@@ -1694,7 +1765,9 @@ CommitTransaction(void)
        s->subTransactionId = InvalidSubTransactionId;
        s->nestingLevel = 0;
        s->gucNestLevel = 0;
-       s->childXids = NIL;
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
 
        /*
         * done with commit processing, set current transaction state back to
@@ -1770,6 +1843,26 @@ PrepareTransaction(void)
 
        /* NOTIFY and flatfiles will be handled below */
 
+       /*
+        * Don't allow PREPARE TRANSACTION if we've accessed a temporary table
+        * in this transaction.  Having the prepared xact hold locks on another
+        * backend's temp table seems a bad idea --- for instance it would prevent
+        * the backend from exiting.  There are other problems too, such as how
+        * to clean up the source backend's local buffers and ON COMMIT state
+        * if the prepared xact includes a DROP of a temp table.
+        *
+        * We must check this after executing any ON COMMIT actions, because
+        * they might still access a temp relation.
+        *
+        * XXX In principle this could be relaxed to allow some useful special
+        * cases, such as a temp table created and dropped all within the
+        * transaction.  That seems to require much more bookkeeping though.
+        */
+       if (MyXactAccessedTempRel)
+               ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("cannot PREPARE a transaction that has operated on temporary tables")));
+
        /* Prevent cancel/die interrupt while cleaning up */
        HOLD_INTERRUPTS();
 
@@ -1903,7 +1996,9 @@ PrepareTransaction(void)
        s->subTransactionId = InvalidSubTransactionId;
        s->nestingLevel = 0;
        s->gucNestLevel = 0;
-       s->childXids = NIL;
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
 
        /*
         * done with 1st phase commit processing, set current transaction state
@@ -1990,7 +2085,7 @@ AbortTransaction(void)
         */
        latestXid = RecordTransactionAbort(false);
 
-       PG_TRACE1(transaction__abort, MyProc->lxid);
+       TRACE_POSTGRESQL_TRANSACTION_ABORT(MyProc->lxid);
 
        /*
         * Let others know about no transaction in progress by me. Note that this
@@ -2073,7 +2168,9 @@ CleanupTransaction(void)
        s->subTransactionId = InvalidSubTransactionId;
        s->nestingLevel = 0;
        s->gucNestLevel = 0;
-       s->childXids = NIL;
+       s->childXids = NULL;
+       s->nChildXids = 0;
+       s->maxChildXids = 0;
 
        /*
         * done with abort processing, set current transaction state back to
@@ -4023,6 +4120,19 @@ ShowTransactionState(const char *str)
 static void
 ShowTransactionStateRec(TransactionState s)
 {
+       StringInfoData buf;
+
+       initStringInfo(&buf);
+
+       if (s->nChildXids > 0)
+       {
+               int i;
+
+               appendStringInfo(&buf, "%u", s->childXids[0]);
+               for (i = 1; i < s->nChildXids; i++)
+                       appendStringInfo(&buf, " %u", s->childXids[i]);
+       }
+
        if (s->parent)
                ShowTransactionStateRec(s->parent);
 
@@ -4036,8 +4146,9 @@ ShowTransactionStateRec(TransactionState s)
                                                         (unsigned int) s->subTransactionId,
                                                         (unsigned int) currentCommandId,
                                                         currentCommandIdUsed ? " (used)" : "",
-                                                        s->nestingLevel,
-                                                        nodeToString(s->childXids))));
+                                                        s->nestingLevel, buf.data)));
+
+       pfree(buf.data);
 }
 
 /*
@@ -4116,36 +4227,22 @@ TransStateAsString(TransState state)
  * xactGetCommittedChildren
  *
  * Gets the list of committed children of the current transaction.     The return
- * value is the number of child transactions.  *children is set to point to a
- * palloc'd array of TransactionIds.  If there are no subxacts, *children is
- * set to NULL.
+ * value is the number of child transactions.  *ptr is set to point to an
+ * array of TransactionIds.  The array is allocated in TopTransactionContext;
+ * the caller should *not* pfree() it (this is a change from pre-8.4 code!).
+ * If there are no subxacts, *ptr is set to NULL.
  */
 int
 xactGetCommittedChildren(TransactionId **ptr)
 {
        TransactionState s = CurrentTransactionState;
-       int                     nchildren;
-       TransactionId *children;
-       ListCell   *p;
 
-       nchildren = list_length(s->childXids);
-       if (nchildren == 0)
-       {
+       if (s->nChildXids == 0)
                *ptr = NULL;
-               return 0;
-       }
-
-       children = (TransactionId *) palloc(nchildren * sizeof(TransactionId));
-       *ptr = children;
-
-       foreach(p, s->childXids)
-       {
-               TransactionId child = lfirst_xid(p);
-
-               *children++ = child;
-       }
+       else
+               *ptr = s->childXids;
 
-       return nchildren;
+       return s->nChildXids;
 }
 
 /*