diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index de450cd661..12263f5fd5 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -103,6 +103,7 @@
 <!ENTITY archive-modules SYSTEM "archive-modules.sgml">
 <!ENTITY protocol   SYSTEM "protocol.sgml">
 <!ENTITY sources    SYSTEM "sources.sgml">
+<!ENTITY transactions    SYSTEM "xact.sgml">
 <!ENTITY storage    SYSTEM "storage.sgml">
 <!ENTITY tablesample-method SYSTEM "tablesample-method.sgml">
 <!ENTITY generic-wal SYSTEM "generic-wal.sgml">
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index ee515cec8f..6aca1e756a 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -24635,6 +24635,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
         Returns the current transaction's ID.  It will assign a new one if the
         current transaction does not have one already (because it has not
         performed any database updates).
+        If executed in a subtransaction this will return the top-level xid.
        </para></entry>
       </row>
 
@@ -24651,6 +24652,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
         ID is assigned yet.  (It's best to use this variant if the transaction
         might otherwise be read-only, to avoid unnecessary consumption of an
         XID.)
+        If executed in a subtransaction this will return the top-level xid.
        </para></entry>
       </row>
 
@@ -24694,6 +24696,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
        <para>
         Returns a current <firstterm>snapshot</firstterm>, a data structure
         showing which transaction IDs are now in-progress.
+        Only top-level xids are included in the snapshot; subxids are not shown.
        </para></entry>
       </row>
 
@@ -24748,7 +24751,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
         Is the given transaction ID <firstterm>visible</firstterm> according
         to this snapshot (that is, was it completed before the snapshot was
         taken)?  Note that this function will not give the correct answer for
-        a subtransaction ID.
+        a subtransaction ID (subxid).
        </para></entry>
       </row>
      </tbody>
@@ -24807,7 +24810,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
         xmax</literal> and not in this list was already completed at the time
         of the snapshot, and thus is either visible or dead according to its
         commit status.  This list does not include the transaction IDs of
-        subtransactions.
+        subtransactions (subxids).
        </entry>
       </row>
      </tbody>
diff --git a/doc/src/sgml/ref/commit.sgml b/doc/src/sgml/ref/commit.sgml
index 5f244cdd3c..53d830998c 100644
--- a/doc/src/sgml/ref/commit.sgml
+++ b/doc/src/sgml/ref/commit.sgml
@@ -62,6 +62,9 @@ COMMIT [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
       linkend="sql-set-transaction"/>) as the just finished one.  Otherwise,
       no new transaction is started.
      </para>
+     <para>
+      The SQL Standard describes this as a chained transaction.
+     </para>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/doc/src/sgml/ref/rollback.sgml b/doc/src/sgml/ref/rollback.sgml
index 142f71e774..02b118fc04 100644
--- a/doc/src/sgml/ref/rollback.sgml
+++ b/doc/src/sgml/ref/rollback.sgml
@@ -56,11 +56,14 @@ ROLLBACK [ WORK | TRANSACTION ] [ AND [ NO ] CHAIN ]
     <term><literal>AND CHAIN</literal></term>
     <listitem>
      <para>
-      If <literal>AND CHAIN</literal> is specified, a new transaction is
+      If <literal>AND CHAIN</literal> is specified, a new unaborted transaction is
       immediately started with the same transaction characteristics (see <xref
       linkend="sql-set-transaction"/>) as the just finished one.  Otherwise,
       no new transaction is started.
      </para>
+     <para>
+      The SQL Standard describes this as a chained transaction.
+     </para>
     </listitem>
    </varlistentry>
   </variablelist>
