Commit d0a89683 authored by Tom Lane's avatar Tom Lane

Two-phase commit. Original patch by Heikki Linnakangas, with additional

hacking by Alvaro Herrera and Tom Lane.
parent 54955759
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.103 2005/06/13 23:14:47 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.104 2005/06/17 22:32:41 tgl Exp $
-->
<chapter id="catalogs">
......@@ -3932,6 +3932,11 @@
<entry>currently held locks</entry>
</row>
<row>
<entry><link linkend="view-pg-prepared-xacts"><structname>pg_prepared_xacts</structname></link></entry>
<entry>currently prepared transactions</entry>
</row>
<row>
<entry><link linkend="view-pg-rules"><structname>pg_rules</structname></link></entry>
<entry>rules</entry>
......@@ -4167,8 +4172,10 @@
<entry><structfield>pid</structfield></entry>
<entry><type>integer</type></entry>
<entry></entry>
<entry>process ID of the server process holding or awaiting this
lock</entry>
<entry>
Process ID of the server process holding or awaiting this
lock. Zero if the lock is held by a prepared transaction.
</entry>
</row>
<row>
<entry><structfield>mode</structfield></entry>
......@@ -4250,6 +4257,87 @@
</sect1>
<sect1 id="view-pg-prepared-xacts">
<title><structname>pg_prepared_xacts</structname></title>
<indexterm zone="view-pg-prepared-xacts">
<primary>pg_prepared_xacts</primary>
</indexterm>
<para>
The view <structname>pg_prepared_xacts</structname> displays
information about transactions that are currently prepared for two-phase
commit (see <xref linkend="sql-prepare-transaction"
endterm="sql-prepare-transaction-title"> for details).
</para>
<para>
<structname>pg_prepared_xacts</structname> contains one row per prepared
transaction. An entry is removed when the transaction is committed or
rolled back.
</para>
<table>
<title><structname>pg_prepared_xacts</> Columns</title>
<tgroup cols=4>
<thead>
<row>
<entry>Name</entry>
<entry>Type</entry>
<entry>References</entry>
<entry>Description</entry>
</row>
</thead>
<tbody>
<row>
<entry><structfield>transaction</structfield></entry>
<entry><type>xid</type></entry>
<entry></entry>
<entry>
Numeric transaction identifier of the prepared transaction
</entry>
</row>
<row>
<entry><structfield>gid</structfield></entry>
<entry><type>text</type></entry>
<entry></entry>
<entry>
Global transaction identifier that was assigned to the transaction
</entry>
</row>
<row>
<entry><structfield>owner</structfield></entry>
<entry><type>name</type></entry>
<entry><literal><link linkend="catalog-pg-shadow"><structname>pg_shadow</structname></link>.usename</literal></entry>
<entry>
Name of the user that executed the transaction
</entry>
</row>
<row>
<entry><structfield>database</structfield></entry>
<entry><type>name</type></entry>
<entry><literal><link linkend="catalog-pg-database"><structname>pg_database</structname></link>.datname</literal></entry>
<entry>
Name of the database in which the transaction was executed
</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
When the <structname>pg_prepared_xacts</structname> view is accessed, the
internal transaction manager data structures are momentarily locked, and
a copy is made for the view to display. This ensures that the
view produces a consistent set of results, while not blocking
normal operations longer than necessary. Nonetheless
there could be some impact on database performance if this view is
read often.
</para>
</sect1>
<sect1 id="view-pg-rules">
<title><structname>pg_rules</structname></title>
......
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.62 2004/08/21 16:16:04 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/allfiles.sgml,v 1.63 2005/06/17 22:32:42 tgl Exp $
PostgreSQL documentation
Complete list of usable sgml source files in this directory.
-->
......@@ -30,6 +30,7 @@ Complete list of usable sgml source files in this directory.
<!entity cluster system "cluster.sgml">
<!entity commentOn system "comment.sgml">
<!entity commit system "commit.sgml">
<!entity commitPrepared system "commit_prepared.sgml">
<!entity copyTable system "copy.sgml">
<!entity createAggregate system "create_aggregate.sgml">
<!entity createCast system "create_cast.sgml">
......@@ -88,11 +89,13 @@ Complete list of usable sgml source files in this directory.
<!entity move system "move.sgml">
<!entity notify system "notify.sgml">
<!entity prepare system "prepare.sgml">
<!entity prepareTransaction system "prepare_transaction.sgml">
<!entity reindex system "reindex.sgml">
<!entity releaseSavepoint system "release_savepoint.sgml">
<!entity reset system "reset.sgml">
<!entity revoke system "revoke.sgml">
<!entity rollback system "rollback.sgml">
<!entity rollbackPrepared system "rollback_prepared.sgml">
<!entity rollbackTo system "rollback_to.sgml">
<!entity savepoint system "savepoint.sgml">
<!entity select system "select.sgml">
......
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/commit_prepared.sgml,v 1.1 2005/06/17 22:32:42 tgl Exp $
PostgreSQL documentation
-->
<refentry id="SQL-COMMIT-PREPARED">
<refmeta>
<refentrytitle id="sql-commit-prepared-title">COMMIT PREPARED</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>COMMIT PREPARED</refname>
<refpurpose>commit a transaction that was earlier prepared for two-phase commit</refpurpose>
</refnamediv>
<indexterm zone="sql-commit-prepared">
<primary>COMMIT PREPARED</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
COMMIT PREPARED <replaceable class="PARAMETER">transaction_id</replaceable>
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>COMMIT PREPARED</command> commits a transaction that is in
prepared state.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">transaction_id</replaceable></term>
<listitem>
<para>
The transaction identifier of the transaction that is to be
committed.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para>
To commit a prepared transaction, you must be either the same user that
executed the transaction originally, or a superuser. But you do not
have to be in the same session that executed the transaction.
</para>
<para>
This command cannot be executed inside a transaction block. The prepared
transaction is committed immediately.
</para>
<para>
All currently available prepared transactions are listed in the
<structname>pg_prepared_xacts</> system view.
</para>
</refsect1>
<refsect1 id="sql-commit-prepared-examples">
<title id="sql-commit-prepared-examples-title">Examples</title>
<para>
Commit the transaction identified by the transaction
identifier <literal>foobar</>:
<programlisting>
COMMIT PREPARED 'foobar';
</programlisting>
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-prepare-transaction" endterm="sql-prepare-transaction-title"></member>
<member><xref linkend="sql-rollback-prepared" endterm="sql-rollback-prepared-title"></member>
</simplelist>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/prepare_transaction.sgml,v 1.1 2005/06/17 22:32:42 tgl Exp $
PostgreSQL documentation
-->
<refentry id="SQL-PREPARE-TRANSACTION">
<refmeta>
<refentrytitle id="sql-prepare-transaction-title">PREPARE TRANSACTION</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>PREPARE TRANSACTION</refname>
<refpurpose>prepare the current transaction for two-phase commit</refpurpose>
</refnamediv>
<indexterm zone="sql-prepare-transaction">
<primary>PREPARE TRANSACTION</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
PREPARE TRANSACTION <replaceable class="PARAMETER">transaction_id</replaceable>
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>PREPARE TRANSACTION</command> prepares the current transaction
for two-phase commit. After this command, the transaction is no longer
associated with the current session; instead, its state is fully stored on
disk, and there is a very high probability that it can be committed
successfully, even if a database crash occurs before the commit is
requested.
</para>
<para>
Once prepared, a transaction can later be committed or rolled
back with <command>COMMIT PREPARED</command> or
<command>ROLLBACK PREPARED</command>, respectively. Those commands
can be issued from any session, not only the one that executed the
original transaction.
</para>
<para>
From the point of view of the issuing session, <command>PREPARE
TRANSACTION</command> is not unlike a <command>ROLLBACK</> command:
after executing it, there is no active current transaction, and the
effects of the prepared transaction are no longer visible. (The effects
will become visible again if the transaction is committed.)
</para>
<para>
If the <command>PREPARE TRANSACTION</command> command fails for any
reason, it becomes a <command>ROLLBACK</>: the current transaction
is canceled.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">transaction_id</replaceable></term>
<listitem>
<para>
An arbitrary identifier that later identifies this transaction for
<command>COMMIT PREPARED</> or <command>ROLLBACK PREPARED</>.
The identifier must be written as a string literal, and must be
less than 200 bytes long. It must not be the same as the identifier
used for any currently prepared transaction.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para>
This command must be used inside a transaction block. Use
<command>BEGIN</command> to start one.
</para>
<para>
It is not currently allowed to <command>PREPARE</> a transaction that
has executed any operations involving temporary tables nor
created any cursors <literal>WITH HOLD</>. Those features are too tightly
tied to the current session to be useful in a transaction to be prepared.
</para>
<para>
If the transaction modified any run-time parameters with <command>SET</>,
those effects persist after <command>PREPARE TRANSACTION</>, and will not
be affected by any later <command>COMMIT PREPARED</command> or
<command>ROLLBACK PREPARED</command>. Thus, in this one respect
<command>PREPARE TRANSACTION</> acts more like <command>COMMIT</> than
<command>ROLLBACK</>.
</para>
<para>
All currently available prepared transactions are listed in the
<structname>pg_prepared_xacts</> system view.
</para>
<para>
From a performance standpoint, it is unwise to leave transactions in
the prepared state for a long time: this will for instance interfere with
the ability of <command>VACUUM</> to reclaim storage. Keep in mind also
that the transaction continues to hold whatever locks it held.
The intended
usage of the feature is that a prepared transaction will normally be
committed or rolled back as soon as an external transaction manager
has verified that other databases are also prepared to commit.
</para>
</refsect1>
<refsect1 id="sql-prepare-transaction-examples">
<title id="sql-prepare-transaction-examples-title">Examples</title>
<para>
Prepare the current transaction for two-phase commit, using
<literal>foobar</> as the transaction identifier:
<programlisting>
PREPARE TRANSACTION 'foobar';
</programlisting>
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-commit-prepared" endterm="sql-commit-prepared-title"></member>
<member><xref linkend="sql-rollback-prepared" endterm="sql-rollback-prepared-title"></member>
</simplelist>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/rollback_prepared.sgml,v 1.1 2005/06/17 22:32:42 tgl Exp $
PostgreSQL documentation
-->
<refentry id="SQL-ROLLBACK-PREPARED">
<refmeta>
<refentrytitle id="sql-rollback-prepared-title">ROLLBACK PREPARED</refentrytitle>
<refmiscinfo>SQL - Language Statements</refmiscinfo>
</refmeta>
<refnamediv>
<refname>ROLLBACK PREPARED</refname>
<refpurpose>cancel a transaction that was earlier prepared for two-phase commit</refpurpose>
</refnamediv>
<indexterm zone="sql-rollback-prepared">
<primary>ROLLBACK PREPARED</primary>
</indexterm>
<refsynopsisdiv>
<synopsis>
ROLLBACK PREPARED <replaceable class="PARAMETER">transaction_id</replaceable>
</synopsis>
</refsynopsisdiv>
<refsect1>
<title>Description</title>
<para>
<command>ROLLBACK PREPARED</command> rolls back a transaction that is in
prepared state.
</para>
</refsect1>
<refsect1>
<title>Parameters</title>
<variablelist>
<varlistentry>
<term><replaceable class="PARAMETER">transaction_id</replaceable></term>
<listitem>
<para>
The transaction identifier of the transaction that is to be
rolled back.
</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>
<refsect1>
<title>Notes</title>
<para>
To roll back a prepared transaction, you must be either the same user that
executed the transaction originally, or a superuser. But you do not
have to be in the same session that executed the transaction.
</para>
<para>
This command cannot be executed inside a transaction block. The prepared
transaction is rolled back immediately.
</para>
<para>
All currently available prepared transactions are listed in the
<structname>pg_prepared_xacts</> system view.
</para>
</refsect1>
<refsect1 id="sql-rollback-prepared-examples">
<title id="sql-rollback-prepared-examples-title">Examples</title>
<para>
Roll back the transaction identified by the transaction
identifier <literal>foobar</>:
<programlisting>
ROLLBACK PREPARED 'foobar';
</programlisting>
</para>
</refsect1>
<refsect1>
<title>See Also</title>
<simplelist type="inline">
<member><xref linkend="sql-prepare-transaction" endterm="sql-prepare-transaction-title"></member>
<member><xref linkend="sql-commit-prepared" endterm="sql-commit-prepared-title"></member>
</simplelist>
</refsect1>
</refentry>
<!-- Keep this comment at the end of the file
Local variables:
mode: sgml
sgml-omittag:nil
sgml-shorttag:t
sgml-minimize-attributes:nil
sgml-always-quote-attributes:t
sgml-indent-step:1
sgml-indent-data:t
sgml-parent-document:nil
sgml-default-dtd-file:"../reference.ced"
sgml-exposed-tags:nil
sgml-local-catalogs:"/usr/lib/sgml/catalog"
sgml-local-ecat-files:nil
End:
-->
<!-- reference.sgml
$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.52 2004/08/21 16:16:03 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/reference.sgml,v 1.53 2005/06/17 22:32:42 tgl Exp $
PostgreSQL Reference Manual
-->
......@@ -62,6 +62,7 @@ PostgreSQL Reference Manual
&cluster;
&commentOn;
&commit;
&commitPrepared;
&copyTable;
&createAggregate;
&createCast;
......@@ -120,11 +121,13 @@ PostgreSQL Reference Manual
&move;
&notify;
&prepare;
&prepareTransaction;
&reindex;
&releaseSavepoint;
&reset;
&revoke;
&rollback;
&rollbackPrepared;
&rollbackTo;
&savepoint;
&select;
......
<!--
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.328 2005/06/17 13:12:01 momjian Exp $
$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.329 2005/06/17 22:32:42 tgl Exp $
-->
<chapter Id="runtime">
......@@ -956,7 +956,7 @@ SET ENABLE_SEQSCAN TO OFF;
<para>
Sets the location of the Kerberos server key file. See
<xref linkend="kerberos-auth"> for details. This parameter
can only be set at server start.
can only be set at server start.
</para>
</listitem>
</varlistentry>
......@@ -1113,6 +1113,33 @@ SET ENABLE_SEQSCAN TO OFF;
</listitem>
</varlistentry>
<varlistentry id="guc-max-prepared-transactions" xreflabel="max_prepared_transactions">
<term><varname>max_prepared_transactions</varname> (<type>integer</type>)</term>
<indexterm>
<primary><varname>max_prepared_transactions</> configuration parameter</primary>
</indexterm>
<listitem>
<para>
Sets the maximum number of transactions that can be in the
<quote>prepared</> state simultaneously (see <xref
linkend="sql-prepare-transaction"
endterm="sql-prepare-transaction-title">).
Setting this parameter to zero disables the prepared-transaction
feature.
The default is 50.
This option can only be set at server start.
</para>
<para>
Increasing this parameter may cause <productname>PostgreSQL</>
to request more <systemitem class="osname">System V</> shared
memory than your operating system's default configuration
allows. See <xref linkend="sysvipc"> for information on how to
adjust those parameters, if necessary.
</para>
</listitem>
</varlistentry>
<varlistentry id="guc-work-mem" xreflabel="work_mem">
<term><varname>work_mem</varname> (<type>integer</type>)</term>
<indexterm>
......
......@@ -4,7 +4,7 @@
# Makefile for access/transam
#
# IDENTIFICATION
# $PostgreSQL: pgsql/src/backend/access/transam/Makefile,v 1.20 2005/04/28 21:47:10 tgl Exp $
# $PostgreSQL: pgsql/src/backend/access/transam/Makefile,v 1.21 2005/06/17 22:32: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 subtrans.o multixact.o
OBJS = clog.o transam.o varsup.o xact.o xlog.o xlogutils.o rmgr.o slru.o subtrans.o multixact.o twophase.o twophase_rmgr.o
all: SUBSYS.o
......
......@@ -22,7 +22,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.8 2005/05/19 21:35:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/subtrans.c,v 1.9 2005/06/17 22:32:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -222,22 +222,33 @@ ZeroSUBTRANSPage(int pageno)
/*
* This must be called ONCE during postmaster or standalone-backend startup,
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
*
* oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
* if there are none.
*/
void
StartupSUBTRANS(void)
StartupSUBTRANS(TransactionId oldestActiveXID)
{
int startPage;
int endPage;
/*
* Since we don't expect pg_subtrans to be valid across crashes, we
* initialize the currently-active page to zeroes during startup.
* initialize the currently-active page(s) to zeroes during startup.
* Whenever we advance into a new page, ExtendSUBTRANS will likewise
* zero the new page without regard to whatever was previously on
* disk.
*/
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
startPage = TransactionIdToPage(ShmemVariableCache->nextXid);
startPage = TransactionIdToPage(oldestActiveXID);
endPage = TransactionIdToPage(ShmemVariableCache->nextXid);
while (startPage != endPage)
{
(void) ZeroSUBTRANSPage(startPage);
startPage++;
}
(void) ZeroSUBTRANSPage(startPage);
LWLockRelease(SubtransControlLock);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.64 2005/02/20 21:46:48 tgl Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/transam.c,v 1.65 2005/06/17 22:32:42 tgl Exp $
*
* NOTES
* This file contains the high level access-method interface to the
......@@ -173,6 +173,14 @@ TransactionIdDidCommit(TransactionId transactionId)
* recursively. However, if it's older than TransactionXmin, we can't
* look at pg_subtrans; instead assume that the parent crashed without
* cleaning up its children.
*
* Originally we Assert'ed that the result of SubTransGetParent was
* not zero. However with the introduction of prepared transactions,
* there can be a window just after database startup where we do not
* have complete knowledge in pg_subtrans of the transactions after
* TransactionXmin. StartupSUBTRANS() has ensured that any missing
* information will be zeroed. Since this case should not happen under
* normal conditions, it seems reasonable to emit a WARNING for it.
*/
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
{
......@@ -181,7 +189,12 @@ TransactionIdDidCommit(TransactionId transactionId)
if (TransactionIdPrecedes(transactionId, TransactionXmin))
return false;
parentXid = SubTransGetParent(transactionId);
Assert(TransactionIdIsValid(parentXid));
if (!TransactionIdIsValid(parentXid))
{
elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
transactionId);
return false;
}
return TransactionIdDidCommit(parentXid);
}
......@@ -224,7 +237,13 @@ TransactionIdDidAbort(TransactionId transactionId)
if (TransactionIdPrecedes(transactionId, TransactionXmin))
return true;
parentXid = SubTransGetParent(transactionId);
Assert(TransactionIdIsValid(parentXid));
if (!TransactionIdIsValid(parentXid))
{
/* see notes in TransactionIdDidCommit */
elog(WARNING, "no pg_subtrans entry for subcommitted XID %u",
transactionId);
return true;
}
return TransactionIdDidAbort(parentXid);
}
......
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* twophase_rmgr.c
* Two-phase-commit resource managers tables
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/access/transam/twophase_rmgr.c,v 1.1 2005/06/17 22:32:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/twophase_rmgr.h"
#include "commands/async.h"
#include "storage/lock.h"
#include "utils/flatfiles.h"
#include "utils/inval.h"
const TwoPhaseCallback twophase_recover_callbacks[TWOPHASE_RM_MAX_ID + 1] =
{
NULL, /* END ID */
lock_twophase_recover, /* Lock */
NULL, /* Inval */
NULL, /* flat file update */
NULL /* notify/listen */
};
const TwoPhaseCallback twophase_postcommit_callbacks[TWOPHASE_RM_MAX_ID + 1] =
{
NULL, /* END ID */
lock_twophase_postcommit, /* Lock */
inval_twophase_postcommit, /* Inval */
flatfile_twophase_postcommit, /* flat file update */
notify_twophase_postcommit /* notify/listen */
};
const TwoPhaseCallback twophase_postabort_callbacks[TWOPHASE_RM_MAX_ID + 1] =
{
NULL, /* END ID */
lock_twophase_postabort, /* Lock */
NULL, /* Inval */
NULL, /* flat file update */
NULL /* notify/listen */
};
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.200 2005/06/15 01:36:08 momjian Exp $
* $PostgreSQL: pgsql/src/backend/access/transam/xlog.c,v 1.201 2005/06/17 22:32:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -25,6 +25,7 @@
#include "access/clog.h"
#include "access/multixact.h"
#include "access/subtrans.h"
#include "access/twophase.h"
#include "access/xact.h"
#include "access/xlog.h"
#include "access/xlog_internal.h"
......@@ -814,18 +815,6 @@ begin:;
/* Compute record's XLOG location */
INSERT_RECPTR(RecPtr, Insert, curridx);
/* If first XLOG record of transaction, save it in PGPROC array */
if (MyLastRecPtr.xrecoff == 0 && !no_tran)
{
/*
* We do not acquire ProcArrayLock here because of possible deadlock.
* Anyone who wants to inspect other procs' logRec must acquire
* WALInsertLock, instead. A better solution would be a per-PROC
* spinlock, but no time for that before 7.2 --- tgl 12/19/01.
*/
MyProc->logRec = RecPtr;
}
#ifdef WAL_DEBUG
if (XLOG_DEBUG)
{
......@@ -3827,6 +3816,7 @@ BootStrapXLOG(void)
BootStrapCLOG();
BootStrapSUBTRANS();
BootStrapMultiXact();
free(buffer);
}
......@@ -4268,6 +4258,7 @@ StartupXLOG(void)
uint32 endLogSeg;
XLogRecord *record;
uint32 freespace;
TransactionId oldestActiveXID;
CritSectionCount++;
......@@ -4678,33 +4669,8 @@ StartupXLOG(void)
XLogCtl->Write.curridx = NextBufIdx(0);
}
#ifdef NOT_USED
/* UNDO */
if (InRecovery)
{
RecPtr = ReadRecPtr;
if (XLByteLT(checkPoint.undo, RecPtr))
{
ereport(LOG,
(errmsg("undo starts at %X/%X",
RecPtr.xlogid, RecPtr.xrecoff)));
do
{
record = ReadRecord(&RecPtr, PANIC);
if (TransactionIdIsValid(record->xl_xid) &&
!TransactionIdDidCommit(record->xl_xid))
RmgrTable[record->xl_rmid].rm_undo(EndRecPtr, record);
RecPtr = record->xl_prev;
} while (XLByteLE(checkPoint.undo, RecPtr));
ereport(LOG,
(errmsg("undo done at %X/%X",
ReadRecPtr.xlogid, ReadRecPtr.xrecoff)));
}
else
ereport(LOG,
(errmsg("undo is not required")));
}
#endif
/* Pre-scan prepared transactions to find out the range of XIDs present */
oldestActiveXID = PrescanPreparedTransactions();
if (InRecovery)
{
......@@ -4767,9 +4733,12 @@ StartupXLOG(void)
/* Start up the commit log and related stuff, too */
StartupCLOG();
StartupSUBTRANS();
StartupSUBTRANS(oldestActiveXID);
StartupMultiXact();
/* Reload shared-memory state for prepared transactions */
RecoverPreparedTransactions();
ereport(LOG,
(errmsg("database system is ready")));
CritSectionCount--;
......@@ -5095,31 +5064,6 @@ CreateCheckPoint(bool shutdown, bool force)
SpinLockRelease_NoHoldoff(&xlogctl->info_lck);
}
/*
* Get UNDO record ptr - this is oldest of PGPROC->logRec values. We
* do this while holding insert lock to ensure that we won't miss any
* about-to-commit transactions (UNDO must include all xacts that have
* commits after REDO point).
*
* XXX temporarily ifdef'd out to avoid three-way deadlock condition:
* GetUndoRecPtr needs to grab ProcArrayLock to ensure that it is looking
* at a stable set of proc records, but grabbing ProcArrayLock while
* holding WALInsertLock is no good. GetNewTransactionId may cause a
* WAL record to be written while holding XidGenLock, and
* GetSnapshotData needs to get XidGenLock while holding ProcArrayLock,
* so there's a risk of deadlock. Need to find a better solution. See
* pgsql-hackers discussion of 17-Dec-01.
*
* XXX actually, the whole UNDO code is dead code and unlikely to ever be
* revived, so the lack of a good solution here is not troubling.
*/
#ifdef NOT_USED
checkPoint.undo = GetUndoRecPtr();
if (shutdown && checkPoint.undo.xrecoff != 0)
elog(PANIC, "active transaction while database system is shutting down");
#endif
/*
* Now we can release insert lock and checkpoint start lock, allowing
* other xacts to proceed even while we are flushing disk buffers.
......@@ -5195,22 +5139,8 @@ CreateCheckPoint(bool shutdown, bool force)
/*
* Select point at which we can truncate the log, which we base on the
* prior checkpoint's earliest info.
*
* With UNDO support: oldest item is redo or undo, whichever is older;
* but watch out for case that undo = 0.
*
* Without UNDO support: just use the redo pointer. This allows xlog
* space to be freed much faster when there are long-running
* transactions.
*/
#ifdef NOT_USED
if (ControlFile->checkPointCopy.undo.xrecoff != 0 &&
XLByteLT(ControlFile->checkPointCopy.undo,
ControlFile->checkPointCopy.redo))
XLByteToSeg(ControlFile->checkPointCopy.undo, _logId, _logSeg);
else
#endif
XLByteToSeg(ControlFile->checkPointCopy.redo, _logId, _logSeg);
XLByteToSeg(ControlFile->checkPointCopy.redo, _logId, _logSeg);
/*
* Update the control file.
......
......@@ -3,7 +3,7 @@
*
* Copyright (c) 1996-2005, PostgreSQL Global Development Group
*
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.13 2005/05/17 21:46:09 tgl Exp $
* $PostgreSQL: pgsql/src/backend/catalog/system_views.sql,v 1.14 2005/06/17 22:32:43 tgl Exp $
*/
CREATE VIEW pg_user AS
......@@ -102,6 +102,39 @@ CREATE VIEW pg_stats AS
REVOKE ALL on pg_statistic FROM public;
CREATE VIEW pg_locks AS
SELECT *
FROM pg_lock_status() AS L
(locktype text, database oid, relation oid, page int4, tuple int2,
transaction xid, classid oid, objid oid, objsubid int2,
pid int4, mode text, granted boolean);
CREATE VIEW pg_prepared_xacts AS
SELECT P.transaction, P.gid, U.usename AS owner, D.datname AS database
FROM pg_prepared_xact() AS P
(transaction xid, gid text, ownerid int4, dbid oid)
LEFT JOIN pg_database D ON P.dbid = D.oid
LEFT JOIN pg_shadow U ON P.ownerid = U.usesysid;
CREATE VIEW pg_settings AS
SELECT *
FROM pg_show_all_settings() AS A
(name text, setting text, category text, short_desc text, extra_desc text,
context text, vartype text, source text, min_val text, max_val text);
CREATE RULE pg_settings_u AS
ON UPDATE TO pg_settings
WHERE new.name = old.name DO
SELECT set_config(old.name, new.setting, 'f');
CREATE RULE pg_settings_n AS
ON UPDATE TO pg_settings
DO INSTEAD NOTHING;
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
-- Statistics views
CREATE VIEW pg_stat_all_tables AS
SELECT
C.oid AS relid,
......@@ -258,27 +291,3 @@ CREATE VIEW pg_stat_database AS
pg_stat_get_db_blocks_hit(D.oid) AS blks_read,
pg_stat_get_db_blocks_hit(D.oid) AS blks_hit
FROM pg_database D;
CREATE VIEW pg_locks AS
SELECT *
FROM pg_lock_status() AS L
(locktype text, database oid, relation oid, page int4, tuple int2,
transaction xid, classid oid, objid oid, objsubid int2,
pid int4, mode text, granted boolean);
CREATE VIEW pg_settings AS
SELECT *
FROM pg_show_all_settings() AS A
(name text, setting text, category text, short_desc text, extra_desc text,
context text, vartype text, source text, min_val text, max_val text);
CREATE RULE pg_settings_u AS
ON UPDATE TO pg_settings
WHERE new.name = old.name DO
SELECT set_config(old.name, new.setting, 'f');
CREATE RULE pg_settings_n AS
ON UPDATE TO pg_settings
DO INSTEAD NOTHING;
GRANT SELECT, UPDATE ON pg_settings TO PUBLIC;
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.122 2005/05/06 17:24:53 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/async.c,v 1.123 2005/06/17 22:32:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -78,6 +78,7 @@
#include <netinet/in.h>
#include "access/heapam.h"
#include "access/twophase_rmgr.h"
#include "catalog/pg_listener.h"
#include "commands/async.h"
#include "libpq/libpq.h"
......@@ -407,6 +408,36 @@ Async_UnlistenOnExit(int code, Datum arg)
CommitTransactionCommand();
}
/*
*--------------------------------------------------------------
* AtPrepare_Notify
*
* This is called at the prepare phase of a two-phase
* transaction. Save the state for possible commit later.
*--------------------------------------------------------------
*/
void
AtPrepare_Notify(void)
{
ListCell *p;
foreach(p, pendingNotifies)
{
const char *relname = (const char *) lfirst(p);
RegisterTwoPhaseRecord(TWOPHASE_RM_NOTIFY_ID, 0,
relname, strlen(relname) + 1);
}
/*
* We can clear the state immediately, rather than needing a separate
* PostPrepare call, because if the transaction fails we'd just
* discard the state anyway.
*/
ClearPendingNotifies();
}
/*
*--------------------------------------------------------------
* AtCommit_Notify
......@@ -1016,8 +1047,9 @@ AsyncExistsPendingNotify(const char *relname)
foreach(p, pendingNotifies)
{
/* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */
if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
const char *prelname = (const char *) lfirst(p);
if (strcmp(prelname, relname) == 0)
return true;
}
......@@ -1037,3 +1069,22 @@ ClearPendingNotifies(void)
*/
pendingNotifies = NIL;
}
/*
* 2PC processing routine for COMMIT PREPARED case.
*
* (We don't have to do anything for ROLLBACK PREPARED.)
*/
void
notify_twophase_postcommit(TransactionId xid, uint16 info,
void *recdata, uint32 len)
{
/*
* Set up to issue the NOTIFY at the end of my own
* current transaction. (XXX this has some issues if my own
* transaction later rolls back, or if there is any significant
* delay before I commit. OK for now because we disallow
* COMMIT PREPARED inside a transaction block.)
*/
Async_Notify((char *) recdata);
}
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.306 2005/06/09 04:18:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.307 2005/06/17 22:32:43 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -2085,6 +2085,7 @@ _copyTransactionStmt(TransactionStmt *from)
COPY_SCALAR_FIELD(kind);
COPY_NODE_FIELD(options);
COPY_STRING_FIELD(gid);
return newnode;
}
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.243 2005/06/09 04:18:58 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.244 2005/06/17 22:32:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1053,6 +1053,7 @@ _equalTransactionStmt(TransactionStmt *a, TransactionStmt *b)
{
COMPARE_SCALAR_FIELD(kind);
COMPARE_NODE_FIELD(options);
COMPARE_STRING_FIELD(gid);
return true;
}
......
......@@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.494 2005/06/15 19:44:05 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.495 2005/06/17 22:32:44 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
......@@ -387,7 +387,7 @@ static void doNegateFloat(Value *v);
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
PARTIAL PASSWORD PLACING POSITION
PRECISION PRESERVE PREPARE PRIMARY
PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
QUOTE
......@@ -4121,6 +4121,27 @@ TransactionStmt:
(Node *)makeString($4)));
$$ = (Node *)n;
}
| PREPARE TRANSACTION Sconst
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_PREPARE;
n->gid = $3;
$$ = (Node *)n;
}
| COMMIT PREPARED Sconst
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_COMMIT_PREPARED;
n->gid = $3;
$$ = (Node *)n;
}
| ROLLBACK PREPARED Sconst
{
TransactionStmt *n = makeNode(TransactionStmt);
n->kind = TRANS_STMT_ROLLBACK_PREPARED;
n->gid = $3;
$$ = (Node *)n;
}
;
opt_transaction: WORK {}
......@@ -6334,19 +6355,18 @@ a_expr: c_expr { $$ = $1; }
{
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "!=", $1, (Node *) $6);
}
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
| a_expr BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
{
$$ = (Node *) makeA_Expr(AEXPR_AND, NIL,
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4),
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6));
}
| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
| a_expr NOT BETWEEN opt_asymmetric b_expr AND b_expr %prec BETWEEN
{
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5),
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7));
}
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
{
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
......@@ -6367,8 +6387,6 @@ a_expr: c_expr { $$ = $1; }
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $7),
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5)));
}
| a_expr IN_P in_expr
{
/* in_expr returns a SubLink or a list of a_exprs */
......@@ -6467,11 +6485,6 @@ a_expr: c_expr { $$ = $1; }
}
;
opt_asymmetric: ASYMMETRIC {}
| /*EMPTY*/ {}
;
/*
* Restricted expressions
*
......@@ -7401,6 +7414,10 @@ opt_indirection:
| opt_indirection indirection_el { $$ = lappend($1, $2); }
;
opt_asymmetric: ASYMMETRIC
| /*EMPTY*/
;
/*****************************************************************************
*
......@@ -7855,6 +7872,7 @@ unreserved_keyword:
| PARTIAL
| PASSWORD
| PREPARE
| PREPARED
| PRESERVE
| PRIOR
| PRIVILEGES
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.156 2005/06/14 23:47:39 momjian Exp $
* $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.157 2005/06/17 22:32:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -243,6 +243,7 @@ static const ScanKeyword ScanKeywords[] = {
{"position", POSITION},
{"precision", PRECISION},
{"prepare", PREPARE},
{"prepared", PREPARED},
{"preserve", PRESERVE},
{"primary", PRIMARY},
{"prior", PRIOR},
......
......@@ -37,7 +37,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.453 2005/06/14 21:04:39 momjian Exp $
* $PostgreSQL: pgsql/src/backend/postmaster/postmaster.c,v 1.454 2005/06/17 22:32:44 tgl Exp $
*
* NOTES
*
......@@ -252,7 +252,7 @@ static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode,
static void pmdaemonize(void);
static Port *ConnCreate(int serverFd);
static void ConnFree(Port *port);
static void reset_shared(unsigned short port);
static void reset_shared(int port);
static void SIGHUP_handler(SIGNAL_ARGS);
static void pmdie(SIGNAL_ARGS);
static void reaper(SIGNAL_ARGS);
......@@ -1783,7 +1783,7 @@ ClosePostmasterPorts(bool am_syslogger)
* reset_shared -- reset shared memory and semaphores
*/
static void
reset_shared(unsigned short port)
reset_shared(int port)
{
/*
* Create or re-create shared memory and semaphores.
......@@ -1793,7 +1793,7 @@ reset_shared(unsigned short port)
* used to determine IPC keys. This helps ensure that we will clean
* up dead IPC objects if the postmaster crashes and is restarted.
*/
CreateSharedMemoryAndSemaphores(false, MaxBackends, port);
CreateSharedMemoryAndSemaphores(false, port);
}
......@@ -3182,7 +3182,7 @@ SubPostmasterMain(int argc, char *argv[])
/* BackendRun will close sockets */
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
CreateSharedMemoryAndSemaphores(false, 0);
#ifdef USE_SSL
/*
......@@ -3203,7 +3203,7 @@ SubPostmasterMain(int argc, char *argv[])
ClosePostmasterPorts(false);
/* Attach process to shared data structures */
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
CreateSharedMemoryAndSemaphores(false, 0);
BootstrapMain(argc - 2, argv + 2);
proc_exit(0);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.76 2005/05/19 21:35:46 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/ipci.c,v 1.77 2005/06/17 22:32:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -17,6 +17,7 @@
#include "access/clog.h"
#include "access/multixact.h"
#include "access/subtrans.h"
#include "access/twophase.h"
#include "access/xlog.h"
#include "miscadmin.h"
#include "postmaster/bgwriter.h"
......@@ -54,9 +55,7 @@
* memory. This is true for a standalone backend, false for a postmaster.
*/
void
CreateSharedMemoryAndSemaphores(bool makePrivate,
int maxBackends,
int port)
CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
{
PGShmemHeader *seghdr = NULL;
......@@ -72,15 +71,16 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
*/
size = hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt));
size += BufferShmemSize();
size += LockShmemSize(maxBackends);
size += ProcGlobalShmemSize(maxBackends);
size += LockShmemSize();
size += ProcGlobalShmemSize();
size += XLOGShmemSize();
size += CLOGShmemSize();
size += SUBTRANSShmemSize();
size += TwoPhaseShmemSize();
size += MultiXactShmemSize();
size += LWLockShmemSize();
size += ProcArrayShmemSize(maxBackends);
size += SInvalShmemSize(maxBackends);
size += ProcArrayShmemSize();
size += SInvalShmemSize(MaxBackends);
size += FreeSpaceShmemSize();
size += BgWriterShmemSize();
#ifdef EXEC_BACKEND
......@@ -100,7 +100,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
/*
* Create semaphores
*/
numSemas = ProcGlobalSemas(maxBackends);
numSemas = ProcGlobalSemas();
numSemas += SpinlockSemas();
PGReserveSemaphores(numSemas, port);
}
......@@ -144,6 +144,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
XLOGShmemInit();
CLOGShmemInit();
SUBTRANSShmemInit();
TwoPhaseShmemInit();
MultiXactShmemInit();
InitBufferPool();
......@@ -151,18 +152,18 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
* Set up lock manager
*/
InitLocks();
InitLockTable(maxBackends);
InitLockTable();
/*
* Set up process table
*/
InitProcGlobal(maxBackends);
CreateSharedProcArray(maxBackends);
InitProcGlobal();
CreateSharedProcArray();
/*
* Set up shared-inval messaging
*/
CreateSharedInvalidationState(maxBackends);
CreateSharedInvalidationState(MaxBackends);
/*
* Set up free-space map
......
......@@ -11,6 +11,11 @@
* Because of various subtle race conditions it is critical that a backend
* hold the correct locks while setting or clearing its MyProc->xid field.
* See notes in GetSnapshotData.
*
* The process array now also includes PGPROC structures representing
* prepared transactions. The xid and subxids fields of these are valid,
* as is the procLocks list. They can be distinguished from regular backend
* PGPROCs at need by checking for pid == 0.
*
*
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
......@@ -18,13 +23,14 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.2 2005/05/19 23:57:11 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/ipc/procarray.c,v 1.3 2005/06/17 22:32:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/subtrans.h"
#include "access/twophase.h"
#include "miscadmin.h"
#include "storage/proc.h"
#include "storage/procarray.h"
......@@ -76,25 +82,23 @@ static void DisplayXidCache(void);
* Report shared-memory space needed by CreateSharedProcArray.
*/
int
ProcArrayShmemSize(int maxBackends)
ProcArrayShmemSize(void)
{
/* sizeof(ProcArrayStruct) includes the first array element */
return MAXALIGN(sizeof(ProcArrayStruct) +
(maxBackends - 1) * sizeof(PGPROC *));
return MAXALIGN(offsetof(ProcArrayStruct, procs) +
(MaxBackends + max_prepared_xacts) * sizeof(PGPROC *));
}
/*
* Initialize the shared PGPROC array during postmaster startup.
*/
void
CreateSharedProcArray(int maxBackends)
CreateSharedProcArray(void)
{
bool found;
/* Create or attach to the ProcArray shared structure */
procArray = (ProcArrayStruct *)
ShmemInitStruct("Proc Array", ProcArrayShmemSize(maxBackends),
&found);
ShmemInitStruct("Proc Array", ProcArrayShmemSize(), &found);
if (!found)
{
......@@ -102,18 +106,15 @@ CreateSharedProcArray(int maxBackends)
* We're the first - initialize.
*/
procArray->numProcs = 0;
procArray->maxProcs = maxBackends;
procArray->maxProcs = MaxBackends + max_prepared_xacts;
}
}
/*
* Add my own PGPROC (found in the global MyProc) to the shared array.
*
* This must be called during backend startup, after fully initializing
* the contents of MyProc.
* Add the specified PGPROC to the shared array.
*/
void
ProcArrayAddMyself(void)
ProcArrayAdd(PGPROC *proc)
{
ProcArrayStruct *arrayP = procArray;
......@@ -132,32 +133,32 @@ ProcArrayAddMyself(void)
errmsg("sorry, too many clients already")));
}
arrayP->procs[arrayP->numProcs] = MyProc;
arrayP->procs[arrayP->numProcs] = proc;
arrayP->numProcs++;
LWLockRelease(ProcArrayLock);
}
/*
* Remove my own PGPROC (found in the global MyProc) from the shared array.
*
* This must be called during backend shutdown.
* Remove the specified PGPROC from the shared array.
*/
void
ProcArrayRemoveMyself(void)
ProcArrayRemove(PGPROC *proc)
{
ProcArrayStruct *arrayP = procArray;
int index;
#ifdef XIDCACHE_DEBUG
DisplayXidCache();
/* dump stats at backend shutdown, but not prepared-xact end */
if (proc->pid != 0)
DisplayXidCache();
#endif
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
for (index = 0; index < arrayP->numProcs; index++)
{
if (arrayP->procs[index] == MyProc)
if (arrayP->procs[index] == proc)
{
arrayP->procs[index] = arrayP->procs[arrayP->numProcs - 1];
arrayP->numProcs--;
......@@ -169,7 +170,7 @@ ProcArrayRemoveMyself(void)
/* Ooops */
LWLockRelease(ProcArrayLock);
elog(LOG, "failed to find my own proc %p in ProcArray", MyProc);
elog(LOG, "failed to find proc %p in ProcArray", proc);
}
......@@ -329,6 +330,55 @@ result_known:
return result;
}
/*
* TransactionIdIsActive -- is xid the top-level XID of an active backend?
*
* This differs from TransactionIdIsInProgress in that it ignores prepared
* transactions. Also, we ignore subtransactions since that's not needed
* for current uses.
*/
bool
TransactionIdIsActive(TransactionId xid)
{
bool result = false;
ProcArrayStruct *arrayP = procArray;
int i;
/*
* Don't bother checking a transaction older than RecentXmin; it
* could not possibly still be running.
*/
if (TransactionIdPrecedes(xid, RecentXmin))
return false;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (i = 0; i < arrayP->numProcs; i++)
{
PGPROC *proc = arrayP->procs[i];
/* Fetch xid just once - see GetNewTransactionId */
TransactionId pxid = proc->xid;
if (!TransactionIdIsValid(pxid))
continue;
if (proc->pid == 0)
continue; /* ignore prepared transactions */
if (TransactionIdEquals(pxid, xid))
{
result = true;
break;
}
}
LWLockRelease(ProcArrayLock);
return result;
}
/*
* GetOldestXmin -- returns oldest transaction that was running
* when any current transaction was started.
......@@ -441,12 +491,12 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
TransactionIdIsValid(MyProc->xmin));
/*
* Allocating space for MaxBackends xids is usually overkill;
* Allocating space for maxProcs xids is usually overkill;
* numProcs would be sufficient. But it seems better to do the
* malloc while not holding the lock, so we can't look at numProcs.
*
* This does open a possibility for avoiding repeated malloc/free: since
* MaxBackends does not change at runtime, we can simply reuse the
* maxProcs does not change at runtime, we can simply reuse the
* previous xip array if any. (This relies on the fact that all
* callers pass static SnapshotData structs.)
*/
......@@ -456,7 +506,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
* First call for this snapshot
*/
snapshot->xip = (TransactionId *)
malloc(MaxBackends * sizeof(TransactionId));
malloc(arrayP->maxProcs * sizeof(TransactionId));
if (snapshot->xip == NULL)
ereport(ERROR,
(errcode(ERRCODE_OUT_OF_MEMORY),
......@@ -602,14 +652,21 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
/*
* BackendPidGetProc -- get a backend's PGPROC given its PID
*
* Returns NULL if not found. Note that it is up to the caller to be
* sure that the question remains meaningful for long enough for the
* answer to be used ...
*/
struct PGPROC *
PGPROC *
BackendPidGetProc(int pid)
{
PGPROC *result = NULL;
ProcArrayStruct *arrayP = procArray;
int index;
if (pid == 0) /* never match dummy PGPROCs */
return NULL;
LWLockAcquire(ProcArrayLock, LW_SHARED);
for (index = 0; index < arrayP->numProcs; index++)
......@@ -642,10 +699,8 @@ IsBackendPid(int pid)
* active transactions. This is used as a heuristic to decide if
* a pre-XLOG-flush delay is worthwhile during commit.
*
* An active transaction is something that has written at least one XLOG
* record; read-only transactions don't count. Also, do not count backends
* that are blocked waiting for locks, since they are not going to get to
* run until someone else commits.
* Do not count backends that are blocked waiting for locks, since they are
* not going to get to run until someone else commits.
*/
int
CountActiveBackends(void)
......@@ -656,7 +711,7 @@ CountActiveBackends(void)
/*
* Note: for speed, we don't acquire ProcArrayLock. This is a little bit
* bogus, but since we are only testing xrecoff for zero or nonzero,
* bogus, but since we are only testing fields for zero or nonzero,
* it should be OK. The result is only used for heuristic purposes
* anyway...
*/
......@@ -666,7 +721,9 @@ CountActiveBackends(void)
if (proc == MyProc)
continue; /* do not count myself */
if (proc->logRec.xrecoff == 0)
if (proc->pid == 0)
continue; /* do not count prepared xacts */
if (proc->xid == InvalidTransactionId)
continue; /* do not count if not in a transaction */
if (proc->waitLock != NULL)
continue; /* do not count if blocked on a lock */
......@@ -676,25 +733,6 @@ CountActiveBackends(void)
return count;
}
/*
* CountEmptyBackendSlots - count empty slots in backend process table
*
* Acquiring the lock here is almost certainly overkill, but just in
* case fetching an int is not atomic on your machine ...
*/
int
CountEmptyBackendSlots(void)
{
int count;
LWLockAcquire(ProcArrayLock, LW_SHARED);
count = procArray->maxProcs - procArray->numProcs;
LWLockRelease(ProcArrayLock);
return count;
}
#define XidCacheRemove(i) \
do { \
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.76 2005/06/14 22:15:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lmgr.c,v 1.77 2005/06/17 22:32:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -77,7 +77,7 @@ static LOCKMETHODID LockTableId = INVALID_LOCKMETHOD;
* Create the lock table described by LockConflicts
*/
void
InitLockTable(int maxBackends)
InitLockTable(void)
{
LOCKMETHODID LongTermTableId;
......@@ -91,8 +91,7 @@ InitLockTable(int maxBackends)
/* number of lock modes is lengthof()-1 because of dummy zero */
LockTableId = LockMethodTableInit("LockTable",
LockConflicts,
lengthof(LockConflicts) - 1,
maxBackends);
lengthof(LockConflicts) - 1);
if (!LockMethodIsValid(LockTableId))
elog(ERROR, "could not initialize lock table");
Assert(LockTableId == DEFAULT_LOCKMETHOD);
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.159 2005/06/14 22:15:32 tgl Exp $
* $PostgreSQL: pgsql/src/backend/storage/lmgr/proc.c,v 1.160 2005/06/17 22:32:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -92,13 +92,13 @@ static bool CheckStatementTimeout(void);
* Report shared-memory space needed by InitProcGlobal.
*/
int
ProcGlobalShmemSize(int maxBackends)
ProcGlobalShmemSize(void)
{
int size = 0;
size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
size += MAXALIGN(NUM_DUMMY_PROCS * sizeof(PGPROC)); /* DummyProcs */
size += MAXALIGN(maxBackends * sizeof(PGPROC)); /* MyProcs */
size += MAXALIGN(MaxBackends * sizeof(PGPROC)); /* MyProcs */
size += MAXALIGN(sizeof(slock_t)); /* ProcStructLock */
return size;
......@@ -108,10 +108,10 @@ ProcGlobalShmemSize(int maxBackends)
* Report number of semaphores needed by InitProcGlobal.
*/
int
ProcGlobalSemas(int maxBackends)
ProcGlobalSemas(void)
{
/* We need a sema per backend, plus one for each dummy process. */
return maxBackends + NUM_DUMMY_PROCS;
return MaxBackends + NUM_DUMMY_PROCS;
}
/*
......@@ -134,7 +134,7 @@ ProcGlobalSemas(int maxBackends)
* postmaster, not in backends.
*/
void
InitProcGlobal(int maxBackends)
InitProcGlobal(void)
{
bool foundProcGlobal,
foundDummy;
......@@ -170,13 +170,13 @@ InitProcGlobal(int maxBackends)
* Pre-create the PGPROC structures and create a semaphore for
* each.
*/
procs = (PGPROC *) ShmemAlloc(maxBackends * sizeof(PGPROC));
procs = (PGPROC *) ShmemAlloc(MaxBackends * sizeof(PGPROC));
if (!procs)
ereport(FATAL,
(errcode(ERRCODE_OUT_OF_MEMORY),
errmsg("out of shared memory")));
MemSet(procs, 0, maxBackends * sizeof(PGPROC));
for (i = 0; i < maxBackends; i++)
MemSet(procs, 0, MaxBackends * sizeof(PGPROC));
for (i = 0; i < MaxBackends; i++)
{
PGSemaphoreCreate(&(procs[i].sem));
procs[i].links.next = ProcGlobal->freeProcs;
......@@ -254,7 +254,6 @@ InitProcess(void)
MyProc->xmin = InvalidTransactionId;
MyProc->pid = MyProcPid;
MyProc->databaseId = MyDatabaseId;
MyProc->logRec.xrecoff = 0;
MyProc->lwWaiting = false;
MyProc->lwExclusive = false;
MyProc->lwWaitLink = NULL;
......@@ -265,7 +264,7 @@ InitProcess(void)
/*
* Add our PGPROC to the PGPROC array in shared memory.
*/
ProcArrayAddMyself();
ProcArrayAdd(MyProc);
/*
* Arrange to clean up at backend exit.
......@@ -332,7 +331,6 @@ InitDummyProcess(int proctype)
MyProc->xid = InvalidTransactionId;
MyProc->xmin = InvalidTransactionId;
MyProc->databaseId = MyDatabaseId;
MyProc->logRec.xrecoff = 0;
MyProc->lwWaiting = false;
MyProc->lwExclusive = false;
MyProc->lwWaitLink = NULL;
......@@ -352,6 +350,35 @@ InitDummyProcess(int proctype)
PGSemaphoreReset(&MyProc->sem);
}
/*
* Check whether there are at least N free PGPROC objects.
*
* Note: this is designed on the assumption that N will generally be small.
*/
bool
HaveNFreeProcs(int n)
{
SHMEM_OFFSET offset;
PGPROC *proc;
/* use volatile pointer to prevent code rearrangement */
volatile PROC_HDR *procglobal = ProcGlobal;
SpinLockAcquire(ProcStructLock);
offset = procglobal->freeProcs;
while (n > 0 && offset != INVALID_OFFSET)
{
proc = (PGPROC *) MAKE_PTR(offset);
offset = proc->links.next;
n--;
}
SpinLockRelease(ProcStructLock);
return (n <= 0);
}
/*
* Cancel any pending wait for lock, when aborting a transaction.
*
......@@ -478,7 +505,7 @@ ProcKill(int code, Datum arg)
#endif
/* Remove our PGPROC from the PGPROC array in shared memory */
ProcArrayRemoveMyself();
ProcArrayRemove(MyProc);
SpinLockAcquire(ProcStructLock);
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.448 2005/06/14 21:04:40 momjian Exp $
* $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.449 2005/06/17 22:32:46 tgl Exp $
*
* NOTES
* this is the "main" module of the postgres backend and
......@@ -930,6 +930,7 @@ exec_simple_query(const char *query_string)
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
......@@ -1261,6 +1262,7 @@ exec_parse_message(const char *query_string, /* string to execute */
TransactionStmt *stmt = (TransactionStmt *) parsetree;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
allowit = true;
......@@ -1751,6 +1753,7 @@ exec_execute_message(const char *portal_name, long max_rows)
is_trans_stmt = true;
if (stmt->kind == TRANS_STMT_COMMIT ||
stmt->kind == TRANS_STMT_PREPARE ||
stmt->kind == TRANS_STMT_ROLLBACK ||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
is_trans_exit = true;
......
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