Two-phase commit. Original patch by Heikki Linnakangas, with additional
hacking by Alvaro Herrera and Tom Lane.
This commit is contained in:
parent
5495575903
commit
d0a89683a3
|
@ -1,6 +1,6 @@
|
||||||
<!--
|
<!--
|
||||||
Documentation of the system catalogs, directed toward PostgreSQL developers
|
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">
|
<chapter id="catalogs">
|
||||||
|
@ -3932,6 +3932,11 @@
|
||||||
<entry>currently held locks</entry>
|
<entry>currently held locks</entry>
|
||||||
</row>
|
</row>
|
||||||
|
|
||||||
|
<row>
|
||||||
|
<entry><link linkend="view-pg-prepared-xacts"><structname>pg_prepared_xacts</structname></link></entry>
|
||||||
|
<entry>currently prepared transactions</entry>
|
||||||
|
</row>
|
||||||
|
|
||||||
<row>
|
<row>
|
||||||
<entry><link linkend="view-pg-rules"><structname>pg_rules</structname></link></entry>
|
<entry><link linkend="view-pg-rules"><structname>pg_rules</structname></link></entry>
|
||||||
<entry>rules</entry>
|
<entry>rules</entry>
|
||||||
|
@ -4167,8 +4172,10 @@
|
||||||
<entry><structfield>pid</structfield></entry>
|
<entry><structfield>pid</structfield></entry>
|
||||||
<entry><type>integer</type></entry>
|
<entry><type>integer</type></entry>
|
||||||
<entry></entry>
|
<entry></entry>
|
||||||
<entry>process ID of the server process holding or awaiting this
|
<entry>
|
||||||
lock</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>
|
||||||
<row>
|
<row>
|
||||||
<entry><structfield>mode</structfield></entry>
|
<entry><structfield>mode</structfield></entry>
|
||||||
|
@ -4250,6 +4257,87 @@
|
||||||
|
|
||||||
</sect1>
|
</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">
|
<sect1 id="view-pg-rules">
|
||||||
<title><structname>pg_rules</structname></title>
|
<title><structname>pg_rules</structname></title>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
$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
|
PostgreSQL documentation
|
||||||
Complete list of usable sgml source files in this directory.
|
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 cluster system "cluster.sgml">
|
||||||
<!entity commentOn system "comment.sgml">
|
<!entity commentOn system "comment.sgml">
|
||||||
<!entity commit system "commit.sgml">
|
<!entity commit system "commit.sgml">
|
||||||
|
<!entity commitPrepared system "commit_prepared.sgml">
|
||||||
<!entity copyTable system "copy.sgml">
|
<!entity copyTable system "copy.sgml">
|
||||||
<!entity createAggregate system "create_aggregate.sgml">
|
<!entity createAggregate system "create_aggregate.sgml">
|
||||||
<!entity createCast system "create_cast.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 move system "move.sgml">
|
||||||
<!entity notify system "notify.sgml">
|
<!entity notify system "notify.sgml">
|
||||||
<!entity prepare system "prepare.sgml">
|
<!entity prepare system "prepare.sgml">
|
||||||
|
<!entity prepareTransaction system "prepare_transaction.sgml">
|
||||||
<!entity reindex system "reindex.sgml">
|
<!entity reindex system "reindex.sgml">
|
||||||
<!entity releaseSavepoint system "release_savepoint.sgml">
|
<!entity releaseSavepoint system "release_savepoint.sgml">
|
||||||
<!entity reset system "reset.sgml">
|
<!entity reset system "reset.sgml">
|
||||||
<!entity revoke system "revoke.sgml">
|
<!entity revoke system "revoke.sgml">
|
||||||
<!entity rollback system "rollback.sgml">
|
<!entity rollback system "rollback.sgml">
|
||||||
|
<!entity rollbackPrepared system "rollback_prepared.sgml">
|
||||||
<!entity rollbackTo system "rollback_to.sgml">
|
<!entity rollbackTo system "rollback_to.sgml">
|
||||||
<!entity savepoint system "savepoint.sgml">
|
<!entity savepoint system "savepoint.sgml">
|
||||||
<!entity select system "select.sgml">
|
<!entity select system "select.sgml">
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
<!--
|
||||||
|
$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:
|
||||||
|
-->
|
|
@ -0,0 +1,160 @@
|
||||||
|
<!--
|
||||||
|
$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:
|
||||||
|
-->
|
|
@ -0,0 +1,111 @@
|
||||||
|
<!--
|
||||||
|
$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:
|
||||||
|
-->
|
|
@ -1,5 +1,5 @@
|
||||||
<!-- reference.sgml
|
<!-- 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
|
PostgreSQL Reference Manual
|
||||||
-->
|
-->
|
||||||
|
@ -62,6 +62,7 @@ PostgreSQL Reference Manual
|
||||||
&cluster;
|
&cluster;
|
||||||
&commentOn;
|
&commentOn;
|
||||||
&commit;
|
&commit;
|
||||||
|
&commitPrepared;
|
||||||
©Table;
|
©Table;
|
||||||
&createAggregate;
|
&createAggregate;
|
||||||
&createCast;
|
&createCast;
|
||||||
|
@ -120,11 +121,13 @@ PostgreSQL Reference Manual
|
||||||
&move;
|
&move;
|
||||||
¬ify;
|
¬ify;
|
||||||
&prepare;
|
&prepare;
|
||||||
|
&prepareTransaction;
|
||||||
&reindex;
|
&reindex;
|
||||||
&releaseSavepoint;
|
&releaseSavepoint;
|
||||||
&reset;
|
&reset;
|
||||||
&revoke;
|
&revoke;
|
||||||
&rollback;
|
&rollback;
|
||||||
|
&rollbackPrepared;
|
||||||
&rollbackTo;
|
&rollbackTo;
|
||||||
&savepoint;
|
&savepoint;
|
||||||
&select;
|
&select;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<!--
|
<!--
|
||||||
$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">
|
<chapter Id="runtime">
|
||||||
|
@ -956,7 +956,7 @@ SET ENABLE_SEQSCAN TO OFF;
|
||||||
<para>
|
<para>
|
||||||
Sets the location of the Kerberos server key file. See
|
Sets the location of the Kerberos server key file. See
|
||||||
<xref linkend="kerberos-auth"> for details. This parameter
|
<xref linkend="kerberos-auth"> for details. This parameter
|
||||||
can only be set at server start.
|
can only be set at server start.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
@ -1113,6 +1113,33 @@ SET ENABLE_SEQSCAN TO OFF;
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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">
|
<varlistentry id="guc-work-mem" xreflabel="work_mem">
|
||||||
<term><varname>work_mem</varname> (<type>integer</type>)</term>
|
<term><varname>work_mem</varname> (<type>integer</type>)</term>
|
||||||
<indexterm>
|
<indexterm>
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
# Makefile for access/transam
|
# Makefile for access/transam
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# 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 = ../../../..
|
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 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
|
all: SUBSYS.o
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/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,
|
* This must be called ONCE during postmaster or standalone-backend startup,
|
||||||
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
|
* after StartupXLOG has initialized ShmemVariableCache->nextXid.
|
||||||
|
*
|
||||||
|
* oldestActiveXID is the oldest XID of any prepared transaction, or nextXid
|
||||||
|
* if there are none.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
StartupSUBTRANS(void)
|
StartupSUBTRANS(TransactionId oldestActiveXID)
|
||||||
{
|
{
|
||||||
int startPage;
|
int startPage;
|
||||||
|
int endPage;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Since we don't expect pg_subtrans to be valid across crashes, we
|
* 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
|
* Whenever we advance into a new page, ExtendSUBTRANS will likewise
|
||||||
* zero the new page without regard to whatever was previously on
|
* zero the new page without regard to whatever was previously on
|
||||||
* disk.
|
* disk.
|
||||||
*/
|
*/
|
||||||
LWLockAcquire(SubtransControlLock, LW_EXCLUSIVE);
|
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);
|
(void) ZeroSUBTRANSPage(startPage);
|
||||||
|
|
||||||
LWLockRelease(SubtransControlLock);
|
LWLockRelease(SubtransControlLock);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* This file contains the high level access-method interface to the
|
* 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
|
* recursively. However, if it's older than TransactionXmin, we can't
|
||||||
* look at pg_subtrans; instead assume that the parent crashed without
|
* look at pg_subtrans; instead assume that the parent crashed without
|
||||||
* cleaning up its children.
|
* 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)
|
if (xidstatus == TRANSACTION_STATUS_SUB_COMMITTED)
|
||||||
{
|
{
|
||||||
|
@ -181,7 +189,12 @@ TransactionIdDidCommit(TransactionId transactionId)
|
||||||
if (TransactionIdPrecedes(transactionId, TransactionXmin))
|
if (TransactionIdPrecedes(transactionId, TransactionXmin))
|
||||||
return false;
|
return false;
|
||||||
parentXid = SubTransGetParent(transactionId);
|
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);
|
return TransactionIdDidCommit(parentXid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,7 +237,13 @@ TransactionIdDidAbort(TransactionId transactionId)
|
||||||
if (TransactionIdPrecedes(transactionId, TransactionXmin))
|
if (TransactionIdPrecedes(transactionId, TransactionXmin))
|
||||||
return true;
|
return true;
|
||||||
parentXid = SubTransGetParent(transactionId);
|
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);
|
return TransactionIdDidAbort(parentXid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,49 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* 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 */
|
||||||
|
};
|
|
@ -10,7 +10,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.204 2005/06/06 20:22:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/access/transam/xact.c,v 1.205 2005/06/17 22:32:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include "access/multixact.h"
|
#include "access/multixact.h"
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/heap.h"
|
#include "catalog/heap.h"
|
||||||
#include "catalog/index.h"
|
#include "catalog/index.h"
|
||||||
|
@ -68,7 +69,8 @@ typedef enum TransState
|
||||||
TRANS_START,
|
TRANS_START,
|
||||||
TRANS_INPROGRESS,
|
TRANS_INPROGRESS,
|
||||||
TRANS_COMMIT,
|
TRANS_COMMIT,
|
||||||
TRANS_ABORT
|
TRANS_ABORT,
|
||||||
|
TRANS_PREPARE
|
||||||
} TransState;
|
} TransState;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -90,6 +92,7 @@ typedef enum TBlockState
|
||||||
TBLOCK_ABORT, /* failed xact, awaiting ROLLBACK */
|
TBLOCK_ABORT, /* failed xact, awaiting ROLLBACK */
|
||||||
TBLOCK_ABORT_END, /* failed xact, ROLLBACK received */
|
TBLOCK_ABORT_END, /* failed xact, ROLLBACK received */
|
||||||
TBLOCK_ABORT_PENDING, /* live xact, ROLLBACK received */
|
TBLOCK_ABORT_PENDING, /* live xact, ROLLBACK received */
|
||||||
|
TBLOCK_PREPARE, /* live xact, PREPARE received */
|
||||||
|
|
||||||
/* subtransaction states */
|
/* subtransaction states */
|
||||||
TBLOCK_SUBBEGIN, /* starting a subtransaction */
|
TBLOCK_SUBBEGIN, /* starting a subtransaction */
|
||||||
|
@ -172,6 +175,12 @@ static CommandId currentCommandId;
|
||||||
static AbsoluteTime xactStartTime; /* integer part */
|
static AbsoluteTime xactStartTime; /* integer part */
|
||||||
static int xactStartTimeUsec; /* microsecond part */
|
static int xactStartTimeUsec; /* microsecond part */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GID to be used for preparing the current transaction. This is also
|
||||||
|
* global to a whole transaction, so we don't keep it in the state stack.
|
||||||
|
*/
|
||||||
|
static char *prepareGID;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* List of add-on start- and end-of-xact callbacks
|
* List of add-on start- and end-of-xact callbacks
|
||||||
|
@ -267,10 +276,12 @@ IsTransactionState(void)
|
||||||
return true;
|
return true;
|
||||||
case TRANS_ABORT:
|
case TRANS_ABORT:
|
||||||
return true;
|
return true;
|
||||||
|
case TRANS_PREPARE:
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shouldn't get here, but lint is not happy with this...
|
* Shouldn't get here, but lint is not happy without this...
|
||||||
*/
|
*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -660,12 +671,12 @@ void
|
||||||
RecordTransactionCommit(void)
|
RecordTransactionCommit(void)
|
||||||
{
|
{
|
||||||
int nrels;
|
int nrels;
|
||||||
RelFileNode *rptr;
|
RelFileNode *rels;
|
||||||
int nchildren;
|
int nchildren;
|
||||||
TransactionId *children;
|
TransactionId *children;
|
||||||
|
|
||||||
/* Get data needed for commit record */
|
/* Get data needed for commit record */
|
||||||
nrels = smgrGetPendingDeletes(true, &rptr);
|
nrels = smgrGetPendingDeletes(true, &rels);
|
||||||
nchildren = xactGetCommittedChildren(&children);
|
nchildren = xactGetCommittedChildren(&children);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -726,7 +737,7 @@ RecordTransactionCommit(void)
|
||||||
if (nrels > 0)
|
if (nrels > 0)
|
||||||
{
|
{
|
||||||
rdata[0].next = &(rdata[1]);
|
rdata[0].next = &(rdata[1]);
|
||||||
rdata[1].data = (char *) rptr;
|
rdata[1].data = (char *) rels;
|
||||||
rdata[1].len = nrels * sizeof(RelFileNode);
|
rdata[1].len = nrels * sizeof(RelFileNode);
|
||||||
rdata[1].buffer = InvalidBuffer;
|
rdata[1].buffer = InvalidBuffer;
|
||||||
lastrdata = 1;
|
lastrdata = 1;
|
||||||
|
@ -809,12 +820,9 @@ RecordTransactionCommit(void)
|
||||||
MyXactMadeXLogEntry = false;
|
MyXactMadeXLogEntry = false;
|
||||||
MyXactMadeTempRelUpdate = false;
|
MyXactMadeTempRelUpdate = false;
|
||||||
|
|
||||||
/* Show myself as out of the transaction in PGPROC array */
|
|
||||||
MyProc->logRec.xrecoff = 0;
|
|
||||||
|
|
||||||
/* And clean up local data */
|
/* And clean up local data */
|
||||||
if (rptr)
|
if (rels)
|
||||||
pfree(rptr);
|
pfree(rels);
|
||||||
if (children)
|
if (children)
|
||||||
pfree(children);
|
pfree(children);
|
||||||
}
|
}
|
||||||
|
@ -970,12 +978,12 @@ static void
|
||||||
RecordTransactionAbort(void)
|
RecordTransactionAbort(void)
|
||||||
{
|
{
|
||||||
int nrels;
|
int nrels;
|
||||||
RelFileNode *rptr;
|
RelFileNode *rels;
|
||||||
int nchildren;
|
int nchildren;
|
||||||
TransactionId *children;
|
TransactionId *children;
|
||||||
|
|
||||||
/* Get data needed for abort record */
|
/* Get data needed for abort record */
|
||||||
nrels = smgrGetPendingDeletes(false, &rptr);
|
nrels = smgrGetPendingDeletes(false, &rels);
|
||||||
nchildren = xactGetCommittedChildren(&children);
|
nchildren = xactGetCommittedChildren(&children);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1026,7 +1034,7 @@ RecordTransactionAbort(void)
|
||||||
if (nrels > 0)
|
if (nrels > 0)
|
||||||
{
|
{
|
||||||
rdata[0].next = &(rdata[1]);
|
rdata[0].next = &(rdata[1]);
|
||||||
rdata[1].data = (char *) rptr;
|
rdata[1].data = (char *) rels;
|
||||||
rdata[1].len = nrels * sizeof(RelFileNode);
|
rdata[1].len = nrels * sizeof(RelFileNode);
|
||||||
rdata[1].buffer = InvalidBuffer;
|
rdata[1].buffer = InvalidBuffer;
|
||||||
lastrdata = 1;
|
lastrdata = 1;
|
||||||
|
@ -1069,12 +1077,9 @@ RecordTransactionAbort(void)
|
||||||
MyXactMadeXLogEntry = false;
|
MyXactMadeXLogEntry = false;
|
||||||
MyXactMadeTempRelUpdate = false;
|
MyXactMadeTempRelUpdate = false;
|
||||||
|
|
||||||
/* Show myself as out of the transaction in PGPROC array */
|
|
||||||
MyProc->logRec.xrecoff = 0;
|
|
||||||
|
|
||||||
/* And clean up local data */
|
/* And clean up local data */
|
||||||
if (rptr)
|
if (rels)
|
||||||
pfree(rptr);
|
pfree(rels);
|
||||||
if (children)
|
if (children)
|
||||||
pfree(children);
|
pfree(children);
|
||||||
}
|
}
|
||||||
|
@ -1166,13 +1171,13 @@ static void
|
||||||
RecordSubTransactionAbort(void)
|
RecordSubTransactionAbort(void)
|
||||||
{
|
{
|
||||||
int nrels;
|
int nrels;
|
||||||
RelFileNode *rptr;
|
RelFileNode *rels;
|
||||||
TransactionId xid = GetCurrentTransactionId();
|
TransactionId xid = GetCurrentTransactionId();
|
||||||
int nchildren;
|
int nchildren;
|
||||||
TransactionId *children;
|
TransactionId *children;
|
||||||
|
|
||||||
/* Get data needed for abort record */
|
/* Get data needed for abort record */
|
||||||
nrels = smgrGetPendingDeletes(false, &rptr);
|
nrels = smgrGetPendingDeletes(false, &rels);
|
||||||
nchildren = xactGetCommittedChildren(&children);
|
nchildren = xactGetCommittedChildren(&children);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1212,7 +1217,7 @@ RecordSubTransactionAbort(void)
|
||||||
if (nrels > 0)
|
if (nrels > 0)
|
||||||
{
|
{
|
||||||
rdata[0].next = &(rdata[1]);
|
rdata[0].next = &(rdata[1]);
|
||||||
rdata[1].data = (char *) rptr;
|
rdata[1].data = (char *) rels;
|
||||||
rdata[1].len = nrels * sizeof(RelFileNode);
|
rdata[1].len = nrels * sizeof(RelFileNode);
|
||||||
rdata[1].buffer = InvalidBuffer;
|
rdata[1].buffer = InvalidBuffer;
|
||||||
lastrdata = 1;
|
lastrdata = 1;
|
||||||
|
@ -1256,8 +1261,8 @@ RecordSubTransactionAbort(void)
|
||||||
XidCacheRemoveRunningXids(xid, nchildren, children);
|
XidCacheRemoveRunningXids(xid, nchildren, children);
|
||||||
|
|
||||||
/* And clean up local data */
|
/* And clean up local data */
|
||||||
if (rptr)
|
if (rels)
|
||||||
pfree(rptr);
|
pfree(rels);
|
||||||
if (children)
|
if (children)
|
||||||
pfree(children);
|
pfree(children);
|
||||||
}
|
}
|
||||||
|
@ -1419,8 +1424,11 @@ StartTransaction(void)
|
||||||
ShowTransactionState("StartTransaction");
|
ShowTransactionState("StartTransaction");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CommitTransaction
|
* CommitTransaction
|
||||||
|
*
|
||||||
|
* NB: if you change this routine, better look at PrepareTransaction too!
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
CommitTransaction(void)
|
CommitTransaction(void)
|
||||||
|
@ -1510,6 +1518,8 @@ CommitTransaction(void)
|
||||||
* xid 0 as running as well, or it will be able to see two tuple versions
|
* xid 0 as running as well, or it will be able to see two tuple versions
|
||||||
* - one deleted by xid 1 and one inserted by xid 0. See notes in
|
* - one deleted by xid 1 and one inserted by xid 0. See notes in
|
||||||
* GetSnapshotData.
|
* GetSnapshotData.
|
||||||
|
*
|
||||||
|
* Note: MyProc may be null during bootstrap.
|
||||||
*----------
|
*----------
|
||||||
*/
|
*/
|
||||||
if (MyProc != NULL)
|
if (MyProc != NULL)
|
||||||
|
@ -1608,6 +1618,225 @@ CommitTransaction(void)
|
||||||
RESUME_INTERRUPTS();
|
RESUME_INTERRUPTS();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PrepareTransaction
|
||||||
|
*
|
||||||
|
* NB: if you change this routine, better look at CommitTransaction too!
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PrepareTransaction(void)
|
||||||
|
{
|
||||||
|
TransactionState s = CurrentTransactionState;
|
||||||
|
TransactionId xid = GetCurrentTransactionId();
|
||||||
|
GlobalTransaction gxact;
|
||||||
|
|
||||||
|
ShowTransactionState("PrepareTransaction");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* check the current transaction state
|
||||||
|
*/
|
||||||
|
if (s->state != TRANS_INPROGRESS)
|
||||||
|
elog(WARNING, "PrepareTransaction while in %s state",
|
||||||
|
TransStateAsString(s->state));
|
||||||
|
Assert(s->parent == NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do pre-commit processing (most of this stuff requires database
|
||||||
|
* access, and in fact could still cause an error...)
|
||||||
|
*
|
||||||
|
* It is possible for PrepareHoldablePortals to invoke functions that
|
||||||
|
* queue deferred triggers, and it's also possible that triggers create
|
||||||
|
* holdable cursors. So we have to loop until there's nothing left to
|
||||||
|
* do.
|
||||||
|
*/
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Fire all currently pending deferred triggers.
|
||||||
|
*/
|
||||||
|
AfterTriggerFireDeferred();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert any open holdable cursors into static portals. If there
|
||||||
|
* weren't any, we are done ... otherwise loop back to check if they
|
||||||
|
* queued deferred triggers. Lather, rinse, repeat.
|
||||||
|
*/
|
||||||
|
if (!PrepareHoldablePortals())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now we can shut down the deferred-trigger manager */
|
||||||
|
AfterTriggerEndXact(true);
|
||||||
|
|
||||||
|
/* Close any open regular cursors */
|
||||||
|
AtCommit_Portals();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let ON COMMIT management do its thing (must happen after closing
|
||||||
|
* cursors, to avoid dangling-reference problems)
|
||||||
|
*/
|
||||||
|
PreCommit_on_commit_actions();
|
||||||
|
|
||||||
|
/* close large objects before lower-level cleanup */
|
||||||
|
AtEOXact_LargeObject(true);
|
||||||
|
|
||||||
|
/* NOTIFY and flatfiles will be handled below */
|
||||||
|
|
||||||
|
/* Prevent cancel/die interrupt while cleaning up */
|
||||||
|
HOLD_INTERRUPTS();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* set the current transaction state information appropriately during
|
||||||
|
* the processing
|
||||||
|
*/
|
||||||
|
s->state = TRANS_PREPARE;
|
||||||
|
|
||||||
|
/* Tell bufmgr and smgr to prepare for commit */
|
||||||
|
BufmgrCommit();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Reserve the GID for this transaction. This could fail if the
|
||||||
|
* requested GID is invalid or already in use.
|
||||||
|
*/
|
||||||
|
gxact = MarkAsPreparing(xid, MyDatabaseId, prepareGID, GetUserId());
|
||||||
|
prepareGID = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Collect data for the 2PC state file. Note that in general, no actual
|
||||||
|
* state change should happen in the called modules during this step,
|
||||||
|
* since it's still possible to fail before commit, and in that case we
|
||||||
|
* want transaction abort to be able to clean up. (In particular, the
|
||||||
|
* AtPrepare routines may error out if they find cases they cannot
|
||||||
|
* handle.) State cleanup should happen in the PostPrepare routines
|
||||||
|
* below. However, some modules can go ahead and clear state here
|
||||||
|
* because they wouldn't do anything with it during abort anyway.
|
||||||
|
*
|
||||||
|
* Note: because the 2PC state file records will be replayed in the same
|
||||||
|
* order they are made, the order of these calls has to match the order
|
||||||
|
* in which we want things to happen during COMMIT PREPARED or
|
||||||
|
* ROLLBACK PREPARED; in particular, pay attention to whether things
|
||||||
|
* should happen before or after releasing the transaction's locks.
|
||||||
|
*/
|
||||||
|
StartPrepare(gxact);
|
||||||
|
|
||||||
|
AtPrepare_Notify();
|
||||||
|
AtPrepare_UpdateFlatFiles();
|
||||||
|
AtPrepare_Inval();
|
||||||
|
AtPrepare_Locks();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here is where we really truly prepare.
|
||||||
|
*
|
||||||
|
* We have to record transaction prepares even if we didn't
|
||||||
|
* make any updates, because the transaction manager might
|
||||||
|
* get confused if we lose a global transaction.
|
||||||
|
*/
|
||||||
|
EndPrepare(gxact);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Mark the prepared transaction as valid. As soon as we mark ourselves
|
||||||
|
* not running in MyProc below, others can commit/rollback the xact.
|
||||||
|
*
|
||||||
|
* NB: a side effect of this is to make a dummy ProcArray entry for the
|
||||||
|
* prepared XID. This must happen before we clear the XID from MyProc,
|
||||||
|
* else there is a window where the XID is not running according to
|
||||||
|
* TransactionIdInProgress, and onlookers would be entitled to assume
|
||||||
|
* the xact crashed. Instead we have a window where the same XID
|
||||||
|
* appears twice in ProcArray, which is OK.
|
||||||
|
*/
|
||||||
|
MarkAsPrepared(gxact);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Now we clean up backend-internal state and release internal
|
||||||
|
* resources.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Break the chain of back-links in the XLOG records I output */
|
||||||
|
MyLastRecPtr.xrecoff = 0;
|
||||||
|
MyXactMadeXLogEntry = false;
|
||||||
|
MyXactMadeTempRelUpdate = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Let others know about no transaction in progress by me. This has
|
||||||
|
* to be done *after* the prepared transaction has been marked valid,
|
||||||
|
* else someone may think it is unlocked and recyclable.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Lock ProcArrayLock because that's what GetSnapshotData uses. */
|
||||||
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
|
MyProc->xid = InvalidTransactionId;
|
||||||
|
MyProc->xmin = InvalidTransactionId;
|
||||||
|
|
||||||
|
/* Clear the subtransaction-XID cache too while holding the lock */
|
||||||
|
MyProc->subxids.nxids = 0;
|
||||||
|
MyProc->subxids.overflowed = false;
|
||||||
|
|
||||||
|
LWLockRelease(ProcArrayLock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is all post-transaction cleanup. Note that if an error is raised
|
||||||
|
* here, it's too late to abort the transaction. This should be just
|
||||||
|
* noncritical resource releasing. See notes in CommitTransaction.
|
||||||
|
*/
|
||||||
|
|
||||||
|
CallXactCallbacks(XACT_EVENT_PREPARE);
|
||||||
|
|
||||||
|
ResourceOwnerRelease(TopTransactionResourceOwner,
|
||||||
|
RESOURCE_RELEASE_BEFORE_LOCKS,
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
/* Check we've released all buffer pins */
|
||||||
|
AtEOXact_Buffers(true);
|
||||||
|
|
||||||
|
/* notify and flatfiles don't need a postprepare call */
|
||||||
|
|
||||||
|
PostPrepare_Inval();
|
||||||
|
|
||||||
|
PostPrepare_smgr();
|
||||||
|
|
||||||
|
AtEOXact_MultiXact();
|
||||||
|
|
||||||
|
PostPrepare_Locks(xid);
|
||||||
|
|
||||||
|
ResourceOwnerRelease(TopTransactionResourceOwner,
|
||||||
|
RESOURCE_RELEASE_LOCKS,
|
||||||
|
true, true);
|
||||||
|
ResourceOwnerRelease(TopTransactionResourceOwner,
|
||||||
|
RESOURCE_RELEASE_AFTER_LOCKS,
|
||||||
|
true, true);
|
||||||
|
|
||||||
|
/* PREPARE acts the same as COMMIT as far as GUC is concerned */
|
||||||
|
AtEOXact_GUC(true, false);
|
||||||
|
AtEOXact_SPI(true);
|
||||||
|
AtEOXact_on_commit_actions(true);
|
||||||
|
AtEOXact_Namespace(true);
|
||||||
|
/* smgrcommit already done */
|
||||||
|
AtEOXact_Files();
|
||||||
|
|
||||||
|
CurrentResourceOwner = NULL;
|
||||||
|
ResourceOwnerDelete(TopTransactionResourceOwner);
|
||||||
|
s->curTransactionOwner = NULL;
|
||||||
|
CurTransactionResourceOwner = NULL;
|
||||||
|
TopTransactionResourceOwner = NULL;
|
||||||
|
|
||||||
|
AtCommit_Memory();
|
||||||
|
|
||||||
|
s->transactionId = InvalidTransactionId;
|
||||||
|
s->subTransactionId = InvalidSubTransactionId;
|
||||||
|
s->nestingLevel = 0;
|
||||||
|
s->childXids = NIL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* done with 1st phase commit processing, set current transaction
|
||||||
|
* state back to default
|
||||||
|
*/
|
||||||
|
s->state = TRANS_DEFAULT;
|
||||||
|
|
||||||
|
RESUME_INTERRUPTS();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AbortTransaction
|
* AbortTransaction
|
||||||
*/
|
*/
|
||||||
|
@ -1640,7 +1869,7 @@ AbortTransaction(void)
|
||||||
/*
|
/*
|
||||||
* check the current transaction state
|
* check the current transaction state
|
||||||
*/
|
*/
|
||||||
if (s->state != TRANS_INPROGRESS)
|
if (s->state != TRANS_INPROGRESS && s->state != TRANS_PREPARE)
|
||||||
elog(WARNING, "AbortTransaction while in %s state",
|
elog(WARNING, "AbortTransaction while in %s state",
|
||||||
TransStateAsString(s->state));
|
TransStateAsString(s->state));
|
||||||
Assert(s->parent == NULL);
|
Assert(s->parent == NULL);
|
||||||
|
@ -1833,6 +2062,7 @@ StartTransactionCommand(void)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(ERROR, "StartTransactionCommand: unexpected state %s",
|
elog(ERROR, "StartTransactionCommand: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -1934,6 +2164,15 @@ CommitTransactionCommand(void)
|
||||||
s->blockState = TBLOCK_DEFAULT;
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We are completing a "PREPARE TRANSACTION" command. Do it and
|
||||||
|
* return to the idle state.
|
||||||
|
*/
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
|
PrepareTransaction();
|
||||||
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We were just issued a SAVEPOINT inside a transaction block.
|
* We were just issued a SAVEPOINT inside a transaction block.
|
||||||
* Start a subtransaction. (DefineSavepoint already did
|
* Start a subtransaction. (DefineSavepoint already did
|
||||||
|
@ -1964,6 +2203,12 @@ CommitTransactionCommand(void)
|
||||||
CommitTransaction();
|
CommitTransaction();
|
||||||
s->blockState = TBLOCK_DEFAULT;
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
}
|
}
|
||||||
|
else if (s->blockState == TBLOCK_PREPARE)
|
||||||
|
{
|
||||||
|
Assert(s->parent == NULL);
|
||||||
|
PrepareTransaction();
|
||||||
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Assert(s->blockState == TBLOCK_INPROGRESS ||
|
Assert(s->blockState == TBLOCK_INPROGRESS ||
|
||||||
|
@ -2155,6 +2400,17 @@ AbortCurrentTransaction(void)
|
||||||
s->blockState = TBLOCK_DEFAULT;
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Here, we failed while trying to PREPARE. Clean up the
|
||||||
|
* transaction and return to idle state (we do not want to
|
||||||
|
* stay in the transaction).
|
||||||
|
*/
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
|
AbortTransaction();
|
||||||
|
CleanupTransaction();
|
||||||
|
s->blockState = TBLOCK_DEFAULT;
|
||||||
|
break;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We got an error inside a subtransaction. Abort just the
|
* We got an error inside a subtransaction. Abort just the
|
||||||
* subtransaction, and go to the persistent SUBABORT state
|
* subtransaction, and go to the persistent SUBABORT state
|
||||||
|
@ -2487,12 +2743,64 @@ BeginTransactionBlock(void)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "BeginTransactionBlock: unexpected state %s",
|
elog(FATAL, "BeginTransactionBlock: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PrepareTransactionBlock
|
||||||
|
* This executes a PREPARE command.
|
||||||
|
*
|
||||||
|
* Since PREPARE may actually do a ROLLBACK, the result indicates what
|
||||||
|
* happened: TRUE for PREPARE, FALSE for ROLLBACK.
|
||||||
|
*
|
||||||
|
* Note that we don't actually do anything here except change blockState.
|
||||||
|
* The real work will be done in the upcoming PrepareTransaction().
|
||||||
|
* We do it this way because it's not convenient to change memory context,
|
||||||
|
* resource owner, etc while executing inside a Portal.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
PrepareTransactionBlock(char *gid)
|
||||||
|
{
|
||||||
|
TransactionState s;
|
||||||
|
bool result;
|
||||||
|
|
||||||
|
/* Set up to commit the current transaction */
|
||||||
|
result = EndTransactionBlock();
|
||||||
|
|
||||||
|
/* If successful, change outer tblock state to PREPARE */
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
s = CurrentTransactionState;
|
||||||
|
|
||||||
|
while (s->parent != NULL)
|
||||||
|
s = s->parent;
|
||||||
|
|
||||||
|
if (s->blockState == TBLOCK_END)
|
||||||
|
{
|
||||||
|
/* Save GID where PrepareTransaction can find it again */
|
||||||
|
prepareGID = MemoryContextStrdup(TopTransactionContext, gid);
|
||||||
|
|
||||||
|
s->blockState = TBLOCK_PREPARE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* ignore case where we are not in a transaction;
|
||||||
|
* EndTransactionBlock already issued a warning.
|
||||||
|
*/
|
||||||
|
Assert(s->blockState == TBLOCK_STARTED);
|
||||||
|
/* Don't send back a PREPARE result tag... */
|
||||||
|
result = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EndTransactionBlock
|
* EndTransactionBlock
|
||||||
* This executes a COMMIT command.
|
* This executes a COMMIT command.
|
||||||
|
@ -2603,6 +2911,7 @@ EndTransactionBlock(void)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "EndTransactionBlock: unexpected state %s",
|
elog(FATAL, "EndTransactionBlock: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -2694,6 +3003,7 @@ UserAbortTransactionBlock(void)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
|
elog(FATAL, "UserAbortTransactionBlock: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -2740,6 +3050,7 @@ DefineSavepoint(char *name)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "DefineSavepoint: unexpected state %s",
|
elog(FATAL, "DefineSavepoint: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -2795,6 +3106,7 @@ ReleaseSavepoint(List *options)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "ReleaseSavepoint: unexpected state %s",
|
elog(FATAL, "ReleaseSavepoint: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -2892,6 +3204,7 @@ RollbackToSavepoint(List *options)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
|
elog(FATAL, "RollbackToSavepoint: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -2999,6 +3312,7 @@ BeginInternalSubTransaction(char *name)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
|
elog(FATAL, "BeginInternalSubTransaction: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -3064,6 +3378,7 @@ RollbackAndReleaseCurrentSubTransaction(void)
|
||||||
case TBLOCK_SUBABORT_PENDING:
|
case TBLOCK_SUBABORT_PENDING:
|
||||||
case TBLOCK_SUBRESTART:
|
case TBLOCK_SUBRESTART:
|
||||||
case TBLOCK_SUBABORT_RESTART:
|
case TBLOCK_SUBABORT_RESTART:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
|
elog(FATAL, "RollbackAndReleaseCurrentSubTransaction: unexpected state %s",
|
||||||
BlockStateAsString(s->blockState));
|
BlockStateAsString(s->blockState));
|
||||||
break;
|
break;
|
||||||
|
@ -3111,6 +3426,7 @@ AbortOutOfAnyTransaction(void)
|
||||||
case TBLOCK_INPROGRESS:
|
case TBLOCK_INPROGRESS:
|
||||||
case TBLOCK_END:
|
case TBLOCK_END:
|
||||||
case TBLOCK_ABORT_PENDING:
|
case TBLOCK_ABORT_PENDING:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
/* In a transaction, so clean up */
|
/* In a transaction, so clean up */
|
||||||
AbortTransaction();
|
AbortTransaction();
|
||||||
CleanupTransaction();
|
CleanupTransaction();
|
||||||
|
@ -3202,6 +3518,7 @@ TransactionBlockStatusCode(void)
|
||||||
case TBLOCK_SUBINPROGRESS:
|
case TBLOCK_SUBINPROGRESS:
|
||||||
case TBLOCK_END:
|
case TBLOCK_END:
|
||||||
case TBLOCK_SUBEND:
|
case TBLOCK_SUBEND:
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
return 'T'; /* in transaction */
|
return 'T'; /* in transaction */
|
||||||
case TBLOCK_ABORT:
|
case TBLOCK_ABORT:
|
||||||
case TBLOCK_SUBABORT:
|
case TBLOCK_SUBABORT:
|
||||||
|
@ -3684,6 +4001,8 @@ BlockStateAsString(TBlockState blockState)
|
||||||
return "ABORT END";
|
return "ABORT END";
|
||||||
case TBLOCK_ABORT_PENDING:
|
case TBLOCK_ABORT_PENDING:
|
||||||
return "ABORT PEND";
|
return "ABORT PEND";
|
||||||
|
case TBLOCK_PREPARE:
|
||||||
|
return "PREPARE";
|
||||||
case TBLOCK_SUBBEGIN:
|
case TBLOCK_SUBBEGIN:
|
||||||
return "SUB BEGIN";
|
return "SUB BEGIN";
|
||||||
case TBLOCK_SUBINPROGRESS:
|
case TBLOCK_SUBINPROGRESS:
|
||||||
|
@ -3717,12 +4036,14 @@ TransStateAsString(TransState state)
|
||||||
return "DEFAULT";
|
return "DEFAULT";
|
||||||
case TRANS_START:
|
case TRANS_START:
|
||||||
return "START";
|
return "START";
|
||||||
|
case TRANS_INPROGRESS:
|
||||||
|
return "INPROGR";
|
||||||
case TRANS_COMMIT:
|
case TRANS_COMMIT:
|
||||||
return "COMMIT";
|
return "COMMIT";
|
||||||
case TRANS_ABORT:
|
case TRANS_ABORT:
|
||||||
return "ABORT";
|
return "ABORT";
|
||||||
case TRANS_INPROGRESS:
|
case TRANS_PREPARE:
|
||||||
return "INPROGR";
|
return "PREPARE";
|
||||||
}
|
}
|
||||||
return "UNRECOGNIZED";
|
return "UNRECOGNIZED";
|
||||||
}
|
}
|
||||||
|
@ -3767,6 +4088,76 @@ xactGetCommittedChildren(TransactionId **ptr)
|
||||||
* XLOG support routines
|
* XLOG support routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
xact_redo_commit(xl_xact_commit *xlrec, TransactionId xid)
|
||||||
|
{
|
||||||
|
TransactionId *sub_xids;
|
||||||
|
TransactionId max_xid;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
TransactionIdCommit(xid);
|
||||||
|
|
||||||
|
/* Mark committed subtransactions as committed */
|
||||||
|
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
||||||
|
TransactionIdCommitTree(xlrec->nsubxacts, sub_xids);
|
||||||
|
|
||||||
|
/* Make sure nextXid is beyond any XID mentioned in the record */
|
||||||
|
max_xid = xid;
|
||||||
|
for (i = 0; i < xlrec->nsubxacts; i++)
|
||||||
|
{
|
||||||
|
if (TransactionIdPrecedes(max_xid, sub_xids[i]))
|
||||||
|
max_xid = sub_xids[i];
|
||||||
|
}
|
||||||
|
if (TransactionIdFollowsOrEquals(max_xid,
|
||||||
|
ShmemVariableCache->nextXid))
|
||||||
|
{
|
||||||
|
ShmemVariableCache->nextXid = max_xid;
|
||||||
|
TransactionIdAdvance(ShmemVariableCache->nextXid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure files supposed to be dropped are dropped */
|
||||||
|
for (i = 0; i < xlrec->nrels; i++)
|
||||||
|
{
|
||||||
|
XLogCloseRelation(xlrec->xnodes[i]);
|
||||||
|
smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xact_redo_abort(xl_xact_abort *xlrec, TransactionId xid)
|
||||||
|
{
|
||||||
|
TransactionId *sub_xids;
|
||||||
|
TransactionId max_xid;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
TransactionIdAbort(xid);
|
||||||
|
|
||||||
|
/* Mark subtransactions as aborted */
|
||||||
|
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
||||||
|
TransactionIdAbortTree(xlrec->nsubxacts, sub_xids);
|
||||||
|
|
||||||
|
/* Make sure nextXid is beyond any XID mentioned in the record */
|
||||||
|
max_xid = xid;
|
||||||
|
for (i = 0; i < xlrec->nsubxacts; i++)
|
||||||
|
{
|
||||||
|
if (TransactionIdPrecedes(max_xid, sub_xids[i]))
|
||||||
|
max_xid = sub_xids[i];
|
||||||
|
}
|
||||||
|
if (TransactionIdFollowsOrEquals(max_xid,
|
||||||
|
ShmemVariableCache->nextXid))
|
||||||
|
{
|
||||||
|
ShmemVariableCache->nextXid = max_xid;
|
||||||
|
TransactionIdAdvance(ShmemVariableCache->nextXid);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure files supposed to be dropped are dropped */
|
||||||
|
for (i = 0; i < xlrec->nrels; i++)
|
||||||
|
{
|
||||||
|
XLogCloseRelation(xlrec->xnodes[i]);
|
||||||
|
smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
xact_redo(XLogRecPtr lsn, XLogRecord *record)
|
xact_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||||
{
|
{
|
||||||
|
@ -3775,138 +4166,137 @@ xact_redo(XLogRecPtr lsn, XLogRecord *record)
|
||||||
if (info == XLOG_XACT_COMMIT)
|
if (info == XLOG_XACT_COMMIT)
|
||||||
{
|
{
|
||||||
xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
|
xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record);
|
||||||
TransactionId *sub_xids;
|
|
||||||
TransactionId max_xid;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
TransactionIdCommit(record->xl_xid);
|
xact_redo_commit(xlrec, record->xl_xid);
|
||||||
|
|
||||||
/* Mark committed subtransactions as committed */
|
|
||||||
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
|
||||||
TransactionIdCommitTree(xlrec->nsubxacts, sub_xids);
|
|
||||||
|
|
||||||
/* Make sure nextXid is beyond any XID mentioned in the record */
|
|
||||||
max_xid = record->xl_xid;
|
|
||||||
for (i = 0; i < xlrec->nsubxacts; i++)
|
|
||||||
{
|
|
||||||
if (TransactionIdPrecedes(max_xid, sub_xids[i]))
|
|
||||||
max_xid = sub_xids[i];
|
|
||||||
}
|
|
||||||
if (TransactionIdFollowsOrEquals(max_xid,
|
|
||||||
ShmemVariableCache->nextXid))
|
|
||||||
{
|
|
||||||
ShmemVariableCache->nextXid = max_xid;
|
|
||||||
TransactionIdAdvance(ShmemVariableCache->nextXid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure files supposed to be dropped are dropped */
|
|
||||||
for (i = 0; i < xlrec->nrels; i++)
|
|
||||||
{
|
|
||||||
XLogCloseRelation(xlrec->xnodes[i]);
|
|
||||||
smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (info == XLOG_XACT_ABORT)
|
else if (info == XLOG_XACT_ABORT)
|
||||||
{
|
{
|
||||||
xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
|
xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record);
|
||||||
TransactionId *sub_xids;
|
|
||||||
TransactionId max_xid;
|
|
||||||
int i;
|
|
||||||
|
|
||||||
TransactionIdAbort(record->xl_xid);
|
xact_redo_abort(xlrec, record->xl_xid);
|
||||||
|
}
|
||||||
|
else if (info == XLOG_XACT_PREPARE)
|
||||||
|
{
|
||||||
|
/* the record contents are exactly the 2PC file */
|
||||||
|
RecreateTwoPhaseFile(record->xl_xid,
|
||||||
|
XLogRecGetData(record), record->xl_len);
|
||||||
|
}
|
||||||
|
else if (info == XLOG_XACT_COMMIT_PREPARED)
|
||||||
|
{
|
||||||
|
xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *) XLogRecGetData(record);
|
||||||
|
|
||||||
/* Mark subtransactions as aborted */
|
xact_redo_commit(&xlrec->crec, xlrec->xid);
|
||||||
sub_xids = (TransactionId *) &(xlrec->xnodes[xlrec->nrels]);
|
RemoveTwoPhaseFile(xlrec->xid, false);
|
||||||
TransactionIdAbortTree(xlrec->nsubxacts, sub_xids);
|
}
|
||||||
|
else if (info == XLOG_XACT_ABORT_PREPARED)
|
||||||
|
{
|
||||||
|
xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) XLogRecGetData(record);
|
||||||
|
|
||||||
/* Make sure nextXid is beyond any XID mentioned in the record */
|
xact_redo_abort(&xlrec->arec, xlrec->xid);
|
||||||
max_xid = record->xl_xid;
|
RemoveTwoPhaseFile(xlrec->xid, false);
|
||||||
for (i = 0; i < xlrec->nsubxacts; i++)
|
|
||||||
{
|
|
||||||
if (TransactionIdPrecedes(max_xid, sub_xids[i]))
|
|
||||||
max_xid = sub_xids[i];
|
|
||||||
}
|
|
||||||
if (TransactionIdFollowsOrEquals(max_xid,
|
|
||||||
ShmemVariableCache->nextXid))
|
|
||||||
{
|
|
||||||
ShmemVariableCache->nextXid = max_xid;
|
|
||||||
TransactionIdAdvance(ShmemVariableCache->nextXid);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Make sure files supposed to be dropped are dropped */
|
|
||||||
for (i = 0; i < xlrec->nrels; i++)
|
|
||||||
{
|
|
||||||
XLogCloseRelation(xlrec->xnodes[i]);
|
|
||||||
smgrdounlink(smgropen(xlrec->xnodes[i]), false, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(PANIC, "xact_redo: unknown op code %u", info);
|
elog(PANIC, "xact_redo: unknown op code %u", info);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xact_desc_commit(char *buf, xl_xact_commit *xlrec)
|
||||||
|
{
|
||||||
|
struct tm *tm = localtime(&xlrec->xtime);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), "%04u-%02u-%02u %02u:%02u:%02u",
|
||||||
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
if (xlrec->nrels > 0)
|
||||||
|
{
|
||||||
|
sprintf(buf + strlen(buf), "; rels:");
|
||||||
|
for (i = 0; i < xlrec->nrels; i++)
|
||||||
|
{
|
||||||
|
RelFileNode rnode = xlrec->xnodes[i];
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), " %u/%u/%u",
|
||||||
|
rnode.spcNode, rnode.dbNode, rnode.relNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xlrec->nsubxacts > 0)
|
||||||
|
{
|
||||||
|
TransactionId *xacts = (TransactionId *)
|
||||||
|
&xlrec->xnodes[xlrec->nrels];
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), "; subxacts:");
|
||||||
|
for (i = 0; i < xlrec->nsubxacts; i++)
|
||||||
|
sprintf(buf + strlen(buf), " %u", xacts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
xact_desc_abort(char *buf, xl_xact_abort *xlrec)
|
||||||
|
{
|
||||||
|
struct tm *tm = localtime(&xlrec->xtime);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), "%04u-%02u-%02u %02u:%02u:%02u",
|
||||||
|
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
||||||
|
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||||
|
if (xlrec->nrels > 0)
|
||||||
|
{
|
||||||
|
sprintf(buf + strlen(buf), "; rels:");
|
||||||
|
for (i = 0; i < xlrec->nrels; i++)
|
||||||
|
{
|
||||||
|
RelFileNode rnode = xlrec->xnodes[i];
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), " %u/%u/%u",
|
||||||
|
rnode.spcNode, rnode.dbNode, rnode.relNode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (xlrec->nsubxacts > 0)
|
||||||
|
{
|
||||||
|
TransactionId *xacts = (TransactionId *)
|
||||||
|
&xlrec->xnodes[xlrec->nrels];
|
||||||
|
|
||||||
|
sprintf(buf + strlen(buf), "; subxacts:");
|
||||||
|
for (i = 0; i < xlrec->nsubxacts; i++)
|
||||||
|
sprintf(buf + strlen(buf), " %u", xacts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
xact_desc(char *buf, uint8 xl_info, char *rec)
|
xact_desc(char *buf, uint8 xl_info, char *rec)
|
||||||
{
|
{
|
||||||
uint8 info = xl_info & ~XLR_INFO_MASK;
|
uint8 info = xl_info & ~XLR_INFO_MASK;
|
||||||
int i;
|
|
||||||
|
|
||||||
if (info == XLOG_XACT_COMMIT)
|
if (info == XLOG_XACT_COMMIT)
|
||||||
{
|
{
|
||||||
xl_xact_commit *xlrec = (xl_xact_commit *) rec;
|
xl_xact_commit *xlrec = (xl_xact_commit *) rec;
|
||||||
struct tm *tm = localtime(&xlrec->xtime);
|
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), "commit: %04u-%02u-%02u %02u:%02u:%02u",
|
strcat(buf, "commit: ");
|
||||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
xact_desc_commit(buf, xlrec);
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
|
||||||
if (xlrec->nrels > 0)
|
|
||||||
{
|
|
||||||
sprintf(buf + strlen(buf), "; rels:");
|
|
||||||
for (i = 0; i < xlrec->nrels; i++)
|
|
||||||
{
|
|
||||||
RelFileNode rnode = xlrec->xnodes[i];
|
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), " %u/%u/%u",
|
|
||||||
rnode.spcNode, rnode.dbNode, rnode.relNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (xlrec->nsubxacts > 0)
|
|
||||||
{
|
|
||||||
TransactionId *xacts = (TransactionId *)
|
|
||||||
&xlrec->xnodes[xlrec->nrels];
|
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), "; subxacts:");
|
|
||||||
for (i = 0; i < xlrec->nsubxacts; i++)
|
|
||||||
sprintf(buf + strlen(buf), " %u", xacts[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (info == XLOG_XACT_ABORT)
|
else if (info == XLOG_XACT_ABORT)
|
||||||
{
|
{
|
||||||
xl_xact_abort *xlrec = (xl_xact_abort *) rec;
|
xl_xact_abort *xlrec = (xl_xact_abort *) rec;
|
||||||
struct tm *tm = localtime(&xlrec->xtime);
|
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), "abort: %04u-%02u-%02u %02u:%02u:%02u",
|
strcat(buf, "abort: ");
|
||||||
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
|
xact_desc_abort(buf, xlrec);
|
||||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
}
|
||||||
if (xlrec->nrels > 0)
|
else if (info == XLOG_XACT_PREPARE)
|
||||||
{
|
{
|
||||||
sprintf(buf + strlen(buf), "; rels:");
|
strcat(buf, "prepare");
|
||||||
for (i = 0; i < xlrec->nrels; i++)
|
}
|
||||||
{
|
else if (info == XLOG_XACT_COMMIT_PREPARED)
|
||||||
RelFileNode rnode = xlrec->xnodes[i];
|
{
|
||||||
|
xl_xact_commit_prepared *xlrec = (xl_xact_commit_prepared *) rec;
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), " %u/%u/%u",
|
sprintf(buf + strlen(buf), "commit %u: ", xlrec->xid);
|
||||||
rnode.spcNode, rnode.dbNode, rnode.relNode);
|
xact_desc_commit(buf, &xlrec->crec);
|
||||||
}
|
}
|
||||||
}
|
else if (info == XLOG_XACT_ABORT_PREPARED)
|
||||||
if (xlrec->nsubxacts > 0)
|
{
|
||||||
{
|
xl_xact_abort_prepared *xlrec = (xl_xact_abort_prepared *) rec;
|
||||||
TransactionId *xacts = (TransactionId *)
|
|
||||||
&xlrec->xnodes[xlrec->nrels];
|
|
||||||
|
|
||||||
sprintf(buf + strlen(buf), "; subxacts:");
|
sprintf(buf + strlen(buf), "abort %u: ", xlrec->xid);
|
||||||
for (i = 0; i < xlrec->nsubxacts; i++)
|
xact_desc_abort(buf, &xlrec->arec);
|
||||||
sprintf(buf + strlen(buf), " %u", xacts[i]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
strcat(buf, "UNKNOWN");
|
strcat(buf, "UNKNOWN");
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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.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/clog.h"
|
||||||
#include "access/multixact.h"
|
#include "access/multixact.h"
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "access/xlog_internal.h"
|
#include "access/xlog_internal.h"
|
||||||
|
@ -814,18 +815,6 @@ begin:;
|
||||||
/* Compute record's XLOG location */
|
/* Compute record's XLOG location */
|
||||||
INSERT_RECPTR(RecPtr, Insert, curridx);
|
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
|
#ifdef WAL_DEBUG
|
||||||
if (XLOG_DEBUG)
|
if (XLOG_DEBUG)
|
||||||
{
|
{
|
||||||
|
@ -3827,6 +3816,7 @@ BootStrapXLOG(void)
|
||||||
BootStrapCLOG();
|
BootStrapCLOG();
|
||||||
BootStrapSUBTRANS();
|
BootStrapSUBTRANS();
|
||||||
BootStrapMultiXact();
|
BootStrapMultiXact();
|
||||||
|
|
||||||
free(buffer);
|
free(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4268,6 +4258,7 @@ StartupXLOG(void)
|
||||||
uint32 endLogSeg;
|
uint32 endLogSeg;
|
||||||
XLogRecord *record;
|
XLogRecord *record;
|
||||||
uint32 freespace;
|
uint32 freespace;
|
||||||
|
TransactionId oldestActiveXID;
|
||||||
|
|
||||||
CritSectionCount++;
|
CritSectionCount++;
|
||||||
|
|
||||||
|
@ -4678,33 +4669,8 @@ StartupXLOG(void)
|
||||||
XLogCtl->Write.curridx = NextBufIdx(0);
|
XLogCtl->Write.curridx = NextBufIdx(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NOT_USED
|
/* Pre-scan prepared transactions to find out the range of XIDs present */
|
||||||
/* UNDO */
|
oldestActiveXID = PrescanPreparedTransactions();
|
||||||
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
|
|
||||||
|
|
||||||
if (InRecovery)
|
if (InRecovery)
|
||||||
{
|
{
|
||||||
|
@ -4767,9 +4733,12 @@ StartupXLOG(void)
|
||||||
|
|
||||||
/* Start up the commit log and related stuff, too */
|
/* Start up the commit log and related stuff, too */
|
||||||
StartupCLOG();
|
StartupCLOG();
|
||||||
StartupSUBTRANS();
|
StartupSUBTRANS(oldestActiveXID);
|
||||||
StartupMultiXact();
|
StartupMultiXact();
|
||||||
|
|
||||||
|
/* Reload shared-memory state for prepared transactions */
|
||||||
|
RecoverPreparedTransactions();
|
||||||
|
|
||||||
ereport(LOG,
|
ereport(LOG,
|
||||||
(errmsg("database system is ready")));
|
(errmsg("database system is ready")));
|
||||||
CritSectionCount--;
|
CritSectionCount--;
|
||||||
|
@ -5095,31 +5064,6 @@ CreateCheckPoint(bool shutdown, bool force)
|
||||||
SpinLockRelease_NoHoldoff(&xlogctl->info_lck);
|
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
|
* Now we can release insert lock and checkpoint start lock, allowing
|
||||||
* other xacts to proceed even while we are flushing disk buffers.
|
* 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
|
* Select point at which we can truncate the log, which we base on the
|
||||||
* prior checkpoint's earliest info.
|
* 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
|
XLByteToSeg(ControlFile->checkPointCopy.redo, _logId, _logSeg);
|
||||||
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);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Update the control file.
|
* Update the control file.
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* 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
|
CREATE VIEW pg_user AS
|
||||||
|
@ -102,6 +102,39 @@ CREATE VIEW pg_stats AS
|
||||||
|
|
||||||
REVOKE ALL on pg_statistic FROM public;
|
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
|
CREATE VIEW pg_stat_all_tables AS
|
||||||
SELECT
|
SELECT
|
||||||
C.oid AS relid,
|
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_read,
|
||||||
pg_stat_get_db_blocks_hit(D.oid) AS blks_hit
|
pg_stat_get_db_blocks_hit(D.oid) AS blks_hit
|
||||||
FROM pg_database D;
|
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
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 <netinet/in.h>
|
||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "access/twophase_rmgr.h"
|
||||||
#include "catalog/pg_listener.h"
|
#include "catalog/pg_listener.h"
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
#include "libpq/libpq.h"
|
#include "libpq/libpq.h"
|
||||||
|
@ -407,6 +408,36 @@ Async_UnlistenOnExit(int code, Datum arg)
|
||||||
CommitTransactionCommand();
|
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
|
* AtCommit_Notify
|
||||||
|
@ -1016,8 +1047,9 @@ AsyncExistsPendingNotify(const char *relname)
|
||||||
|
|
||||||
foreach(p, pendingNotifies)
|
foreach(p, pendingNotifies)
|
||||||
{
|
{
|
||||||
/* Use NAMEDATALEN for relname comparison. DZ - 26-08-1996 */
|
const char *prelname = (const char *) lfirst(p);
|
||||||
if (strncmp((const char *) lfirst(p), relname, NAMEDATALEN) == 0)
|
|
||||||
|
if (strcmp(prelname, relname) == 0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1037,3 +1069,22 @@ ClearPendingNotifies(void)
|
||||||
*/
|
*/
|
||||||
pendingNotifies = NIL;
|
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
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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_SCALAR_FIELD(kind);
|
||||||
COPY_NODE_FIELD(options);
|
COPY_NODE_FIELD(options);
|
||||||
|
COPY_STRING_FIELD(gid);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,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/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_SCALAR_FIELD(kind);
|
||||||
COMPARE_NODE_FIELD(options);
|
COMPARE_NODE_FIELD(options);
|
||||||
|
COMPARE_STRING_FIELD(gid);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
|
@ -387,7 +387,7 @@ static void doNegateFloat(Value *v);
|
||||||
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
|
ORDER OUT_P OUTER_P OVERLAPS OVERLAY OWNER
|
||||||
|
|
||||||
PARTIAL PASSWORD PLACING POSITION
|
PARTIAL PASSWORD PLACING POSITION
|
||||||
PRECISION PRESERVE PREPARE PRIMARY
|
PRECISION PRESERVE PREPARE PREPARED PRIMARY
|
||||||
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
|
PRIOR PRIVILEGES PROCEDURAL PROCEDURE
|
||||||
|
|
||||||
QUOTE
|
QUOTE
|
||||||
|
@ -4121,6 +4121,27 @@ TransactionStmt:
|
||||||
(Node *)makeString($4)));
|
(Node *)makeString($4)));
|
||||||
$$ = (Node *)n;
|
$$ = (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 {}
|
opt_transaction: WORK {}
|
||||||
|
@ -6334,19 +6355,18 @@ a_expr: c_expr { $$ = $1; }
|
||||||
{
|
{
|
||||||
$$ = (Node *) makeSimpleA_Expr(AEXPR_OF, "!=", $1, (Node *) $6);
|
$$ = (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 *) makeA_Expr(AEXPR_AND, NIL,
|
||||||
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4),
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">=", $1, $4),
|
||||||
(Node *) makeSimpleA_Expr(AEXPR_OP, "<=", $1, $6));
|
(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 *) makeA_Expr(AEXPR_OR, NIL,
|
||||||
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5),
|
(Node *) makeSimpleA_Expr(AEXPR_OP, "<", $1, $5),
|
||||||
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7));
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $7));
|
||||||
}
|
}
|
||||||
|
|
||||||
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
|
| a_expr BETWEEN SYMMETRIC b_expr AND b_expr %prec BETWEEN
|
||||||
{
|
{
|
||||||
$$ = (Node *) makeA_Expr(AEXPR_OR, NIL,
|
$$ = (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, $7),
|
||||||
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5)));
|
(Node *) makeSimpleA_Expr(AEXPR_OP, ">", $1, $5)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
| a_expr IN_P in_expr
|
| a_expr IN_P in_expr
|
||||||
{
|
{
|
||||||
/* in_expr returns a SubLink or a list of a_exprs */
|
/* 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
|
* Restricted expressions
|
||||||
*
|
*
|
||||||
|
@ -7401,6 +7414,10 @@ opt_indirection:
|
||||||
| opt_indirection indirection_el { $$ = lappend($1, $2); }
|
| opt_indirection indirection_el { $$ = lappend($1, $2); }
|
||||||
;
|
;
|
||||||
|
|
||||||
|
opt_asymmetric: ASYMMETRIC
|
||||||
|
| /*EMPTY*/
|
||||||
|
;
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -7855,6 +7872,7 @@ unreserved_keyword:
|
||||||
| PARTIAL
|
| PARTIAL
|
||||||
| PASSWORD
|
| PASSWORD
|
||||||
| PREPARE
|
| PREPARE
|
||||||
|
| PREPARED
|
||||||
| PRESERVE
|
| PRESERVE
|
||||||
| PRIOR
|
| PRIOR
|
||||||
| PRIVILEGES
|
| PRIVILEGES
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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},
|
{"position", POSITION},
|
||||||
{"precision", PRECISION},
|
{"precision", PRECISION},
|
||||||
{"prepare", PREPARE},
|
{"prepare", PREPARE},
|
||||||
|
{"prepared", PREPARED},
|
||||||
{"preserve", PRESERVE},
|
{"preserve", PRESERVE},
|
||||||
{"primary", PRIMARY},
|
{"primary", PRIMARY},
|
||||||
{"prior", PRIOR},
|
{"prior", PRIOR},
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
*
|
*
|
||||||
|
@ -252,7 +252,7 @@ static void reg_reply(DNSServiceRegistrationReplyErrorType errorCode,
|
||||||
static void pmdaemonize(void);
|
static void pmdaemonize(void);
|
||||||
static Port *ConnCreate(int serverFd);
|
static Port *ConnCreate(int serverFd);
|
||||||
static void ConnFree(Port *port);
|
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 SIGHUP_handler(SIGNAL_ARGS);
|
||||||
static void pmdie(SIGNAL_ARGS);
|
static void pmdie(SIGNAL_ARGS);
|
||||||
static void reaper(SIGNAL_ARGS);
|
static void reaper(SIGNAL_ARGS);
|
||||||
|
@ -1783,7 +1783,7 @@ ClosePostmasterPorts(bool am_syslogger)
|
||||||
* reset_shared -- reset shared memory and semaphores
|
* reset_shared -- reset shared memory and semaphores
|
||||||
*/
|
*/
|
||||||
static void
|
static void
|
||||||
reset_shared(unsigned short port)
|
reset_shared(int port)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* Create or re-create shared memory and semaphores.
|
* 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
|
* used to determine IPC keys. This helps ensure that we will clean
|
||||||
* up dead IPC objects if the postmaster crashes and is restarted.
|
* 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 */
|
/* BackendRun will close sockets */
|
||||||
|
|
||||||
/* Attach process to shared data structures */
|
/* Attach process to shared data structures */
|
||||||
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
|
CreateSharedMemoryAndSemaphores(false, 0);
|
||||||
|
|
||||||
#ifdef USE_SSL
|
#ifdef USE_SSL
|
||||||
/*
|
/*
|
||||||
|
@ -3203,7 +3203,7 @@ SubPostmasterMain(int argc, char *argv[])
|
||||||
ClosePostmasterPorts(false);
|
ClosePostmasterPorts(false);
|
||||||
|
|
||||||
/* Attach process to shared data structures */
|
/* Attach process to shared data structures */
|
||||||
CreateSharedMemoryAndSemaphores(false, MaxBackends, 0);
|
CreateSharedMemoryAndSemaphores(false, 0);
|
||||||
|
|
||||||
BootstrapMain(argc - 2, argv + 2);
|
BootstrapMain(argc - 2, argv + 2);
|
||||||
proc_exit(0);
|
proc_exit(0);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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/clog.h"
|
||||||
#include "access/multixact.h"
|
#include "access/multixact.h"
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "access/xlog.h"
|
#include "access/xlog.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "postmaster/bgwriter.h"
|
#include "postmaster/bgwriter.h"
|
||||||
|
@ -54,9 +55,7 @@
|
||||||
* memory. This is true for a standalone backend, false for a postmaster.
|
* memory. This is true for a standalone backend, false for a postmaster.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
CreateSharedMemoryAndSemaphores(bool makePrivate,
|
CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
|
||||||
int maxBackends,
|
|
||||||
int port)
|
|
||||||
{
|
{
|
||||||
PGShmemHeader *seghdr = NULL;
|
PGShmemHeader *seghdr = NULL;
|
||||||
|
|
||||||
|
@ -72,15 +71,16 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
*/
|
*/
|
||||||
size = hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt));
|
size = hash_estimate_size(SHMEM_INDEX_SIZE, sizeof(ShmemIndexEnt));
|
||||||
size += BufferShmemSize();
|
size += BufferShmemSize();
|
||||||
size += LockShmemSize(maxBackends);
|
size += LockShmemSize();
|
||||||
size += ProcGlobalShmemSize(maxBackends);
|
size += ProcGlobalShmemSize();
|
||||||
size += XLOGShmemSize();
|
size += XLOGShmemSize();
|
||||||
size += CLOGShmemSize();
|
size += CLOGShmemSize();
|
||||||
size += SUBTRANSShmemSize();
|
size += SUBTRANSShmemSize();
|
||||||
|
size += TwoPhaseShmemSize();
|
||||||
size += MultiXactShmemSize();
|
size += MultiXactShmemSize();
|
||||||
size += LWLockShmemSize();
|
size += LWLockShmemSize();
|
||||||
size += ProcArrayShmemSize(maxBackends);
|
size += ProcArrayShmemSize();
|
||||||
size += SInvalShmemSize(maxBackends);
|
size += SInvalShmemSize(MaxBackends);
|
||||||
size += FreeSpaceShmemSize();
|
size += FreeSpaceShmemSize();
|
||||||
size += BgWriterShmemSize();
|
size += BgWriterShmemSize();
|
||||||
#ifdef EXEC_BACKEND
|
#ifdef EXEC_BACKEND
|
||||||
|
@ -100,7 +100,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
/*
|
/*
|
||||||
* Create semaphores
|
* Create semaphores
|
||||||
*/
|
*/
|
||||||
numSemas = ProcGlobalSemas(maxBackends);
|
numSemas = ProcGlobalSemas();
|
||||||
numSemas += SpinlockSemas();
|
numSemas += SpinlockSemas();
|
||||||
PGReserveSemaphores(numSemas, port);
|
PGReserveSemaphores(numSemas, port);
|
||||||
}
|
}
|
||||||
|
@ -144,6 +144,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
XLOGShmemInit();
|
XLOGShmemInit();
|
||||||
CLOGShmemInit();
|
CLOGShmemInit();
|
||||||
SUBTRANSShmemInit();
|
SUBTRANSShmemInit();
|
||||||
|
TwoPhaseShmemInit();
|
||||||
MultiXactShmemInit();
|
MultiXactShmemInit();
|
||||||
InitBufferPool();
|
InitBufferPool();
|
||||||
|
|
||||||
|
@ -151,18 +152,18 @@ CreateSharedMemoryAndSemaphores(bool makePrivate,
|
||||||
* Set up lock manager
|
* Set up lock manager
|
||||||
*/
|
*/
|
||||||
InitLocks();
|
InitLocks();
|
||||||
InitLockTable(maxBackends);
|
InitLockTable();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up process table
|
* Set up process table
|
||||||
*/
|
*/
|
||||||
InitProcGlobal(maxBackends);
|
InitProcGlobal();
|
||||||
CreateSharedProcArray(maxBackends);
|
CreateSharedProcArray();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up shared-inval messaging
|
* Set up shared-inval messaging
|
||||||
*/
|
*/
|
||||||
CreateSharedInvalidationState(maxBackends);
|
CreateSharedInvalidationState(MaxBackends);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Set up free-space map
|
* Set up free-space map
|
||||||
|
|
|
@ -11,6 +11,11 @@
|
||||||
* Because of various subtle race conditions it is critical that a backend
|
* Because of various subtle race conditions it is critical that a backend
|
||||||
* hold the correct locks while setting or clearing its MyProc->xid field.
|
* hold the correct locks while setting or clearing its MyProc->xid field.
|
||||||
* See notes in GetSnapshotData.
|
* 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
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
@ -18,13 +23,14 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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 "postgres.h"
|
||||||
|
|
||||||
#include "access/subtrans.h"
|
#include "access/subtrans.h"
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
#include "storage/procarray.h"
|
#include "storage/procarray.h"
|
||||||
|
@ -76,25 +82,23 @@ static void DisplayXidCache(void);
|
||||||
* Report shared-memory space needed by CreateSharedProcArray.
|
* Report shared-memory space needed by CreateSharedProcArray.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ProcArrayShmemSize(int maxBackends)
|
ProcArrayShmemSize(void)
|
||||||
{
|
{
|
||||||
/* sizeof(ProcArrayStruct) includes the first array element */
|
return MAXALIGN(offsetof(ProcArrayStruct, procs) +
|
||||||
return MAXALIGN(sizeof(ProcArrayStruct) +
|
(MaxBackends + max_prepared_xacts) * sizeof(PGPROC *));
|
||||||
(maxBackends - 1) * sizeof(PGPROC *));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the shared PGPROC array during postmaster startup.
|
* Initialize the shared PGPROC array during postmaster startup.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
CreateSharedProcArray(int maxBackends)
|
CreateSharedProcArray(void)
|
||||||
{
|
{
|
||||||
bool found;
|
bool found;
|
||||||
|
|
||||||
/* Create or attach to the ProcArray shared structure */
|
/* Create or attach to the ProcArray shared structure */
|
||||||
procArray = (ProcArrayStruct *)
|
procArray = (ProcArrayStruct *)
|
||||||
ShmemInitStruct("Proc Array", ProcArrayShmemSize(maxBackends),
|
ShmemInitStruct("Proc Array", ProcArrayShmemSize(), &found);
|
||||||
&found);
|
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
{
|
{
|
||||||
|
@ -102,18 +106,15 @@ CreateSharedProcArray(int maxBackends)
|
||||||
* We're the first - initialize.
|
* We're the first - initialize.
|
||||||
*/
|
*/
|
||||||
procArray->numProcs = 0;
|
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.
|
* Add the specified PGPROC to the shared array.
|
||||||
*
|
|
||||||
* This must be called during backend startup, after fully initializing
|
|
||||||
* the contents of MyProc.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ProcArrayAddMyself(void)
|
ProcArrayAdd(PGPROC *proc)
|
||||||
{
|
{
|
||||||
ProcArrayStruct *arrayP = procArray;
|
ProcArrayStruct *arrayP = procArray;
|
||||||
|
|
||||||
|
@ -132,32 +133,32 @@ ProcArrayAddMyself(void)
|
||||||
errmsg("sorry, too many clients already")));
|
errmsg("sorry, too many clients already")));
|
||||||
}
|
}
|
||||||
|
|
||||||
arrayP->procs[arrayP->numProcs] = MyProc;
|
arrayP->procs[arrayP->numProcs] = proc;
|
||||||
arrayP->numProcs++;
|
arrayP->numProcs++;
|
||||||
|
|
||||||
LWLockRelease(ProcArrayLock);
|
LWLockRelease(ProcArrayLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Remove my own PGPROC (found in the global MyProc) from the shared array.
|
* Remove the specified PGPROC from the shared array.
|
||||||
*
|
|
||||||
* This must be called during backend shutdown.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
ProcArrayRemoveMyself(void)
|
ProcArrayRemove(PGPROC *proc)
|
||||||
{
|
{
|
||||||
ProcArrayStruct *arrayP = procArray;
|
ProcArrayStruct *arrayP = procArray;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
#ifdef XIDCACHE_DEBUG
|
#ifdef XIDCACHE_DEBUG
|
||||||
DisplayXidCache();
|
/* dump stats at backend shutdown, but not prepared-xact end */
|
||||||
|
if (proc->pid != 0)
|
||||||
|
DisplayXidCache();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
for (index = 0; index < arrayP->numProcs; index++)
|
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->procs[index] = arrayP->procs[arrayP->numProcs - 1];
|
||||||
arrayP->numProcs--;
|
arrayP->numProcs--;
|
||||||
|
@ -169,7 +170,7 @@ ProcArrayRemoveMyself(void)
|
||||||
/* Ooops */
|
/* Ooops */
|
||||||
LWLockRelease(ProcArrayLock);
|
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;
|
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
|
* GetOldestXmin -- returns oldest transaction that was running
|
||||||
* when any current transaction was started.
|
* when any current transaction was started.
|
||||||
|
@ -441,12 +491,12 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
|
||||||
TransactionIdIsValid(MyProc->xmin));
|
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
|
* numProcs would be sufficient. But it seems better to do the
|
||||||
* malloc while not holding the lock, so we can't look at numProcs.
|
* malloc while not holding the lock, so we can't look at numProcs.
|
||||||
*
|
*
|
||||||
* This does open a possibility for avoiding repeated malloc/free: since
|
* 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
|
* previous xip array if any. (This relies on the fact that all
|
||||||
* callers pass static SnapshotData structs.)
|
* callers pass static SnapshotData structs.)
|
||||||
*/
|
*/
|
||||||
|
@ -456,7 +506,7 @@ GetSnapshotData(Snapshot snapshot, bool serializable)
|
||||||
* First call for this snapshot
|
* First call for this snapshot
|
||||||
*/
|
*/
|
||||||
snapshot->xip = (TransactionId *)
|
snapshot->xip = (TransactionId *)
|
||||||
malloc(MaxBackends * sizeof(TransactionId));
|
malloc(arrayP->maxProcs * sizeof(TransactionId));
|
||||||
if (snapshot->xip == NULL)
|
if (snapshot->xip == NULL)
|
||||||
ereport(ERROR,
|
ereport(ERROR,
|
||||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
@ -602,14 +652,21 @@ DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* BackendPidGetProc -- get a backend's PGPROC given its PID
|
* 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)
|
BackendPidGetProc(int pid)
|
||||||
{
|
{
|
||||||
PGPROC *result = NULL;
|
PGPROC *result = NULL;
|
||||||
ProcArrayStruct *arrayP = procArray;
|
ProcArrayStruct *arrayP = procArray;
|
||||||
int index;
|
int index;
|
||||||
|
|
||||||
|
if (pid == 0) /* never match dummy PGPROCs */
|
||||||
|
return NULL;
|
||||||
|
|
||||||
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
LWLockAcquire(ProcArrayLock, LW_SHARED);
|
||||||
|
|
||||||
for (index = 0; index < arrayP->numProcs; index++)
|
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
|
* active transactions. This is used as a heuristic to decide if
|
||||||
* a pre-XLOG-flush delay is worthwhile during commit.
|
* a pre-XLOG-flush delay is worthwhile during commit.
|
||||||
*
|
*
|
||||||
* An active transaction is something that has written at least one XLOG
|
* Do not count backends that are blocked waiting for locks, since they are
|
||||||
* record; read-only transactions don't count. Also, do not count backends
|
* not going to get to run until someone else commits.
|
||||||
* that are blocked waiting for locks, since they are not going to get to
|
|
||||||
* run until someone else commits.
|
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
CountActiveBackends(void)
|
CountActiveBackends(void)
|
||||||
|
@ -656,7 +711,7 @@ CountActiveBackends(void)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: for speed, we don't acquire ProcArrayLock. This is a little bit
|
* 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
|
* it should be OK. The result is only used for heuristic purposes
|
||||||
* anyway...
|
* anyway...
|
||||||
*/
|
*/
|
||||||
|
@ -666,7 +721,9 @@ CountActiveBackends(void)
|
||||||
|
|
||||||
if (proc == MyProc)
|
if (proc == MyProc)
|
||||||
continue; /* do not count myself */
|
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 */
|
continue; /* do not count if not in a transaction */
|
||||||
if (proc->waitLock != NULL)
|
if (proc->waitLock != NULL)
|
||||||
continue; /* do not count if blocked on a lock */
|
continue; /* do not count if blocked on a lock */
|
||||||
|
@ -676,25 +733,6 @@ CountActiveBackends(void)
|
||||||
return count;
|
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) \
|
#define XidCacheRemove(i) \
|
||||||
do { \
|
do { \
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* Create the lock table described by LockConflicts
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
InitLockTable(int maxBackends)
|
InitLockTable(void)
|
||||||
{
|
{
|
||||||
LOCKMETHODID LongTermTableId;
|
LOCKMETHODID LongTermTableId;
|
||||||
|
|
||||||
|
@ -91,8 +91,7 @@ InitLockTable(int maxBackends)
|
||||||
/* number of lock modes is lengthof()-1 because of dummy zero */
|
/* number of lock modes is lengthof()-1 because of dummy zero */
|
||||||
LockTableId = LockMethodTableInit("LockTable",
|
LockTableId = LockMethodTableInit("LockTable",
|
||||||
LockConflicts,
|
LockConflicts,
|
||||||
lengthof(LockConflicts) - 1,
|
lengthof(LockConflicts) - 1);
|
||||||
maxBackends);
|
|
||||||
if (!LockMethodIsValid(LockTableId))
|
if (!LockMethodIsValid(LockTableId))
|
||||||
elog(ERROR, "could not initialize lock table");
|
elog(ERROR, "could not initialize lock table");
|
||||||
Assert(LockTableId == DEFAULT_LOCKMETHOD);
|
Assert(LockTableId == DEFAULT_LOCKMETHOD);
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.155 2005/06/14 22:15:32 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/lmgr/lock.c,v 1.156 2005/06/17 22:32:45 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* Outside modules can create a lock table and acquire/release
|
* Outside modules can create a lock table and acquire/release
|
||||||
|
@ -33,6 +33,8 @@
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "access/twophase.h"
|
||||||
|
#include "access/twophase_rmgr.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
#include "storage/proc.h"
|
#include "storage/proc.h"
|
||||||
|
@ -44,7 +46,15 @@
|
||||||
/* This configuration variable is used to set the lock table size */
|
/* This configuration variable is used to set the lock table size */
|
||||||
int max_locks_per_xact; /* set by guc.c */
|
int max_locks_per_xact; /* set by guc.c */
|
||||||
|
|
||||||
#define NLOCKENTS(maxBackends) (max_locks_per_xact * (maxBackends))
|
#define NLOCKENTS() (max_locks_per_xact * (MaxBackends + max_prepared_xacts))
|
||||||
|
|
||||||
|
|
||||||
|
/* Record that's written to 2PC state file when a lock is persisted */
|
||||||
|
typedef struct TwoPhaseLockRecord
|
||||||
|
{
|
||||||
|
LOCKTAG locktag;
|
||||||
|
LOCKMODE lockmode;
|
||||||
|
} TwoPhaseLockRecord;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -168,8 +178,7 @@ static void CleanUpLock(LOCKMETHODID lockmethodid, LOCK *lock,
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* InitLocks -- Init the lock module. Create a private data
|
* InitLocks -- Init the lock module. Nothing to do here at present.
|
||||||
* structure for constructing conflict masks.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
InitLocks(void)
|
InitLocks(void)
|
||||||
|
@ -222,8 +231,7 @@ LockMethodInit(LockMethod lockMethodTable,
|
||||||
LOCKMETHODID
|
LOCKMETHODID
|
||||||
LockMethodTableInit(const char *tabName,
|
LockMethodTableInit(const char *tabName,
|
||||||
const LOCKMASK *conflictsP,
|
const LOCKMASK *conflictsP,
|
||||||
int numModes,
|
int numModes)
|
||||||
int maxBackends)
|
|
||||||
{
|
{
|
||||||
LockMethod newLockMethod;
|
LockMethod newLockMethod;
|
||||||
LOCKMETHODID lockmethodid;
|
LOCKMETHODID lockmethodid;
|
||||||
|
@ -239,7 +247,7 @@ LockMethodTableInit(const char *tabName,
|
||||||
numModes, MAX_LOCKMODES - 1);
|
numModes, MAX_LOCKMODES - 1);
|
||||||
|
|
||||||
/* Compute init/max size to request for lock hashtables */
|
/* Compute init/max size to request for lock hashtables */
|
||||||
max_table_size = NLOCKENTS(maxBackends);
|
max_table_size = NLOCKENTS();
|
||||||
init_table_size = max_table_size / 2;
|
init_table_size = max_table_size / 2;
|
||||||
|
|
||||||
/* Allocate a string for the shmem index table lookups. */
|
/* Allocate a string for the shmem index table lookups. */
|
||||||
|
@ -1418,10 +1426,10 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
|
||||||
while (proclock)
|
while (proclock)
|
||||||
{
|
{
|
||||||
bool wakeupNeeded = false;
|
bool wakeupNeeded = false;
|
||||||
PROCLOCK *nextHolder;
|
PROCLOCK *nextplock;
|
||||||
|
|
||||||
/* Get link first, since we may unlink/delete this proclock */
|
/* Get link first, since we may unlink/delete this proclock */
|
||||||
nextHolder = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->procLink,
|
nextplock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->procLink,
|
||||||
offsetof(PROCLOCK, procLink));
|
offsetof(PROCLOCK, procLink));
|
||||||
|
|
||||||
Assert(proclock->tag.proc == MAKE_OFFSET(MyProc));
|
Assert(proclock->tag.proc == MAKE_OFFSET(MyProc));
|
||||||
|
@ -1474,7 +1482,7 @@ LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks)
|
||||||
CleanUpLock(lockmethodid, lock, proclock, wakeupNeeded);
|
CleanUpLock(lockmethodid, lock, proclock, wakeupNeeded);
|
||||||
|
|
||||||
next_item:
|
next_item:
|
||||||
proclock = nextHolder;
|
proclock = nextplock;
|
||||||
}
|
}
|
||||||
|
|
||||||
LWLockRelease(masterLock);
|
LWLockRelease(masterLock);
|
||||||
|
@ -1605,14 +1613,262 @@ LockReassignCurrentOwner(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AtPrepare_Locks
|
||||||
|
* Do the preparatory work for a PREPARE: make 2PC state file records
|
||||||
|
* for all locks currently held.
|
||||||
|
*
|
||||||
|
* User locks are non-transactional and are therefore ignored.
|
||||||
|
*
|
||||||
|
* There are some special cases that we error out on: we can't be holding
|
||||||
|
* any session locks (should be OK since only VACUUM uses those) and we
|
||||||
|
* can't be holding any locks on temporary objects (since that would mess
|
||||||
|
* up the current backend if it tries to exit before the prepared xact is
|
||||||
|
* committed).
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AtPrepare_Locks(void)
|
||||||
|
{
|
||||||
|
LOCKMETHODID lockmethodid = DEFAULT_LOCKMETHOD;
|
||||||
|
HASH_SEQ_STATUS status;
|
||||||
|
LOCALLOCK *locallock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We don't need to touch shared memory for this --- all the necessary
|
||||||
|
* state information is in the locallock table.
|
||||||
|
*/
|
||||||
|
hash_seq_init(&status, LockMethodLocalHash[lockmethodid]);
|
||||||
|
|
||||||
|
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||||
|
{
|
||||||
|
TwoPhaseLockRecord record;
|
||||||
|
LOCALLOCKOWNER *lockOwners = locallock->lockOwners;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* Ignore items that are not of the lockmethod to be processed */
|
||||||
|
if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Ignore it if we don't actually hold the lock */
|
||||||
|
if (locallock->nLocks <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* Scan to verify there are no session locks */
|
||||||
|
for (i = locallock->numLockOwners - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
/* elog not ereport since this should not happen */
|
||||||
|
if (lockOwners[i].owner == NULL)
|
||||||
|
elog(ERROR, "cannot PREPARE when session locks exist");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Can't handle it if the lock is on a temporary object */
|
||||||
|
if (locallock->isTempObject)
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot PREPARE a transaction that has operated on temporary tables")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create a 2PC record.
|
||||||
|
*/
|
||||||
|
memcpy(&(record.locktag), &(locallock->tag.lock), sizeof(LOCKTAG));
|
||||||
|
record.lockmode = locallock->tag.mode;
|
||||||
|
|
||||||
|
RegisterTwoPhaseRecord(TWOPHASE_RM_LOCK_ID, 0,
|
||||||
|
&record, sizeof(TwoPhaseLockRecord));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PostPrepare_Locks
|
||||||
|
* Clean up after successful PREPARE
|
||||||
|
*
|
||||||
|
* Here, we want to transfer ownership of our locks to a dummy PGPROC
|
||||||
|
* that's now associated with the prepared transaction, and we want to
|
||||||
|
* clean out the corresponding entries in the LOCALLOCK table.
|
||||||
|
*
|
||||||
|
* Note: by removing the LOCALLOCK entries, we are leaving dangling
|
||||||
|
* pointers in the transaction's resource owner. This is OK at the
|
||||||
|
* moment since resowner.c doesn't try to free locks retail at a toplevel
|
||||||
|
* transaction commit or abort. We could alternatively zero out nLocks
|
||||||
|
* and leave the LOCALLOCK entries to be garbage-collected by LockReleaseAll,
|
||||||
|
* but that probably costs more cycles.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PostPrepare_Locks(TransactionId xid)
|
||||||
|
{
|
||||||
|
PGPROC *newproc = TwoPhaseGetDummyProc(xid);
|
||||||
|
LOCKMETHODID lockmethodid = DEFAULT_LOCKMETHOD;
|
||||||
|
HASH_SEQ_STATUS status;
|
||||||
|
SHM_QUEUE *procLocks = &(MyProc->procLocks);
|
||||||
|
LWLockId masterLock;
|
||||||
|
LockMethod lockMethodTable;
|
||||||
|
int numLockModes;
|
||||||
|
LOCALLOCK *locallock;
|
||||||
|
PROCLOCK *proclock;
|
||||||
|
PROCLOCKTAG proclocktag;
|
||||||
|
bool found;
|
||||||
|
LOCK *lock;
|
||||||
|
|
||||||
|
/* This is a critical section: any error means big trouble */
|
||||||
|
START_CRIT_SECTION();
|
||||||
|
|
||||||
|
lockMethodTable = LockMethods[lockmethodid];
|
||||||
|
if (!lockMethodTable)
|
||||||
|
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
|
||||||
|
|
||||||
|
numLockModes = lockMethodTable->numLockModes;
|
||||||
|
masterLock = lockMethodTable->masterLock;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* First we run through the locallock table and get rid of unwanted
|
||||||
|
* entries, then we scan the process's proclocks and transfer them
|
||||||
|
* to the target proc.
|
||||||
|
*
|
||||||
|
* We do this separately because we may have multiple locallock
|
||||||
|
* entries pointing to the same proclock, and we daren't end up with
|
||||||
|
* any dangling pointers.
|
||||||
|
*/
|
||||||
|
hash_seq_init(&status, LockMethodLocalHash[lockmethodid]);
|
||||||
|
|
||||||
|
while ((locallock = (LOCALLOCK *) hash_seq_search(&status)) != NULL)
|
||||||
|
{
|
||||||
|
if (locallock->proclock == NULL || locallock->lock == NULL)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We must've run out of shared memory while trying to set up
|
||||||
|
* this lock. Just forget the local entry.
|
||||||
|
*/
|
||||||
|
Assert(locallock->nLocks == 0);
|
||||||
|
RemoveLocalLock(locallock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ignore items that are not of the lockmethod to be removed */
|
||||||
|
if (LOCALLOCK_LOCKMETHOD(*locallock) != lockmethodid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* We already checked there are no session locks */
|
||||||
|
|
||||||
|
/* Mark the proclock to show we need to release this lockmode */
|
||||||
|
if (locallock->nLocks > 0)
|
||||||
|
locallock->proclock->releaseMask |= LOCKBIT_ON(locallock->tag.mode);
|
||||||
|
|
||||||
|
/* And remove the locallock hashtable entry */
|
||||||
|
RemoveLocalLock(locallock);
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockAcquire(masterLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
proclock = (PROCLOCK *) SHMQueueNext(procLocks, procLocks,
|
||||||
|
offsetof(PROCLOCK, procLink));
|
||||||
|
|
||||||
|
while (proclock)
|
||||||
|
{
|
||||||
|
PROCLOCK *nextplock;
|
||||||
|
LOCKMASK holdMask;
|
||||||
|
PROCLOCK *newproclock;
|
||||||
|
|
||||||
|
/* Get link first, since we may unlink/delete this proclock */
|
||||||
|
nextplock = (PROCLOCK *) SHMQueueNext(procLocks, &proclock->procLink,
|
||||||
|
offsetof(PROCLOCK, procLink));
|
||||||
|
|
||||||
|
Assert(proclock->tag.proc == MAKE_OFFSET(MyProc));
|
||||||
|
|
||||||
|
lock = (LOCK *) MAKE_PTR(proclock->tag.lock);
|
||||||
|
|
||||||
|
/* Ignore items that are not of the lockmethod to be removed */
|
||||||
|
if (LOCK_LOCKMETHOD(*lock) != lockmethodid)
|
||||||
|
goto next_item;
|
||||||
|
|
||||||
|
PROCLOCK_PRINT("PostPrepare_Locks", proclock);
|
||||||
|
LOCK_PRINT("PostPrepare_Locks", lock, 0);
|
||||||
|
Assert(lock->nRequested >= 0);
|
||||||
|
Assert(lock->nGranted >= 0);
|
||||||
|
Assert(lock->nGranted <= lock->nRequested);
|
||||||
|
Assert((proclock->holdMask & ~lock->grantMask) == 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Since there were no session locks, we should be releasing all locks
|
||||||
|
*/
|
||||||
|
if (proclock->releaseMask != proclock->holdMask)
|
||||||
|
elog(PANIC, "we seem to have dropped a bit somewhere");
|
||||||
|
|
||||||
|
holdMask = proclock->holdMask;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We cannot simply modify proclock->tag.proc to reassign ownership
|
||||||
|
* of the lock, because that's part of the hash key and the proclock
|
||||||
|
* would then be in the wrong hash chain. So, unlink and delete the
|
||||||
|
* old proclock; create a new one with the right contents; and link
|
||||||
|
* it into place. We do it in this order to be certain we won't
|
||||||
|
* run out of shared memory (the way dynahash.c works, the deleted
|
||||||
|
* object is certain to be available for reallocation).
|
||||||
|
*/
|
||||||
|
SHMQueueDelete(&proclock->lockLink);
|
||||||
|
SHMQueueDelete(&proclock->procLink);
|
||||||
|
if (!hash_search(LockMethodProcLockHash[lockmethodid],
|
||||||
|
(void *) &(proclock->tag),
|
||||||
|
HASH_REMOVE, NULL))
|
||||||
|
elog(PANIC, "proclock table corrupted");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the hash key for the new proclock table.
|
||||||
|
*/
|
||||||
|
MemSet(&proclocktag, 0, sizeof(PROCLOCKTAG));
|
||||||
|
proclocktag.lock = MAKE_OFFSET(lock);
|
||||||
|
proclocktag.proc = MAKE_OFFSET(newproc);
|
||||||
|
|
||||||
|
newproclock = (PROCLOCK *) hash_search(LockMethodProcLockHash[lockmethodid],
|
||||||
|
(void *) &proclocktag,
|
||||||
|
HASH_ENTER_NULL, &found);
|
||||||
|
if (!newproclock)
|
||||||
|
ereport(PANIC, /* should not happen */
|
||||||
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
errmsg("out of shared memory"),
|
||||||
|
errdetail("Not enough memory for reassigning the prepared transaction's locks.")));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If new, initialize the new entry
|
||||||
|
*/
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
newproclock->holdMask = 0;
|
||||||
|
newproclock->releaseMask = 0;
|
||||||
|
/* Add new proclock to appropriate lists */
|
||||||
|
SHMQueueInsertBefore(&lock->procLocks, &newproclock->lockLink);
|
||||||
|
SHMQueueInsertBefore(&newproc->procLocks, &newproclock->procLink);
|
||||||
|
PROCLOCK_PRINT("PostPrepare_Locks: new", newproclock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PROCLOCK_PRINT("PostPrepare_Locks: found", newproclock);
|
||||||
|
Assert((newproclock->holdMask & ~lock->grantMask) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass over the identified lock ownership.
|
||||||
|
*/
|
||||||
|
Assert((newproclock->holdMask & holdMask) == 0);
|
||||||
|
newproclock->holdMask |= holdMask;
|
||||||
|
|
||||||
|
next_item:
|
||||||
|
proclock = nextplock;
|
||||||
|
}
|
||||||
|
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
|
||||||
|
END_CRIT_SECTION();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Estimate shared-memory space used for lock tables
|
* Estimate shared-memory space used for lock tables
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
LockShmemSize(int maxBackends)
|
LockShmemSize(void)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
long max_table_size = NLOCKENTS(maxBackends);
|
long max_table_size = NLOCKENTS();
|
||||||
|
|
||||||
/* lock method headers */
|
/* lock method headers */
|
||||||
size += MAX_LOCK_METHODS * MAXALIGN(sizeof(LockMethodData));
|
size += MAX_LOCK_METHODS * MAXALIGN(sizeof(LockMethodData));
|
||||||
|
@ -1704,21 +1960,19 @@ GetLockmodeName(LOCKMODE mode)
|
||||||
|
|
||||||
#ifdef LOCK_DEBUG
|
#ifdef LOCK_DEBUG
|
||||||
/*
|
/*
|
||||||
* Dump all locks in the MyProc->procLocks list.
|
* Dump all locks in the given proc's procLocks list.
|
||||||
*
|
*
|
||||||
* Must have already acquired the masterLock.
|
* Must have already acquired the masterLock.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
DumpLocks(void)
|
DumpLocks(PGPROC *proc)
|
||||||
{
|
{
|
||||||
PGPROC *proc;
|
|
||||||
SHM_QUEUE *procLocks;
|
SHM_QUEUE *procLocks;
|
||||||
PROCLOCK *proclock;
|
PROCLOCK *proclock;
|
||||||
LOCK *lock;
|
LOCK *lock;
|
||||||
int lockmethodid = DEFAULT_LOCKMETHOD;
|
int lockmethodid = DEFAULT_LOCKMETHOD;
|
||||||
LockMethod lockMethodTable;
|
LockMethod lockMethodTable;
|
||||||
|
|
||||||
proc = MyProc;
|
|
||||||
if (proc == NULL)
|
if (proc == NULL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -1793,3 +2047,254 @@ DumpAllLocks(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* LOCK_DEBUG */
|
#endif /* LOCK_DEBUG */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* LOCK 2PC resource manager's routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Re-acquire a lock belonging to a transaction that was prepared.
|
||||||
|
*
|
||||||
|
* Because this function is run at db startup, re-acquiring the locks should
|
||||||
|
* never conflict with running transactions because there are none. We
|
||||||
|
* assume that the lock state represented by the stored 2PC files is legal.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lock_twophase_recover(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len)
|
||||||
|
{
|
||||||
|
TwoPhaseLockRecord *rec = (TwoPhaseLockRecord *) recdata;
|
||||||
|
PGPROC *proc = TwoPhaseGetDummyProc(xid);
|
||||||
|
LOCKTAG *locktag;
|
||||||
|
LOCKMODE lockmode;
|
||||||
|
LOCKMETHODID lockmethodid;
|
||||||
|
LOCK *lock;
|
||||||
|
PROCLOCK *proclock;
|
||||||
|
PROCLOCKTAG proclocktag;
|
||||||
|
bool found;
|
||||||
|
LWLockId masterLock;
|
||||||
|
LockMethod lockMethodTable;
|
||||||
|
|
||||||
|
Assert(len == sizeof(TwoPhaseLockRecord));
|
||||||
|
locktag = &rec->locktag;
|
||||||
|
lockmode = rec->lockmode;
|
||||||
|
lockmethodid = locktag->locktag_lockmethodid;
|
||||||
|
|
||||||
|
Assert(lockmethodid < NumLockMethods);
|
||||||
|
lockMethodTable = LockMethods[lockmethodid];
|
||||||
|
if (!lockMethodTable)
|
||||||
|
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
|
||||||
|
|
||||||
|
masterLock = lockMethodTable->masterLock;
|
||||||
|
|
||||||
|
LWLockAcquire(masterLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find or create a lock with this tag.
|
||||||
|
*/
|
||||||
|
lock = (LOCK *) hash_search(LockMethodLockHash[lockmethodid],
|
||||||
|
(void *) locktag,
|
||||||
|
HASH_ENTER_NULL, &found);
|
||||||
|
if (!lock)
|
||||||
|
{
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
errmsg("out of shared memory"),
|
||||||
|
errhint("You may need to increase max_locks_per_transaction.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* if it's a new lock object, initialize it
|
||||||
|
*/
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
lock->grantMask = 0;
|
||||||
|
lock->waitMask = 0;
|
||||||
|
SHMQueueInit(&(lock->procLocks));
|
||||||
|
ProcQueueInit(&(lock->waitProcs));
|
||||||
|
lock->nRequested = 0;
|
||||||
|
lock->nGranted = 0;
|
||||||
|
MemSet(lock->requested, 0, sizeof(int) * MAX_LOCKMODES);
|
||||||
|
MemSet(lock->granted, 0, sizeof(int) * MAX_LOCKMODES);
|
||||||
|
LOCK_PRINT("lock_twophase_recover: new", lock, lockmode);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOCK_PRINT("lock_twophase_recover: found", lock, lockmode);
|
||||||
|
Assert((lock->nRequested >= 0) && (lock->requested[lockmode] >= 0));
|
||||||
|
Assert((lock->nGranted >= 0) && (lock->granted[lockmode] >= 0));
|
||||||
|
Assert(lock->nGranted <= lock->nRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Create the hash key for the proclock table.
|
||||||
|
*/
|
||||||
|
MemSet(&proclocktag, 0, sizeof(PROCLOCKTAG)); /* must clear padding */
|
||||||
|
proclocktag.lock = MAKE_OFFSET(lock);
|
||||||
|
proclocktag.proc = MAKE_OFFSET(proc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Find or create a proclock entry with this tag
|
||||||
|
*/
|
||||||
|
proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash[lockmethodid],
|
||||||
|
(void *) &proclocktag,
|
||||||
|
HASH_ENTER_NULL, &found);
|
||||||
|
if (!proclock)
|
||||||
|
{
|
||||||
|
/* Ooops, not enough shmem for the proclock */
|
||||||
|
if (lock->nRequested == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* There are no other requestors of this lock, so garbage-collect
|
||||||
|
* the lock object. We *must* do this to avoid a permanent leak
|
||||||
|
* of shared memory, because there won't be anything to cause
|
||||||
|
* anyone to release the lock object later.
|
||||||
|
*/
|
||||||
|
Assert(SHMQueueEmpty(&(lock->procLocks)));
|
||||||
|
if (!hash_search(LockMethodLockHash[lockmethodid],
|
||||||
|
(void *) &(lock->tag),
|
||||||
|
HASH_REMOVE, NULL))
|
||||||
|
elog(PANIC, "lock table corrupted");
|
||||||
|
}
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
|
errmsg("out of shared memory"),
|
||||||
|
errhint("You may need to increase max_locks_per_transaction.")));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If new, initialize the new entry
|
||||||
|
*/
|
||||||
|
if (!found)
|
||||||
|
{
|
||||||
|
proclock->holdMask = 0;
|
||||||
|
proclock->releaseMask = 0;
|
||||||
|
/* Add proclock to appropriate lists */
|
||||||
|
SHMQueueInsertBefore(&lock->procLocks, &proclock->lockLink);
|
||||||
|
SHMQueueInsertBefore(&proc->procLocks, &proclock->procLink);
|
||||||
|
PROCLOCK_PRINT("lock_twophase_recover: new", proclock);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PROCLOCK_PRINT("lock_twophase_recover: found", proclock);
|
||||||
|
Assert((proclock->holdMask & ~lock->grantMask) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* lock->nRequested and lock->requested[] count the total number of
|
||||||
|
* requests, whether granted or waiting, so increment those
|
||||||
|
* immediately.
|
||||||
|
*/
|
||||||
|
lock->nRequested++;
|
||||||
|
lock->requested[lockmode]++;
|
||||||
|
Assert((lock->nRequested > 0) && (lock->requested[lockmode] > 0));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We shouldn't already hold the desired lock.
|
||||||
|
*/
|
||||||
|
if (proclock->holdMask & LOCKBIT_ON(lockmode))
|
||||||
|
elog(ERROR, "lock %s on object %u/%u/%u is already held",
|
||||||
|
lock_mode_names[lockmode],
|
||||||
|
lock->tag.locktag_field1, lock->tag.locktag_field2,
|
||||||
|
lock->tag.locktag_field3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We ignore any possible conflicts and just grant ourselves the lock.
|
||||||
|
*/
|
||||||
|
GrantLock(lock, proclock, lockmode);
|
||||||
|
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2PC processing routine for COMMIT PREPARED case.
|
||||||
|
*
|
||||||
|
* Find and release the lock indicated by the 2PC record.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lock_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len)
|
||||||
|
{
|
||||||
|
TwoPhaseLockRecord *rec = (TwoPhaseLockRecord *) recdata;
|
||||||
|
PGPROC *proc = TwoPhaseGetDummyProc(xid);
|
||||||
|
LOCKTAG *locktag;
|
||||||
|
LOCKMODE lockmode;
|
||||||
|
LOCKMETHODID lockmethodid;
|
||||||
|
PROCLOCKTAG proclocktag;
|
||||||
|
LOCK *lock;
|
||||||
|
PROCLOCK *proclock;
|
||||||
|
LWLockId masterLock;
|
||||||
|
LockMethod lockMethodTable;
|
||||||
|
bool wakeupNeeded;
|
||||||
|
|
||||||
|
Assert(len == sizeof(TwoPhaseLockRecord));
|
||||||
|
locktag = &rec->locktag;
|
||||||
|
lockmode = rec->lockmode;
|
||||||
|
lockmethodid = locktag->locktag_lockmethodid;
|
||||||
|
|
||||||
|
Assert(lockmethodid < NumLockMethods);
|
||||||
|
lockMethodTable = LockMethods[lockmethodid];
|
||||||
|
if (!lockMethodTable)
|
||||||
|
elog(ERROR, "unrecognized lock method: %d", lockmethodid);
|
||||||
|
|
||||||
|
masterLock = lockMethodTable->masterLock;
|
||||||
|
|
||||||
|
LWLockAcquire(masterLock, LW_EXCLUSIVE);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Re-find the lock object (it had better be there).
|
||||||
|
*/
|
||||||
|
lock = (LOCK *) hash_search(LockMethodLockHash[lockmethodid],
|
||||||
|
(void *) locktag,
|
||||||
|
HASH_FIND, NULL);
|
||||||
|
if (!lock)
|
||||||
|
elog(PANIC, "failed to re-find shared lock object");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Re-find the proclock object (ditto).
|
||||||
|
*/
|
||||||
|
MemSet(&proclocktag, 0, sizeof(PROCLOCKTAG)); /* must clear padding */
|
||||||
|
proclocktag.lock = MAKE_OFFSET(lock);
|
||||||
|
proclocktag.proc = MAKE_OFFSET(proc);
|
||||||
|
proclock = (PROCLOCK *) hash_search(LockMethodProcLockHash[lockmethodid],
|
||||||
|
(void *) &proclocktag,
|
||||||
|
HASH_FIND, NULL);
|
||||||
|
if (!proclock)
|
||||||
|
elog(PANIC, "failed to re-find shared proclock object");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Double-check that we are actually holding a lock of the type we
|
||||||
|
* want to release.
|
||||||
|
*/
|
||||||
|
if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
|
||||||
|
{
|
||||||
|
PROCLOCK_PRINT("lock_twophase_postcommit: WRONGTYPE", proclock);
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
elog(WARNING, "you don't own a lock of type %s",
|
||||||
|
lock_mode_names[lockmode]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Do the releasing. CleanUpLock will waken any now-wakable waiters.
|
||||||
|
*/
|
||||||
|
wakeupNeeded = UnGrantLock(lock, lockmode, proclock, lockMethodTable);
|
||||||
|
|
||||||
|
CleanUpLock(lockmethodid, lock, proclock, wakeupNeeded);
|
||||||
|
|
||||||
|
LWLockRelease(masterLock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2PC processing routine for ROLLBACK PREPARED case.
|
||||||
|
*
|
||||||
|
* This is actually just the same as the COMMIT case.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
lock_twophase_postabort(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len)
|
||||||
|
{
|
||||||
|
lock_twophase_postcommit(xid, info, recdata, len);
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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.
|
* Report shared-memory space needed by InitProcGlobal.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ProcGlobalShmemSize(int maxBackends)
|
ProcGlobalShmemSize(void)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
|
size += MAXALIGN(sizeof(PROC_HDR)); /* ProcGlobal */
|
||||||
size += MAXALIGN(NUM_DUMMY_PROCS * sizeof(PGPROC)); /* DummyProcs */
|
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 */
|
size += MAXALIGN(sizeof(slock_t)); /* ProcStructLock */
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
|
@ -108,10 +108,10 @@ ProcGlobalShmemSize(int maxBackends)
|
||||||
* Report number of semaphores needed by InitProcGlobal.
|
* Report number of semaphores needed by InitProcGlobal.
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
ProcGlobalSemas(int maxBackends)
|
ProcGlobalSemas(void)
|
||||||
{
|
{
|
||||||
/* We need a sema per backend, plus one for each dummy process. */
|
/* 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.
|
* postmaster, not in backends.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
InitProcGlobal(int maxBackends)
|
InitProcGlobal(void)
|
||||||
{
|
{
|
||||||
bool foundProcGlobal,
|
bool foundProcGlobal,
|
||||||
foundDummy;
|
foundDummy;
|
||||||
|
@ -170,13 +170,13 @@ InitProcGlobal(int maxBackends)
|
||||||
* Pre-create the PGPROC structures and create a semaphore for
|
* Pre-create the PGPROC structures and create a semaphore for
|
||||||
* each.
|
* each.
|
||||||
*/
|
*/
|
||||||
procs = (PGPROC *) ShmemAlloc(maxBackends * sizeof(PGPROC));
|
procs = (PGPROC *) ShmemAlloc(MaxBackends * sizeof(PGPROC));
|
||||||
if (!procs)
|
if (!procs)
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
(errcode(ERRCODE_OUT_OF_MEMORY),
|
(errcode(ERRCODE_OUT_OF_MEMORY),
|
||||||
errmsg("out of shared memory")));
|
errmsg("out of shared memory")));
|
||||||
MemSet(procs, 0, maxBackends * sizeof(PGPROC));
|
MemSet(procs, 0, MaxBackends * sizeof(PGPROC));
|
||||||
for (i = 0; i < maxBackends; i++)
|
for (i = 0; i < MaxBackends; i++)
|
||||||
{
|
{
|
||||||
PGSemaphoreCreate(&(procs[i].sem));
|
PGSemaphoreCreate(&(procs[i].sem));
|
||||||
procs[i].links.next = ProcGlobal->freeProcs;
|
procs[i].links.next = ProcGlobal->freeProcs;
|
||||||
|
@ -254,7 +254,6 @@ InitProcess(void)
|
||||||
MyProc->xmin = InvalidTransactionId;
|
MyProc->xmin = InvalidTransactionId;
|
||||||
MyProc->pid = MyProcPid;
|
MyProc->pid = MyProcPid;
|
||||||
MyProc->databaseId = MyDatabaseId;
|
MyProc->databaseId = MyDatabaseId;
|
||||||
MyProc->logRec.xrecoff = 0;
|
|
||||||
MyProc->lwWaiting = false;
|
MyProc->lwWaiting = false;
|
||||||
MyProc->lwExclusive = false;
|
MyProc->lwExclusive = false;
|
||||||
MyProc->lwWaitLink = NULL;
|
MyProc->lwWaitLink = NULL;
|
||||||
|
@ -265,7 +264,7 @@ InitProcess(void)
|
||||||
/*
|
/*
|
||||||
* Add our PGPROC to the PGPROC array in shared memory.
|
* Add our PGPROC to the PGPROC array in shared memory.
|
||||||
*/
|
*/
|
||||||
ProcArrayAddMyself();
|
ProcArrayAdd(MyProc);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arrange to clean up at backend exit.
|
* Arrange to clean up at backend exit.
|
||||||
|
@ -332,7 +331,6 @@ InitDummyProcess(int proctype)
|
||||||
MyProc->xid = InvalidTransactionId;
|
MyProc->xid = InvalidTransactionId;
|
||||||
MyProc->xmin = InvalidTransactionId;
|
MyProc->xmin = InvalidTransactionId;
|
||||||
MyProc->databaseId = MyDatabaseId;
|
MyProc->databaseId = MyDatabaseId;
|
||||||
MyProc->logRec.xrecoff = 0;
|
|
||||||
MyProc->lwWaiting = false;
|
MyProc->lwWaiting = false;
|
||||||
MyProc->lwExclusive = false;
|
MyProc->lwExclusive = false;
|
||||||
MyProc->lwWaitLink = NULL;
|
MyProc->lwWaitLink = NULL;
|
||||||
|
@ -352,6 +350,35 @@ InitDummyProcess(int proctype)
|
||||||
PGSemaphoreReset(&MyProc->sem);
|
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.
|
* Cancel any pending wait for lock, when aborting a transaction.
|
||||||
*
|
*
|
||||||
|
@ -478,7 +505,7 @@ ProcKill(int code, Datum arg)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Remove our PGPROC from the PGPROC array in shared memory */
|
/* Remove our PGPROC from the PGPROC array in shared memory */
|
||||||
ProcArrayRemoveMyself();
|
ProcArrayRemove(MyProc);
|
||||||
|
|
||||||
SpinLockAcquire(ProcStructLock);
|
SpinLockAcquire(ProcStructLock);
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.89 2005/06/06 20:22:58 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/storage/smgr/smgr.c,v 1.90 2005/06/17 22:32:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -434,7 +434,7 @@ smgrscheduleunlink(SMgrRelation reln, bool isTemp)
|
||||||
* during transactional operations, since it can't be undone.
|
* during transactional operations, since it can't be undone.
|
||||||
*
|
*
|
||||||
* If isRedo is true, it is okay for the underlying file to be gone
|
* If isRedo is true, it is okay for the underlying file to be gone
|
||||||
* already. (In practice isRedo will always be true.)
|
* already.
|
||||||
*
|
*
|
||||||
* This also implies smgrclose() on the SMgrRelation object.
|
* This also implies smgrclose() on the SMgrRelation object.
|
||||||
*/
|
*/
|
||||||
|
@ -677,6 +677,30 @@ smgrimmedsync(SMgrRelation reln)
|
||||||
reln->smgr_rnode.relNode)));
|
reln->smgr_rnode.relNode)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PostPrepare_smgr -- Clean up after a successful PREPARE
|
||||||
|
*
|
||||||
|
* What we have to do here is throw away the in-memory state about pending
|
||||||
|
* relation deletes. It's all been recorded in the 2PC state file and
|
||||||
|
* it's no longer smgr's job to worry about it.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PostPrepare_smgr(void)
|
||||||
|
{
|
||||||
|
PendingRelDelete *pending;
|
||||||
|
PendingRelDelete *next;
|
||||||
|
|
||||||
|
for (pending = pendingDeletes; pending != NULL; pending = next)
|
||||||
|
{
|
||||||
|
next = pending->next;
|
||||||
|
pendingDeletes = next;
|
||||||
|
/* must explicitly free the list entry */
|
||||||
|
pfree(pending);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
|
* smgrDoPendingDeletes() -- Take care of relation deletes at end of xact.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
|
* NOTES
|
||||||
* this is the "main" module of the postgres backend and
|
* 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;
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
||||||
|
|
||||||
if (stmt->kind == TRANS_STMT_COMMIT ||
|
if (stmt->kind == TRANS_STMT_COMMIT ||
|
||||||
|
stmt->kind == TRANS_STMT_PREPARE ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK ||
|
stmt->kind == TRANS_STMT_ROLLBACK ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
||||||
allowit = true;
|
allowit = true;
|
||||||
|
@ -1261,6 +1262,7 @@ exec_parse_message(const char *query_string, /* string to execute */
|
||||||
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
TransactionStmt *stmt = (TransactionStmt *) parsetree;
|
||||||
|
|
||||||
if (stmt->kind == TRANS_STMT_COMMIT ||
|
if (stmt->kind == TRANS_STMT_COMMIT ||
|
||||||
|
stmt->kind == TRANS_STMT_PREPARE ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK ||
|
stmt->kind == TRANS_STMT_ROLLBACK ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
||||||
allowit = true;
|
allowit = true;
|
||||||
|
@ -1751,6 +1753,7 @@ exec_execute_message(const char *portal_name, long max_rows)
|
||||||
|
|
||||||
is_trans_stmt = true;
|
is_trans_stmt = true;
|
||||||
if (stmt->kind == TRANS_STMT_COMMIT ||
|
if (stmt->kind == TRANS_STMT_COMMIT ||
|
||||||
|
stmt->kind == TRANS_STMT_PREPARE ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK ||
|
stmt->kind == TRANS_STMT_ROLLBACK ||
|
||||||
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
stmt->kind == TRANS_STMT_ROLLBACK_TO)
|
||||||
is_trans_exit = true;
|
is_trans_exit = true;
|
||||||
|
|
|
@ -10,13 +10,14 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.236 2005/04/28 21:47:15 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.237 2005/06/17 22:32:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_shadow.h"
|
#include "catalog/pg_shadow.h"
|
||||||
|
@ -383,11 +384,11 @@ ProcessUtility(Node *parsetree,
|
||||||
if (strcmp(item->defname, "transaction_isolation") == 0)
|
if (strcmp(item->defname, "transaction_isolation") == 0)
|
||||||
SetPGVariable("transaction_isolation",
|
SetPGVariable("transaction_isolation",
|
||||||
list_make1(item->arg),
|
list_make1(item->arg),
|
||||||
false);
|
true);
|
||||||
else if (strcmp(item->defname, "transaction_read_only") == 0)
|
else if (strcmp(item->defname, "transaction_read_only") == 0)
|
||||||
SetPGVariable("transaction_read_only",
|
SetPGVariable("transaction_read_only",
|
||||||
list_make1(item->arg),
|
list_make1(item->arg),
|
||||||
false);
|
true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -401,6 +402,25 @@ ProcessUtility(Node *parsetree,
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_PREPARE:
|
||||||
|
if (!PrepareTransactionBlock(stmt->gid))
|
||||||
|
{
|
||||||
|
/* report unsuccessful commit in completionTag */
|
||||||
|
if (completionTag)
|
||||||
|
strcpy(completionTag, "ROLLBACK");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_COMMIT_PREPARED:
|
||||||
|
PreventTransactionChain(stmt, "COMMIT PREPARED");
|
||||||
|
FinishPreparedTransaction(stmt->gid, true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
||||||
|
PreventTransactionChain(stmt, "ROLLBACK PREPARED");
|
||||||
|
FinishPreparedTransaction(stmt->gid, false);
|
||||||
|
break;
|
||||||
|
|
||||||
case TRANS_STMT_ROLLBACK:
|
case TRANS_STMT_ROLLBACK:
|
||||||
UserAbortTransactionBlock();
|
UserAbortTransactionBlock();
|
||||||
break;
|
break;
|
||||||
|
@ -1215,6 +1235,18 @@ CreateCommandTag(Node *parsetree)
|
||||||
tag = "RELEASE";
|
tag = "RELEASE";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_PREPARE:
|
||||||
|
tag = "PREPARE TRANSACTION";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_COMMIT_PREPARED:
|
||||||
|
tag = "COMMIT PREPARED";
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TRANS_STMT_ROLLBACK_PREPARED:
|
||||||
|
tag = "ROLLBACK PREPARED";
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
tag = "???";
|
tag = "???";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -80,12 +80,13 @@
|
||||||
* 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/utils/cache/inval.c,v 1.71 2005/04/14 01:38:19 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/cache/inval.c,v 1.72 2005/06/17 22:32:46 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#include "access/twophase_rmgr.h"
|
||||||
#include "access/xact.h"
|
#include "access/xact.h"
|
||||||
#include "catalog/catalog.h"
|
#include "catalog/catalog.h"
|
||||||
#include "miscadmin.h"
|
#include "miscadmin.h"
|
||||||
|
@ -171,6 +172,13 @@ static struct CACHECALLBACK
|
||||||
|
|
||||||
static int cache_callback_count = 0;
|
static int cache_callback_count = 0;
|
||||||
|
|
||||||
|
/* info values for 2PC callback */
|
||||||
|
#define TWOPHASE_INFO_MSG 0 /* SharedInvalidationMessage */
|
||||||
|
#define TWOPHASE_INFO_FILE_BEFORE 1 /* relcache file inval */
|
||||||
|
#define TWOPHASE_INFO_FILE_AFTER 2 /* relcache file inval */
|
||||||
|
|
||||||
|
static void PersistInvalidationMessage(SharedInvalidationMessage *msg);
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------------------------------
|
/* ----------------------------------------------------------------
|
||||||
* Invalidation list support functions
|
* Invalidation list support functions
|
||||||
|
@ -636,6 +644,56 @@ AtStart_Inval(void)
|
||||||
transInvalInfo->my_level = GetCurrentTransactionNestLevel();
|
transInvalInfo->my_level = GetCurrentTransactionNestLevel();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* AtPrepare_Inval
|
||||||
|
* Save the inval lists state at 2PC transaction prepare.
|
||||||
|
*
|
||||||
|
* In this phase we just generate 2PC records for all the pending invalidation
|
||||||
|
* work.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AtPrepare_Inval(void)
|
||||||
|
{
|
||||||
|
/* Must be at top of stack */
|
||||||
|
Assert(transInvalInfo != NULL && transInvalInfo->parent == NULL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Relcache init file invalidation requires processing both before
|
||||||
|
* and after we send the SI messages.
|
||||||
|
*/
|
||||||
|
if (transInvalInfo->RelcacheInitFileInval)
|
||||||
|
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_FILE_BEFORE,
|
||||||
|
NULL, 0);
|
||||||
|
|
||||||
|
AppendInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
|
||||||
|
&transInvalInfo->CurrentCmdInvalidMsgs);
|
||||||
|
|
||||||
|
ProcessInvalidationMessages(&transInvalInfo->PriorCmdInvalidMsgs,
|
||||||
|
PersistInvalidationMessage);
|
||||||
|
|
||||||
|
if (transInvalInfo->RelcacheInitFileInval)
|
||||||
|
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_FILE_AFTER,
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PostPrepare_Inval
|
||||||
|
* Clean up after successful PREPARE.
|
||||||
|
*
|
||||||
|
* Here, we want to act as though the transaction aborted, so that we will
|
||||||
|
* undo any syscache changes it made, thereby bringing us into sync with the
|
||||||
|
* outside world, which doesn't believe the transaction committed yet.
|
||||||
|
*
|
||||||
|
* If the prepared transaction is later aborted, there is nothing more to
|
||||||
|
* do; if it commits, we will receive the consequent inval messages just
|
||||||
|
* like everyone else.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
PostPrepare_Inval(void)
|
||||||
|
{
|
||||||
|
AtEOXact_Inval(false);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AtSubStart_Inval
|
* AtSubStart_Inval
|
||||||
* Initialize inval lists at start of a subtransaction.
|
* Initialize inval lists at start of a subtransaction.
|
||||||
|
@ -654,6 +712,47 @@ AtSubStart_Inval(void)
|
||||||
transInvalInfo = myInfo;
|
transInvalInfo = myInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* PersistInvalidationMessage
|
||||||
|
* Write an invalidation message to the 2PC state file.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
PersistInvalidationMessage(SharedInvalidationMessage *msg)
|
||||||
|
{
|
||||||
|
RegisterTwoPhaseRecord(TWOPHASE_RM_INVAL_ID, TWOPHASE_INFO_MSG,
|
||||||
|
msg, sizeof(SharedInvalidationMessage));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* inval_twophase_postcommit
|
||||||
|
* Process an invalidation message from the 2PC state file.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
inval_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len)
|
||||||
|
{
|
||||||
|
SharedInvalidationMessage *msg;
|
||||||
|
|
||||||
|
switch (info)
|
||||||
|
{
|
||||||
|
case TWOPHASE_INFO_MSG:
|
||||||
|
msg = (SharedInvalidationMessage *) recdata;
|
||||||
|
Assert(len == sizeof(SharedInvalidationMessage));
|
||||||
|
SendSharedInvalidMessage(msg);
|
||||||
|
break;
|
||||||
|
case TWOPHASE_INFO_FILE_BEFORE:
|
||||||
|
RelationCacheInitFileInvalidate(true);
|
||||||
|
break;
|
||||||
|
case TWOPHASE_INFO_FILE_AFTER:
|
||||||
|
RelationCacheInitFileInvalidate(false);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Assert(false);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AtEOXact_Inval
|
* AtEOXact_Inval
|
||||||
* Process queued-up invalidation messages at end of main transaction.
|
* Process queued-up invalidation messages at end of main transaction.
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/utils/init/flatfiles.c,v 1.7 2005/06/06 17:01:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/init/flatfiles.c,v 1.8 2005/06/17 22:32:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -32,6 +32,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "access/heapam.h"
|
#include "access/heapam.h"
|
||||||
|
#include "access/twophase_rmgr.h"
|
||||||
#include "catalog/pg_database.h"
|
#include "catalog/pg_database.h"
|
||||||
#include "catalog/pg_group.h"
|
#include "catalog/pg_group.h"
|
||||||
#include "catalog/pg_namespace.h"
|
#include "catalog/pg_namespace.h"
|
||||||
|
@ -48,10 +49,16 @@
|
||||||
#include "utils/syscache.h"
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
|
||||||
|
/* Actual names of the flat files (within $PGDATA/global/) */
|
||||||
#define DATABASE_FLAT_FILE "pg_database"
|
#define DATABASE_FLAT_FILE "pg_database"
|
||||||
#define GROUP_FLAT_FILE "pg_group"
|
#define GROUP_FLAT_FILE "pg_group"
|
||||||
#define USER_FLAT_FILE "pg_pwd"
|
#define USER_FLAT_FILE "pg_pwd"
|
||||||
|
|
||||||
|
/* Info bits in a flatfiles 2PC record */
|
||||||
|
#define FF_BIT_DATABASE 1
|
||||||
|
#define FF_BIT_GROUP 2
|
||||||
|
#define FF_BIT_USER 4
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The need-to-update-files flags are SubTransactionIds that show
|
* The need-to-update-files flags are SubTransactionIds that show
|
||||||
|
@ -757,6 +764,43 @@ AtEOXact_UpdateFlatFiles(bool isCommit)
|
||||||
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
|
SendPostmasterSignal(PMSIGNAL_PASSWORD_CHANGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This routine is called during transaction prepare.
|
||||||
|
*
|
||||||
|
* Record which files need to be refreshed if this transaction later
|
||||||
|
* commits.
|
||||||
|
*
|
||||||
|
* Note: it's OK to clear the flags immediately, since if the PREPARE fails
|
||||||
|
* further on, we'd only reset the flags anyway. So there's no need for a
|
||||||
|
* separate PostPrepare call.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
AtPrepare_UpdateFlatFiles(void)
|
||||||
|
{
|
||||||
|
uint16 info = 0;
|
||||||
|
|
||||||
|
if (database_file_update_subid != InvalidSubTransactionId)
|
||||||
|
{
|
||||||
|
database_file_update_subid = InvalidSubTransactionId;
|
||||||
|
info |= FF_BIT_DATABASE;
|
||||||
|
}
|
||||||
|
if (group_file_update_subid != InvalidSubTransactionId)
|
||||||
|
{
|
||||||
|
group_file_update_subid = InvalidSubTransactionId;
|
||||||
|
info |= FF_BIT_GROUP;
|
||||||
|
}
|
||||||
|
if (user_file_update_subid != InvalidSubTransactionId)
|
||||||
|
{
|
||||||
|
user_file_update_subid = InvalidSubTransactionId;
|
||||||
|
info |= FF_BIT_USER;
|
||||||
|
}
|
||||||
|
if (info != 0)
|
||||||
|
RegisterTwoPhaseRecord(TWOPHASE_RM_FLATFILES_ID, info,
|
||||||
|
NULL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* AtEOSubXact_UpdateFlatFiles
|
* AtEOSubXact_UpdateFlatFiles
|
||||||
*
|
*
|
||||||
|
@ -831,3 +875,28 @@ flatfile_update_trigger(PG_FUNCTION_ARGS)
|
||||||
|
|
||||||
return PointerGetDatum(NULL);
|
return PointerGetDatum(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2PC processing routine for COMMIT PREPARED case.
|
||||||
|
*
|
||||||
|
* (We don't have to do anything for ROLLBACK PREPARED.)
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
flatfile_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Set flags to do the needed file updates 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.)
|
||||||
|
*/
|
||||||
|
if (info & FF_BIT_DATABASE)
|
||||||
|
database_file_update_needed();
|
||||||
|
if (info & FF_BIT_GROUP)
|
||||||
|
group_file_update_needed();
|
||||||
|
if (info & FF_BIT_USER)
|
||||||
|
user_file_update_needed();
|
||||||
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.147 2005/05/19 21:35:47 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/init/postinit.c,v 1.148 2005/06/17 22:32:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
|
@ -232,7 +232,7 @@ InitCommunication(void)
|
||||||
* We're running a postgres bootstrap process or a standalone
|
* We're running a postgres bootstrap process or a standalone
|
||||||
* backend. Create private "shmem" and semaphores.
|
* backend. Create private "shmem" and semaphores.
|
||||||
*/
|
*/
|
||||||
CreateSharedMemoryAndSemaphores(true, MaxBackends, 0);
|
CreateSharedMemoryAndSemaphores(true, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -456,7 +456,7 @@ InitPostgres(const char *dbname, const char *username)
|
||||||
*/
|
*/
|
||||||
if (!am_superuser &&
|
if (!am_superuser &&
|
||||||
ReservedBackends > 0 &&
|
ReservedBackends > 0 &&
|
||||||
CountEmptyBackendSlots() < ReservedBackends)
|
!HaveNFreeProcs(ReservedBackends))
|
||||||
ereport(FATAL,
|
ereport(FATAL,
|
||||||
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
(errcode(ERRCODE_TOO_MANY_CONNECTIONS),
|
||||||
errmsg("connection limit exceeded for non-superusers")));
|
errmsg("connection limit exceeded for non-superusers")));
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
* Written by Peter Eisentraut <peter_e@gmx.net>.
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.267 2005/06/16 20:47:20 momjian Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.268 2005/06/17 22:32:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*--------------------------------------------------------------------
|
*--------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -25,6 +25,7 @@
|
||||||
#include "utils/guc.h"
|
#include "utils/guc.h"
|
||||||
#include "utils/guc_tables.h"
|
#include "utils/guc_tables.h"
|
||||||
|
|
||||||
|
#include "access/twophase.h"
|
||||||
#include "catalog/namespace.h"
|
#include "catalog/namespace.h"
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "commands/async.h"
|
#include "commands/async.h"
|
||||||
|
@ -1102,6 +1103,15 @@ static struct config_int ConfigureNamesInt[] =
|
||||||
1000, 25, INT_MAX, NULL, NULL
|
1000, 25, INT_MAX, NULL, NULL
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
{"max_prepared_transactions", PGC_POSTMASTER, RESOURCES,
|
||||||
|
gettext_noop("Sets the maximum number of simultaneously prepared transactions."),
|
||||||
|
NULL
|
||||||
|
},
|
||||||
|
&max_prepared_xacts,
|
||||||
|
50, 0, 10000, NULL, NULL
|
||||||
|
},
|
||||||
|
|
||||||
#ifdef LOCK_DEBUG
|
#ifdef LOCK_DEBUG
|
||||||
{
|
{
|
||||||
{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,
|
{"trace_lock_oidmin", PGC_SUSET, DEVELOPER_OPTIONS,
|
||||||
|
|
|
@ -79,6 +79,7 @@
|
||||||
|
|
||||||
#shared_buffers = 1000 # min 16, at least max_connections*2, 8KB each
|
#shared_buffers = 1000 # min 16, at least max_connections*2, 8KB each
|
||||||
#temp_buffers = 1000 # min 100, 8KB each
|
#temp_buffers = 1000 # min 100, 8KB each
|
||||||
|
#max_prepared_transactions = 50 # 0-10000
|
||||||
#work_mem = 1024 # min 64, size in KB
|
#work_mem = 1024 # min 64, size in KB
|
||||||
#maintenance_work_mem = 16384 # min 1024, size in KB
|
#maintenance_work_mem = 16384 # min 1024, size in KB
|
||||||
#max_stack_depth = 2048 # min 100, size in KB
|
#max_stack_depth = 2048 # min 100, size in KB
|
||||||
|
|
|
@ -12,7 +12,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/utils/mmgr/portalmem.c,v 1.80 2005/05/29 04:23:06 tgl Exp $
|
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.81 2005/06/17 22:32:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -466,6 +466,48 @@ CommitHoldablePortals(void)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pre-prepare processing for portals.
|
||||||
|
*
|
||||||
|
* Currently we refuse PREPARE if the transaction created any holdable
|
||||||
|
* cursors, since it's quite unclear what to do with one. However, this
|
||||||
|
* has the same API as CommitHoldablePortals and is invoked in the same
|
||||||
|
* way by xact.c, so that we can easily do something reasonable if anyone
|
||||||
|
* comes up with something reasonable to do.
|
||||||
|
*
|
||||||
|
* Returns TRUE if any holdable cursors were processed, FALSE if not.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
PrepareHoldablePortals(void)
|
||||||
|
{
|
||||||
|
bool result = false;
|
||||||
|
HASH_SEQ_STATUS status;
|
||||||
|
PortalHashEnt *hentry;
|
||||||
|
|
||||||
|
hash_seq_init(&status, PortalHashTable);
|
||||||
|
|
||||||
|
while ((hentry = (PortalHashEnt *) hash_seq_search(&status)) != NULL)
|
||||||
|
{
|
||||||
|
Portal portal = hentry->portal;
|
||||||
|
|
||||||
|
/* Is it a holdable portal created in the current xact? */
|
||||||
|
if ((portal->cursorOptions & CURSOR_OPT_HOLD) &&
|
||||||
|
portal->createSubid != InvalidSubTransactionId &&
|
||||||
|
portal->status == PORTAL_READY)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We are exiting the transaction that created a holdable
|
||||||
|
* cursor. Can't do PREPARE.
|
||||||
|
*/
|
||||||
|
ereport(ERROR,
|
||||||
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
||||||
|
errmsg("cannot PREPARE a transaction that has created a cursor WITH HOLD")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pre-commit processing for portals.
|
* Pre-commit processing for portals.
|
||||||
*
|
*
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
* Portions taken from FreeBSD.
|
* Portions taken from FreeBSD.
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.83 2005/04/30 08:08:51 neilc Exp $
|
* $PostgreSQL: pgsql/src/bin/initdb/initdb.c,v 1.84 2005/06/17 22:32:47 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -2124,6 +2124,7 @@ main(int argc, char *argv[])
|
||||||
"pg_xlog/archive_status",
|
"pg_xlog/archive_status",
|
||||||
"pg_clog",
|
"pg_clog",
|
||||||
"pg_subtrans",
|
"pg_subtrans",
|
||||||
|
"pg_twophase",
|
||||||
"pg_multixact/members",
|
"pg_multixact/members",
|
||||||
"pg_multixact/offsets",
|
"pg_multixact/offsets",
|
||||||
"base",
|
"base",
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
*
|
*
|
||||||
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.102 2005/06/14 02:57:41 momjian Exp $
|
* $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.103 2005/06/17 22:32:47 tgl Exp $
|
||||||
*/
|
*/
|
||||||
#include "postgres_fe.h"
|
#include "postgres_fe.h"
|
||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
@ -1216,6 +1216,21 @@ command_no_begin(const char *query)
|
||||||
return true;
|
return true;
|
||||||
if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
|
if (wordlen == 8 && pg_strncasecmp(query, "rollback", 8) == 0)
|
||||||
return true;
|
return true;
|
||||||
|
if (wordlen == 7 && pg_strncasecmp(query, "prepare", 7) == 0)
|
||||||
|
{
|
||||||
|
/* PREPARE TRANSACTION is a TC command, PREPARE foo is not */
|
||||||
|
query += wordlen;
|
||||||
|
|
||||||
|
query = skip_white_space(query);
|
||||||
|
|
||||||
|
wordlen = 0;
|
||||||
|
while (isalpha((unsigned char) query[wordlen]))
|
||||||
|
wordlen += PQmblen(&query[wordlen], pset.encoding);
|
||||||
|
|
||||||
|
if (wordlen == 11 && pg_strncasecmp(query, "transaction", 11) == 0)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Commands not allowed within transactions. The statements checked
|
* Commands not allowed within transactions. The statements checked
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/access/subtrans.h,v 1.5 2004/12/31 22:03:21 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/access/subtrans.h,v 1.6 2005/06/17 22:32:48 tgl Exp $
|
||||||
*/
|
*/
|
||||||
#ifndef SUBTRANS_H
|
#ifndef SUBTRANS_H
|
||||||
#define SUBTRANS_H
|
#define SUBTRANS_H
|
||||||
|
@ -18,7 +18,7 @@ extern TransactionId SubTransGetTopmostTransaction(TransactionId xid);
|
||||||
extern int SUBTRANSShmemSize(void);
|
extern int SUBTRANSShmemSize(void);
|
||||||
extern void SUBTRANSShmemInit(void);
|
extern void SUBTRANSShmemInit(void);
|
||||||
extern void BootStrapSUBTRANS(void);
|
extern void BootStrapSUBTRANS(void);
|
||||||
extern void StartupSUBTRANS(void);
|
extern void StartupSUBTRANS(TransactionId oldestActiveXID);
|
||||||
extern void ShutdownSUBTRANS(void);
|
extern void ShutdownSUBTRANS(void);
|
||||||
extern void CheckPointSUBTRANS(void);
|
extern void CheckPointSUBTRANS(void);
|
||||||
extern void ExtendSUBTRANS(TransactionId newestXact);
|
extern void ExtendSUBTRANS(TransactionId newestXact);
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* twophase.h
|
||||||
|
* Two-phase-commit related declarations.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: pgsql/src/include/access/twophase.h,v 1.1 2005/06/17 22:32:48 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef TWOPHASE_H
|
||||||
|
#define TWOPHASE_H
|
||||||
|
|
||||||
|
#include "storage/lock.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* GlobalTransactionData is defined in twophase.c; other places have no
|
||||||
|
* business knowing the internal definition.
|
||||||
|
*/
|
||||||
|
typedef struct GlobalTransactionData *GlobalTransaction;
|
||||||
|
|
||||||
|
/* GUC variable */
|
||||||
|
extern int max_prepared_xacts;
|
||||||
|
|
||||||
|
extern int TwoPhaseShmemSize(void);
|
||||||
|
extern void TwoPhaseShmemInit(void);
|
||||||
|
|
||||||
|
extern PGPROC *TwoPhaseGetDummyProc(TransactionId xid);
|
||||||
|
|
||||||
|
extern GlobalTransaction MarkAsPreparing(TransactionId xid, Oid databaseid,
|
||||||
|
char *gid, AclId owner);
|
||||||
|
extern void MarkAsPrepared(GlobalTransaction gxact);
|
||||||
|
|
||||||
|
extern void StartPrepare(GlobalTransaction gxact);
|
||||||
|
extern void EndPrepare(GlobalTransaction gxact);
|
||||||
|
|
||||||
|
extern TransactionId PrescanPreparedTransactions(void);
|
||||||
|
extern void RecoverPreparedTransactions(void);
|
||||||
|
|
||||||
|
extern void RecreateTwoPhaseFile(TransactionId xid, void *content, int len);
|
||||||
|
extern void RemoveTwoPhaseFile(TransactionId xid, bool giveWarning);
|
||||||
|
|
||||||
|
extern void FinishPreparedTransaction(char *gid, bool isCommit);
|
||||||
|
|
||||||
|
#endif /* TWOPHASE_H */
|
|
@ -0,0 +1,39 @@
|
||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* twophase_rmgr.h
|
||||||
|
* Two-phase-commit resource managers definition
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
||||||
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $PostgreSQL: pgsql/src/include/access/twophase_rmgr.h,v 1.1 2005/06/17 22:32:48 tgl Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef TWOPHASE_RMGR_H
|
||||||
|
#define TWOPHASE_RMGR_H
|
||||||
|
|
||||||
|
typedef void (*TwoPhaseCallback) (TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
typedef uint8 TwoPhaseRmgrId;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Built-in resource managers
|
||||||
|
*/
|
||||||
|
#define TWOPHASE_RM_END_ID 0
|
||||||
|
#define TWOPHASE_RM_LOCK_ID 1
|
||||||
|
#define TWOPHASE_RM_INVAL_ID 2
|
||||||
|
#define TWOPHASE_RM_FLATFILES_ID 3
|
||||||
|
#define TWOPHASE_RM_NOTIFY_ID 4
|
||||||
|
#define TWOPHASE_RM_MAX_ID TWOPHASE_RM_NOTIFY_ID
|
||||||
|
|
||||||
|
extern const TwoPhaseCallback twophase_recover_callbacks[];
|
||||||
|
extern const TwoPhaseCallback twophase_postcommit_callbacks[];
|
||||||
|
extern const TwoPhaseCallback twophase_postabort_callbacks[];
|
||||||
|
|
||||||
|
|
||||||
|
extern void RegisterTwoPhaseRecord(TwoPhaseRmgrId rmid, uint16 info,
|
||||||
|
const void *data, uint32 len);
|
||||||
|
|
||||||
|
#endif /* TWOPHASE_RMGR_H */
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/access/xact.h,v 1.76 2005/06/06 17:01:24 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/access/xact.h,v 1.77 2005/06/17 22:32:48 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -47,7 +47,8 @@ extern bool XactReadOnly;
|
||||||
typedef enum
|
typedef enum
|
||||||
{
|
{
|
||||||
XACT_EVENT_COMMIT,
|
XACT_EVENT_COMMIT,
|
||||||
XACT_EVENT_ABORT
|
XACT_EVENT_ABORT,
|
||||||
|
XACT_EVENT_PREPARE
|
||||||
} XactEvent;
|
} XactEvent;
|
||||||
|
|
||||||
typedef void (*XactCallback) (XactEvent event, void *arg);
|
typedef void (*XactCallback) (XactEvent event, void *arg);
|
||||||
|
@ -72,8 +73,11 @@ typedef void (*SubXactCallback) (SubXactEvent event, SubTransactionId mySubid,
|
||||||
* XLOG allows to store some information in high 4 bits of log
|
* XLOG allows to store some information in high 4 bits of log
|
||||||
* record xl_info field
|
* record xl_info field
|
||||||
*/
|
*/
|
||||||
#define XLOG_XACT_COMMIT 0x00
|
#define XLOG_XACT_COMMIT 0x00
|
||||||
#define XLOG_XACT_ABORT 0x20
|
#define XLOG_XACT_PREPARE 0x10
|
||||||
|
#define XLOG_XACT_ABORT 0x20
|
||||||
|
#define XLOG_XACT_COMMIT_PREPARED 0x30
|
||||||
|
#define XLOG_XACT_ABORT_PREPARED 0x40
|
||||||
|
|
||||||
typedef struct xl_xact_commit
|
typedef struct xl_xact_commit
|
||||||
{
|
{
|
||||||
|
@ -99,6 +103,31 @@ typedef struct xl_xact_abort
|
||||||
|
|
||||||
#define MinSizeOfXactAbort offsetof(xl_xact_abort, xnodes)
|
#define MinSizeOfXactAbort offsetof(xl_xact_abort, xnodes)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* COMMIT_PREPARED and ABORT_PREPARED are identical to COMMIT/ABORT records
|
||||||
|
* except that we have to store the XID of the prepared transaction explicitly
|
||||||
|
* --- the XID in the record header will be for the transaction doing the
|
||||||
|
* COMMIT PREPARED or ABORT PREPARED command.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct xl_xact_commit_prepared
|
||||||
|
{
|
||||||
|
TransactionId xid; /* XID of prepared xact */
|
||||||
|
xl_xact_commit crec; /* COMMIT record */
|
||||||
|
/* MORE DATA FOLLOWS AT END OF STRUCT */
|
||||||
|
} xl_xact_commit_prepared;
|
||||||
|
|
||||||
|
#define MinSizeOfXactCommitPrepared offsetof(xl_xact_commit_prepared, crec.xnodes)
|
||||||
|
|
||||||
|
typedef struct xl_xact_abort_prepared
|
||||||
|
{
|
||||||
|
TransactionId xid; /* XID of prepared xact */
|
||||||
|
xl_xact_abort arec; /* ABORT record */
|
||||||
|
/* MORE DATA FOLLOWS AT END OF STRUCT */
|
||||||
|
} xl_xact_abort_prepared;
|
||||||
|
|
||||||
|
#define MinSizeOfXactAbortPrepared offsetof(xl_xact_abort_prepared, arec.xnodes)
|
||||||
|
|
||||||
|
|
||||||
/* ----------------
|
/* ----------------
|
||||||
* extern definitions
|
* extern definitions
|
||||||
|
@ -121,6 +150,7 @@ extern void CommitTransactionCommand(void);
|
||||||
extern void AbortCurrentTransaction(void);
|
extern void AbortCurrentTransaction(void);
|
||||||
extern void BeginTransactionBlock(void);
|
extern void BeginTransactionBlock(void);
|
||||||
extern bool EndTransactionBlock(void);
|
extern bool EndTransactionBlock(void);
|
||||||
|
extern bool PrepareTransactionBlock(char *gid);
|
||||||
extern void UserAbortTransactionBlock(void);
|
extern void UserAbortTransactionBlock(void);
|
||||||
extern void ReleaseSavepoint(List *options);
|
extern void ReleaseSavepoint(List *options);
|
||||||
extern void DefineSavepoint(char *name);
|
extern void DefineSavepoint(char *name);
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/catalog/catversion.h,v 1.276 2005/06/15 12:56:35 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.277 2005/06/17 22:32:48 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -53,6 +53,6 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* yyyymmddN */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200506151
|
#define CATALOG_VERSION_NO 200506171
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/catalog/pg_proc.h,v 1.367 2005/06/14 21:04:41 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.368 2005/06/17 22:32:48 tgl Exp $
|
||||||
*
|
*
|
||||||
* NOTES
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* The script catalog/genbki.sh reads this file and generates .bki
|
||||||
|
@ -3005,6 +3005,8 @@ DATA(insert OID = 2084 ( pg_show_all_settings PGNSP PGUID 12 f f t t s 0 2249 "
|
||||||
DESCR("SHOW ALL as a function");
|
DESCR("SHOW ALL as a function");
|
||||||
DATA(insert OID = 1371 ( pg_lock_status PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_lock_status - _null_ ));
|
DATA(insert OID = 1371 ( pg_lock_status PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_lock_status - _null_ ));
|
||||||
DESCR("view system lock information");
|
DESCR("view system lock information");
|
||||||
|
DATA(insert OID = 1065 ( pg_prepared_xact PGNSP PGUID 12 f f t t v 0 2249 "" _null_ _null_ _null_ pg_prepared_xact - _null_ ));
|
||||||
|
DESCR("view two-phase transactions");
|
||||||
|
|
||||||
DATA(insert OID = 2079 ( pg_table_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" _null_ _null_ _null_ pg_table_is_visible - _null_ ));
|
DATA(insert OID = 2079 ( pg_table_is_visible PGNSP PGUID 12 f f t f s 1 16 "26" _null_ _null_ _null_ pg_table_is_visible - _null_ ));
|
||||||
DESCR("is table visible in search path?");
|
DESCR("is table visible in search path?");
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/commands/async.h,v 1.27 2004/12/31 22:03:28 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/commands/async.h,v 1.28 2005/06/17 22:32:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -26,6 +26,7 @@ extern void AtAbort_Notify(void);
|
||||||
extern void AtSubStart_Notify(void);
|
extern void AtSubStart_Notify(void);
|
||||||
extern void AtSubCommit_Notify(void);
|
extern void AtSubCommit_Notify(void);
|
||||||
extern void AtSubAbort_Notify(void);
|
extern void AtSubAbort_Notify(void);
|
||||||
|
extern void AtPrepare_Notify(void);
|
||||||
|
|
||||||
/* signal handler for inbound notifies (SIGUSR2) */
|
/* signal handler for inbound notifies (SIGUSR2) */
|
||||||
extern void NotifyInterruptHandler(SIGNAL_ARGS);
|
extern void NotifyInterruptHandler(SIGNAL_ARGS);
|
||||||
|
@ -38,4 +39,7 @@ extern void NotifyInterruptHandler(SIGNAL_ARGS);
|
||||||
extern void EnableNotifyInterrupt(void);
|
extern void EnableNotifyInterrupt(void);
|
||||||
extern bool DisableNotifyInterrupt(void);
|
extern bool DisableNotifyInterrupt(void);
|
||||||
|
|
||||||
|
extern void notify_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
|
||||||
#endif /* ASYNC_H */
|
#endif /* ASYNC_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/nodes/parsenodes.h,v 1.281 2005/06/05 22:32:57 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.282 2005/06/17 22:32:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -1556,7 +1556,10 @@ typedef enum TransactionStmtKind
|
||||||
TRANS_STMT_ROLLBACK,
|
TRANS_STMT_ROLLBACK,
|
||||||
TRANS_STMT_SAVEPOINT,
|
TRANS_STMT_SAVEPOINT,
|
||||||
TRANS_STMT_RELEASE,
|
TRANS_STMT_RELEASE,
|
||||||
TRANS_STMT_ROLLBACK_TO
|
TRANS_STMT_ROLLBACK_TO,
|
||||||
|
TRANS_STMT_PREPARE,
|
||||||
|
TRANS_STMT_COMMIT_PREPARED,
|
||||||
|
TRANS_STMT_ROLLBACK_PREPARED
|
||||||
} TransactionStmtKind;
|
} TransactionStmtKind;
|
||||||
|
|
||||||
typedef struct TransactionStmt
|
typedef struct TransactionStmt
|
||||||
|
@ -1564,6 +1567,7 @@ typedef struct TransactionStmt
|
||||||
NodeTag type;
|
NodeTag type;
|
||||||
TransactionStmtKind kind; /* see above */
|
TransactionStmtKind kind; /* see above */
|
||||||
List *options; /* for BEGIN/START and savepoint commands */
|
List *options; /* for BEGIN/START and savepoint commands */
|
||||||
|
char *gid; /* for two-phase-commit related commands */
|
||||||
} TransactionStmt;
|
} TransactionStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/ipc.h,v 1.70 2004/12/31 22:03:42 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/storage/ipc.h,v 1.71 2005/06/17 22:32:49 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -29,8 +29,6 @@ extern void on_shmem_exit(void (*function) (int code, Datum arg), Datum arg);
|
||||||
extern void on_exit_reset(void);
|
extern void on_exit_reset(void);
|
||||||
|
|
||||||
/* ipci.c */
|
/* ipci.c */
|
||||||
extern void CreateSharedMemoryAndSemaphores(bool makePrivate,
|
extern void CreateSharedMemoryAndSemaphores(bool makePrivate, int port);
|
||||||
int maxBackends,
|
|
||||||
int port);
|
|
||||||
|
|
||||||
#endif /* IPC_H */
|
#endif /* IPC_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/lmgr.h,v 1.49 2005/06/14 22:15:33 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lmgr.h,v 1.50 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -41,7 +41,7 @@
|
||||||
* so increase that if you want to add more modes.
|
* so increase that if you want to add more modes.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
extern void InitLockTable(int maxBackends);
|
extern void InitLockTable(void);
|
||||||
extern void RelationInitLockInfo(Relation relation);
|
extern void RelationInitLockInfo(Relation relation);
|
||||||
|
|
||||||
/* Lock a relation */
|
/* Lock a relation */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/lock.h,v 1.88 2005/06/14 22:15:33 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lock.h,v 1.89 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -370,7 +370,7 @@ extern void InitLocks(void);
|
||||||
extern LockMethod GetLocksMethodTable(LOCK *lock);
|
extern LockMethod GetLocksMethodTable(LOCK *lock);
|
||||||
extern LOCKMETHODID LockMethodTableInit(const char *tabName,
|
extern LOCKMETHODID LockMethodTableInit(const char *tabName,
|
||||||
const LOCKMASK *conflictsP,
|
const LOCKMASK *conflictsP,
|
||||||
int numModes, int maxBackends);
|
int numModes);
|
||||||
extern LOCKMETHODID LockMethodTableRename(LOCKMETHODID lockmethodid);
|
extern LOCKMETHODID LockMethodTableRename(LOCKMETHODID lockmethodid);
|
||||||
extern LockAcquireResult LockAcquire(LOCKMETHODID lockmethodid,
|
extern LockAcquireResult LockAcquire(LOCKMETHODID lockmethodid,
|
||||||
LOCKTAG *locktag,
|
LOCKTAG *locktag,
|
||||||
|
@ -383,13 +383,15 @@ extern bool LockRelease(LOCKMETHODID lockmethodid, LOCKTAG *locktag,
|
||||||
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
|
extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
|
||||||
extern void LockReleaseCurrentOwner(void);
|
extern void LockReleaseCurrentOwner(void);
|
||||||
extern void LockReassignCurrentOwner(void);
|
extern void LockReassignCurrentOwner(void);
|
||||||
|
extern void AtPrepare_Locks(void);
|
||||||
|
extern void PostPrepare_Locks(TransactionId xid);
|
||||||
extern int LockCheckConflicts(LockMethod lockMethodTable,
|
extern int LockCheckConflicts(LockMethod lockMethodTable,
|
||||||
LOCKMODE lockmode,
|
LOCKMODE lockmode,
|
||||||
LOCK *lock, PROCLOCK *proclock, PGPROC *proc);
|
LOCK *lock, PROCLOCK *proclock, PGPROC *proc);
|
||||||
extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
|
extern void GrantLock(LOCK *lock, PROCLOCK *proclock, LOCKMODE lockmode);
|
||||||
extern void GrantAwaitedLock(void);
|
extern void GrantAwaitedLock(void);
|
||||||
extern void RemoveFromWaitQueue(PGPROC *proc);
|
extern void RemoveFromWaitQueue(PGPROC *proc);
|
||||||
extern int LockShmemSize(int maxBackends);
|
extern int LockShmemSize(void);
|
||||||
extern bool DeadLockCheck(PGPROC *proc);
|
extern bool DeadLockCheck(PGPROC *proc);
|
||||||
extern void DeadLockReport(void);
|
extern void DeadLockReport(void);
|
||||||
extern void RememberSimpleDeadLock(PGPROC *proc1,
|
extern void RememberSimpleDeadLock(PGPROC *proc1,
|
||||||
|
@ -400,8 +402,15 @@ extern void InitDeadLockChecking(void);
|
||||||
extern LockData *GetLockStatusData(void);
|
extern LockData *GetLockStatusData(void);
|
||||||
extern const char *GetLockmodeName(LOCKMODE mode);
|
extern const char *GetLockmodeName(LOCKMODE mode);
|
||||||
|
|
||||||
|
extern void lock_twophase_recover(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
extern void lock_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
extern void lock_twophase_postabort(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
|
||||||
#ifdef LOCK_DEBUG
|
#ifdef LOCK_DEBUG
|
||||||
extern void DumpLocks(void);
|
extern void DumpLocks(PGPROC *proc);
|
||||||
extern void DumpAllLocks(void);
|
extern void DumpAllLocks(void);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/lwlock.h,v 1.19 2005/05/19 21:35:47 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/lwlock.h,v 1.20 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +46,7 @@ typedef enum LWLockId
|
||||||
MultiXactMemberControlLock,
|
MultiXactMemberControlLock,
|
||||||
RelCacheInitLock,
|
RelCacheInitLock,
|
||||||
BgWriterCommLock,
|
BgWriterCommLock,
|
||||||
|
TwoPhaseStateLock,
|
||||||
|
|
||||||
NumFixedLWLocks, /* must be last except for
|
NumFixedLWLocks, /* must be last except for
|
||||||
* MaxDynamicLWLock */
|
* MaxDynamicLWLock */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/proc.h,v 1.78 2005/05/19 21:35:47 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/proc.h,v 1.79 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -46,6 +46,13 @@ struct XidCache
|
||||||
* links: list link for any list the PGPROC is in. When waiting for a lock,
|
* links: list link for any list the PGPROC is in. When waiting for a lock,
|
||||||
* the PGPROC is linked into that lock's waitProcs queue. A recycled PGPROC
|
* the PGPROC is linked into that lock's waitProcs queue. A recycled PGPROC
|
||||||
* is linked into ProcGlobal's freeProcs list.
|
* is linked into ProcGlobal's freeProcs list.
|
||||||
|
*
|
||||||
|
* Note: twophase.c also sets up a dummy PGPROC struct for each currently
|
||||||
|
* prepared transaction. These PGPROCs appear in the ProcArray data structure
|
||||||
|
* so that the prepared transactions appear to be still running and are
|
||||||
|
* correctly shown as holding locks. A prepared transaction PGPROC can be
|
||||||
|
* distinguished from a real one at need by the fact that it has pid == 0.
|
||||||
|
* The semaphore and lock-related fields in a prepared-xact PGPROC are unused.
|
||||||
*/
|
*/
|
||||||
struct PGPROC
|
struct PGPROC
|
||||||
{
|
{
|
||||||
|
@ -62,16 +69,9 @@ struct PGPROC
|
||||||
* were starting our xact: vacuum must not
|
* were starting our xact: vacuum must not
|
||||||
* remove tuples deleted by xid >= xmin ! */
|
* remove tuples deleted by xid >= xmin ! */
|
||||||
|
|
||||||
int pid; /* This backend's process id */
|
int pid; /* This backend's process id, or 0 */
|
||||||
Oid databaseId; /* OID of database this backend is using */
|
Oid databaseId; /* OID of database this backend is using */
|
||||||
|
|
||||||
/*
|
|
||||||
* XLOG location of first XLOG record written by this backend's
|
|
||||||
* current transaction. If backend is not in a transaction or hasn't
|
|
||||||
* yet modified anything, logRec.xrecoff is zero.
|
|
||||||
*/
|
|
||||||
XLogRecPtr logRec;
|
|
||||||
|
|
||||||
/* Info about LWLock the process is currently waiting for, if any. */
|
/* Info about LWLock the process is currently waiting for, if any. */
|
||||||
bool lwWaiting; /* true if waiting for an LW lock */
|
bool lwWaiting; /* true if waiting for an LW lock */
|
||||||
bool lwExclusive; /* true if waiting for exclusive access */
|
bool lwExclusive; /* true if waiting for exclusive access */
|
||||||
|
@ -120,11 +120,12 @@ extern int StatementTimeout;
|
||||||
/*
|
/*
|
||||||
* Function Prototypes
|
* Function Prototypes
|
||||||
*/
|
*/
|
||||||
extern int ProcGlobalSemas(int maxBackends);
|
extern int ProcGlobalSemas(void);
|
||||||
extern int ProcGlobalShmemSize(int maxBackends);
|
extern int ProcGlobalShmemSize(void);
|
||||||
extern void InitProcGlobal(int maxBackends);
|
extern void InitProcGlobal(void);
|
||||||
extern void InitProcess(void);
|
extern void InitProcess(void);
|
||||||
extern void InitDummyProcess(int proctype);
|
extern void InitDummyProcess(int proctype);
|
||||||
|
extern bool HaveNFreeProcs(int n);
|
||||||
extern void ProcReleaseLocks(bool isCommit);
|
extern void ProcReleaseLocks(bool isCommit);
|
||||||
|
|
||||||
extern void ProcQueueInit(PROC_QUEUE *queue);
|
extern void ProcQueueInit(PROC_QUEUE *queue);
|
||||||
|
|
|
@ -7,28 +7,30 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/procarray.h,v 1.1 2005/05/19 21:35:47 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/procarray.h,v 1.2 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
#ifndef PROCARRAY_H
|
#ifndef PROCARRAY_H
|
||||||
#define PROCARRAY_H
|
#define PROCARRAY_H
|
||||||
|
|
||||||
extern int ProcArrayShmemSize(int maxBackends);
|
#include "storage/lock.h"
|
||||||
extern void CreateSharedProcArray(int maxBackends);
|
|
||||||
extern void ProcArrayAddMyself(void);
|
|
||||||
extern void ProcArrayRemoveMyself(void);
|
extern int ProcArrayShmemSize(void);
|
||||||
|
extern void CreateSharedProcArray(void);
|
||||||
|
extern void ProcArrayAdd(PGPROC *proc);
|
||||||
|
extern void ProcArrayRemove(PGPROC *proc);
|
||||||
|
|
||||||
extern bool TransactionIdIsInProgress(TransactionId xid);
|
extern bool TransactionIdIsInProgress(TransactionId xid);
|
||||||
|
extern bool TransactionIdIsActive(TransactionId xid);
|
||||||
extern TransactionId GetOldestXmin(bool allDbs);
|
extern TransactionId GetOldestXmin(bool allDbs);
|
||||||
|
|
||||||
/* Use "struct PGPROC", not PGPROC, to avoid including proc.h here */
|
extern PGPROC *BackendPidGetProc(int pid);
|
||||||
extern struct PGPROC *BackendPidGetProc(int pid);
|
|
||||||
extern bool IsBackendPid(int pid);
|
extern bool IsBackendPid(int pid);
|
||||||
extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
|
extern bool DatabaseHasActiveBackends(Oid databaseId, bool ignoreMyself);
|
||||||
|
|
||||||
extern int CountActiveBackends(void);
|
extern int CountActiveBackends(void);
|
||||||
extern int CountEmptyBackendSlots(void);
|
|
||||||
|
|
||||||
extern void XidCacheRemoveRunningXids(TransactionId xid,
|
extern void XidCacheRemoveRunningXids(TransactionId xid,
|
||||||
int nxids, TransactionId *xids);
|
int nxids, TransactionId *xids);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/storage/smgr.h,v 1.51 2005/06/06 17:01:25 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/storage/smgr.h,v 1.52 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -79,6 +79,7 @@ extern void smgrDoPendingDeletes(bool isCommit);
|
||||||
extern int smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr);
|
extern int smgrGetPendingDeletes(bool forCommit, RelFileNode **ptr);
|
||||||
extern void AtSubCommit_smgr(void);
|
extern void AtSubCommit_smgr(void);
|
||||||
extern void AtSubAbort_smgr(void);
|
extern void AtSubAbort_smgr(void);
|
||||||
|
extern void PostPrepare_smgr(void);
|
||||||
extern void smgrcommit(void);
|
extern void smgrcommit(void);
|
||||||
extern void smgrabort(void);
|
extern void smgrabort(void);
|
||||||
extern void smgrsync(void);
|
extern void smgrsync(void);
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/utils/builtins.h,v 1.257 2005/05/27 00:57:49 neilc Exp $
|
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.258 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -825,6 +825,9 @@ extern Datum show_all_settings(PG_FUNCTION_ARGS);
|
||||||
/* lockfuncs.c */
|
/* lockfuncs.c */
|
||||||
extern Datum pg_lock_status(PG_FUNCTION_ARGS);
|
extern Datum pg_lock_status(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
/* access/transam/twophase.c */
|
||||||
|
extern Datum pg_prepared_xact(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
/* catalog/pg_conversion.c */
|
/* catalog/pg_conversion.c */
|
||||||
extern Datum pg_convert_using(PG_FUNCTION_ARGS);
|
extern Datum pg_convert_using(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* Routines for maintaining "flat file" images of the shared catalogs.
|
* Routines for maintaining "flat file" images of the shared catalogs.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* $PostgreSQL: pgsql/src/include/utils/flatfiles.h,v 1.3 2005/05/10 22:27:30 momjian Exp $
|
* $PostgreSQL: pgsql/src/include/utils/flatfiles.h,v 1.4 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +23,7 @@ extern char *user_getflatfilename(void);
|
||||||
|
|
||||||
extern void BuildFlatFiles(bool database_only);
|
extern void BuildFlatFiles(bool database_only);
|
||||||
|
|
||||||
|
extern void AtPrepare_UpdateFlatFiles(void);
|
||||||
extern void AtEOXact_UpdateFlatFiles(bool isCommit);
|
extern void AtEOXact_UpdateFlatFiles(bool isCommit);
|
||||||
extern void AtEOSubXact_UpdateFlatFiles(bool isCommit,
|
extern void AtEOSubXact_UpdateFlatFiles(bool isCommit,
|
||||||
SubTransactionId mySubid,
|
SubTransactionId mySubid,
|
||||||
|
@ -30,4 +31,7 @@ extern void AtEOSubXact_UpdateFlatFiles(bool isCommit,
|
||||||
|
|
||||||
extern Datum flatfile_update_trigger(PG_FUNCTION_ARGS);
|
extern Datum flatfile_update_trigger(PG_FUNCTION_ARGS);
|
||||||
|
|
||||||
|
extern void flatfile_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
|
||||||
#endif /* FLATFILES_H */
|
#endif /* FLATFILES_H */
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/utils/inval.h,v 1.35 2004/12/31 22:03:46 pgsql Exp $
|
* $PostgreSQL: pgsql/src/include/utils/inval.h,v 1.36 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +30,10 @@ extern void AtEOXact_Inval(bool isCommit);
|
||||||
|
|
||||||
extern void AtEOSubXact_Inval(bool isCommit);
|
extern void AtEOSubXact_Inval(bool isCommit);
|
||||||
|
|
||||||
|
extern void AtPrepare_Inval(void);
|
||||||
|
|
||||||
|
extern void PostPrepare_Inval(void);
|
||||||
|
|
||||||
extern void CommandEndInvalidationMessages(void);
|
extern void CommandEndInvalidationMessages(void);
|
||||||
|
|
||||||
extern void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple);
|
extern void CacheInvalidateHeapTuple(Relation relation, HeapTuple tuple);
|
||||||
|
@ -47,4 +51,7 @@ extern void CacheRegisterSyscacheCallback(int cacheid,
|
||||||
extern void CacheRegisterRelcacheCallback(CacheCallbackFunction func,
|
extern void CacheRegisterRelcacheCallback(CacheCallbackFunction func,
|
||||||
Datum arg);
|
Datum arg);
|
||||||
|
|
||||||
|
extern void inval_twophase_postcommit(TransactionId xid, uint16 info,
|
||||||
|
void *recdata, uint32 len);
|
||||||
|
|
||||||
#endif /* INVAL_H */
|
#endif /* INVAL_H */
|
||||||
|
|
|
@ -39,7 +39,7 @@
|
||||||
* Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2005, 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/include/utils/portal.h,v 1.55 2005/04/11 19:51:16 tgl Exp $
|
* $PostgreSQL: pgsql/src/include/utils/portal.h,v 1.56 2005/06/17 22:32:50 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
@ -183,6 +183,7 @@ typedef struct PortalData
|
||||||
/* Prototypes for functions in utils/mmgr/portalmem.c */
|
/* Prototypes for functions in utils/mmgr/portalmem.c */
|
||||||
extern void EnablePortalManager(void);
|
extern void EnablePortalManager(void);
|
||||||
extern bool CommitHoldablePortals(void);
|
extern bool CommitHoldablePortals(void);
|
||||||
|
extern bool PrepareHoldablePortals(void);
|
||||||
extern void AtCommit_Portals(void);
|
extern void AtCommit_Portals(void);
|
||||||
extern void AtAbort_Portals(void);
|
extern void AtAbort_Portals(void);
|
||||||
extern void AtCleanup_Portals(void);
|
extern void AtCleanup_Portals(void);
|
||||||
|
|
|
@ -0,0 +1,213 @@
|
||||||
|
--
|
||||||
|
-- PREPARED TRANSACTIONS (two-phase commit)
|
||||||
|
--
|
||||||
|
-- We can't readily test persistence of prepared xacts within the
|
||||||
|
-- regression script framework, unfortunately. Note that a crash
|
||||||
|
-- isn't really needed ... stopping and starting the postmaster would
|
||||||
|
-- be enough, but we can't even do that here.
|
||||||
|
-- create a simple table that we'll use in the tests
|
||||||
|
CREATE TABLE pxtest1 (foobar VARCHAR(10));
|
||||||
|
INSERT INTO pxtest1 VALUES ('aaa');
|
||||||
|
-- Test PREPARE TRANSACTION
|
||||||
|
BEGIN;
|
||||||
|
UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
bbb
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
PREPARE TRANSACTION 'foo1';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Test pg_prepared_xacts system view
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
------
|
||||||
|
foo1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Test ROLLBACK PREPARED
|
||||||
|
ROLLBACK PREPARED 'foo1';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- Test COMMIT PREPARED
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO pxtest1 VALUES ('ddd');
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
ddd
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
PREPARE TRANSACTION 'foo2';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
COMMIT PREPARED 'foo2';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
ddd
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Test duplicate gids
|
||||||
|
BEGIN;
|
||||||
|
UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
eee
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
PREPARE TRANSACTION 'foo3';
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
------
|
||||||
|
foo3
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO pxtest1 VALUES ('fff');
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
ddd
|
||||||
|
fff
|
||||||
|
(3 rows)
|
||||||
|
|
||||||
|
-- This should fail, because the gid foo3 is already in use
|
||||||
|
PREPARE TRANSACTION 'foo3';
|
||||||
|
ERROR: global transaction identifier "foo3" is already in use
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
ddd
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
ROLLBACK PREPARED 'foo3';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
foobar
|
||||||
|
--------
|
||||||
|
aaa
|
||||||
|
ddd
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
DROP TABLE pxtest1;
|
||||||
|
-- Test subtransactions
|
||||||
|
BEGIN;
|
||||||
|
CREATE TABLE pxtest2 (a int);
|
||||||
|
INSERT INTO pxtest2 VALUES (1);
|
||||||
|
SAVEPOINT a;
|
||||||
|
INSERT INTO pxtest2 VALUES (2);
|
||||||
|
ROLLBACK TO a;
|
||||||
|
SAVEPOINT b;
|
||||||
|
INSERT INTO pxtest2 VALUES (3);
|
||||||
|
PREPARE TRANSACTION 'regress-one';
|
||||||
|
CREATE TABLE pxtest3(fff int);
|
||||||
|
-- Test shared invalidation
|
||||||
|
BEGIN;
|
||||||
|
DROP TABLE pxtest3;
|
||||||
|
CREATE TABLE pxtest4 (a int);
|
||||||
|
INSERT INTO pxtest4 VALUES (1);
|
||||||
|
INSERT INTO pxtest4 VALUES (2);
|
||||||
|
DECLARE foo CURSOR FOR SELECT * FROM pxtest4;
|
||||||
|
-- Fetch 1 tuple, keeping the cursor open
|
||||||
|
FETCH 1 FROM foo;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
1
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
PREPARE TRANSACTION 'regress-two';
|
||||||
|
-- No such cursor
|
||||||
|
FETCH 1 FROM foo;
|
||||||
|
ERROR: cursor "foo" does not exist
|
||||||
|
-- Table doesn't exist, the creation hasn't been committed yet
|
||||||
|
SELECT * FROM pxtest2;
|
||||||
|
ERROR: relation "pxtest2" does not exist
|
||||||
|
-- There should be two prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
-------------
|
||||||
|
regress-one
|
||||||
|
regress-two
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- pxtest3 should be locked because of the pending DROP
|
||||||
|
set statement_timeout to 1000;
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
ERROR: canceling query due to user request
|
||||||
|
reset statement_timeout;
|
||||||
|
-- Disconnect, we will continue testing in a different backend
|
||||||
|
\c -
|
||||||
|
-- There should still be two prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
-------------
|
||||||
|
regress-one
|
||||||
|
regress-two
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- pxtest3 should still be locked because of the pending DROP
|
||||||
|
set statement_timeout to 1000;
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
ERROR: canceling query due to user request
|
||||||
|
reset statement_timeout;
|
||||||
|
-- Commit table creation
|
||||||
|
COMMIT PREPARED 'regress-one';
|
||||||
|
\d pxtest2
|
||||||
|
Table "public.pxtest2"
|
||||||
|
Column | Type | Modifiers
|
||||||
|
--------+---------+-----------
|
||||||
|
a | integer |
|
||||||
|
|
||||||
|
SELECT * FROM pxtest2;
|
||||||
|
a
|
||||||
|
---
|
||||||
|
1
|
||||||
|
3
|
||||||
|
(2 rows)
|
||||||
|
|
||||||
|
-- There should be one prepared transaction
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
-------------
|
||||||
|
regress-two
|
||||||
|
(1 row)
|
||||||
|
|
||||||
|
-- Commit table drop
|
||||||
|
COMMIT PREPARED 'regress-two';
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
ERROR: relation "pxtest3" does not exist
|
||||||
|
-- There should be no prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
gid
|
||||||
|
-----
|
||||||
|
(0 rows)
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
DROP TABLE pxtest2;
|
||||||
|
DROP TABLE pxtest4;
|
|
@ -1279,6 +1279,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
||||||
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
|
iexit | SELECT ih.name, ih.thepath, interpt_pp(ih.thepath, r.thepath) AS exit FROM ihighway ih, ramp r WHERE (ih.thepath ## r.thepath);
|
||||||
pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
|
pg_indexes | SELECT n.nspname AS schemaname, c.relname AS tablename, i.relname AS indexname, t.spcname AS "tablespace", pg_get_indexdef(i.oid) AS indexdef FROM ((((pg_index x JOIN pg_class c ON ((c.oid = x.indrelid))) JOIN pg_class i ON ((i.oid = x.indexrelid))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) LEFT JOIN pg_tablespace t ON ((t.oid = i.reltablespace))) WHERE ((c.relkind = 'r'::"char") AND (i.relkind = 'i'::"char"));
|
||||||
pg_locks | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l."transaction", l.classid, l.objid, l.objsubid, l.pid, l."mode", l.granted FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, "transaction" xid, classid oid, objid oid, objsubid smallint, pid integer, "mode" text, granted boolean);
|
pg_locks | SELECT l.locktype, l."database", l.relation, l.page, l.tuple, l."transaction", l.classid, l.objid, l.objsubid, l.pid, l."mode", l.granted FROM pg_lock_status() l(locktype text, "database" oid, relation oid, page integer, tuple smallint, "transaction" xid, classid oid, objid oid, objsubid smallint, pid integer, "mode" text, granted boolean);
|
||||||
|
pg_prepared_xacts | SELECT p."transaction", p.gid, u.usename AS "owner", d.datname AS "database" FROM ((pg_prepared_xact() p("transaction" xid, gid text, ownerid integer, dbid oid) LEFT JOIN pg_database d ON ((p.dbid = d.oid))) LEFT JOIN pg_shadow u ON ((p.ownerid = u.usesysid)));
|
||||||
pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
|
pg_rules | SELECT n.nspname AS schemaname, c.relname AS tablename, r.rulename, pg_get_ruledef(r.oid) AS definition FROM ((pg_rewrite r JOIN pg_class c ON ((c.oid = r.ev_class))) LEFT JOIN pg_namespace n ON ((n.oid = c.relnamespace))) WHERE (r.rulename <> '_RETURN'::name);
|
||||||
pg_settings | SELECT a.name, a.setting, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val FROM pg_show_all_settings() 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);
|
pg_settings | SELECT a.name, a.setting, a.category, a.short_desc, a.extra_desc, a.context, a.vartype, a.source, a.min_val, a.max_val FROM pg_show_all_settings() 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);
|
||||||
pg_stat_activity | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid, pg_stat_get_backend_userid(s.backendid) AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS current_query, pg_stat_get_backend_activity_start(s.backendid) AS query_start, pg_stat_get_backend_start(s.backendid) AS backend_start, pg_stat_get_backend_client_addr(s.backendid) AS client_addr, pg_stat_get_backend_client_port(s.backendid) AS client_port FROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE ((pg_stat_get_backend_dbid(s.backendid) = d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid));
|
pg_stat_activity | SELECT d.oid AS datid, d.datname, pg_stat_get_backend_pid(s.backendid) AS procpid, pg_stat_get_backend_userid(s.backendid) AS usesysid, u.usename, pg_stat_get_backend_activity(s.backendid) AS current_query, pg_stat_get_backend_activity_start(s.backendid) AS query_start, pg_stat_get_backend_start(s.backendid) AS backend_start, pg_stat_get_backend_client_addr(s.backendid) AS client_addr, pg_stat_get_backend_client_port(s.backendid) AS client_port FROM pg_database d, (SELECT pg_stat_get_backend_idset() AS backendid) s, pg_shadow u WHERE ((pg_stat_get_backend_dbid(s.backendid) = d.oid) AND (pg_stat_get_backend_userid(s.backendid) = u.usesysid));
|
||||||
|
@ -1316,7 +1317,7 @@ SELECT viewname, definition FROM pg_views WHERE schemaname <> 'information_schem
|
||||||
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
|
shoelace_obsolete | SELECT shoelace.sl_name, shoelace.sl_avail, shoelace.sl_color, shoelace.sl_len, shoelace.sl_unit, shoelace.sl_len_cm FROM shoelace WHERE (NOT (EXISTS (SELECT shoe.shoename FROM shoe WHERE (shoe.slcolor = shoelace.sl_color))));
|
||||||
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
|
street | SELECT r.name, r.thepath, c.cname FROM ONLY road r, real_city c WHERE (c.outline ## r.thepath);
|
||||||
toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp;
|
toyemp | SELECT emp.name, emp.age, emp."location", (12 * emp.salary) AS annualsal FROM emp;
|
||||||
(40 rows)
|
(41 rows)
|
||||||
|
|
||||||
SELECT tablename, rulename, definition FROM pg_rules
|
SELECT tablename, rulename, definition FROM pg_rules
|
||||||
ORDER BY tablename, rulename;
|
ORDER BY tablename, rulename;
|
||||||
|
|
|
@ -60,7 +60,7 @@ ignore: random
|
||||||
# ----------
|
# ----------
|
||||||
# The fourth group of parallel test
|
# The fourth group of parallel test
|
||||||
# ----------
|
# ----------
|
||||||
test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace
|
test: select_into select_distinct select_distinct_on select_implicit select_having subselect union case join aggregates transactions random portals arrays btree_index hash_index update namespace prepared_xacts
|
||||||
|
|
||||||
test: privileges
|
test: privileges
|
||||||
test: misc
|
test: misc
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.26 2004/06/18 06:14:25 tgl Exp $
|
# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.27 2005/06/17 22:32:50 tgl Exp $
|
||||||
# This should probably be in an order similar to parallel_schedule.
|
# This should probably be in an order similar to parallel_schedule.
|
||||||
test: boolean
|
test: boolean
|
||||||
test: char
|
test: char
|
||||||
|
@ -74,6 +74,7 @@ test: btree_index
|
||||||
test: hash_index
|
test: hash_index
|
||||||
test: update
|
test: update
|
||||||
test: namespace
|
test: namespace
|
||||||
|
test: prepared_xacts
|
||||||
test: privileges
|
test: privileges
|
||||||
test: misc
|
test: misc
|
||||||
test: select_views
|
test: select_views
|
||||||
|
|
|
@ -0,0 +1,137 @@
|
||||||
|
--
|
||||||
|
-- PREPARED TRANSACTIONS (two-phase commit)
|
||||||
|
--
|
||||||
|
-- We can't readily test persistence of prepared xacts within the
|
||||||
|
-- regression script framework, unfortunately. Note that a crash
|
||||||
|
-- isn't really needed ... stopping and starting the postmaster would
|
||||||
|
-- be enough, but we can't even do that here.
|
||||||
|
|
||||||
|
|
||||||
|
-- create a simple table that we'll use in the tests
|
||||||
|
CREATE TABLE pxtest1 (foobar VARCHAR(10));
|
||||||
|
|
||||||
|
INSERT INTO pxtest1 VALUES ('aaa');
|
||||||
|
|
||||||
|
|
||||||
|
-- Test PREPARE TRANSACTION
|
||||||
|
BEGIN;
|
||||||
|
UPDATE pxtest1 SET foobar = 'bbb' WHERE foobar = 'aaa';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
PREPARE TRANSACTION 'foo1';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
-- Test pg_prepared_xacts system view
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
-- Test ROLLBACK PREPARED
|
||||||
|
ROLLBACK PREPARED 'foo1';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
|
||||||
|
-- Test COMMIT PREPARED
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO pxtest1 VALUES ('ddd');
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
PREPARE TRANSACTION 'foo2';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
COMMIT PREPARED 'foo2';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
-- Test duplicate gids
|
||||||
|
BEGIN;
|
||||||
|
UPDATE pxtest1 SET foobar = 'eee' WHERE foobar = 'ddd';
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
PREPARE TRANSACTION 'foo3';
|
||||||
|
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
BEGIN;
|
||||||
|
INSERT INTO pxtest1 VALUES ('fff');
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
-- This should fail, because the gid foo3 is already in use
|
||||||
|
PREPARE TRANSACTION 'foo3';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
ROLLBACK PREPARED 'foo3';
|
||||||
|
|
||||||
|
SELECT * FROM pxtest1;
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
DROP TABLE pxtest1;
|
||||||
|
|
||||||
|
-- Test subtransactions
|
||||||
|
BEGIN;
|
||||||
|
CREATE TABLE pxtest2 (a int);
|
||||||
|
INSERT INTO pxtest2 VALUES (1);
|
||||||
|
SAVEPOINT a;
|
||||||
|
INSERT INTO pxtest2 VALUES (2);
|
||||||
|
ROLLBACK TO a;
|
||||||
|
SAVEPOINT b;
|
||||||
|
INSERT INTO pxtest2 VALUES (3);
|
||||||
|
PREPARE TRANSACTION 'regress-one';
|
||||||
|
|
||||||
|
CREATE TABLE pxtest3(fff int);
|
||||||
|
|
||||||
|
-- Test shared invalidation
|
||||||
|
BEGIN;
|
||||||
|
DROP TABLE pxtest3;
|
||||||
|
CREATE TABLE pxtest4 (a int);
|
||||||
|
INSERT INTO pxtest4 VALUES (1);
|
||||||
|
INSERT INTO pxtest4 VALUES (2);
|
||||||
|
DECLARE foo CURSOR FOR SELECT * FROM pxtest4;
|
||||||
|
-- Fetch 1 tuple, keeping the cursor open
|
||||||
|
FETCH 1 FROM foo;
|
||||||
|
PREPARE TRANSACTION 'regress-two';
|
||||||
|
|
||||||
|
-- No such cursor
|
||||||
|
FETCH 1 FROM foo;
|
||||||
|
|
||||||
|
-- Table doesn't exist, the creation hasn't been committed yet
|
||||||
|
SELECT * FROM pxtest2;
|
||||||
|
|
||||||
|
-- There should be two prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
-- pxtest3 should be locked because of the pending DROP
|
||||||
|
set statement_timeout to 1000;
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
reset statement_timeout;
|
||||||
|
|
||||||
|
-- Disconnect, we will continue testing in a different backend
|
||||||
|
\c -
|
||||||
|
|
||||||
|
-- There should still be two prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
-- pxtest3 should still be locked because of the pending DROP
|
||||||
|
set statement_timeout to 1000;
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
reset statement_timeout;
|
||||||
|
|
||||||
|
-- Commit table creation
|
||||||
|
COMMIT PREPARED 'regress-one';
|
||||||
|
\d pxtest2
|
||||||
|
SELECT * FROM pxtest2;
|
||||||
|
|
||||||
|
-- There should be one prepared transaction
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
-- Commit table drop
|
||||||
|
COMMIT PREPARED 'regress-two';
|
||||||
|
SELECT * FROM pxtest3;
|
||||||
|
|
||||||
|
-- There should be no prepared transactions
|
||||||
|
SELECT gid FROM pg_prepared_xacts;
|
||||||
|
|
||||||
|
-- Clean up
|
||||||
|
DROP TABLE pxtest2;
|
||||||
|
DROP TABLE pxtest4;
|
Loading…
Reference in New Issue