diff --git a/doc/src/sgml/xact.sgml b/doc/src/sgml/xact.sgml
new file mode 100644
index 0000000000..9c34b6e721
--- /dev/null
+++ b/doc/src/sgml/xact.sgml
@@ -0,0 +1,225 @@
+<!-- doc/src/sgml/xact.sgml -->
+
+<chapter id="transactions">
+
+<title>Transaction Management</title>
+
+<para>
+This chapter provides an overview of transaction management for
+<productname>PostgreSQL</productname> databases.
+The word transaction is often abbreviated to "xact".
+</para>
+
+<para>
+<productname>PostgreSQL</productname> supports transactions and chained
+transactions according to the SQL Standard.
+<productname>PostgreSQL</productname> supports SAVEPOINTs through the
+implementation-defined mechanism of subtransactions, which offer a superset of
+the required features. Prepared transactions allow PostgreSQL to implement what
+the SQL Standard refers to as encompassing transactions belonging to an external
+agent.
+</para>
+
+<para>
+Some features implemented in other databases, such as
+autonomous transactions and nested transactions are not supported
+by <productname>PostgreSQL</productname>, though be careful since those terms may
+be used slightly differently in various external contexts.
+</para>
+
+<sect1 id="transaction-id">
+
+<title>Transactions and Identifiers</title>
+
+<para>
+Transactions may be managed explicitly using <command>BEGIN</command> and
+<command>COMMIT</command>, known as a transaction block.
+A transaction will also be started and ended implicitly for
+each request when outside of a transaction block.
+</para>
+
+<para>
+All transactions are identified by a unique VirtualTransactionId (virtualxid or
+vxid), e.g. 4/12532, which is comprised of the BackendId (in this example, 4)
+and a sequentially-assigned number unique to that backend, known as LocalXid
+(in this example, 12532).
+</para>
+
+<para>
+If a transaction attempts to write to the database, it will first be assigned a
+TransactionId (xid), e.g. 278394. xids are assigned sequentially using a global
+counter used by all databases within a postgresql cluster (instance). This
+property is used by the transaction system to say that if one xid is earlier
+than another xid, then the earlier transaction attempted to write before the
+later xid. However, the start and end of those two transactions are not
+constrained to occur in that sequence.
+</para>
+
+<para>
+When a top-level transaction with an assigned xid commits, it is marked as
+committed in the pg_xact directory. Additional information may also be recorded
+in the pg_commit_ts directory.
+</para>
+
+<para>
+The internal transaction ID type xid is 32-bits wide and wraps around every
+4 billion transactions. A 32-bit epoch is incremented by one for each turn
+of the transaction clock. There is also a 64-bit type xid8 that does not wrap
+around during the life of an installation, and can be converted to xid by
+casting if required.
+</para>
+
+<para>
+Functions to inspect xids are shown in in Table 9.81, which return values of
+type xid8.
+</para>
+
+<para>
+xids are used as the basis for <productname>PostgreSQL</productname>'s MVCC
+visibility mechanism, described elsewhere.
+Hot Standby, or Read Replica servers, emulate the transaction management
+details described here so that users have MVCC visibility with read-only
+transactions.
+</para>
+
+<para>
+In addition to vxid and xid, when a transaction is prepared it is also identified by a
+Global Transaction Identifier (GID). GIDs are string literals up to 200 bytes long,
+which must be unique amongst other currently prepared transactions.
+The mapping of GID to xid is only shown in pg_prepared_xacts.
+</para>
+
+</sect1>
+
+<sect1 id="xact-locking">
+
+<title>Transactions and Locking</title>
+
+<para>
+Currently-executing transactions are shown in
+<link linkend="view-pg-locks"><structname>pg_locks</structname></link>.
+in columns "virtualxid" (text) and "transactionid" (xid).
+Read-only transactions will have a virtualxid but a NULL xid, while read-write
+transactions will have both a virtualxid and an xid assigned.
+</para>
+
+<para>
+Virtualxid is a text column, rather than a separate datatype.
+</para>
+
+<para>
+Lock waits on table-level locks are shown against virtualxid, while lock waits
+against row-level locks are shown against transactionid.
+</para>
+
+<para>
+Row-level locks are recorded directly onto the locked rows. Row-level locks can
+be inspected using the pgrowlocks extension.
+</para>
+
+<para>
+Row-level read locks may require the assignment of a multixact ID (mxid). Mxids
+are recorded in the pg_multixact directory.
+</para>
+
+</sect1>
+
+<sect1 id="subxacts">
+
+<title>Subtransactions</title>
+
+<para>
+Subtransactions may be started from the main transaction, allowing a large
+transaction to be broken into smaller units.
+Subtransactions may commit or abort without affecting their parent
+transaction, allowing the parent transaction to continue. This allows ERRORs
+to be handled more easily, so is a common application development pattern.
+Committed subtransactions are only recorded as committed if the main
+transaction commits, otherwise any work done in a subtransaction will be
+rolled back. The word subtransaction is often abbreviated as "subxact".
+</para>
+
+<para>
+Subtransactions may be started explicitly by using <command>SAVEPOINT</command>,
+but may also be started in other ways, such as PL/pgSQL's
+<command>EXCEPTION</command> clause.
+PL/Python and PL/TCL also support explicit subtransactions.
+Working with the C API, users may also call BeginInternalSubTransaction().
+</para>
+
+<para>
+Over time, a transaction may have many child subtransactions.
+Subtransactions can also be started from other subtransactions.
+As a result, the arrangement of the main transaction and
+its child subtransactions form a hierarchy or tree, which is why we refer
+to the main transaction as the top-level transaction.
+Thus, each subtransaction has only one parent transaction.
+</para>
+
+<para>
+At present in <productname>PostgreSQL</productname>, only one transaction or
+subtransaction can be active at one time. When a subtransaction ends, the parent
+transaction becomes active again.
+</para>
+
+<para>
+If a subtransaction is assigned an xid, we refer to this as a subxid. Read-only
+subtransactions are not assigned a subxid, but when a subtransaction attempts to
+write, it will be assigned a subxid. We ensure that all of a subxid's parents, up
+to and including the top-level transaction are also assigned an xid.
+We ensure that a parent xid is always earlier than any of its child subxids.
+</para>
+
+<para>
+The parent xid of each subxid is recorded in the pg_subtrans directory. No entry is
+made for top-level xids since they do not have a parent, nor is an entry made for
+read only subtransactions.
+</para>
+
+<para>
+When a subtransaction commits, all of the committed child subtransactions with
+assigned subxids will be recorded as committed. When a top-level transaction with
+an assigned xid commits, all of its committed child subtransactions are also
+marked as committed in the pg_xact directory.
+</para>
+
+<para>
+The more subtransactions each transaction uses, the greater the overhead for
+transaction management. Up to 64 subxids are cached in shared memory for each backend;
+after that point, the overhead increases more significantly since we must look up
+the entry in pg_subtrans for each xid.
+</para>
+
+</sect1>
+
+<sect1 id="xact-2pc">
+
+<title>Two-Phase Transactions</title>
+
+<para>
+<productname>PostgreSQL</productname> supports a two-phase commit (2PC) protocol
+that allows multiple distributed systems to work together in a transactional manner.
+The commands <command>PREPARE TRANSACTION</command>, <command>COMMIT PREPARED</command>
+and <command>ROLLBACK PREPARED</command> are extensions to the SQL Standard, since the
+SQL syntax is not yet part of the standard, though the Standard does refer to encompassing
+transactions made by an external SQL agent. Two-phase transactions are intended
+for use programmatically by external transaction management systems.
+<productname>PostgreSQL</productname> follows the features and model proposed by
+the X/Open XA standard, but does not implement some less often used aspects.
+</para>
+
+<para>
+When the user executes <command>PREPARE TRANSACTION</command>, this prevents further
+work from being performed other than a <command>COMMIT PREPARED</command> or
+<command>ROLLBACK PREPARED</command>. In general, this prepared state is intended
+to be of very short duration, but external availability issues may mean transactions stay
+in this state for an extended interval. Transactions that were prepared before the
+last checkpoint are stored in files in the pg_twophase directory. In the typical
+case, shorter-lived prepared transactions are stored only in shared memory and WAL.
+Transactions that are currently prepared can be inspected using
+<link linkend="view-pg-prepared-xacts"><structname>pg_prepared_xacts</structname></link>.
+</para>
+
+</sect1>
+
+</chapter>
