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) ...@@ -75,8 +75,7 @@ user_write_unlock_oid(Oid oid)
int int
user_unlock_all(void) user_unlock_all(void)
{ {
return LockReleaseAll(USER_LOCKMETHOD, MyProc, false, return LockReleaseAll(USER_LOCKMETHOD, MyProc, ReleaseAll, 0, NULL);
InvalidTransactionId);
} }
/* end of file */ /* end of file */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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, ...@@ -41,6 +41,7 @@ static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
typedef struct GISTScanListData typedef struct GISTScanListData
{ {
IndexScanDesc gsl_scan; IndexScanDesc gsl_scan;
TransactionId gsl_creatingXid;
struct GISTScanListData *gsl_next; struct GISTScanListData *gsl_next;
} GISTScanListData; } GISTScanListData;
...@@ -223,6 +224,7 @@ gistregscan(IndexScanDesc s) ...@@ -223,6 +224,7 @@ gistregscan(IndexScanDesc s)
l = (GISTScanList) palloc(sizeof(GISTScanListData)); l = (GISTScanList) palloc(sizeof(GISTScanListData));
l->gsl_scan = s; l->gsl_scan = s;
l->gsl_creatingXid = GetCurrentTransactionId();
l->gsl_next = GISTScans; l->gsl_next = GISTScans;
GISTScans = l; GISTScans = l;
} }
...@@ -271,6 +273,46 @@ AtEOXact_gist(void) ...@@ -271,6 +273,46 @@ AtEOXact_gist(void)
GISTScans = NULL; 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 void
gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum) gistadjscans(Relation rel, int op, BlockNumber blkno, OffsetNumber offnum)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -21,6 +21,7 @@
typedef struct HashScanListData typedef struct HashScanListData
{ {
IndexScanDesc hashsl_scan; IndexScanDesc hashsl_scan;
TransactionId hashsl_creatingXid;
struct HashScanListData *hashsl_next; struct HashScanListData *hashsl_next;
} HashScanListData; } HashScanListData;
...@@ -50,6 +51,46 @@ AtEOXact_hash(void) ...@@ -50,6 +51,46 @@ AtEOXact_hash(void)
HashScans = NULL; 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. * _Hash_regscan() -- register a new scan.
*/ */
...@@ -60,6 +101,7 @@ _hash_regscan(IndexScanDesc scan) ...@@ -60,6 +101,7 @@ _hash_regscan(IndexScanDesc scan)
new_el = (HashScanList) palloc(sizeof(HashScanListData)); new_el = (HashScanList) palloc(sizeof(HashScanListData));
new_el->hashsl_scan = scan; new_el->hashsl_scan = scan;
new_el->hashsl_creatingXid = GetCurrentTransactionId();
new_el->hashsl_next = HashScans; new_el->hashsl_next = HashScans;
HashScans = new_el; HashScans = new_el;
} }
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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, ...@@ -42,6 +42,7 @@ static void adjustiptr(IndexScanDesc s, ItemPointer iptr,
typedef struct RTScanListData typedef struct RTScanListData
{ {
IndexScanDesc rtsl_scan; IndexScanDesc rtsl_scan;
TransactionId rtsl_creatingXid;
struct RTScanListData *rtsl_next; struct RTScanListData *rtsl_next;
} RTScanListData; } RTScanListData;
...@@ -240,6 +241,7 @@ rtregscan(IndexScanDesc s) ...@@ -240,6 +241,7 @@ rtregscan(IndexScanDesc s)
l = (RTScanList) palloc(sizeof(RTScanListData)); l = (RTScanList) palloc(sizeof(RTScanListData));
l->rtsl_scan = s; l->rtsl_scan = s;
l->rtsl_creatingXid = GetCurrentTransactionId();
l->rtsl_next = RTScans; l->rtsl_next = RTScans;
RTScans = l; RTScans = l;
} }
...@@ -290,6 +292,46 @@ AtEOXact_rtree(void) ...@@ -290,6 +292,46 @@ AtEOXact_rtree(void)
RTScans = NULL; 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 void
rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum) rtadjscans(Relation r, int op, BlockNumber blkno, OffsetNumber offnum)
{ {
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for access/transam # Makefile for access/transam
# #
# IDENTIFICATION # 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 ...@@ -12,7 +12,7 @@ subdir = src/backend/access/transam
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global 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 all: SUBSYS.o
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -21,14 +21,13 @@
#include <fcntl.h> #include <fcntl.h>
#include <dirent.h> #include <dirent.h>
#include <errno.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "access/clog.h" #include "access/clog.h"
#include "access/slru.h" #include "access/slru.h"
#include "storage/lwlock.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "storage/lwlock.h"
/* /*
...@@ -65,7 +64,7 @@ ...@@ -65,7 +64,7 @@
* is guaranteed flushed through the XLOG commit record before we are called * 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 * 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, * 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. * synchronization already.
*---------- *----------
*/ */
...@@ -95,20 +94,22 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status) ...@@ -95,20 +94,22 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status)
char *byteptr; char *byteptr;
Assert(status == TRANSACTION_STATUS_COMMITTED || Assert(status == TRANSACTION_STATUS_COMMITTED ||
status == TRANSACTION_STATUS_ABORTED); status == TRANSACTION_STATUS_ABORTED ||
status == TRANSACTION_STATUS_SUB_COMMITTED);
LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE); LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE);
byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true); byteptr = SimpleLruReadPage(ClogCtl, pageno, xid, true);
byteptr += byteno; 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 || Assert(((*byteptr >> bshift) & CLOG_XACT_BITMASK) == 0 ||
((*byteptr >> bshift) & CLOG_XACT_BITMASK) == TRANSACTION_STATUS_SUB_COMMITTED ||
((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status); ((*byteptr >> bshift) & CLOG_XACT_BITMASK) == status);
*byteptr |= (status << bshift); *byteptr |= (status << bshift);
/* ...->page_status[slotno] = CLOG_PAGE_DIRTY; already done */ /* ...->page_status[slotno] = SLRU_PAGE_DIRTY; already done */
LWLockRelease(ClogCtl->ControlLock); LWLockRelease(ClogCtl->ControlLock);
} }
...@@ -117,7 +118,7 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status) ...@@ -117,7 +118,7 @@ TransactionIdSetStatus(TransactionId xid, XidStatus status)
* Interrogate the state of a transaction in the commit log. * Interrogate the state of a transaction in the commit log.
* *
* NB: this is a low-level routine and is NOT the preferred entry point * 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 XidStatus
TransactionIdGetStatus(TransactionId xid) TransactionIdGetStatus(TransactionId xid)
...@@ -176,7 +177,7 @@ BootStrapCLOG(void) ...@@ -176,7 +177,7 @@ BootStrapCLOG(void)
/* Make sure it's written out */ /* Make sure it's written out */
SimpleLruWritePage(ClogCtl, slotno, NULL); SimpleLruWritePage(ClogCtl, slotno, NULL);
/* Assert(ClogCtl->page_status[slotno] == CLOG_PAGE_CLEAN); */ /* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
LWLockRelease(ClogCtl->ControlLock); LWLockRelease(ClogCtl->ControlLock);
} }
...@@ -211,7 +212,8 @@ StartupCLOG(void) ...@@ -211,7 +212,8 @@ StartupCLOG(void)
/* /*
* Initialize our idea of the latest page number. * Initialize our idea of the latest page number.
*/ */
SimpleLruSetLatestPage(ClogCtl, TransactionIdToPage(ShmemVariableCache->nextXid)); SimpleLruSetLatestPage(ClogCtl,
TransactionIdToPage(ShmemVariableCache->nextXid));
} }
/* /*
...@@ -333,24 +335,15 @@ WriteZeroPageXlogRec(int pageno) ...@@ -333,24 +335,15 @@ WriteZeroPageXlogRec(int pageno)
rdata.data = (char *) (&pageno); rdata.data = (char *) (&pageno);
rdata.len = sizeof(int); rdata.len = sizeof(int);
rdata.next = NULL; rdata.next = NULL;
(void) XLogInsert(RM_CLOG_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata); (void) XLogInsert(RM_SLRU_ID, CLOG_ZEROPAGE | XLOG_NO_TRAN, &rdata);
} }
/* /* Redo a ZEROPAGE action during WAL replay */
* CLOG resource manager's routines
*/
void void
clog_redo(XLogRecPtr lsn, XLogRecord *record) clog_zeropage_redo(int pageno)
{ {
uint8 info = record->xl_info & ~XLR_INFO_MASK;
if (info == CLOG_ZEROPAGE)
{
int pageno;
int slotno; int slotno;
memcpy(&pageno, XLogRecGetData(record), sizeof(int));
LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE); LWLockAcquire(ClogCtl->ControlLock, LW_EXCLUSIVE);
slotno = ZeroCLOGPage(pageno, false); slotno = ZeroCLOGPage(pageno, false);
...@@ -358,26 +351,4 @@ clog_redo(XLogRecPtr lsn, XLogRecord *record) ...@@ -358,26 +351,4 @@ clog_redo(XLogRecPtr lsn, XLogRecord *record)
/* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */ /* Assert(ClogCtl->page_status[slotno] == SLRU_PAGE_CLEAN); */
LWLockRelease(ClogCtl->ControlLock); LWLockRelease(ClogCtl->ControlLock);
}
}
void
clog_undo(XLogRecPtr lsn, XLogRecord *record)
{
}
void
clog_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), "zeropage: %d", pageno);
}
else
strcat(buf, "UNKNOWN");
} }
...@@ -3,16 +3,16 @@ ...@@ -3,16 +3,16 @@
* *
* Resource managers definition * 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 "postgres.h"
#include "access/clog.h"
#include "access/gist.h" #include "access/gist.h"
#include "access/hash.h" #include "access/hash.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/nbtree.h" #include "access/nbtree.h"
#include "access/rtree.h" #include "access/rtree.h"
#include "access/slru.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "storage/smgr.h" #include "storage/smgr.h"
...@@ -23,7 +23,7 @@ RmgrData RmgrTable[RM_MAX_ID + 1] = { ...@@ -23,7 +23,7 @@ RmgrData RmgrTable[RM_MAX_ID + 1] = {
{"XLOG", xlog_redo, xlog_undo, xlog_desc, NULL, NULL}, {"XLOG", xlog_redo, xlog_undo, xlog_desc, NULL, NULL},
{"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL}, {"Transaction", xact_redo, xact_undo, xact_desc, NULL, NULL},
{"Storage", smgr_redo, smgr_undo, smgr_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 4", NULL, NULL, NULL, NULL, NULL},
{"Reserved 5", NULL, NULL, NULL, NULL, NULL}, {"Reserved 5", NULL, NULL, NULL, NULL, NULL},
{"Reserved 6", NULL, NULL, NULL, NULL, NULL}, {"Reserved 6", NULL, NULL, NULL, NULL, NULL},
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -16,8 +16,9 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include "access/clog.h"
#include "access/slru.h" #include "access/slru.h"
#include "access/clog.h" /* only for NUM_CLOG_BUFFERS */ #include "access/subtrans.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "storage/fd.h" #include "storage/fd.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
...@@ -1025,3 +1026,55 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions) ...@@ -1025,3 +1026,55 @@ SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions)
return found; 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 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 * NOTES
* This file contains the high level access-method interface to the * This file contains the high level access-method interface to the
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/clog.h" #include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h" #include "access/transam.h"
...@@ -35,44 +36,40 @@ ...@@ -35,44 +36,40 @@
bool AMI_OVERRIDE = false; bool AMI_OVERRIDE = false;
static bool TransactionLogTest(TransactionId transactionId, XidStatus status); static XidStatus TransactionLogFetch(TransactionId transactionId);
static void TransactionLogUpdate(TransactionId transactionId, static void TransactionLogUpdate(TransactionId transactionId,
XidStatus status); XidStatus status);
/* ---------------- /* ----------------
* Single-item cache for results of TransactionLogTest. * Single-item cache for results of TransactionLogFetch.
* ---------------- * ----------------
*/ */
static TransactionId cachedTestXid = InvalidTransactionId; static TransactionId cachedFetchXid = InvalidTransactionId;
static XidStatus cachedTestXidStatus; static XidStatus cachedFetchXidStatus;
/* ---------------------------------------------------------------- /* ----------------------------------------------------------------
* postgres log access method interface * postgres log access method interface
* *
* TransactionLogTest * TransactionLogFetch
* TransactionLogUpdate * TransactionLogUpdate
* ---------------------------------------------------------------- * ----------------------------------------------------------------
*/ */
/* -------------------------------- /*
* TransactionLogTest * TransactionLogFetch --- fetch commit status of specified transaction id
* --------------------------------
*/ */
static XidStatus
static bool /* true/false: does transaction id have TransactionLogFetch(TransactionId transactionId)
* specified status? */
TransactionLogTest(TransactionId transactionId, /* transaction id to test */
XidStatus status) /* transaction status */
{ {
XidStatus xidstatus; /* recorded status of xid */ XidStatus xidstatus;
/* /*
* Before going to the commit log manager, check our single item cache * 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. * to see if we didn't just check the transaction status a moment ago.
*/ */
if (TransactionIdEquals(transactionId, cachedTestXid)) if (TransactionIdEquals(transactionId, cachedFetchXid))
return (status == cachedTestXidStatus); return cachedFetchXidStatus;
/* /*
* Also, check to see if the transaction ID is a permanent one. * Also, check to see if the transaction ID is a permanent one.
...@@ -80,10 +77,10 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */ ...@@ -80,10 +77,10 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */
if (!TransactionIdIsNormal(transactionId)) if (!TransactionIdIsNormal(transactionId))
{ {
if (TransactionIdEquals(transactionId, BootstrapTransactionId)) if (TransactionIdEquals(transactionId, BootstrapTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED); return TRANSACTION_STATUS_COMMITTED;
if (TransactionIdEquals(transactionId, FrozenTransactionId)) if (TransactionIdEquals(transactionId, FrozenTransactionId))
return (status == TRANSACTION_STATUS_COMMITTED); return TRANSACTION_STATUS_COMMITTED;
return (status == TRANSACTION_STATUS_ABORTED); return TRANSACTION_STATUS_ABORTED;
} }
/* /*
...@@ -92,15 +89,17 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */ ...@@ -92,15 +89,17 @@ TransactionLogTest(TransactionId transactionId, /* transaction id to test */
xidstatus = TransactionIdGetStatus(transactionId); 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); TransactionIdStore(transactionId, &cachedFetchXid);
cachedTestXidStatus = xidstatus; cachedFetchXidStatus = xidstatus;
} }
return (status == xidstatus); return xidstatus;
} }
/* -------------------------------- /* --------------------------------
...@@ -115,12 +114,23 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */ ...@@ -115,12 +114,23 @@ TransactionLogUpdate(TransactionId transactionId, /* trans id to update */
* update the commit log * update the commit log
*/ */
TransactionIdSetStatus(transactionId, status); TransactionIdSetStatus(transactionId, status);
}
/* /*
* update (invalidate) our single item TransactionLogTest cache. * TransactionLogMultiUpdate
*
* Update multiple transaction identifiers to a given status.
* Don't depend on this being atomic; it's not.
*/ */
TransactionIdStore(transactionId, &cachedTestXid); static void
cachedTestXidStatus = status; 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) ...@@ -171,13 +181,38 @@ AmiTransactionOverride(bool flag)
bool /* true if given transaction committed */ bool /* true if given transaction committed */
TransactionIdDidCommit(TransactionId transactionId) TransactionIdDidCommit(TransactionId transactionId)
{ {
XidStatus xidstatus;
if (AMI_OVERRIDE) if (AMI_OVERRIDE)
{ {
Assert(transactionId == BootstrapTransactionId); Assert(transactionId == BootstrapTransactionId);
return true; 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) ...@@ -190,35 +225,49 @@ TransactionIdDidCommit(TransactionId transactionId)
bool /* true if given transaction aborted */ bool /* true if given transaction aborted */
TransactionIdDidAbort(TransactionId transactionId) TransactionIdDidAbort(TransactionId transactionId)
{ {
XidStatus xidstatus;
if (AMI_OVERRIDE) if (AMI_OVERRIDE)
{ {
Assert(transactionId == BootstrapTransactionId); Assert(transactionId == BootstrapTransactionId);
return false; return false;
} }
return TransactionLogTest(transactionId, TRANSACTION_STATUS_ABORTED); xidstatus = TransactionLogFetch(transactionId);
}
/* /*
* Now this func in shmem.c and gives quality answer by scanning * If it's marked aborted, it's aborted.
* PGPROC structures of all running backend. - vadim 11/26/96 */
if (xidstatus == TRANSACTION_STATUS_ABORTED)
return true;
/*
* If it's marked subcommitted, we have to check the parent recursively.
* *
* Old comments: * If we detect that the parent has aborted, update pg_clog to show the
* true if given transaction has neither committed nor aborted * 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.
*/ */
#ifdef NOT_USED if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
bool
TransactionIdIsInProgress(TransactionId transactionId)
{
if (AMI_OVERRIDE)
{ {
Assert(transactionId == BootstrapTransactionId); TransactionId parentXid;
return false; 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 * TransactionId Commit
...@@ -252,6 +301,46 @@ TransactionIdAbort(TransactionId transactionId) ...@@ -252,6 +301,46 @@ TransactionIdAbort(TransactionId transactionId)
TransactionLogUpdate(transactionId, TRANSACTION_STATUS_ABORTED); 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? * TransactionIdPrecedes --- is id1 logically < id2?
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Copyright (c) 2000-2003, PostgreSQL Global Development Group * Copyright (c) 2000-2003, PostgreSQL Global Development Group
* *
* IDENTIFICATION * 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 @@ ...@@ -14,6 +14,7 @@
#include "postgres.h" #include "postgres.h"
#include "access/clog.h" #include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h" #include "access/transam.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
...@@ -30,7 +31,7 @@ VariableCache ShmemVariableCache = NULL; ...@@ -30,7 +31,7 @@ VariableCache ShmemVariableCache = NULL;
* Allocate the next XID for my new transaction. * Allocate the next XID for my new transaction.
*/ */
TransactionId TransactionId
GetNewTransactionId(void) GetNewTransactionId(bool isSubXact)
{ {
TransactionId xid; TransactionId xid;
...@@ -52,8 +53,11 @@ GetNewTransactionId(void) ...@@ -52,8 +53,11 @@ GetNewTransactionId(void)
* commit a later XID before we zero the page. Fortunately, a page of * 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 * the commit log holds 32K or more transactions, so we don't have to
* do this very often. * do this very often.
*
* Extend pg_subtrans too.
*/ */
ExtendCLOG(xid); ExtendCLOG(xid);
ExtendSUBTRANS(xid);
/* /*
* Now advance the nextXid counter. This must not happen until after * Now advance the nextXid counter. This must not happen until after
...@@ -82,8 +86,11 @@ GetNewTransactionId(void) ...@@ -82,8 +86,11 @@ GetNewTransactionId(void)
* its own spinlock used only for fetching/storing that PGPROC's xid. * its own spinlock used only for fetching/storing that PGPROC's xid.
* (SInvalLock would then mean primarily that PGPROCs couldn't be added/ * (SInvalLock would then mean primarily that PGPROCs couldn't be added/
* removed while holding the lock.) * 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; MyProc->xid = xid;
LWLockRelease(XidGenLock); LWLockRelease(XidGenLock);
......
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * 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 @@ ...@@ -22,6 +22,7 @@
#include <sys/time.h> #include <sys/time.h>
#include "access/clog.h" #include "access/clog.h"
#include "access/subtrans.h"
#include "access/transam.h" #include "access/transam.h"
#include "access/xact.h" #include "access/xact.h"
#include "access/xlog.h" #include "access/xlog.h"
...@@ -2755,6 +2756,7 @@ BootStrapXLOG(void) ...@@ -2755,6 +2756,7 @@ BootStrapXLOG(void)
/* Bootstrap the commit log, too */ /* Bootstrap the commit log, too */
BootStrapCLOG(); BootStrapCLOG();
BootStrapSUBTRANS();
} }
static char * static char *
...@@ -3154,6 +3156,7 @@ StartupXLOG(void) ...@@ -3154,6 +3156,7 @@ StartupXLOG(void)
/* Start up the commit log, too */ /* Start up the commit log, too */
StartupCLOG(); StartupCLOG();
StartupSUBTRANS();
ereport(LOG, ereport(LOG,
(errmsg("database system is ready"))); (errmsg("database system is ready")));
...@@ -3292,6 +3295,7 @@ ShutdownXLOG(int code, Datum arg) ...@@ -3292,6 +3295,7 @@ ShutdownXLOG(int code, Datum arg)
CritSectionCount++; CritSectionCount++;
CreateCheckPoint(true, true); CreateCheckPoint(true, true);
ShutdownCLOG(); ShutdownCLOG();
ShutdownSUBTRANS();
CritSectionCount--; CritSectionCount--;
ereport(LOG, ereport(LOG,
...@@ -3467,6 +3471,7 @@ CreateCheckPoint(bool shutdown, bool force) ...@@ -3467,6 +3471,7 @@ CreateCheckPoint(bool shutdown, bool force)
END_CRIT_SECTION(); END_CRIT_SECTION();
CheckPointCLOG(); CheckPointCLOG();
CheckPointSUBTRANS();
FlushBufferPool(); FlushBufferPool();
START_CRIT_SECTION(); START_CRIT_SECTION();
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 @@ ...@@ -97,11 +97,17 @@
* State for outbound notifies consists of a list of all relnames NOTIFYed * State for outbound notifies consists of a list of all relnames NOTIFYed
* in the current transaction. We do not actually perform a NOTIFY until * in the current transaction. We do not actually perform a NOTIFY until
* and unless the transaction commits. pendingNotifies is NIL if no * and unless the transaction commits. pendingNotifies is NIL if no
* NOTIFYs have been done in the current transaction. The List nodes and * NOTIFYs have been done in the current transaction.
* referenced strings are all palloc'd in TopTransactionContext. *
* 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 *pendingNotifies = NIL;
static List *upperPendingNotifies = NIL; /* list of upper-xact lists */
/* /*
* State for inbound notifies consists of two flags: one saying whether * State for inbound notifies consists of two flags: one saying whether
* the signal handler is currently allowed to call ProcessIncomingNotify * the signal handler is currently allowed to call ProcessIncomingNotify
...@@ -155,11 +161,11 @@ Async_Notify(char *relname) ...@@ -155,11 +161,11 @@ Async_Notify(char *relname)
{ {
/* /*
* The name list needs to live until end of transaction, so store * 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; MemoryContext oldcontext;
oldcontext = MemoryContextSwitchTo(TopTransactionContext); oldcontext = MemoryContextSwitchTo(CurTransactionContext);
pendingNotifies = lcons(pstrdup(relname), pendingNotifies); pendingNotifies = lcons(pstrdup(relname), pendingNotifies);
...@@ -606,6 +612,60 @@ AtAbort_Notify(void) ...@@ -606,6 +612,60 @@ AtAbort_Notify(void)
ClearPendingNotifies(); 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 * NotifyInterruptHandler
...@@ -951,7 +1011,7 @@ ClearPendingNotifies(void) ...@@ -951,7 +1011,7 @@ ClearPendingNotifies(void)
/* /*
* We used to have to explicitly deallocate the list members and * We used to have to explicitly deallocate the list members and
* nodes, because they were malloc'd. Now, since we know they are * 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 * go away automatically at transaction exit. We need only reset the
* list head pointer. * list head pointer.
*/ */
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 ...@@ -76,8 +76,8 @@ typedef struct OnCommitItem
* entries in the list until commit so that we can roll back if * entries in the list until commit so that we can roll back if
* needed. * needed.
*/ */
bool created_in_cur_xact; TransactionId creating_xid;
bool deleted_in_cur_xact; TransactionId deleting_xid;
} OnCommitItem; } OnCommitItem;
static List *on_commits = NIL; static List *on_commits = NIL;
...@@ -5483,8 +5483,8 @@ register_on_commit_action(Oid relid, OnCommitAction action) ...@@ -5483,8 +5483,8 @@ register_on_commit_action(Oid relid, OnCommitAction action)
oc = (OnCommitItem *) palloc(sizeof(OnCommitItem)); oc = (OnCommitItem *) palloc(sizeof(OnCommitItem));
oc->relid = relid; oc->relid = relid;
oc->oncommit = action; oc->oncommit = action;
oc->created_in_cur_xact = true; oc->creating_xid = GetCurrentTransactionId();
oc->deleted_in_cur_xact = false; oc->deleting_xid = InvalidTransactionId;
on_commits = lcons(oc, on_commits); on_commits = lcons(oc, on_commits);
...@@ -5507,7 +5507,7 @@ remove_on_commit_action(Oid relid) ...@@ -5507,7 +5507,7 @@ remove_on_commit_action(Oid relid)
if (oc->relid == relid) if (oc->relid == relid)
{ {
oc->deleted_in_cur_xact = true; oc->deleting_xid = GetCurrentTransactionId();
break; break;
} }
} }
...@@ -5522,6 +5522,7 @@ remove_on_commit_action(Oid relid) ...@@ -5522,6 +5522,7 @@ remove_on_commit_action(Oid relid)
void void
PreCommit_on_commit_actions(void) PreCommit_on_commit_actions(void)
{ {
TransactionId xid = GetCurrentTransactionId();
ListCell *l; ListCell *l;
foreach(l, on_commits) foreach(l, on_commits)
...@@ -5529,7 +5530,7 @@ PreCommit_on_commit_actions(void) ...@@ -5529,7 +5530,7 @@ PreCommit_on_commit_actions(void)
OnCommitItem *oc = (OnCommitItem *) lfirst(l); OnCommitItem *oc = (OnCommitItem *) lfirst(l);
/* Ignore entry if already dropped in this xact */ /* Ignore entry if already dropped in this xact */
if (oc->deleted_in_cur_xact) if (oc->deleting_xid == xid)
continue; continue;
switch (oc->oncommit) switch (oc->oncommit)
...@@ -5556,7 +5557,7 @@ PreCommit_on_commit_actions(void) ...@@ -5556,7 +5557,7 @@ PreCommit_on_commit_actions(void)
* remove_on_commit_action, so the entry should get * remove_on_commit_action, so the entry should get
* marked as deleted. * marked as deleted.
*/ */
Assert(oc->deleted_in_cur_xact); Assert(oc->deleting_xid == xid);
break; break;
} }
} }
...@@ -5572,7 +5573,7 @@ PreCommit_on_commit_actions(void) ...@@ -5572,7 +5573,7 @@ PreCommit_on_commit_actions(void)
* during abort, remove those created during this transaction. * during abort, remove those created during this transaction.
*/ */
void void
AtEOXact_on_commit_actions(bool isCommit) AtEOXact_on_commit_actions(bool isCommit, TransactionId xid)
{ {
ListCell *cur_item; ListCell *cur_item;
ListCell *prev_item; ListCell *prev_item;
...@@ -5584,8 +5585,8 @@ AtEOXact_on_commit_actions(bool isCommit) ...@@ -5584,8 +5585,8 @@ AtEOXact_on_commit_actions(bool isCommit)
{ {
OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item); OnCommitItem *oc = (OnCommitItem *) lfirst(cur_item);
if (isCommit ? oc->deleted_in_cur_xact : if (isCommit ? TransactionIdEquals(oc->deleting_xid, xid) :
oc->created_in_cur_xact) TransactionIdEquals(oc->creating_xid, xid))
{ {
/* cur_item must be removed */ /* cur_item must be removed */
on_commits = list_delete_cell(on_commits, cur_item, prev_item); on_commits = list_delete_cell(on_commits, cur_item, prev_item);
...@@ -5598,8 +5599,52 @@ AtEOXact_on_commit_actions(bool isCommit) ...@@ -5598,8 +5599,52 @@ AtEOXact_on_commit_actions(bool isCommit)
else else
{ {
/* cur_item must be preserved */ /* cur_item must be preserved */
oc->deleted_in_cur_xact = false; oc->creating_xid = InvalidTransactionId;
oc->created_in_cur_xact = false; 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; prev_item = cur_item;
cur_item = lnext(prev_item); cur_item = lnext(prev_item);
} }
......
This diff is collapsed.
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -25,6 +25,7 @@
#include "access/clog.h" #include "access/clog.h"
#include "access/genam.h" #include "access/genam.h"
#include "access/heapam.h" #include "access/heapam.h"
#include "access/subtrans.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "catalog/catalog.h" #include "catalog/catalog.h"
#include "catalog/catname.h" #include "catalog/catname.h"
...@@ -798,8 +799,9 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID) ...@@ -798,8 +799,9 @@ vac_truncate_clog(TransactionId vacuumXID, TransactionId frozenXID)
return; return;
} }
/* Truncate CLOG to the oldest vacuumxid */ /* Truncate CLOG and SUBTRANS to the oldest vacuumxid */
TruncateCLOG(vacuumXID); TruncateCLOG(vacuumXID);
TruncateSUBTRANS(vacuumXID);
/* Give warning about impending wraparound problems */ /* Give warning about impending wraparound problems */
if (frozenAlreadyWrapped) if (frozenAlreadyWrapped)
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * 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) ...@@ -470,10 +470,17 @@ show_timezone(void)
const char * const char *
assign_XactIsoLevel(const char *value, bool doit, GucSource source) 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, ereport(ERROR,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("SET TRANSACTION ISOLATION LEVEL must be called before any query"))); 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) if (strcmp(value, "serializable") == 0)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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; ...@@ -29,6 +29,7 @@ int SPI_result;
static _SPI_connection *_SPI_stack = NULL; static _SPI_connection *_SPI_stack = NULL;
static _SPI_connection *_SPI_current = 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_connected = -1;
static int _SPI_curid = -1; static int _SPI_curid = -1;
...@@ -59,7 +60,7 @@ static bool _SPI_checktuples(void); ...@@ -59,7 +60,7 @@ static bool _SPI_checktuples(void);
int int
SPI_connect(void) SPI_connect(void)
{ {
_SPI_connection *new_SPI_stack; int newdepth;
/* /*
* When procedure called by Executor _SPI_curid expected to be equal * When procedure called by Executor _SPI_curid expected to be equal
...@@ -70,39 +71,46 @@ SPI_connect(void) ...@@ -70,39 +71,46 @@ SPI_connect(void)
if (_SPI_stack == NULL) if (_SPI_stack == NULL)
{ {
if (_SPI_connected != -1) if (_SPI_connected != -1 || _SPI_stack_depth != 0)
elog(ERROR, "SPI stack corrupted"); 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 else
{ {
if (_SPI_connected < 0) if (_SPI_stack_depth <= 0 || _SPI_stack_depth <= _SPI_connected)
elog(ERROR, "SPI stack corrupted"); elog(ERROR, "SPI stack corrupted");
new_SPI_stack = (_SPI_connection *) realloc(_SPI_stack, if (_SPI_stack_depth == _SPI_connected + 1)
(_SPI_connected + 2) * sizeof(_SPI_connection)); {
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++; _SPI_connected++;
Assert(_SPI_connected >= 0 && _SPI_connected < _SPI_stack_depth);
_SPI_current = &(_SPI_stack[_SPI_connected]); _SPI_current = &(_SPI_stack[_SPI_connected]);
_SPI_current->processed = 0; _SPI_current->processed = 0;
_SPI_current->tuptable = NULL; _SPI_current->tuptable = NULL;
_SPI_current->connectXid = GetCurrentTransactionId();
/* /*
* Create memory contexts for this procedure * Create memory contexts for this procedure
* *
* XXX it would be better to use PortalContext as the parent context, but * XXX it would be better to use PortalContext as the parent context,
* we may not be inside a portal (consider deferred-trigger * but we may not be inside a portal (consider deferred-trigger
* execution). * 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_current->procCxt = AllocSetContextCreate(TopTransactionContext,
"SPI Proc", "SPI Proc",
...@@ -152,28 +160,11 @@ SPI_finish(void) ...@@ -152,28 +160,11 @@ SPI_finish(void)
_SPI_connected--; _SPI_connected--;
_SPI_curid--; _SPI_curid--;
if (_SPI_connected == -1) if (_SPI_connected == -1)
{
free(_SPI_stack);
_SPI_stack = NULL;
_SPI_current = NULL; _SPI_current = NULL;
}
else 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]); _SPI_current = &(_SPI_stack[_SPI_connected]);
}
return SPI_OK_FINISH; return SPI_OK_FINISH;
} }
/* /*
...@@ -187,23 +178,54 @@ AtEOXact_SPI(bool isCommit) ...@@ -187,23 +178,54 @@ AtEOXact_SPI(bool isCommit)
* freed automatically, so we can ignore them here. We just need to * freed automatically, so we can ignore them here. We just need to
* restore our static variables to initial state. * restore our static variables to initial state.
*/ */
if (_SPI_stack != NULL) if (isCommit && _SPI_connected != -1)
{
free(_SPI_stack);
if (isCommit)
ereport(WARNING, ereport(WARNING,
(errcode(ERRCODE_WARNING), (errcode(ERRCODE_WARNING),
errmsg("freeing non-empty SPI stack"), errmsg("transaction left non-empty SPI stack"),
errhint("Check for missing \"SPI_finish\" calls"))); errhint("Check for missing \"SPI_finish\" calls")));
}
_SPI_current = _SPI_stack = NULL; _SPI_current = _SPI_stack = NULL;
_SPI_stack_depth = 0;
_SPI_connected = _SPI_curid = -1; _SPI_connected = _SPI_curid = -1;
SPI_processed = 0; SPI_processed = 0;
SPI_lastoid = InvalidOid; SPI_lastoid = InvalidOid;
SPI_tuptable = NULL; 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 */ /* Pushes SPI stack to allow recursive SPI calls */
void void
SPI_push(void) SPI_push(void)
...@@ -1148,15 +1170,17 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan) ...@@ -1148,15 +1170,17 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
res = SPI_ERROR_CURSOR; res = SPI_ERROR_CURSOR;
goto fail; goto fail;
} }
else if (IsA(queryTree->utilityStmt, TransactionStmt))
{
res = SPI_ERROR_TRANSACTION;
goto fail;
}
res = SPI_OK_UTILITY; res = SPI_OK_UTILITY;
if (plan == NULL) if (plan == NULL)
{ {
ProcessUtility(queryTree->utilityStmt, dest, NULL); ProcessUtility(queryTree->utilityStmt, dest, NULL);
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement(); CommandCounterIncrement();
} }
} }
...@@ -1273,6 +1297,13 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls, ...@@ -1273,6 +1297,13 @@ _SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
{ {
ProcessUtility(queryTree->utilityStmt, dest, NULL); ProcessUtility(queryTree->utilityStmt, dest, NULL);
res = SPI_OK_UTILITY; res = SPI_OK_UTILITY;
if (IsA(queryTree->utilityStmt, TransactionStmt))
{
CommitTransactionCommand();
StartTransactionCommand();
}
else
CommandCounterIncrement(); CommandCounterIncrement();
} }
else else
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
* *
* Copyright (c) 2001-2003, PostgreSQL Global Development Group * 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" #include "postgres.h"
...@@ -167,6 +167,7 @@ static void pgstat_write_statsfile(void); ...@@ -167,6 +167,7 @@ static void pgstat_write_statsfile(void);
static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
PgStat_StatBeEntry **betab, PgStat_StatBeEntry **betab,
int *numbackends); int *numbackends);
static void backend_read_statsfile(void);
static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype); static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype);
static void pgstat_send(void *msg, int len); static void pgstat_send(void *msg, int len);
...@@ -786,12 +787,7 @@ pgstat_vacuum_tabstat(void) ...@@ -786,12 +787,7 @@ pgstat_vacuum_tabstat(void)
* If not done for this transaction, read the statistics collector * If not done for this transaction, read the statistics collector
* stats file into some hash tables. * stats file into some hash tables.
*/ */
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) backend_read_statsfile();
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
/* /*
* Lookup our own database entry * Lookup our own database entry
...@@ -1210,15 +1206,9 @@ pgstat_fetch_stat_dbentry(Oid dbid) ...@@ -1210,15 +1206,9 @@ pgstat_fetch_stat_dbentry(Oid dbid)
/* /*
* If not done for this transaction, read the statistics collector * If not done for this transaction, read the statistics collector
* stats file into some hash tables. Be careful with the * stats file into some hash tables.
* read_statsfile() call below!
*/ */
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) backend_read_statsfile();
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
/* /*
* Lookup the requested database * Lookup the requested database
...@@ -1250,15 +1240,9 @@ pgstat_fetch_stat_tabentry(Oid relid) ...@@ -1250,15 +1240,9 @@ pgstat_fetch_stat_tabentry(Oid relid)
/* /*
* If not done for this transaction, read the statistics collector * If not done for this transaction, read the statistics collector
* stats file into some hash tables. Be careful with the * stats file into some hash tables.
* read_statsfile() call below!
*/ */
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) backend_read_statsfile();
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
/* /*
* Lookup our database. * Lookup our database.
...@@ -1296,12 +1280,7 @@ pgstat_fetch_stat_tabentry(Oid relid) ...@@ -1296,12 +1280,7 @@ pgstat_fetch_stat_tabentry(Oid relid)
PgStat_StatBeEntry * PgStat_StatBeEntry *
pgstat_fetch_stat_beentry(int beid) pgstat_fetch_stat_beentry(int beid)
{ {
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) backend_read_statsfile();
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
if (beid < 1 || beid > pgStatNumBackends) if (beid < 1 || beid > pgStatNumBackends)
return NULL; return NULL;
...@@ -1320,12 +1299,7 @@ pgstat_fetch_stat_beentry(int beid) ...@@ -1320,12 +1299,7 @@ pgstat_fetch_stat_beentry(int beid)
int int
pgstat_fetch_stat_numbackends(void) pgstat_fetch_stat_numbackends(void)
{ {
if (!TransactionIdEquals(pgStatDBHashXact, GetCurrentTransactionId())) backend_read_statsfile();
{
pgstat_read_statsfile(&pgStatDBHash, MyDatabaseId,
&pgStatBeTable, &pgStatNumBackends);
pgStatDBHashXact = GetCurrentTransactionId();
}
return pgStatNumBackends; return pgStatNumBackends;
} }
...@@ -2759,11 +2733,32 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, ...@@ -2759,11 +2733,32 @@ pgstat_read_statsfile(HTAB **dbhash, Oid onlydb,
fclose(fpin); 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() - * pgstat_recv_bestart() -
* *
* Process a backend starup message. * Process a backend startup message.
* ---------- * ----------
*/ */
static void static void
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * 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 @@ ...@@ -45,6 +45,7 @@
#include "storage/bufpage.h" #include "storage/bufpage.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/smgr.h" #include "storage/smgr.h"
#include "utils/memutils.h"
#include "utils/relcache.h" #include "utils/relcache.h"
#include "pgstat.h" #include "pgstat.h"
...@@ -64,9 +65,13 @@ long NDirectFileRead; /* some I/O's are direct file access. ...@@ -64,9 +65,13 @@ long NDirectFileRead; /* some I/O's are direct file access.
* bypass bufmgr */ * bypass bufmgr */
long NDirectFileWrite; /* e.g., I/O in psort and hashjoin. */ 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 PinBuffer(BufferDesc *buf);
static void UnpinBuffer(BufferDesc *buf); static void UnpinBuffer(BufferDesc *buf);
static void BufferFixLeak(Buffer bufnum, int32 shouldBe, bool emitWarning);
static void WaitIO(BufferDesc *buf); static void WaitIO(BufferDesc *buf);
static void StartBufferIO(BufferDesc *buf, bool forInput); static void StartBufferIO(BufferDesc *buf, bool forInput);
static void TerminateBufferIO(BufferDesc *buf, int err_flag); static void TerminateBufferIO(BufferDesc *buf, int err_flag);
...@@ -826,28 +831,102 @@ AtEOXact_Buffers(bool isCommit) ...@@ -826,28 +831,102 @@ AtEOXact_Buffers(bool isCommit)
for (i = 0; i < NBuffers; i++) for (i = 0; i < NBuffers; i++)
{ {
if (PrivateRefCount[i] != 0) if (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++)
{ {
BufferDesc *buf = &(BufferDescriptors[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 (isCommit) if (emitWarning)
elog(WARNING, elog(WARNING,
"buffer refcount leak: [%03d] " "buffer refcount leak: [%03d] (rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d, should be=%d)",
"(rel=%u/%u/%u, blockNum=%u, flags=0x%x, refcount=%u %d)", bufnum,
i,
buf->tag.rnode.spcNode, buf->tag.rnode.dbNode, buf->tag.rnode.spcNode, buf->tag.rnode.dbNode,
buf->tag.rnode.relNode, buf->tag.rnode.relNode,
buf->tag.blockNum, buf->flags, buf->tag.blockNum, buf->flags,
buf->refcount, PrivateRefCount[i]); buf->refcount, PrivateRefCount[bufnum], shouldBe);
PrivateRefCount[i] = 1; /* make sure we release shared pin */ /* 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); LWLockAcquire(BufMgrLock, LW_EXCLUSIVE);
UnpinBuffer(buf); UnpinBuffer(buf);
LWLockRelease(BufMgrLock); LWLockRelease(BufMgrLock);
Assert(PrivateRefCount[i] == 0); Assert(PrivateRefCount[bufnum] == 0);
}
} }
AtEOXact_LocalBuffers(isCommit);
} }
/* /*
......
...@@ -8,16 +8,16 @@ ...@@ -8,16 +8,16 @@
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "miscadmin.h"
#include "access/clog.h" #include "access/clog.h"
#include "access/subtrans.h"
#include "access/xlog.h" #include "access/xlog.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h" #include "postmaster/bgwriter.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "storage/freespace.h" #include "storage/freespace.h"
...@@ -70,6 +70,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, ...@@ -70,6 +70,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
size += LockShmemSize(maxBackends); size += LockShmemSize(maxBackends);
size += XLOGShmemSize(); size += XLOGShmemSize();
size += CLOGShmemSize(); size += CLOGShmemSize();
size += SUBTRANSShmemSize();
size += LWLockShmemSize(); size += LWLockShmemSize();
size += SInvalShmemSize(maxBackends); size += SInvalShmemSize(maxBackends);
size += FreeSpaceShmemSize(); size += FreeSpaceShmemSize();
...@@ -133,6 +134,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, ...@@ -133,6 +134,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
*/ */
XLOGShmemInit(); XLOGShmemInit();
CLOGShmemInit(); CLOGShmemInit();
SUBTRANSShmemInit();
InitBufferPool(); InitBufferPool();
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.64 2004/06/02 21:29:28 momjian Exp $ * $PostgreSQL: pgsql/src/backend/storage/ipc/sinval.c,v 1.65 2004/07/01 00:50:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#include <signal.h> #include <signal.h>
#include "access/subtrans.h"
#include "access/transam.h"
#include "commands/async.h" #include "commands/async.h"
#include "storage/ipc.h" #include "storage/ipc.h"
#include "storage/proc.h" #include "storage/proc.h"
...@@ -428,6 +430,22 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself) ...@@ -428,6 +430,22 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
/* /*
* TransactionIdIsInProgress -- is given transaction running by some backend * TransactionIdIsInProgress -- is given transaction running by some backend
*
* There are three possibilities for finding a running transaction:
*
* 1. the given Xid is a main transaction Id. We will find this out cheaply
* by looking at the PGPROC struct for each backend.
*
* 2. the given Xid is one of the cached subxact Xids in the PGPROC array.
* We can find this out cheaply too.
*
* 3. Search the SubTrans tree. This is the slowest, but sadly it has to be
* done always if the other two failed.
*
* SInvalLock has to be held while we do 1 and 2. If we save all the Xids
* while doing 1, we can release the SInvalLock while we do 3. This buys back
* some concurrency (we can't retrieve the main Xids from PGPROC again anyway,
* see GetNewTransactionId)
*/ */
bool bool
TransactionIdIsInProgress(TransactionId xid) TransactionIdIsInProgress(TransactionId xid)
...@@ -435,13 +453,17 @@ TransactionIdIsInProgress(TransactionId xid) ...@@ -435,13 +453,17 @@ TransactionIdIsInProgress(TransactionId xid)
bool result = false; bool result = false;
SISeg *segP = shmInvalBuffer; SISeg *segP = shmInvalBuffer;
ProcState *stateP = segP->procState; ProcState *stateP = segP->procState;
int index; int i;
int nxids = 0;
TransactionId *xids;
xids = (TransactionId *)palloc(sizeof(TransactionId) * segP->maxBackends);
LWLockAcquire(SInvalLock, LW_SHARED); LWLockAcquire(SInvalLock, LW_SHARED);
for (index = 0; index < segP->lastBackend; index++) for (i = 0; i < segP->lastBackend; i++)
{ {
SHMEM_OFFSET pOffset = stateP[index].procStruct; SHMEM_OFFSET pOffset = stateP[i].procStruct;
if (pOffset != INVALID_OFFSET) if (pOffset != INVALID_OFFSET)
{ {
...@@ -450,16 +472,71 @@ TransactionIdIsInProgress(TransactionId xid) ...@@ -450,16 +472,71 @@ TransactionIdIsInProgress(TransactionId xid)
/* Fetch xid just once - see GetNewTransactionId */ /* Fetch xid just once - see GetNewTransactionId */
TransactionId pxid = proc->xid; TransactionId pxid = proc->xid;
/*
* check the main Xid (step 1 above)
*/
if (TransactionIdEquals(pxid, xid))
{
result = true;
break;
}
/*
* save the main Xid for step 3.
*/
xids[nxids++] = pxid;
#ifdef NOT_USED
FIXME -- waiting to save the Xids in PGPROC ...
/*
* check the saved Xids array (step 2)
*/
for (j = 0; j < PGPROC_MAX_SAVED_XIDS; j++)
{
pxid = proc->savedxids[j];
if (!TransactionIdIsValid(pxids))
break;
if (TransactionIdEquals(pxid, xid)) if (TransactionIdEquals(pxid, xid))
{ {
result = true; result = true;
break; break;
} }
} }
#endif
if (result)
break;
}
} }
LWLockRelease(SInvalLock); LWLockRelease(SInvalLock);
/*
* Step 3: have to check pg_subtrans. Use the saved Xids.
*
* XXX Could save the cached Xids too for further improvement.
*/
if (!result)
{
/* this is a potentially expensive call. */
xid = SubTransGetTopmostTransaction(xid);
Assert(TransactionIdIsValid(xid));
/*
* We don't care if it aborted, because if it did, we won't find
* it in the array.
*/
for (i = 0; i < nxids; i++)
if (TransactionIdEquals(xids[i], xid))
return true;
}
return result; return result;
} }
...@@ -596,7 +673,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable) ...@@ -596,7 +673,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
* This does open a possibility for avoiding repeated malloc/free: * This does open a possibility for avoiding repeated malloc/free:
* since MaxBackends does not change at runtime, we can simply reuse * since MaxBackends does not change at runtime, we can simply reuse
* the previous xip array if any. (This relies on the fact that all * the previous xip array if any. (This relies on the fact that all
* calls pass static SnapshotData structs.) * callers pass static SnapshotData structs.)
*/ */
if (snapshot->xip == NULL) if (snapshot->xip == NULL)
{ {
......
This diff is collapsed.
This diff is collapsed.
...@@ -15,13 +15,14 @@ ...@@ -15,13 +15,14 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * 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 "postgres.h"
#include "access/clog.h" #include "access/clog.h"
#include "access/subtrans.h"
#include "storage/lwlock.h" #include "storage/lwlock.h"
#include "storage/proc.h" #include "storage/proc.h"
#include "storage/spin.h" #include "storage/spin.h"
...@@ -111,6 +112,9 @@ NumLWLocks(void) ...@@ -111,6 +112,9 @@ NumLWLocks(void)
/* clog.c needs one per CLOG buffer + one control lock */ /* clog.c needs one per CLOG buffer + one control lock */
numLocks += NUM_CLOG_BUFFERS + 1; 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? */ /* Perhaps create a few more for use by user-defined modules? */
return numLocks; 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.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.45 2004/06/05 19:48:09 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/mmgr/mcxt.c,v 1.46 2004/07/01 00:51:29 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -45,6 +45,7 @@ MemoryContext PostmasterContext = NULL; ...@@ -45,6 +45,7 @@ MemoryContext PostmasterContext = NULL;
MemoryContext CacheMemoryContext = NULL; MemoryContext CacheMemoryContext = NULL;
MemoryContext MessageContext = NULL; MemoryContext MessageContext = NULL;
MemoryContext TopTransactionContext = NULL; MemoryContext TopTransactionContext = NULL;
MemoryContext CurTransactionContext = NULL;
/* These two are transient links to contexts owned by other objects: */ /* These two are transient links to contexts owned by other objects: */
MemoryContext QueryContext = NULL; MemoryContext QueryContext = NULL;
......
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