Commit 573a71a5 authored by Tom Lane's avatar Tom Lane

Nested transactions. There is still much left to do, especially on the

performance front, but with feature freeze upon us I think it's time to
drive a stake in the ground and say that this will be in 7.5.

Alvaro Herrera, with some help from Tom Lane.
parent 4c9aa572
......@@ -75,8 +75,7 @@ user_write_unlock_oid(Oid oid)
int
user_unlock_all(void)
{
return LockReleaseAll(USER_LOCKMETHOD, MyProc, false,
InvalidTransactionId);
return LockReleaseAll(USER_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
}
/* end of file */
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.51 2004/01/07 18:56:23 neilc Exp $
* $PostgreSQL: pgsql/src/backend/access/gist/gistscan.c,v 1.52 2004/07/01 00:49:27 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -41,6 +41,7 @@ static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
typedef struct GISTScanListData
{
IndexScanDesc gsl_scan;
TransactionId gsl_creatingXid;
struct GISTScanListData *gsl_next;
} GISTScanListData;
......@@ -223,6 +224,7 @@ gistregscan(IndexScanDesc s)
l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s;
l->gsl_creatingXid = GetCurrentTransactionId();
l->gsl_next = GISTScans;
GISTScans = l;
}
......@@ -271,6 +273,46 @@ AtEOXact_gist(void)
GISTScans = NULL;
}
/*
* AtEOSubXact_gist() --- clean up gist subsystem at subxact abort or commit.
*
* This is here because it needs to touch this module's static var GISTScans.
*/
void
AtEOSubXact_gist(TransactionId childXid)
{
GISTScanList l;
GISTScanList prev;
GISTScanList next;
/*
* Note: these actions should only be necessary during xact abort; but
* they can't hurt during a commit.
*/
/*
* Forget active scans that were started in this subtransaction.
*/
prev = NULL;
for (l = GISTScans; l != NULL; l = next)
{
next = l->gsl_next;
if (l->gsl_creatingXid == childXid)
{
if (prev == NULL)
GISTScans = next;
else
prev->gsl_next = next;
pfree(l);
/* prev does not change */
}
else
prev = l;
}
}
void
gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum)
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/hash/hashscan.c,v 1.33 2004/01/07 18:56:23 neilc Exp $
* $PostgreSQL: pgsql/src/backend/access/hash/hashscan.c,v 1.34 2004/07/01 00:49:29 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,6 +21,7 @@
typedef struct HashScanListData
{
IndexScanDesc hashsl_scan;
TransactionId hashsl_creatingXid;
struct HashScanListData *hashsl_next;
} HashScanListData;
......@@ -50,6 +51,46 @@ AtEOXact_hash(void)
HashScans = NULL;
}
/*
* AtEOSubXact_hash() --- clean up hash subsystem at subxact abort or commit.
*
* This is here because it needs to touch this module's static var HashScans.
*/
void
AtEOSubXact_hash(TransactionId childXid)
{
HashScanList l;
HashScanList prev;
HashScanList next;
/*
* Note: these actions should only be necessary during xact abort; but
* they can't hurt during a commit.
*/
/*
* Forget active scans that were started in this subtransaction.
*/
prev = NULL;
for (l = HashScans; l != NULL; l = next)
{
next = l->hashsl_next;
if (l->hashsl_creatingXid == childXid)
{
if (prev == NULL)
HashScans = next;
else
prev->hashsl_next = next;
pfree(l);
/* prev does not change */
}
else
prev = l;
}
}
/*
* _Hash_regscan() -- register a new scan.
*/
......@@ -60,6 +101,7 @@ _hash_regscan(IndexScanDesc scan)
new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan;
new_el->hashsl_creatingXid = GetCurrentTransactionId();
new_el->hashsl_next = HashScans;
HashScans = new_el;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/rtree/rtscan.c,v 1.51 2004/01/07 18:56:24 neilc Exp $
* $PostgreSQL: pgsql/src/backend/access/rtree/rtscan.c,v 1.52 2004/07/01 00:49:31 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -42,6 +42,7 @@ static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
typedef struct RTScanListData
{
IndexScanDesc rtsl_scan;
TransactionId rtsl_creatingXid;
struct RTScanListData *rtsl_next;
} RTScanListData;
......@@ -240,6 +241,7 @@ rtregscan(IndexScanDesc s)
l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s;
l->rtsl_creatingXid = GetCurrentTransactionId();
l->rtsl_next = RTScans;
RTScans = l;
}
......@@ -290,6 +292,46 @@ AtEOXact_rtree(void)
RTScans = NULL;
}
/*
* AtEOSubXact_rtree() --- clean up rtree subsystem at subxact abort or commit.
*
* This is here because it needs to touch this module's static var RTScans.
*/
void
AtEOSubXact_rtree(TransactionId childXid)
{
RTScanList l;
RTScanList prev;
RTScanList next;
/*
* Note: these actions should only be necessary during xact abort; but
* they can't hurt during a commit.
*/
/*
* Forget active scans that were started in this subtransaction.
*/
prev = NULL;
for (l = RTScans; l != NULL; l = next)
{
next = l->rtsl_next;
if (l->rtsl_creatingXid == childXid)
{
if (prev == NULL)
RTScans = next;
else
prev->rtsl_next = next;
pfree(l);
/* prev does not change */
}
else
prev = l;
}
}
void
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{
......
......@@ -4,7 +4,7 @@
# Makefile for access/transam
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/access/transam/Makefile,v 1.18 2003/11/29 19:51:40 pgsql Exp $
# $PostgreSQL: pgsql/src/backend/access/transam/Makefile,v 1.19 2004/07/01 00:49:42 tgl Exp $
#
#-------------------------------------------------------------------------
......@@ -12,7 +12,7 @@ subdir = src/backend/access/transam
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o
OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o
all: SUBSYS.o
......
......@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.20 2004/05/31 03:47:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/clog.c,v 1.21 2004/07/01 00:49:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,14 +21,13 @@
#include <fcntl.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <unistd.h>
#include "access/clog.h"
#include "access/slru.h"
#include "storage/lwlock.h"
#include "miscadmin.h"
#include "storage/lwlock.h"
/*
......@@ -65,7 +64,7 @@
* is guaranteed flushed through the XLOG commit record before we are called
* to log a commit, so the WAL rule "write xlog before data" is satisfied
* automatically for commits, and we don't really care for aborts. Therefore,
* we don't need to mark XLOG pages with LSN information; we have enough
* we don't need to mark CLOG pages with LSN information; we have enough
* synchronization already.
*----------
*/
......@@ -95,20 +94,22 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status)
char *byteptr;
Assert(status == TRANSACTION_STATUS_COMMITTED ||
status == TRANSACTION_STATUS_ABORTED);
status == TRANSACTION_STATUS_ABORTED ||
status == TRANSACTION_STATUS_SUB_COMMITTED);
LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE);
byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true);
byteptr += byteno;
/* Current state should be 0 or target state */
/* Current state should be 0, subcommitted or target state */
Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
*byteptr |= (status << bshift);
/* ...->page_status[slotno] = CLOG_PAGE_DIRTY; already done */
/* ...->page_status[slotno] = SLRU_PAGE_DIRTY; already done */
LWLockRelease(ClogCtl->ControlLock);
}
......@@ -117,7 +118,7 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status)
* Interrogate the state of a transaction in the commit log.
*
* NB: this is a low-level routine and is NOT the preferred entry point
* for most uses; TransactionLogTest() in transam.c is the intended caller.
* for most uses; TransactionLogFetch() in transam.c is the intended caller.
*/
XidStatus
TransactionIdGetStatus(TransactionId xid)
......@@ -176,7 +177,7 @@ BootStrapCLOG(void)
/* Make sure it's written out */
SimpleLruWritePage(ClogCtl, slotno, NULL);
/* Assert(ClogCtl->page_status[slotno] == CLOG_PAGE_CLEAN); */
/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
LWLockRelease(ClogCtl->ControlLock);
}
......@@ -211,7 +212,8 @@ StartupCLOG(void)
/*
* Initialize our idea of the latest page number.
*/
SimpleLruSetLatestPage(ClogCtl, TransactionIdToPage(ShmemVariableCache->nextXid));
SimpleLruSetLatestPage(ClogCtl,
TransactionIdToPage(ShmemVariableCache->nextXid));
}
/*
......@@ -333,51 +335,20 @@ WriteZeroPageXlogRec(int pageno)
rdata.data = (char *) (&pageno);
rdata.len = sizeof(int);
rdata.next = NULL;
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
}
/*
* CLOG resource manager's routines
*/
void
clog_redo(XLogRecPtr lsn, XLogRecord *record)
{
uint8 info = record->xl_info & ~XLR_INFO_MASK;
if (info == CLOG_ZEROPAGE)
{
int pageno;
int slotno;
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE);
slotno = ZeroCLOGPage(pageno, false);
SimpleLruWritePage(ClogCtl, slotno, NULL);
/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
LWLockRelease(ClogCtl->ControlLock);
}
(void) XLogInsert(RM_SLRU_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
}
/* Redo a ZEROPAGE action during WAL replay */
void
clog_undo(XLogRecPtr lsn, XLogRecord *record)
clog_zeropage_redo(int pageno)
{
}
int slotno;
void
clog_desc(char *buf, uint8 xl_info, char *rec)
{
uint8 info = xl_info & ~XLR_INFO_MASK;
LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE);
if (info == CLOG_ZEROPAGE)
{
int pageno;
slotno = ZeroCLOGPage(pageno, false);
SimpleLruWritePage(ClogCtl, slotno, NULL);
/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
memcpy(&pageno, rec, sizeof(int));
sprintf(buf + strlen(buf), "zeropage: %d", pageno);
}
else
strcat(buf, "UNKNOWN");
LWLockRelease(ClogCtl->ControlLock);
}
......@@ -3,16 +3,16 @@
*
* Resource managers definition
*
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.12 2003/11/29 19:51:40 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/rmgr.c,v 1.13 2004/07/01 00:49:42 tgl Exp $
*/
#include "postgres.h"
#include "access/clog.h"
#include "access/gist.h"
#include "access/hash.h"
#include "access/heapam.h"
#include "access/nbtree.h"
#include "access/rtree.h"
#include "access/slru.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "storage/smgr.h"
......@@ -23,7 +23,7 @@ RmgrData RmgrTable[RM_MAX_ID + 1] = {
{"XLOG", xlog_redo, xlog_undo, xlog_desc, NULL, NULL},
{"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL},
{"Storage", smgr_redo, smgr_undo, smgr_desc, NULL, NULL},
{"CLOG", clog_redo, clog_undo, clog_desc, NULL, NULL},
{"SLRU", slru_redo, slru_undo, slru_desc, NULL, NULL},
{"Reserved 4", NULL, NULL, NULL, NULL, NULL},
{"Reserved 5", NULL, NULL, NULL, NULL, NULL},
{"Reserved 6", NULL, NULL, NULL, NULL, NULL},
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.16 2004/05/31 03:47:54 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/slru.c,v 1.17 2004/07/01 00:49:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -16,8 +16,9 @@
#include <sys/stat.h>
#include <unistd.h>
#include "access/clog.h"
#include "access/slru.h"
#include "access/clog.h" /* only for NUM_CLOG_BUFFERS */
#include "access/subtrans.h"
#include "postmaster/bgwriter.h"
#include "storage/fd.h"
#include "storage/lwlock.h"
......@@ -1025,3 +1026,55 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions)
return found;
}
/*
* SLRU resource manager's routines
*/
void
slru_redo(XLogRecPtr lsn, XLogRecord *record)
{
uint8 info = record->xl_info & ~XLR_INFO_MASK;
int pageno;
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
switch (info)
{
case CLOG_ZEROPAGE:
clog_zeropage_redo(pageno);
break;
case SUBTRANS_ZEROPAGE:
subtrans_zeropage_redo(pageno);
break;
default:
elog(PANIC, "slru_redo: unknown op code %u", info);
}
}
void
slru_undo(XLogRecPtr lsn, XLogRecord *record)
{
}
void
slru_desc(char *buf, uint8 xl_info, char *rec)
{
uint8 info = xl_info & ~XLR_INFO_MASK;
if (info == CLOG_ZEROPAGE)
{
int pageno;
memcpy(&pageno, rec, sizeof(int));
sprintf(buf + strlen(buf), "clog zeropage: %d", pageno);
}
else if (info == SUBTRANS_ZEROPAGE)
{
int pageno;
memcpy(&pageno, rec, sizeof(int));
sprintf(buf + strlen(buf), "subtrans zeropage: %d", pageno);
}
else
strcat(buf, "UNKNOWN");
}
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.56 2003/11/29 19:51:40 pgsql Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.57 2004/07/01 00:49:42 tgl Exp $
*
* NOTES
* This file contains the high level access-method interface to the
......@@ -20,6 +20,7 @@
#include "postgres.h"
#include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h"
......@@ -35,44 +36,40 @@
bool AMI_OVERRIDE = false;
static bool TransactionLogTest(TransactionId transactionId, XidStatus status);
static XidStatus TransactionLogFetch(TransactionId transactionId);
static void TransactionLogUpdate(TransactionId transactionId,
XidStatus status);
/* ----------------
* Single-item cache for results of TransactionLogTest.
* Single-item cache for results of TransactionLogFetch.
* ----------------
*/
static TransactionId cachedTestXid = InvalidTransactionId;
static XidStatus cachedTestXidStatus;
static TransactionId cachedFetchXid = InvalidTransactionId;
static XidStatus cachedFetchXidStatus;
/* ----------------------------------------------------------------
* postgres log access method interface
*
* TransactionLogTest
* TransactionLogFetch
* TransactionLogUpdate
* ----------------------------------------------------------------
*/
/* --------------------------------
* TransactionLogTest
* --------------------------------
/*
* TransactionLogFetch --- fetch commit status of specified transaction id
*/
static bool /* true/false: does transaction id have
* specified status? */
TransactionLogTest(TransactionId transactionId, /* transaction id to test */
XidStatus status) /* transaction status */
static XidStatus
TransactionLogFetch(TransactionId transactionId)
{
XidStatus xidstatus; /* recorded status of xid */
XidStatus xidstatus;
/*
* Before going to the commit log manager, check our single item cache
* to see if we didn't just check the transaction status a moment ago.
*/
if (TransactionIdEquals(transactionId, cachedTestXid))
return (status == cachedTestXidStatus);
if (TransactionIdEquals(transactionId, cachedFetchXid))
return cachedFetchXidStatus;
/*
* Also, check to see if the transaction ID is a permanent one.
......@@ -80,10 +77,10 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */
if (!TransactionIdIsNormal(transactionId))
{
if (TransactionIdEquals(transactionId, BootstrapTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED);
return TRANSACTION_STATUS_COMMITTED;
if (TransactionIdEquals(transactionId, FrozenTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED);
return (status == TRANSACTION_STATUS_ABORTED);
return TRANSACTION_STATUS_COMMITTED;
return TRANSACTION_STATUS_ABORTED;
}
/*
......@@ -92,15 +89,17 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */
xidstatus = TransactionIdGetStatus(transactionId);
/*
* DO NOT cache status for unfinished transactions!
* DO NOT cache status for unfinished or sub-committed transactions!
* We only cache status that is guaranteed not to change.
*/
if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS)
if (xidstatus != TRANSACTION_STATUS_IN_PROGRESS &&
xidstatus != TRANSACTION_STATUS_SUB_COMMITTED)
{
TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = xidstatus;
TransactionIdStore(transactionId, &cachedFetchXid);
cachedFetchXidStatus = xidstatus;
}
return (status == xidstatus);
return xidstatus;
}
/* --------------------------------
......@@ -115,12 +114,23 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
* update the commit log
*/
TransactionIdSetStatus(transactionId, status);
}
/*
* update (invalidate) our single item TransactionLogTest cache.
*/
TransactionIdStore(transactionId, &cachedTestXid);
cachedTestXidStatus = status;
/*
* TransactionLogMultiUpdate
*
* Update multiple transaction identifiers to a given status.
* Don't depend on this being atomic; it's not.
*/
static void
TransactionLogMultiUpdate(int nxids, TransactionId *xids, XidStatus status)
{
int i;
Assert(nxids != 0);
for (i = 0; i < nxids; i++)
TransactionIdSetStatus(xids[i], status);
}
/* --------------------------------
......@@ -171,13 +181,38 @@ AmiTransactionOverride(bool flag)
bool /* true if given transaction committed */
TransactionIdDidCommit(TransactionId transactionId)
{
XidStatus xidstatus;
if (AMI_OVERRIDE)
{
Assert(transactionId == BootstrapTransactionId);
return true;
}
return TransactionLogTest(transactionId, TRANSACTION_STATUS_COMMITTED);
xidstatus = TransactionLogFetch(transactionId);
/*
* If it's marked committed, it's committed.
*/
if (xidstatus == TRANSACTION_STATUS_COMMITTED)
return true;
/*
* If it's marked subcommitted, we have to check the parent recursively.
*/
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
{
TransactionId parentXid;
parentXid = SubTransGetParent(transactionId);
Assert(TransactionIdIsValid(parentXid));
return TransactionIdDidCommit(parentXid);
}
/*
* It's not committed.
*/
return false;
}
/*
......@@ -190,35 +225,49 @@ TransactionIdDidCommit(TransactionId transactionId)
bool /* true if given transaction aborted */
TransactionIdDidAbort(TransactionId transactionId)
{
XidStatus xidstatus;
if (AMI_OVERRIDE)
{
Assert(transactionId == BootstrapTransactionId);
return false;
}
return TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED);
}
xidstatus = TransactionLogFetch(transactionId);
/*
* Now this func in shmem.c and gives quality answer by scanning
* PGPROC structures of all running backend. - vadim 11/26/96
*
* Old comments:
* true if given transaction has neither committed nor aborted
*/
#ifdef NOT_USED
bool
TransactionIdIsInProgress(TransactionId transactionId)
{
if (AMI_OVERRIDE)
/*
* If it's marked aborted, it's aborted.
*/
if (xidstatus == TRANSACTION_STATUS_ABORTED)
return true;
/*
* If it's marked subcommitted, we have to check the parent recursively.
*
* If we detect that the parent has aborted, update pg_clog to show the
* subtransaction as aborted. This is only needed when the parent
* crashed before either committing or aborting. We want to clean up
* pg_clog so future visitors don't need to make this check again.
*/
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
{
Assert(transactionId == BootstrapTransactionId);
return false;
TransactionId parentXid;
bool parentAborted;
parentXid = SubTransGetParent(transactionId);
parentAborted = TransactionIdDidAbort(parentXid);
if (parentAborted)
TransactionIdAbort(transactionId);
return parentAborted;
}
return TransactionLogTest(transactionId, TRANSACTION_STATUS_IN_PROGRESS);
/*
* It's not aborted.
*/
return false;
}
#endif /* NOT_USED */
/* --------------------------------
* TransactionId Commit
......@@ -252,6 +301,46 @@ TransactionIdAbort(TransactionId transactionId)
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED);
}
/*
* TransactionIdSubCommit
* Marks the subtransaction associated with the identifier as
* sub-committed.
*/
void
TransactionIdSubCommit(TransactionId transactionId)
{
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_SUB_COMMITTED);
}
/*
* TransactionIdCommitTree
* Marks all the given transaction ids as committed.
*
* The caller has to be sure that this is used only to mark subcommitted
* subtransactions as committed, and only *after* marking the toplevel
* parent as committed. Otherwise there is a race condition against
* TransactionIdDidCommit.
*/
void
TransactionIdCommitTree(int nxids, TransactionId *xids)
{
if (nxids > 0)
TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_COMMITTED);
}
/*
* TransactionIdAbortTree
* Marks all the given transaction ids as aborted.
*
* We don't need to worry about the non-atomic behavior, since any onlookers
* will consider all the xacts as not-yet-committed anyway.
*/
void
TransactionIdAbortTree(int nxids, TransactionId *xids)
{
if (nxids > 0)
TransactionLogMultiUpdate(nxids, xids, TRANSACTION_STATUS_ABORTED);
}
/*
* TransactionIdPrecedes --- is id1 logically < id2?
......
......@@ -6,7 +6,7 @@
* Copyright (c) 2000-2003, PostgreSQL Global Development Group
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.55 2004/01/26 19:15:59 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/varsup.c,v 1.56 2004/07/01 00:49:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -14,6 +14,7 @@
#include "postgres.h"
#include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "storage/ipc.h"
#include "storage/proc.h"
......@@ -30,7 +31,7 @@ VariableCache ShmemVariableCache = NULL;
* Allocate the next XID for my new transaction.
*/
TransactionId
GetNewTransactionId(void)
GetNewTransactionId(bool isSubXact)
{
TransactionId xid;
......@@ -52,8 +53,11 @@ GetNewTransactionId(void)
* commit a later XID before we zero the page. Fortunately, a page of
* the commit log holds 32K or more transactions, so we don't have to
* do this very often.
*
* Extend pg_subtrans too.
*/
ExtendCLOG(xid);
ExtendSUBTRANS(xid);
/*
* Now advance the nextXid counter. This must not happen until after
......@@ -82,8 +86,11 @@ GetNewTransactionId(void)
* its own spinlock used only for fetching/storing that PGPROC's xid.
* (SInvalLock would then mean primarily that PGPROCs couldn't be added/
* removed while holding the lock.)
*
* We don't want a subtransaction to update the stored Xid; we'll check
* if a transaction Xid is a running subxact by checking pg_subtrans.
*/
if (MyProc != NULL)
if (MyProc != NULL && !isSubXact)
MyProc->xid = xid;
LWLockRelease(XidGenLock);
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.146 2004/06/03 02:08:00 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.147 2004/07/01 00:49:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,7 @@
#include <sys/time.h>
#include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
#include "access/xlog.h"
......@@ -2755,6 +2756,7 @@ BootStrapXLOG(void)
/* Bootstrap the commit log, too */
BootStrapCLOG();
BootStrapSUBTRANS();
}
static char *
......@@ -3154,6 +3156,7 @@ StartupXLOG(void)
/* Start up the commit log, too */
StartupCLOG();
StartupSUBTRANS();
ereport(LOG,
(errmsg("database system is ready")));
......@@ -3292,6 +3295,7 @@ ShutdownXLOG(int code, Datum arg)
CritSectionCount++;
CreateCheckPoint(true, true);
ShutdownCLOG();
ShutdownSUBTRANS();
CritSectionCount--;
ereport(LOG,
......@@ -3467,6 +3471,7 @@ CreateCheckPoint(bool shutdown, bool force)
END_CRIT_SECTION();
CheckPointCLOG();
CheckPointSUBTRANS();
FlushBufferPool();
START_CRIT_SECTION();
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.112 2004/05/26 04:41:10 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.113 2004/07/01 00:50:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -97,11 +97,17 @@
* State for outbound notifies consists of a list of all relnames NOTIFYed
* in the current transaction. We do not actually perform a NOTIFY until
* and unless the transaction commits. pendingNotifies is NIL if no
* NOTIFYs have been done in the current transaction. The List nodes and
* referenced strings are all palloc'd in TopTransactionContext.
* NOTIFYs have been done in the current transaction.
*
* The list is kept in CurTransactionContext. In subtransactions, each
* subtransaction has its own list in its own CurTransactionContext, but
* successful subtransactions attach their lists to their parent's list.
* Failed subtransactions simply discard their lists.
*/
static List *pendingNotifies = NIL;
static List *upperPendingNotifies = NIL; /* list of upper-xact lists */
/*
* State for inbound notifies consists of two flags: one saying whether
* the signal handler is currently allowed to call ProcessIncomingNotify
......@@ -155,11 +161,11 @@ Async_Notify(char *relname)
{
/*
* The name list needs to live until end of transaction, so store
* it in the top transaction context.
* it in the transaction context.
*/
MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(TopTransactionContext);
oldcontext = MemoryContextSwitchTo(CurTransactionContext);
pendingNotifies = lcons(pstrdup(relname), pendingNotifies);
......@@ -606,6 +612,60 @@ AtAbort_Notify(void)
ClearPendingNotifies();
}
/*
* AtSubStart_Notify() --- Take care of subtransaction start.
*
* Push empty state for the new subtransaction.
*/
void
AtSubStart_Notify(void)
{
MemoryContext old_cxt;
/* Keep the list-of-lists in TopTransactionContext for simplicity */
old_cxt = MemoryContextSwitchTo(TopTransactionContext);
upperPendingNotifies = lcons(pendingNotifies, upperPendingNotifies);
pendingNotifies = NIL;
MemoryContextSwitchTo(old_cxt);
}
/*
* AtSubCommit_Notify() --- Take care of subtransaction commit.
*
* Reassign all items in the pending notifies list to the parent transaction.
*/
void
AtSubCommit_Notify(void)
{
List *parentPendingNotifies;
parentPendingNotifies = (List *) linitial(upperPendingNotifies);
upperPendingNotifies = list_delete_first(upperPendingNotifies);
/*
* We could try to eliminate duplicates here, but it seems not worthwhile.
*/
pendingNotifies = list_concat(parentPendingNotifies, pendingNotifies);
}
/*
* AtSubAbort_Notify() --- Take care of subtransaction abort.
*/
void
AtSubAbort_Notify(void)
{
/*
* All we have to do is pop the stack --- the notifies made in this
* subxact are no longer interesting, and the space will be freed when
* CurTransactionContext is recycled.
*/
pendingNotifies = (List *) linitial(upperPendingNotifies);
upperPendingNotifies = list_delete_first(upperPendingNotifies);
}
/*
*--------------------------------------------------------------
* NotifyInterruptHandler
......@@ -951,7 +1011,7 @@ ClearPendingNotifies(void)
/*
* We used to have to explicitly deallocate the list members and
* nodes, because they were malloc'd. Now, since we know they are
* palloc'd in TopTransactionContext, we need not do that --- they'll
* palloc'd in CurTransactionContext, we need not do that --- they'll
* go away automatically at transaction exit. We need only reset the
* list head pointer.
*/
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.117 2004/06/25 21:55:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.118 2004/07/01 00:50:10 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -76,8 +76,8 @@ typedef struct OnCommitItem
* entries in the list until commit so that we can roll back if
* needed.
*/
bool created_in_cur_xact;
bool deleted_in_cur_xact;
TransactionId creating_xid;
TransactionId deleting_xid;
} OnCommitItem;
static List *on_commits = NIL;
......@@ -5483,8 +5483,8 @@ register_on_commit_action(Oid relid, OnCommitAction action)
oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
oc->relid = relid;
oc->oncommit = action;
oc->created_in_cur_xact = true;
oc->deleted_in_cur_xact = false;
oc->creating_xid = GetCurrentTransactionId();
oc->deleting_xid = InvalidTransactionId;
on_commits = lcons(oc, on_commits);
......@@ -5507,7 +5507,7 @@ remove_on_commit_action(Oid relid)
if (oc->relid == relid)
{
oc->deleted_in_cur_xact = true;
oc->deleting_xid = GetCurrentTransactionId();
break;
}
}
......@@ -5522,6 +5522,7 @@ remove_on_commit_action(Oid relid)
void
PreCommit_on_commit_actions(void)
{
TransactionId xid = GetCurrentTransactionId();
ListCell *l;
foreach(l, on_commits)
......@@ -5529,7 +5530,7 @@ PreCommit_on_commit_actions(void)
OnCommitItem *oc = (OnCommitItem *) lfirst(l);
/* Ignore entry if already dropped in this xact */
if (oc->deleted_in_cur_xact)
if (oc->deleting_xid == xid)
continue;
switch (oc->oncommit)
......@@ -5556,7 +5557,7 @@ PreCommit_on_commit_actions(void)
* remove_on_commit_action, so the entry should get
* marked as deleted.
*/
Assert(oc->deleted_in_cur_xact);
Assert(oc->deleting_xid == xid);
break;
}
}
......@@ -5572,7 +5573,7 @@ PreCommit_on_commit_actions(void)
* during abort, remove those created during this transaction.
*/
void
AtEOXact_on_commit_actions(bool isCommit)
AtEOXact_on_commit_actions(bool isCommit, TransactionId xid)
{
ListCell *cur_item;
ListCell *prev_item;
......@@ -5584,8 +5585,8 @@ AtEOXact_on_commit_actions(bool isCommit)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (isCommit ? oc->deleted_in_cur_xact :
oc->created_in_cur_xact)
if (isCommit ? TransactionIdEquals(oc->deleting_xid, xid) :
TransactionIdEquals(oc->creating_xid, xid))
{
/* cur_item must be removed */
on_commits = list_delete_cell(on_commits, cur_item, prev_item);
......@@ -5598,8 +5599,52 @@ AtEOXact_on_commit_actions(bool isCommit)
else
{
/* cur_item must be preserved */
oc->deleted_in_cur_xact = false;
oc->created_in_cur_xact = false;
oc->creating_xid = InvalidTransactionId;
oc->deleting_xid = InvalidTransactionId;
prev_item = cur_item;
cur_item = lnext(prev_item);
}
}
}
/*
* Post-subcommit or post-subabort cleanup for ON COMMIT management.
*
* During subabort, we can immediately remove entries created during this
* subtransaction. During subcommit, just relabel entries marked during
* this subtransaction as being the parent's responsibility.
*/
void
AtEOSubXact_on_commit_actions(bool isCommit, TransactionId childXid,
TransactionId parentXid)
{
ListCell *cur_item;
ListCell *prev_item;
prev_item = NULL;
cur_item = list_head(on_commits);
while (cur_item != NULL)
{
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (!isCommit && TransactionIdEquals(oc->creating_xid, childXid))
{
/* cur_item must be removed */
on_commits = list_delete_cell(on_commits, cur_item, prev_item);
pfree(oc);
if (prev_item)
cur_item = lnext(prev_item);
else
cur_item = list_head(on_commits);
}
else
{
/* cur_item must be preserved */
if (TransactionIdEquals(oc->creating_xid, childXid))
oc->creating_xid = parentXid;
if (TransactionIdEquals(oc->deleting_xid, childXid))
oc->deleting_xid = isCommit ? parentXid : InvalidTransactionId;
prev_item = cur_item;
cur_item = lnext(prev_item);
}
......
This diff is collapsed.
......@@ -13,7 +13,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.281 2004/06/08 13:59:36 momjian Exp $
* $PostgreSQL: pgsql/src/backend/commands/vacuum.c,v 1.282 2004/07/01 00:50:11 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,6 +25,7 @@
#include "access/clog.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "access/subtrans.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/catname.h"
......@@ -798,8 +799,9 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
return;
}
/* Truncate CLOG to the oldest vacuumxid */
/* Truncate CLOG and SUBTRANS to the oldest vacuumxid */
TruncateCLOG(vacuumXID);
TruncateSUBTRANS(vacuumXID);
/* Give warning about impending wraparound problems */
if (frozenAlreadyWrapped)
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.97 2004/05/26 04:41:13 neilc Exp $
* $PostgreSQL: pgsql/src/backend/commands/variable.c,v 1.98 2004/07/01 00:50:12 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -470,10 +470,17 @@ show_timezone(void)
const char *
assign_XactIsoLevel(const char *value, bool doit, GucSource source)
{
if (doit && source >= PGC_S_INTERACTIVE && SerializableSnapshot != NULL)
if (doit && source >= PGC_S_INTERACTIVE)
{
if (SerializableSnapshot != NULL)
ereport(ERROR,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query")));
if (IsSubTransaction())
ereport(ERROR,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("SET TRANSACTION ISOLATION LEVEL must not be called in a subtransaction")));
}
if (strcmp(value, "serializable") == 0)
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.118 2004/06/11 01:08:43 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.119 2004/07/01 00:50:26 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -29,6 +29,7 @@ int SPI_result;
static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = NULL;
static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
static int _SPI_connected = -1;
static int _SPI_curid = -1;
......@@ -59,7 +60,7 @@ static bool _SPI_checktuples(void);
int
SPI_connect(void)
{
_SPI_connection *new_SPI_stack;
int newdepth;
/*
* When procedure called by Executor _SPI_curid expected to be equal
......@@ -70,39 +71,46 @@ SPI_connect(void)
if (_SPI_stack == NULL)
{
if (_SPI_connected != -1)
if (_SPI_connected != -1 || _SPI_stack_depth != 0)
elog(ERROR, "SPI stack corrupted");
new_SPI_stack = (_SPI_connection *) malloc(sizeof(_SPI_connection));
newdepth = 16;
_SPI_stack = (_SPI_connection *)
MemoryContextAlloc(TopTransactionContext,
newdepth * sizeof(_SPI_connection));
_SPI_stack_depth = newdepth;
}
else
{
if (_SPI_connected < 0)
if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
elog(ERROR, "SPI stack corrupted");
new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
(_SPI_connected + 2) * sizeof(_SPI_connection));
if (_SPI_stack_depth == _SPI_connected + 1)
{
newdepth = _SPI_stack_depth * 2;
_SPI_stack = (_SPI_connection *)
repalloc(_SPI_stack,
newdepth * sizeof(_SPI_connection));
_SPI_stack_depth = newdepth;
}
}
if (new_SPI_stack == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
/*
* We' returning to procedure where _SPI_curid == _SPI_connected - 1
* We're entering procedure where _SPI_curid == _SPI_connected - 1
*/
_SPI_stack = new_SPI_stack;
_SPI_connected++;
Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
_SPI_current = &(_SPI_stack[_SPI_connected]);
_SPI_current->processed = 0;
_SPI_current->tuptable = NULL;
_SPI_current->connectXid = GetCurrentTransactionId();
/*
* Create memory contexts for this procedure
*
* XXX it would be better to use PortalContext as the parent context, but
* we may not be inside a portal (consider deferred-trigger
* execution).
* XXX it would be better to use PortalContext as the parent context,
* but we may not be inside a portal (consider deferred-trigger
* execution). Perhaps CurTransactionContext would do? For now it
* doesn't matter because we clean up explicitly in AtEOSubXact_SPI().
*/
_SPI_current->procCxt = AllocSetContextCreate(TopTransactionContext,
"SPI Proc",
......@@ -152,28 +160,11 @@ SPI_finish(void)
_SPI_connected--;
_SPI_curid--;
if (_SPI_connected == -1)
{
free(_SPI_stack);
_SPI_stack = NULL;
_SPI_current = NULL;
}
else
{
_SPI_connection *new_SPI_stack;
new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack,
(_SPI_connected + 1) * sizeof(_SPI_connection));
/* This could only fail with a pretty stupid malloc package ... */
if (new_SPI_stack == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of memory")));
_SPI_stack = new_SPI_stack;
_SPI_current = &(_SPI_stack[_SPI_connected]);
}
return SPI_OK_FINISH;
}
/*
......@@ -187,23 +178,54 @@ AtEOXact_SPI(bool isCommit)
* freed automatically, so we can ignore them here. We just need to
* restore our static variables to initial state.
*/
if (_SPI_stack != NULL)
{
free(_SPI_stack);
if (isCommit)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("freeing non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
}
if (isCommit && _SPI_connected != -1)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("transaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
_SPI_current = _SPI_stack = NULL;
_SPI_stack_depth = 0;
_SPI_connected = _SPI_curid = -1;
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
}
/*
* Clean up SPI state at subtransaction commit or abort.
*
* During commit, there shouldn't be any unclosed entries remaining from
* the current transaction; we throw them away if found.
*/
void
AtEOSubXact_SPI(bool isCommit, TransactionId childXid)
{
bool found = false;
while (_SPI_connected >= 0)
{
_SPI_connection *connection = &(_SPI_stack[_SPI_connected]);
int res;
if (connection->connectXid != childXid)
break; /* couldn't be any underneath it either */
found = true;
_SPI_curid = _SPI_connected - 1; /* avoid begin_call error */
res = SPI_finish();
Assert(res == SPI_OK_FINISH);
}
if (found && isCommit)
ereport(WARNING,
(errcode(ERRCODE_WARNING),
errmsg("subtransaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls")));
}
/* Pushes SPI stack to allow recursive SPI calls */
void
SPI_push(void)
......@@ -1148,16 +1170,18 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = SPI_ERROR_CURSOR;
goto fail;
}
else if (IsA(queryTree->utilityStmt, TransactionStmt))
{
res = SPI_ERROR_TRANSACTION;
goto fail;
}
res = SPI_OK_UTILITY;
if (plan == NULL)
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
CommandCounterIncrement();
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
}
}
else if (plan == NULL)
......@@ -1273,7 +1297,14 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{
ProcessUtility(queryTree->utilityStmt, dest, NULL);
res = SPI_OK_UTILITY;
CommandCounterIncrement();
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement();
}
else
{
......
......@@ -13,7 +13,7 @@
*
* Copyright (c) 2001-2003, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.76 2004/06/26 16:32:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/pgstat.c,v 1.77 2004/07/01 00:50:36 tgl Exp $
* ----------
*/
#include "postgres.h"
......@@ -167,6 +167,7 @@ static void pgstat_write_statsfile(void);
static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
PgStat_StatBeEntry **betab,
int *numbackends);
static void backend_read_statsfile(void);
static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype);
static void pgstat_send(void *msg, int len);
......@@ -786,12 +787,7 @@ pgstat_vacuum_tabstat(void)
* If not done for this transaction, read the statistics collector
* stats file into some hash tables.
*/
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId()))
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
backend_read_statsfile();
/*
* Lookup our own database entry
......@@ -1210,15 +1206,9 @@ pgstat_fetch_stat_dbentry(Oid dbid)
/*
* If not done for this transaction, read the statistics collector
* stats file into some hash tables. Be careful with the
* read_statsfile() call below!
* stats file into some hash tables.
*/
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId()))
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
backend_read_statsfile();
/*
* Lookup the requested database
......@@ -1250,15 +1240,9 @@ pgstat_fetch_stat_tabentry(Oid relid)
/*
* If not done for this transaction, read the statistics collector
* stats file into some hash tables. Be careful with the
* read_statsfile() call below!
* stats file into some hash tables.
*/
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId()))
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
backend_read_statsfile();
/*
* Lookup our database.
......@@ -1296,12 +1280,7 @@ pgstat_fetch_stat_tabentry(Oid relid)
PgStat_StatBeEntry *
pgstat_fetch_stat_beentry(int beid)
{
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId()))
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
backend_read_statsfile();
if (beid < 1 || beid > pgStatNumBackends)
return NULL;
......@@ -1320,12 +1299,7 @@ pgstat_fetch_stat_beentry(int beid)
int
pgstat_fetch_stat_numbackends(void)
{
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId()))
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
backend_read_statsfile();
return pgStatNumBackends;
}
......@@ -2759,11 +2733,32 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
fclose(fpin);
}
/*
* If not done for this transaction, read the statistics collector
* stats file into some hash tables.
*
* Because we store the hash tables in TopTransactionContext, the result
* is good for the entire current main transaction.
*/
static void
backend_read_statsfile(void)
{
TransactionId topXid = GetTopTransactionId();
if (!TransactionIdEquals(pgStatDBHashXact, topXid))
{
Assert(!pgStatRunningInCollector);
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = topXid;
}
}
/* ----------
* pgstat_recv_bestart() -
*
* Process a backend starup message.
* Process a backend startup message.
* ----------
*/
static void
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.171 2004/06/18 06:13:33 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/buffer/bufmgr.c,v 1.172 2004/07/01 00:50:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -45,6 +45,7 @@
#include "storage/bufpage.h"
#include "storage/proc.h"
#include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/relcache.h"
#include "pgstat.h"
......@@ -64,9 +65,13 @@ long NDirectFileRead; /* some I/O's are direct file access.
* bypass bufmgr */
long NDirectFileWrite; /* e.g., I/O in psort and hashjoin. */
/* List of upper-level-transaction buffer refcount arrays */
static List *upperRefCounts = NIL;
static void PinBuffer(BufferDesc *buf);
static void UnpinBuffer(BufferDesc *buf);
static void BufferFixLeak(Buffer bufnum, int32 shouldBe, bool emitWarning);
static void WaitIO(BufferDesc *buf);
static void StartBufferIO(BufferDesc *buf, bool forInput);
static void TerminateBufferIO(BufferDesc *buf, int err_flag);
......@@ -826,30 +831,104 @@ AtEOXact_Buffers(bool isCommit)
for (i = 0; i < NBuffers; i++)
{
if (PrivateRefCount[i] != 0)
{
BufferDesc *buf = &(BufferDescriptors[i]);
if (isCommit)
elog(WARNING,
"buffer refcount leak: [%03d] "
"(rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)",
i,
buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[i]);
PrivateRefCount[i] = 1; /* make sure we release shared pin */
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
UnpinBuffer(buf);
LWLockRelease(BufMgrLock);
Assert(PrivateRefCount[i] == 0);
}
BufferFixLeak(i, 0, isCommit);
}
AtEOXact_LocalBuffers(isCommit);
}
/*
* During subtransaction start, save buffer reference counts.
*/
void
AtSubStart_Buffers(void)
{
int32 *copyRefCounts;
Size rcSize;
MemoryContext old_cxt;
/* this is probably the active context already, but be safe */
old_cxt = MemoryContextSwitchTo(CurTransactionContext);
/*
* We need to copy the current state of PrivateRefCount[]. In the typical
* scenario, few if any of the entries will be nonzero, and we could save
* space by storing only the nonzero ones. However, copying the whole
* thing is lots simpler and faster both here and in AtEOSubXact_Buffers,
* so it seems best to waste the space.
*/
rcSize = NBuffers * sizeof(int32);
copyRefCounts = (int32 *) palloc(rcSize);
memcpy(copyRefCounts, PrivateRefCount, rcSize);
/* Attach to list */
upperRefCounts = lcons(copyRefCounts, upperRefCounts);
MemoryContextSwitchTo(old_cxt);
}
/*
* AtEOSubXact_Buffers
*
* At subtransaction end, we restore the saved counts. If committing, we
* complain if the refcounts don't match; if aborting, just restore silently.
*/
void
AtEOSubXact_Buffers(bool isCommit)
{
int32 *oldRefCounts;
int i;
oldRefCounts = (int32 *) linitial(upperRefCounts);
upperRefCounts = list_delete_first(upperRefCounts);
for (i = 0; i < NBuffers; i++)
{
if (PrivateRefCount[i] != oldRefCounts[i])
BufferFixLeak(i, oldRefCounts[i], isCommit);
}
pfree(oldRefCounts);
}
/*
* Fix a buffer refcount leak.
*
* The caller does not hold the BufMgrLock.
*/
static void
BufferFixLeak(Buffer bufnum, int32 shouldBe, bool emitWarning)
{
BufferDesc *buf = &(BufferDescriptors[bufnum]);
if (emitWarning)
elog(WARNING,
"buffer refcount leak: [%03d] (rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d, should be=%d)",
bufnum,
buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[bufnum], shouldBe);
/* If it's less, we're in a heap o' trouble */
if (PrivateRefCount[bufnum] <= shouldBe)
elog(FATAL, "buffer refcount was decreased by subtransaction");
if (shouldBe > 0)
{
/* We still keep the shared-memory pin */
PrivateRefCount[bufnum] = shouldBe;
}
else
{
PrivateRefCount[bufnum] = 1; /* make sure we release shared pin */
LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
UnpinBuffer(buf);
LWLockRelease(BufMgrLock);
Assert(PrivateRefCount[bufnum] == 0);
}
}
/*
* FlushBufferPool
*
......
......@@ -8,16 +8,16 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.68 2004/05/29 22:48:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.69 2004/07/01 00:50:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "miscadmin.h"
#include "access/clog.h"
#include "access/subtrans.h"
#include "access/xlog.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
#include "storage/bufmgr.h"
#include "storage/freespace.h"
......@@ -70,6 +70,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
size += LockShmemSize(maxBackends);
size += XLOGShmemSize();
size += CLOGShmemSize();
size += SUBTRANSShmemSize();
size += LWLockShmemSize();
size += SInvalShmemSize(maxBackends);
size += FreeSpaceShmemSize();
......@@ -133,6 +134,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
*/
XLOGShmemInit();
CLOGShmemInit();
SUBTRANSShmemInit();
InitBufferPool();
/*
......
This diff is collapsed.
......@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.63 2004/05/28 05:13:04 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.64 2004/07/01 00:50:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/subtrans.h"
#include "access/transam.h"
#include "access/xact.h"
#include "catalog/catalog.h"
......@@ -333,19 +334,21 @@ XactLockTableInsert(TransactionId xid)
* XactLockTableWait
*
* Wait for the specified transaction to commit or abort.
* We actually wait on the topmost transaction of the transaction tree.
*/
void
XactLockTableWait(TransactionId xid)
{
LOCKTAG tag;
TransactionId myxid = GetCurrentTransactionId();
TransactionId waitXid = SubTransGetTopmostTransaction(xid);
Assert(!TransactionIdEquals(xid, myxid));
Assert(!SubTransXidsHaveCommonAncestor(waitXid, myxid));
MemSet(&tag, 0, sizeof(tag));
tag.relId = XactLockTableId;
tag.dbId = InvalidOid;
tag.objId.xid = xid;
tag.objId.xid = waitXid;
if (!LockAcquire(LockTableId, &tag, myxid,
ShareLock, false))
......@@ -355,8 +358,13 @@ XactLockTableWait(TransactionId xid)
/*
* Transaction was committed/aborted/crashed - we have to update
* pg_clog if transaction is still marked as running.
* pg_clog if transaction is still marked as running. If it's a
* subtransaction, we can update the parent status too.
*/
if (!TransactionIdDidCommit(xid) && !TransactionIdDidAbort(xid))
TransactionIdAbort(xid);
if (!TransactionIdDidCommit(waitXid) && !TransactionIdDidAbort(waitXid))
{
TransactionIdAbort(waitXid);
if (waitXid != xid)
TransactionIdAbort(xid);
}
}
This diff is collapsed.
......@@ -15,13 +15,14 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lwlock.c,v 1.20 2004/06/11 16:43:24 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lwlock.c,v 1.21 2004/07/01 00:50:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/clog.h"
#include "access/subtrans.h"
#include "storage/lwlock.h"
#include "storage/proc.h"
#include "storage/spin.h"
......@@ -111,6 +112,9 @@ NumLWLocks(void)
/* clog.c needs one per CLOG buffer + one control lock */
numLocks += NUM_CLOG_BUFFERS + 1;
/* subtrans.c needs one per SubTrans buffer + one control lock */
numLocks += NUM_SUBTRANS_BUFFERS + 1;
/* Perhaps create a few more for use by user-defined modules? */
return numLocks;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment