Introduce xid8-based functions to replace txid_XXX.

The txid_XXX family of fmgr functions exposes 64 bit transaction IDs to
users as int8.  Now that we have an SQL type xid8 for FullTransactionId,
define a new set of functions including pg_current_xact_id() and
pg_current_snapshot() based on that.  Keep the old functions around too,
for now.

It's a bit sneaky to use the same C functions for both, but since the
binary representation is identical except for the signedness of the
type, and since older functions are the ones using the wrong signedness,
and since we'll presumably drop the older ones after a reasonable period
of time, it seems reasonable to switch to FullTransactionId internally
and share the code for both.

Reviewed-by: Fujii Masao <masao.fujii@oss.nttdata.com>
Reviewed-by: Takao Fujii <btfujiitkp@oss.nttdata.com>
Reviewed-by: Yoshikazu Imai <imai.yoshikazu@fujitsu.com>
Reviewed-by: Mark Dilger <mark.dilger@enterprisedb.com>
Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de
This commit is contained in:
Thomas Munro 2020-04-07 11:33:56 +12:00
parent aeec457de8
commit 4c04be9b05
36 changed files with 871 additions and 355 deletions

View File

@ -382,7 +382,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile
?column?
----------
t

View File

@ -2,7 +2,7 @@
SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
?column?
----------
f
@ -13,7 +13,7 @@ ERROR: cannot create logical replication slot in transaction that has performed
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
?column?
----------
f
@ -50,7 +50,7 @@ CREATE TABLE nobarf(id serial primary key, data text);
INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
?column?
----------
f

View File

@ -2,7 +2,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_getxid s1_begin s1_insert s0_alter s0_commit s0_checkpoint s0_get_changes s0_get_changes s1_commit s0_vacuum s0_get_changes
step s0_begin: BEGIN;
step s0_getxid: SELECT txid_current() IS NULL;
step s0_getxid: SELECT pg_current_xact_id() IS NULL;
?column?
f

View File

@ -2,20 +2,20 @@ Parsed test spec with 3 sessions
starting permutation: s2b s2txid s1init s3b s3txid s2alter s2c s2b s2txid s3c s2c s1insert s1checkpoint s1start s1insert s1alter s1insert s1start
step s2b: BEGIN;
step s2txid: SELECT txid_current() IS NULL;
step s2txid: SELECT pg_current_xact_id() IS NULL;
?column?
f
step s1init: SELECT 'init' FROM pg_create_logical_replication_slot('isolation_slot', 'test_decoding'); <waiting ...>
step s3b: BEGIN;
step s3txid: SELECT txid_current() IS NULL;
step s3txid: SELECT pg_current_xact_id() IS NULL;
?column?
f
step s2alter: ALTER TABLE do_write ADD COLUMN addedbys2 int;
step s2c: COMMIT;
step s2b: BEGIN;
step s2txid: SELECT txid_current() IS NULL;
step s2txid: SELECT pg_current_xact_id() IS NULL;
?column?
f

View File

@ -3,7 +3,7 @@ Parsed test spec with 2 sessions
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
step s0_log_assignment: SELECT txid_current() IS NULL;
step s0_log_assignment: SELECT pg_current_xact_id() IS NULL;
?column?
f
@ -26,7 +26,7 @@ stop
starting permutation: s0_begin s0_begin_sub0 s0_log_assignment s0_begin_sub1 s0_sub_get_base_snap s1_produce_new_snap s0_insert s0_end_sub1 s0_end_sub0 s0_commit s0_get_changes
step s0_begin: BEGIN;
step s0_begin_sub0: SAVEPOINT s0;
step s0_log_assignment: SELECT txid_current() IS NULL;
step s0_log_assignment: SELECT pg_current_xact_id() IS NULL;
?column?
f

View File

@ -19,7 +19,7 @@ teardown
session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
step "s0_getxid" { SELECT txid_current() IS NULL; }
step "s0_getxid" { SELECT pg_current_xact_id() IS NULL; }
step "s0_alter" { ALTER TYPE basket DROP ATTRIBUTE mangos; }
step "s0_commit" { COMMIT; }
step "s0_checkpoint" { CHECKPOINT; }

View File

@ -25,7 +25,7 @@ session "s2"
setup { SET synchronous_commit=on; }
step "s2b" { BEGIN; }
step "s2txid" { SELECT txid_current() IS NULL; }
step "s2txid" { SELECT pg_current_xact_id() IS NULL; }
step "s2alter" { ALTER TABLE do_write ADD COLUMN addedbys2 int; }
step "s2c" { COMMIT; }
@ -34,7 +34,7 @@ session "s3"
setup { SET synchronous_commit=on; }
step "s3b" { BEGIN; }
step "s3txid" { SELECT txid_current() IS NULL; }
step "s3txid" { SELECT pg_current_xact_id() IS NULL; }
step "s3c" { COMMIT; }
# Force usage of ondisk snapshot by starting and not finishing a

View File

@ -20,7 +20,7 @@ session "s0"
setup { SET synchronous_commit=on; }
step "s0_begin" { BEGIN; }
step "s0_begin_sub0" { SAVEPOINT s0; }
step "s0_log_assignment" { SELECT txid_current() IS NULL; }
step "s0_log_assignment" { SELECT pg_current_xact_id() IS NULL; }
step "s0_begin_sub1" { SAVEPOINT s1; }
step "s0_sub_get_base_snap" { INSERT INTO dummy VALUES (0); }
step "s0_insert" { INSERT INTO harvest VALUES (1, 2, 3); }

View File

@ -220,7 +220,7 @@ SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'inc
-- test whether a known, but not yet logged toplevel xact, followed by a
-- subxact commit is handled correctly
BEGIN;
SELECT txid_current() != 0; -- so no fixed xid apears in the outfile
SELECT pg_current_xact_id() != '0'; -- so no fixed xid apears in the outfile
SAVEPOINT a;
INSERT INTO tr_sub(path) VALUES ('4-top-1-#1');
RELEASE SAVEPOINT a;

View File

@ -3,13 +3,13 @@ SET synchronous_commit = on;
-- fail because we're creating a slot while in an xact with xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK;
-- fail because we're creating a slot while in a subxact whose topxact has an xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
SAVEPOINT barf;
SELECT 'init' FROM pg_create_logical_replication_slot('regression_slot', 'test_decoding');
ROLLBACK TO SAVEPOINT barf;
@ -29,7 +29,7 @@ INSERT INTO nobarf(data) VALUES('1');
-- decoding works in transaction with xid
BEGIN;
SELECT txid_current() = 0;
SELECT pg_current_xact_id() = '0';
-- don't show yet, haven't committed
INSERT INTO nobarf(data) VALUES('2');
SELECT data FROM pg_logical_slot_get_changes('regression_slot', NULL, NULL, 'include-xids', '0', 'skip-empty-xacts', '1');

View File

@ -198,6 +198,12 @@
<entry><productname>PostgreSQL</productname> Log Sequence Number</entry>
</row>
<row>
<entry><type>pg_snapshot</type></entry>
<entry></entry>
<entry>user-level transaction ID snapshot</entry>
</row>
<row>
<entry><type>point</type></entry>
<entry></entry>
@ -279,7 +285,7 @@
<row>
<entry><type>txid_snapshot</type></entry>
<entry></entry>
<entry>user-level transaction ID snapshot</entry>
<entry>user-level transaction ID snapshot (deprecated; see <type>pg_snapshot</type>)</entry>
</row>
<row>

View File

@ -18998,6 +18998,38 @@ SELECT collation for ('foo' COLLATE "de_DE");
are stored globally as well.
</para>
<indexterm>
<primary>pg_current_xact_id</primary>
</indexterm>
<indexterm>
<primary>pg_current_xact_id_if_assigned</primary>
</indexterm>
<indexterm>
<primary>pg_current_snapshot</primary>
</indexterm>
<indexterm>
<primary>pg_snapshot_xip</primary>
</indexterm>
<indexterm>
<primary>pg_snapshot_xmax</primary>
</indexterm>
<indexterm>
<primary>pg_snapshot_xmin</primary>
</indexterm>
<indexterm>
<primary>pg_visible_in_snapshot</primary>
</indexterm>
<indexterm>
<primary>pg_xact_status</primary>
</indexterm>
<indexterm>
<primary>txid_current</primary>
</indexterm>
@ -19031,76 +19063,136 @@ SELECT collation for ('foo' COLLATE "de_DE");
</indexterm>
<para>
The functions shown in <xref linkend="functions-txid-snapshot"/>
The functions shown in <xref linkend="functions-pg-snapshot"/>
provide server transaction information in an exportable form. The main
use of these functions is to determine which transactions were committed
between two snapshots.
</para>
<table id="functions-txid-snapshot">
<table id="functions-pg-snapshot">
<title>Transaction IDs and Snapshots</title>
<tgroup cols="3">
<thead>
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
</thead>
<tbody>
<row>
<entry><literal><function>pg_current_xact_id()</function></literal></entry>
<entry><type>xid8</type></entry>
<entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
</row>
<row>
<entry><literal><function>pg_current_xact_id_if_assigned()</function></literal></entry>
<entry><type>xid8</type></entry>
<entry>same as <function>pg_current_xact_id()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
</row>
<row>
<entry><literal><function>pg_xact_status(<parameter>xid8</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
<entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
</row>
<row>
<entry><literal><function>pg_current_snapshot()</function></literal></entry>
<entry><type>pg_snapshot</type></entry>
<entry>get current snapshot</entry>
</row>
<row>
<entry><literal><function>pg_snapshot_xip(<parameter>pg_snapshot</parameter>)</function></literal></entry>
<entry><type>setof xid8</type></entry>
<entry>get in-progress transaction IDs in snapshot</entry>
</row>
<row>
<entry><literal><function>pg_snapshot_xmax(<parameter>pg_snapshot</parameter>)</function></literal></entry>
<entry><type>xid8</type></entry>
<entry>get <literal>xmax</literal> of snapshot</entry>
</row>
<row>
<entry><literal><function>pg_snapshot_xmin(<parameter>pg_snapshot</parameter>)</function></literal></entry>
<entry><type>xid8</type></entry>
<entry>get <literal>xmin</literal> of snapshot</entry>
</row>
<row>
<entry><literal><function>pg_visible_in_snapshot(<parameter>xid8</parameter>, <parameter>pg_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
<entry>is transaction ID visible in snapshot? (do not use with subtransaction IDs)</entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The internal transaction ID type <type>xid</type> is 32 bits wide and
wraps around every 4 billion transactions. However, these functions use a
64-bit variant <type>xid8</type> that does not wrap around during the life
of an installation, and can be converted to <type>xid</type> by casting if
required. The data type <type>pg_snapshot</type> stores information about
transaction ID visibility at a particular moment in time. Its components
are described in <xref linkend="functions-pg-snapshot-parts"/>.
</para>
<para>
In releases of <productname>PostgreSQL</productname> before 13 there was
no <type>xid8</type> type, so variants of these functions were provided
that used <type>bigint</type>. These older functions with
<literal>txid</literal> in their names are still supported for backward
compatibility, but may be removed from a future
release. See <xref linkend="functions-txid-snapshot"/>.
</para>
<table id="functions-txid-snapshot">
<title>Transaction IDs and Snapshots (Deprecated Functions)</title>
<tgroup cols="3">
<thead>
<row><entry>Name</entry> <entry>Return Type</entry> <entry>Description</entry></row>
</thead>
<tbody>
<row>
<entry><literal><function>txid_current()</function></literal></entry>
<entry><type>bigint</type></entry>
<entry>get current transaction ID, assigning a new one if the current transaction does not have one</entry>
<entry>see <function>pg_current_xact_id()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_if_assigned()</function></literal></entry>
<entry><type>bigint</type></entry>
<entry>same as <function>txid_current()</function> but returns null instead of assigning a new transaction ID if none is already assigned</entry>
<entry>see <function>pg_current_xact_id_if_assigned()</function></entry>
</row>
<row>
<entry><literal><function>txid_current_snapshot()</function></literal></entry>
<entry><type>txid_snapshot</type></entry>
<entry>get current snapshot</entry>
<entry>see <function>pg_current_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xip(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>setof bigint</type></entry>
<entry>get in-progress transaction IDs in snapshot</entry>
<entry>see <function>pg_snapshot_xip()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmax(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
<entry>get <literal>xmax</literal> of snapshot</entry>
<entry>see <function>pg_snapshot_xmax()</function></entry>
</row>
<row>
<entry><literal><function>txid_snapshot_xmin(<parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>bigint</type></entry>
<entry>get <literal>xmin</literal> of snapshot</entry>
<entry>see <function>pg_snapshot_xmin()</function></entry>
</row>
<row>
<entry><literal><function>txid_visible_in_snapshot(<parameter>bigint</parameter>, <parameter>txid_snapshot</parameter>)</function></literal></entry>
<entry><type>boolean</type></entry>
<entry>is transaction ID visible in snapshot? (do not use with subtransaction ids)</entry>
<entry>see <function>pg_visible_in_snapshot()</function></entry>
</row>
<row>
<entry><literal><function>txid_status(<parameter>bigint</parameter>)</function></literal></entry>
<entry><type>text</type></entry>
<entry>report the status of the given transaction: <literal>committed</literal>, <literal>aborted</literal>, <literal>in progress</literal>, or null if the transaction ID is too old</entry>
<entry>see <function>pg_xact_status()</function></entry>
</row>
</tbody>
</tgroup>
</table>
<para>
The internal transaction ID type (<type>xid</type>) is 32 bits wide and
wraps around every 4 billion transactions. However, these functions
export a 64-bit format that is extended with an <quote>epoch</quote> counter
so it will not wrap around during the life of an installation.
The data type used by these functions, <type>txid_snapshot</type>,
stores information about transaction ID
visibility at a particular moment in time. Its components are
described in <xref linkend="functions-txid-snapshot-parts"/>.
</para>
<table id="functions-txid-snapshot-parts">
<table id="functions-pg-snapshot-parts">
<title>Snapshot Components</title>
<tgroup cols="2">
<thead>
@ -19115,31 +19207,29 @@ SELECT collation for ('foo' COLLATE "de_DE");
<row>
<entry><type>xmin</type></entry>
<entry>
Earliest transaction ID (txid) that is still active. All earlier
transactions will either be committed and visible, or rolled
back and dead.
Lowest transaction ID that was still active. All transaction IDs
less than <literal>xmin</literal> are either committed and visible,
or rolled back and dead.
</entry>
</row>
<row>
<entry><type>xmax</type></entry>
<entry>
First as-yet-unassigned txid. All txids greater than or equal to this
are not yet started as of the time of the snapshot, and thus invisible.
One past the highest completed transaction ID. All transaction IDs
greater than or equal to <literal>xmax</literal> had not yet
completed as of the time of the snapshot, and thus are invisible.
</entry>
</row>
<row>
<entry><type>xip_list</type></entry>
<entry>
Active txids at the time of the snapshot. The list
includes only those active txids between <literal>xmin</literal>
and <literal>xmax</literal>; there might be active txids higher
than <literal>xmax</literal>. A txid that is <literal>xmin &lt;= txid &lt;
xmax</literal> and not in this list was already completed
at the time of the snapshot, and thus either visible or
dead according to its commit status. The list does not
include txids of subtransactions.
Transactions in progress at the time of the snapshot. A transaction
ID that is <literal>xmin &lt;= X &lt; xmax</literal> and not in this
list was already completed at the time of the snapshot, and is thus
either visible or dead according to its commit status. The list does
not include the transaction IDs of subtransactions.
</entry>
</row>
@ -19148,14 +19238,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
</table>
<para>
<type>txid_snapshot</type>'s textual representation is
<type>pg_snapshot</type>'s textual representation is
<literal><replaceable>xmin</replaceable>:<replaceable>xmax</replaceable>:<replaceable>xip_list</replaceable></literal>.
For example <literal>10:20:10,14,15</literal> means
<literal>xmin=10, xmax=20, xip_list=10, 14, 15</literal>.
</para>
<para>
<function>txid_status(bigint)</function> reports the commit status of a recent
<function>pg_xact_status(xid8)</function> reports the commit status of a recent
transaction. Applications may use it to determine whether a transaction
committed or aborted when the application and database server become
disconnected while a <literal>COMMIT</literal> is in progress.
@ -19169,7 +19259,7 @@ SELECT collation for ('foo' COLLATE "de_DE");
transactions are reported as <literal>in progress</literal>; applications must
check <link
linkend="view-pg-prepared-xacts"><literal>pg_prepared_xacts</literal></link> if they
need to determine whether the txid is a prepared transaction.
need to determine whether the transaction ID belongs to a prepared transaction.
</para>
<para>

View File

@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true);
</programlisting>
Any actions leading to transaction ID assignment are prohibited. That, among others,
includes writing to tables, performing DDL changes, and
calling <literal>txid_current()</literal>.
calling <literal>pg_current_xact_id()</literal>.
</para>
</sect2>

View File

@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
</row>
<row>
<entry><literal>CLogTruncationLock</literal></entry>
<entry>Waiting to execute <function>txid_status</function> or update
<entry>Waiting to execute <function>pg_xact_status</function> or update
the oldest transaction id available to it.</entry>
</row>
<row>

View File

@ -101,7 +101,6 @@ OBJS = \
tsvector.o \
tsvector_op.o \
tsvector_parser.o \
txid.o \
uuid.o \
varbit.o \
varchar.o \
@ -109,6 +108,7 @@ OBJS = \
version.o \
windowfuncs.o \
xid.o \
xid8funcs.o \
xml.o
jsonpath_scan.c: FLEXFLAGS = -CF -p -p

View File

@ -1,20 +1,25 @@
/*-------------------------------------------------------------------------
* txid.c
* xid8funcs.c
*
* Export internal transaction IDs to user level.
*
* Note that only top-level transaction IDs are ever converted to TXID.
* This is important because TXIDs frequently persist beyond the global
* Note that only top-level transaction IDs are exposed to user sessions.
* This is important because xid8s frequently persist beyond the global
* xmin horizon, or may even be shipped to other machines, so we cannot
* rely on being able to correlate subtransaction IDs with their parents
* via functions such as SubTransGetTopmostTransaction().
*
* These functions are used to support the txid_XXX functions and the newer
* pg_current_xact, pg_current_snapshot and related fmgr functions, since the
* only difference between them is whether they expose xid8 or int8 values to
* users. The txid_XXX variants should eventually be dropped.
*
*
* Copyright (c) 2003-2020, PostgreSQL Global Development Group
* Author: Jan Wieck, Afilias USA INC.
* 64-bit txids: Marko Kreen, Skype Technologies
*
* src/backend/utils/adt/txid.c
* src/backend/utils/adt/xid8funcs.c
*
*-------------------------------------------------------------------------
*/
@ -34,25 +39,18 @@
#include "utils/builtins.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/xid8.h"
/* txid will be signed int8 in database, so must limit to 63 bits */
#define MAX_TXID ((uint64) PG_INT64_MAX)
/* Use unsigned variant internally */
typedef uint64 txid;
/* sprintf format code for uint64 */
#define TXID_FMT UINT64_FORMAT
/*
* If defined, use bsearch() function for searching for txids in snapshots
* If defined, use bsearch() function for searching for xid8s in snapshots
* that have more than the specified number of values.
*/
#define USE_BSEARCH_IF_NXIP_GREATER 30
/*
* Snapshot containing 8byte txids.
* Snapshot containing FullTransactionIds.
*/
typedef struct
{
@ -63,39 +61,17 @@ typedef struct
*/
int32 __varsz;
uint32 nxip; /* number of txids in xip array */
txid xmin;
txid xmax;
/* in-progress txids, xmin <= xip[i] < xmax: */
txid xip[FLEXIBLE_ARRAY_MEMBER];
} TxidSnapshot;
uint32 nxip; /* number of fxids in xip array */
FullTransactionId xmin;
FullTransactionId xmax;
/* in-progress fxids, xmin <= xip[i] < xmax: */
FullTransactionId xip[FLEXIBLE_ARRAY_MEMBER];
} pg_snapshot;
#define TXID_SNAPSHOT_SIZE(nxip) \
(offsetof(TxidSnapshot, xip) + sizeof(txid) * (nxip))
#define TXID_SNAPSHOT_MAX_NXIP \
((MaxAllocSize - offsetof(TxidSnapshot, xip)) / sizeof(txid))
/*
* Epoch values from xact.c
*/
typedef struct
{
TransactionId last_xid;
uint32 epoch;
} TxidEpoch;
/*
* Fetch epoch data from xact.c.
*/
static void
load_xid_epoch(TxidEpoch *state)
{
FullTransactionId fullXid = ReadNextFullTransactionId();
state->last_xid = XidFromFullTransactionId(fullXid);
state->epoch = EpochFromFullTransactionId(fullXid);
}
#define PG_SNAPSHOT_SIZE(nxip) \
(offsetof(pg_snapshot, xip) + sizeof(FullTransactionId) * (nxip))
#define PG_SNAPSHOT_MAX_NXIP \
((MaxAllocSize - offsetof(pg_snapshot, xip)) / sizeof(FullTransactionId))
/*
* Helper to get a TransactionId from a 64-bit xid with wraparound detection.
@ -111,10 +87,10 @@ load_xid_epoch(TxidEpoch *state)
* relating to those XIDs.
*/
static bool
TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
TransactionIdInRecentPast(FullTransactionId fxid, TransactionId *extracted_xid)
{
uint32 xid_epoch = (uint32) (xid_with_epoch >> 32);
TransactionId xid = (TransactionId) xid_with_epoch;
uint32 xid_epoch = EpochFromFullTransactionId(fxid);
TransactionId xid = XidFromFullTransactionId(fxid);
uint32 now_epoch;
TransactionId now_epoch_next_xid;
FullTransactionId now_fullxid;
@ -134,11 +110,12 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
return true;
/* If the transaction ID is in the future, throw an error. */
if (xid_with_epoch >= U64FromFullTransactionId(now_fullxid))
if (!FullTransactionIdPrecedes(fxid, now_fullxid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("transaction ID %s is in the future",
psprintf(UINT64_FORMAT, xid_with_epoch))));
psprintf(UINT64_FORMAT,
U64FromFullTransactionId(fxid)))));
/*
* ShmemVariableCache->oldestClogXid is protected by CLogTruncationLock,
@ -164,41 +141,46 @@ TransactionIdInRecentPast(uint64 xid_with_epoch, TransactionId *extracted_xid)
}
/*
* do a TransactionId -> txid conversion for an XID near the given epoch
* Convert a TransactionId obtained from a snapshot held by the caller to a
* FullTransactionId. Use next_fxid as a reference FullTransactionId, so that
* we can compute the high order bits. It must have been obtained by the
* caller with ReadNextFullTransactionId() after the snapshot was created.
*/
static txid
convert_xid(TransactionId xid, const TxidEpoch *state)
static FullTransactionId
widen_snapshot_xid(TransactionId xid, FullTransactionId next_fxid)
{
uint64 epoch;
TransactionId next_xid = XidFromFullTransactionId(next_fxid);
uint32 epoch = EpochFromFullTransactionId(next_fxid);
/* return special xid's as-is */
/* Special transaction ID. */
if (!TransactionIdIsNormal(xid))
return (txid) xid;
return FullTransactionIdFromEpochAndXid(0, xid);
/* xid can be on either side when near wrap-around */
epoch = (uint64) state->epoch;
if (xid > state->last_xid &&
TransactionIdPrecedes(xid, state->last_xid))
/*
* The 64 bit result must be <= next_fxid, since next_fxid hadn't been
* issued yet when the snapshot was created. Every TransactionId in the
* snapshot must therefore be from the same epoch as next_fxid, or the
* epoch before. We know this because next_fxid is never allow to get
* more than one epoch ahead of the TransactionIds in any snapshot.
*/
if (xid > next_xid)
epoch--;
else if (xid < state->last_xid &&
TransactionIdFollows(xid, state->last_xid))
epoch++;
return (epoch << 32) | xid;
return FullTransactionIdFromEpochAndXid(epoch, xid);
}
/*
* txid comparator for qsort/bsearch
*/
static int
cmp_txid(const void *aa, const void *bb)
cmp_fxid(const void *aa, const void *bb)
{
txid a = *(const txid *) aa;
txid b = *(const txid *) bb;
FullTransactionId a = *(const FullTransactionId *) aa;
FullTransactionId b = *(const FullTransactionId *) bb;
if (a < b)
if (FullTransactionIdPrecedes(a, b))
return -1;
if (a > b)
if (FullTransactionIdPrecedes(b, a))
return 1;
return 0;
}
@ -211,31 +193,33 @@ cmp_txid(const void *aa, const void *bb)
* will not be used.
*/
static void
sort_snapshot(TxidSnapshot *snap)
sort_snapshot(pg_snapshot *snap)
{
if (snap->nxip > 1)
{
qsort(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
snap->nxip = qunique(snap->xip, snap->nxip, sizeof(txid), cmp_txid);
qsort(snap->xip, snap->nxip, sizeof(FullTransactionId), cmp_fxid);
snap->nxip = qunique(snap->xip, snap->nxip, sizeof(FullTransactionId),
cmp_fxid);
}
}
/*
* check txid visibility.
* check fxid visibility.
*/
static bool
is_visible_txid(txid value, const TxidSnapshot *snap)
is_visible_fxid(FullTransactionId value, const pg_snapshot *snap)
{
if (value < snap->xmin)
if (FullTransactionIdPrecedes(value, snap->xmin))
return true;
else if (value >= snap->xmax)
else if (!FullTransactionIdPrecedes(value, snap->xmax))
return false;
#ifdef USE_BSEARCH_IF_NXIP_GREATER
else if (snap->nxip > USE_BSEARCH_IF_NXIP_GREATER)
{
void *res;
res = bsearch(&value, snap->xip, snap->nxip, sizeof(txid), cmp_txid);
res = bsearch(&value, snap->xip, snap->nxip, sizeof(FullTransactionId),
cmp_fxid);
/* if found, transaction is still in progress */
return (res) ? false : true;
}
@ -246,7 +230,7 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
for (i = 0; i < snap->nxip; i++)
{
if (value == snap->xip[i])
if (FullTransactionIdEquals(value, snap->xip[i]))
return false;
}
return true;
@ -254,13 +238,13 @@ is_visible_txid(txid value, const TxidSnapshot *snap)
}
/*
* helper functions to use StringInfo for TxidSnapshot creation.
* helper functions to use StringInfo for pg_snapshot creation.
*/
static StringInfo
buf_init(txid xmin, txid xmax)
buf_init(FullTransactionId xmin, FullTransactionId xmax)
{
TxidSnapshot snap;
pg_snapshot snap;
StringInfo buf;
snap.xmin = xmin;
@ -268,25 +252,25 @@ buf_init(txid xmin, txid xmax)
snap.nxip = 0;
buf = makeStringInfo();
appendBinaryStringInfo(buf, (char *) &snap, TXID_SNAPSHOT_SIZE(0));
appendBinaryStringInfo(buf, (char *) &snap, PG_SNAPSHOT_SIZE(0));
return buf;
}
static void
buf_add_txid(StringInfo buf, txid xid)
buf_add_txid(StringInfo buf, FullTransactionId fxid)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
pg_snapshot *snap = (pg_snapshot *) buf->data;
/* do this before possible realloc */
snap->nxip++;
appendBinaryStringInfo(buf, (char *) &xid, sizeof(xid));
appendBinaryStringInfo(buf, (char *) &fxid, sizeof(fxid));
}
static TxidSnapshot *
static pg_snapshot *
buf_finalize(StringInfo buf)
{
TxidSnapshot *snap = (TxidSnapshot *) buf->data;
pg_snapshot *snap = (pg_snapshot *) buf->data;
SET_VARSIZE(snap, buf->len);
@ -297,68 +281,34 @@ buf_finalize(StringInfo buf)
return snap;
}
/*
* simple number parser.
*
* We return 0 on error, which is invalid value for txid.
*/
static txid
str2txid(const char *s, const char **endp)
{
txid val = 0;
txid cutoff = MAX_TXID / 10;
txid cutlim = MAX_TXID % 10;
for (; *s; s++)
{
unsigned d;
if (*s < '0' || *s > '9')
break;
d = *s - '0';
/*
* check for overflow
*/
if (val > cutoff || (val == cutoff && d > cutlim))
{
val = 0;
break;
}
val = val * 10 + d;
}
if (endp)
*endp = s;
return val;
}
/*
* parse snapshot from cstring
*/
static TxidSnapshot *
static pg_snapshot *
parse_snapshot(const char *str)
{
txid xmin;
txid xmax;
txid last_val = 0,
val;
FullTransactionId xmin;
FullTransactionId xmax;
FullTransactionId last_val = InvalidFullTransactionId;
FullTransactionId val;
const char *str_start = str;
const char *endp;
char *endp;
StringInfo buf;
xmin = str2txid(str, &endp);
xmin = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
xmax = str2txid(str, &endp);
xmax = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
if (*endp != ':')
goto bad_format;
str = endp + 1;
/* it should look sane */
if (xmin == 0 || xmax == 0 || xmin > xmax)
if (!FullTransactionIdIsValid(xmin) ||
!FullTransactionIdIsValid(xmax) ||
FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
/* allocate buffer */
@ -368,15 +318,17 @@ parse_snapshot(const char *str)
while (*str != '\0')
{
/* read next value */
val = str2txid(str, &endp);
val = FullTransactionIdFromU64(pg_strtouint64(str, &endp, 10));
str = endp;
/* require the input to be in order */
if (val < xmin || val >= xmax || val < last_val)
if (FullTransactionIdPrecedes(val, xmin) ||
FullTransactionIdFollowsOrEquals(val, xmax) ||
FullTransactionIdPrecedes(val, last_val))
goto bad_format;
/* skip duplicates */
if (val != last_val)
if (!FullTransactionIdEquals(val, last_val))
buf_add_txid(buf, val);
last_val = val;
@ -392,108 +344,82 @@ bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for type %s: \"%s\"",
"txid_snapshot", str_start)));
"pg_snapshot", str_start)));
return NULL; /* keep compiler quiet */
}
/*
* Public functions.
* pg_current_xact_id() returns xid8
*
* txid_current() and txid_current_snapshot() are the only ones that
* communicate with core xid machinery. All the others work on data
* returned by them.
*/
/*
* txid_current() returns int8
*
* Return the current toplevel transaction ID as TXID
* Return the current toplevel full transaction ID.
* If the current transaction does not have one, one is assigned.
*
* This value has the epoch as the high 32 bits and the 32-bit xid
* as the low 32 bits.
*/
Datum
txid_current(PG_FUNCTION_ARGS)
pg_current_xact_id(PG_FUNCTION_ARGS)
{
txid val;
TxidEpoch state;
/*
* Must prevent during recovery because if an xid is not assigned we try
* to assign one, which would fail. Programs already rely on this function
* to always return a valid current xid, so we should not change this to
* return NULL or similar invalid xid.
*/
PreventCommandDuringRecovery("txid_current()");
PreventCommandDuringRecovery("pg_current_xact_id()");
load_xid_epoch(&state);
val = convert_xid(GetTopTransactionId(), &state);
PG_RETURN_INT64(val);
PG_RETURN_FULLTRANSACTIONID(GetTopFullTransactionId());
}
/*
* Same as txid_current() but doesn't assign a new xid if there isn't one
* yet.
* Same as pg_current_xact_if_assigned() but doesn't assign a new xid if there
* isn't one yet.
*/
Datum
txid_current_if_assigned(PG_FUNCTION_ARGS)
pg_current_xact_id_if_assigned(PG_FUNCTION_ARGS)
{
txid val;
TxidEpoch state;
TransactionId topxid = GetTopTransactionIdIfAny();
FullTransactionId topfxid = GetTopFullTransactionIdIfAny();
if (topxid == InvalidTransactionId)
if (!FullTransactionIdIsValid(topfxid))
PG_RETURN_NULL();
load_xid_epoch(&state);
val = convert_xid(topxid, &state);
PG_RETURN_INT64(val);
PG_RETURN_FULLTRANSACTIONID(topfxid);
}
/*
* txid_current_snapshot() returns txid_snapshot
* pg_current_snapshot() returns pg_snapshot
*
* Return current snapshot in TXID format
* Return current snapshot
*
* Note that only top-transaction XIDs are included in the snapshot.
*/
Datum
txid_current_snapshot(PG_FUNCTION_ARGS)
pg_current_snapshot(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap;
pg_snapshot *snap;
uint32 nxip,
i;
TxidEpoch state;
Snapshot cur;
FullTransactionId next_fxid = ReadNextFullTransactionId();
cur = GetActiveSnapshot();
if (cur == NULL)
elog(ERROR, "no active snapshot set");
load_xid_epoch(&state);
/*
* Compile-time limits on the procarray (MAX_BACKENDS processes plus
* MAX_BACKENDS prepared transactions) guarantee nxip won't be too large.
*/
StaticAssertStmt(MAX_BACKENDS * 2 <= TXID_SNAPSHOT_MAX_NXIP,
"possible overflow in txid_current_snapshot()");
StaticAssertStmt(MAX_BACKENDS * 2 <= PG_SNAPSHOT_MAX_NXIP,
"possible overflow in pg_current_snapshot()");
/* allocate */
nxip = cur->xcnt;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
snap = palloc(PG_SNAPSHOT_SIZE(nxip));
/* fill */
snap->xmin = convert_xid(cur->xmin, &state);
snap->xmax = convert_xid(cur->xmax, &state);
snap->xmin = widen_snapshot_xid(cur->xmin, next_fxid);
snap->xmax = widen_snapshot_xid(cur->xmax, next_fxid);
snap->nxip = nxip;
for (i = 0; i < nxip; i++)
snap->xip[i] = convert_xid(cur->xip[i], &state);
snap->xip[i] = widen_snapshot_xid(cur->xip[i], next_fxid);
/*
* We want them guaranteed to be in ascending order. This also removes
@ -505,21 +431,21 @@ txid_current_snapshot(PG_FUNCTION_ARGS)
sort_snapshot(snap);
/* set size after sorting, because it may have removed duplicate xips */
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(snap->nxip));
SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(snap->nxip));
PG_RETURN_POINTER(snap);
}
/*
* txid_snapshot_in(cstring) returns txid_snapshot
* pg_snapshot_in(cstring) returns pg_snapshot
*
* input function for type txid_snapshot
* input function for type pg_snapshot
*/
Datum
txid_snapshot_in(PG_FUNCTION_ARGS)
pg_snapshot_in(PG_FUNCTION_ARGS)
{
char *str = PG_GETARG_CSTRING(0);
TxidSnapshot *snap;
pg_snapshot *snap;
snap = parse_snapshot(str);
@ -527,73 +453,81 @@ txid_snapshot_in(PG_FUNCTION_ARGS)
}
/*
* txid_snapshot_out(txid_snapshot) returns cstring
* pg_snapshot_out(pg_snapshot) returns cstring
*
* output function for type txid_snapshot
* output function for type pg_snapshot
*/
Datum
txid_snapshot_out(PG_FUNCTION_ARGS)
pg_snapshot_out(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData str;
uint32 i;
initStringInfo(&str);
appendStringInfo(&str, TXID_FMT ":", snap->xmin);
appendStringInfo(&str, TXID_FMT ":", snap->xmax);
appendStringInfo(&str, UINT64_FORMAT ":",
U64FromFullTransactionId(snap->xmin));
appendStringInfo(&str, UINT64_FORMAT ":",
U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
{
if (i > 0)
appendStringInfoChar(&str, ',');
appendStringInfo(&str, TXID_FMT, snap->xip[i]);
appendStringInfo(&str, UINT64_FORMAT,
U64FromFullTransactionId(snap->xip[i]));
}
PG_RETURN_CSTRING(str.data);
}
/*
* txid_snapshot_recv(internal) returns txid_snapshot
* pg_snapshot_recv(internal) returns pg_snapshot
*
* binary input function for type txid_snapshot
* binary input function for type pg_snapshot
*
* format: int4 nxip, int8 xmin, int8 xmax, int8 xip
*/
Datum
txid_snapshot_recv(PG_FUNCTION_ARGS)
pg_snapshot_recv(PG_FUNCTION_ARGS)
{
StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
TxidSnapshot *snap;
txid last = 0;
pg_snapshot *snap;
FullTransactionId last = InvalidFullTransactionId;
int nxip;
int i;
txid xmin,
xmax;
FullTransactionId xmin;
FullTransactionId xmax;
/* load and validate nxip */
nxip = pq_getmsgint(buf, 4);
if (nxip < 0 || nxip > TXID_SNAPSHOT_MAX_NXIP)
if (nxip < 0 || nxip > PG_SNAPSHOT_MAX_NXIP)
goto bad_format;
xmin = pq_getmsgint64(buf);
xmax = pq_getmsgint64(buf);
if (xmin == 0 || xmax == 0 || xmin > xmax || xmax > MAX_TXID)
xmin = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
xmax = FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
if (!FullTransactionIdIsValid(xmin) ||
!FullTransactionIdIsValid(xmax) ||
FullTransactionIdPrecedes(xmax, xmin))
goto bad_format;
snap = palloc(TXID_SNAPSHOT_SIZE(nxip));
snap = palloc(PG_SNAPSHOT_SIZE(nxip));
snap->xmin = xmin;
snap->xmax = xmax;
for (i = 0; i < nxip; i++)
{
txid cur = pq_getmsgint64(buf);
FullTransactionId cur =
FullTransactionIdFromU64((uint64) pq_getmsgint64(buf));
if (cur < last || cur < xmin || cur >= xmax)
if (FullTransactionIdPrecedes(cur, last) ||
FullTransactionIdPrecedes(cur, xmin) ||
FullTransactionIdPrecedes(xmax, cur))
goto bad_format;
/* skip duplicate xips */
if (cur == last)
if (FullTransactionIdEquals(cur, last))
{
i--;
nxip--;
@ -604,95 +538,95 @@ txid_snapshot_recv(PG_FUNCTION_ARGS)
last = cur;
}
snap->nxip = nxip;
SET_VARSIZE(snap, TXID_SNAPSHOT_SIZE(nxip));
SET_VARSIZE(snap, PG_SNAPSHOT_SIZE(nxip));
PG_RETURN_POINTER(snap);
bad_format:
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("invalid external txid_snapshot data")));
errmsg("invalid external pg_snapshot data")));
PG_RETURN_POINTER(NULL); /* keep compiler quiet */
}
/*
* txid_snapshot_send(txid_snapshot) returns bytea
* pg_snapshot_send(pg_snapshot) returns bytea
*
* binary output function for type txid_snapshot
* binary output function for type pg_snapshot
*
* format: int4 nxip, int8 xmin, int8 xmax, int8 xip
* format: int4 nxip, u64 xmin, u64 xmax, u64 xip...
*/
Datum
txid_snapshot_send(PG_FUNCTION_ARGS)
pg_snapshot_send(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
StringInfoData buf;
uint32 i;
pq_begintypsend(&buf);
pq_sendint32(&buf, snap->nxip);
pq_sendint64(&buf, snap->xmin);
pq_sendint64(&buf, snap->xmax);
pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmin));
pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xmax));
for (i = 0; i < snap->nxip; i++)
pq_sendint64(&buf, snap->xip[i]);
pq_sendint64(&buf, (int64) U64FromFullTransactionId(snap->xip[i]));
PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}
/*
* txid_visible_in_snapshot(int8, txid_snapshot) returns bool
* pg_visible_in_snapshot(xid8, pg_snapshot) returns bool
*
* is txid visible in snapshot ?
*/
Datum
txid_visible_in_snapshot(PG_FUNCTION_ARGS)
pg_visible_in_snapshot(PG_FUNCTION_ARGS)
{
txid value = PG_GETARG_INT64(0);
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(1);
FullTransactionId value = PG_GETARG_FULLTRANSACTIONID(0);
pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(1);
PG_RETURN_BOOL(is_visible_txid(value, snap));
PG_RETURN_BOOL(is_visible_fxid(value, snap));
}
/*
* txid_snapshot_xmin(txid_snapshot) returns int8
* pg_snapshot_xmin(pg_snapshot) returns xid8
*
* return snapshot's xmin
*/
Datum
txid_snapshot_xmin(PG_FUNCTION_ARGS)
pg_snapshot_xmin(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
PG_RETURN_INT64(snap->xmin);
PG_RETURN_FULLTRANSACTIONID(snap->xmin);
}
/*
* txid_snapshot_xmax(txid_snapshot) returns int8
* pg_snapshot_xmax(pg_snapshot) returns xid8
*
* return snapshot's xmax
*/
Datum
txid_snapshot_xmax(PG_FUNCTION_ARGS)
pg_snapshot_xmax(PG_FUNCTION_ARGS)
{
TxidSnapshot *snap = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
pg_snapshot *snap = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
PG_RETURN_INT64(snap->xmax);
PG_RETURN_FULLTRANSACTIONID(snap->xmax);
}
/*
* txid_snapshot_xip(txid_snapshot) returns setof int8
* pg_snapshot_xip(pg_snapshot) returns setof xid8
*
* return in-progress TXIDs in snapshot.
* return in-progress xid8s in snapshot.
*/
Datum
txid_snapshot_xip(PG_FUNCTION_ARGS)
pg_snapshot_xip(PG_FUNCTION_ARGS)
{
FuncCallContext *fctx;
TxidSnapshot *snap;
txid value;
pg_snapshot *snap;
FullTransactionId value;
/* on first call initialize fctx and get copy of snapshot */
if (SRF_IS_FIRSTCALL())
{
TxidSnapshot *arg = (TxidSnapshot *) PG_GETARG_VARLENA_P(0);
pg_snapshot *arg = (pg_snapshot *) PG_GETARG_VARLENA_P(0);
fctx = SRF_FIRSTCALL_INIT();
@ -709,7 +643,7 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
if (fctx->call_cntr < snap->nxip)
{
value = snap->xip[fctx->call_cntr];
SRF_RETURN_NEXT(fctx, Int64GetDatum(value));
SRF_RETURN_NEXT(fctx, FullTransactionIdGetDatum(value));
}
else
{
@ -728,10 +662,10 @@ txid_snapshot_xip(PG_FUNCTION_ARGS)
* though the parent xact may still be in progress or may have aborted.
*/
Datum
txid_status(PG_FUNCTION_ARGS)
pg_xact_status(PG_FUNCTION_ARGS)
{
const char *status;
uint64 xid_with_epoch = PG_GETARG_INT64(0);
FullTransactionId fxid = PG_GETARG_FULLTRANSACTIONID(0);
TransactionId xid;
/*
@ -739,7 +673,7 @@ txid_status(PG_FUNCTION_ARGS)
* an I/O error on SLRU lookup.
*/
LWLockAcquire(CLogTruncationLock, LW_SHARED);
if (TransactionIdInRecentPast(xid_with_epoch, &xid))
if (TransactionIdInRecentPast(fxid, &xid))
{
Assert(TransactionIdIsValid(xid));

View File

@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 202004061
#define CATALOG_VERSION_NO 202004062
#endif

View File

@ -9505,46 +9505,89 @@
proname => 'jsonb_path_match_opr', prorettype => 'bool',
proargtypes => 'jsonb jsonpath', prosrc => 'jsonb_path_match_opr' },
# txid
# historical int8/txid_snapshot variants of xid8 functions
{ oid => '2939', descr => 'I/O',
proname => 'txid_snapshot_in', prorettype => 'txid_snapshot',
proargtypes => 'cstring', prosrc => 'txid_snapshot_in' },
proargtypes => 'cstring', prosrc => 'pg_snapshot_in' },
{ oid => '2940', descr => 'I/O',
proname => 'txid_snapshot_out', prorettype => 'cstring',
proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_out' },
proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_out' },
{ oid => '2941', descr => 'I/O',
proname => 'txid_snapshot_recv', prorettype => 'txid_snapshot',
proargtypes => 'internal', prosrc => 'txid_snapshot_recv' },
proargtypes => 'internal', prosrc => 'pg_snapshot_recv' },
{ oid => '2942', descr => 'I/O',
proname => 'txid_snapshot_send', prorettype => 'bytea',
proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_send' },
proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_send' },
{ oid => '2943', descr => 'get current transaction ID',
proname => 'txid_current', provolatile => 's', proparallel => 'u',
prorettype => 'int8', proargtypes => '', prosrc => 'txid_current' },
prorettype => 'int8', proargtypes => '', prosrc => 'pg_current_xact_id' },
{ oid => '3348', descr => 'get current transaction ID',
proname => 'txid_current_if_assigned', provolatile => 's', proparallel => 'u',
prorettype => 'int8', proargtypes => '',
prosrc => 'txid_current_if_assigned' },
prosrc => 'pg_current_xact_id_if_assigned' },
{ oid => '2944', descr => 'get current snapshot',
proname => 'txid_current_snapshot', provolatile => 's',
prorettype => 'txid_snapshot', proargtypes => '',
prosrc => 'txid_current_snapshot' },
prosrc => 'pg_current_snapshot' },
{ oid => '2945', descr => 'get xmin of snapshot',
proname => 'txid_snapshot_xmin', prorettype => 'int8',
proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmin' },
proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmin' },
{ oid => '2946', descr => 'get xmax of snapshot',
proname => 'txid_snapshot_xmax', prorettype => 'int8',
proargtypes => 'txid_snapshot', prosrc => 'txid_snapshot_xmax' },
proargtypes => 'txid_snapshot', prosrc => 'pg_snapshot_xmax' },
{ oid => '2947', descr => 'get set of in-progress txids in snapshot',
proname => 'txid_snapshot_xip', prorows => '50', proretset => 't',
prorettype => 'int8', proargtypes => 'txid_snapshot',
prosrc => 'txid_snapshot_xip' },
prosrc => 'pg_snapshot_xip' },
{ oid => '2948', descr => 'is txid visible in snapshot?',
proname => 'txid_visible_in_snapshot', prorettype => 'bool',
proargtypes => 'int8 txid_snapshot', prosrc => 'txid_visible_in_snapshot' },
proargtypes => 'int8 txid_snapshot', prosrc => 'pg_visible_in_snapshot' },
{ oid => '3360', descr => 'commit status of transaction',
proname => 'txid_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'int8', prosrc => 'txid_status' },
proargtypes => 'int8', prosrc => 'pg_xact_status' },
# pg_snapshot functions
{ oid => '9247', descr => 'I/O',
proname => 'pg_snapshot_in', prorettype => 'pg_snapshot',
proargtypes => 'cstring', prosrc => 'pg_snapshot_in' },
{ oid => '9248', descr => 'I/O',
proname => 'pg_snapshot_out', prorettype => 'cstring',
proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_out' },
{ oid => '9249', descr => 'I/O',
proname => 'pg_snapshot_recv', prorettype => 'pg_snapshot',
proargtypes => 'internal', prosrc => 'pg_snapshot_recv' },
{ oid => '9250', descr => 'I/O',
proname => 'pg_snapshot_send', prorettype => 'bytea',
proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_send' },
{ oid => '9253', descr => 'get current snapshot',
proname => 'pg_current_snapshot', provolatile => 's',
prorettype => 'pg_snapshot', proargtypes => '',
prosrc => 'pg_current_snapshot' },
{ oid => '9254', descr => 'get xmin of snapshot',
proname => 'pg_snapshot_xmin', prorettype => 'xid8',
proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmin' },
{ oid => '9255', descr => 'get xmax of snapshot',
proname => 'pg_snapshot_xmax', prorettype => 'xid8',
proargtypes => 'pg_snapshot', prosrc => 'pg_snapshot_xmax' },
{ oid => '9256', descr => 'get set of in-progress transactions in snapshot',
proname => 'pg_snapshot_xip', prorows => '50', proretset => 't',
prorettype => 'xid8', proargtypes => 'pg_snapshot',
prosrc => 'pg_snapshot_xip' },
{ oid => '9257', descr => 'is xid8 visible in snapshot?',
proname => 'pg_visible_in_snapshot', prorettype => 'bool',
proargtypes => 'xid8 pg_snapshot', prosrc => 'pg_visible_in_snapshot' },
# transaction ID and status functions
{ oid => '9251', descr => 'get current transaction ID',
proname => 'pg_current_xact_id', provolatile => 's', proparallel => 'u',
prorettype => 'xid8', proargtypes => '', prosrc => 'pg_current_xact_id' },
{ oid => '9252', descr => 'get current transaction ID',
proname => 'pg_current_xact_id_if_assigned', provolatile => 's', proparallel => 'u',
prorettype => 'xid8', proargtypes => '',
prosrc => 'pg_current_xact_id_if_assigned' },
{ oid => '9258', descr => 'commit status of transaction',
proname => 'pg_xact_status', provolatile => 'v', prorettype => 'text',
proargtypes => 'xid8', prosrc => 'pg_xact_status' },
# record comparison using normal comparison rules
{ oid => '2981',

View File

@ -460,6 +460,11 @@
typcategory => 'U', typinput => 'txid_snapshot_in',
typoutput => 'txid_snapshot_out', typreceive => 'txid_snapshot_recv',
typsend => 'txid_snapshot_send', typalign => 'd', typstorage => 'x' },
{ oid => '8355', array_type_oid => '8356', descr => 'snapshot',
typname => 'pg_snapshot', typlen => '-1', typbyval => 'f',
typcategory => 'U', typinput => 'pg_snapshot_in',
typoutput => 'pg_snapshot_out', typreceive => 'pg_snapshot_recv',
typsend => 'pg_snapshot_send', typalign => 'd', typstorage => 'x' },
# range types
{ oid => '3904', array_type_oid => '3905', descr => 'range of integers',

View File

@ -45,7 +45,7 @@ my $xid = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (1, current_timestamp);
SELECT txid_current();
SELECT pg_current_xact_id()::xid;
COMMIT;
]);
@ -93,7 +93,7 @@ DECLARE
i int;
BEGIN
FOR i in 1..cnt LOOP
EXECUTE 'SELECT txid_current()';
EXECUTE 'SELECT pg_current_xact_id()';
COMMIT;
END LOOP;
END;
@ -115,7 +115,7 @@ my $xid_disabled = $node_master->safe_psql(
'postgres', qq[
BEGIN;
INSERT INTO committs_test(x, y) VALUES (2, current_timestamp);
SELECT txid_current();
SELECT pg_current_xact_id();
COMMIT;
]);

View File

@ -43,7 +43,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
v_txid_snapshot txid_snapshot,
v_pg_snapshot pg_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,

View File

@ -44,7 +44,7 @@ CREATE TABLE datatype_table (
v_json JSON,
v_xml XML,
v_uuid UUID,
v_txid_snapshot txid_snapshot,
v_pg_snapshot pg_snapshot,
v_enum ENUM_TEST,
v_postal_code japanese_postal_code,
v_int2range int2range,

View File

@ -68,7 +68,7 @@ $node_master->backup('my_backup');
$node_master->safe_psql('postgres',
"INSERT INTO tab_int VALUES (generate_series(1001,2000))");
my $ret = $node_master->safe_psql('postgres',
"SELECT pg_current_wal_lsn(), txid_current();");
"SELECT pg_current_wal_lsn(), pg_current_xact_id();");
my ($lsn2, $recovery_txid) = split /\|/, $ret;
# More data, with recovery target timestamp

View File

@ -24,7 +24,7 @@ $node->start;
my ($stdin, $stdout, $stderr) = ('', '', '');
# Ensure that txid_status reports 'aborted' for xacts
# Ensure that pg_xact_status reports 'aborted' for xacts
# that were in-progress during crash. To do that, we need
# an xact to be in-progress when we crash and we need to know
# its xid.
@ -42,7 +42,7 @@ my $tx = IPC::Run::start(
$stdin .= q[
BEGIN;
CREATE TABLE mine(x integer);
SELECT txid_current();
SELECT pg_current_xact_id();
];
$tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
@ -50,7 +50,7 @@ $tx->pump until $stdout =~ /[[:digit:]]+[\r\n]$/;
my $xid = $stdout;
chomp($xid);
is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]),
'in progress', 'own xid is in-progress');
# Crash and restart the postmaster
@ -58,11 +58,11 @@ $node->stop('immediate');
$node->start;
# Make sure we really got a new xid
cmp_ok($node->safe_psql('postgres', 'SELECT txid_current()'),
cmp_ok($node->safe_psql('postgres', 'SELECT pg_current_xact_id()'),
'>', $xid, 'new xid after restart is greater');
# and make sure we show the in-progress xact as aborted
is($node->safe_psql('postgres', qq[SELECT txid_status('$xid');]),
is($node->safe_psql('postgres', qq[SELECT pg_xact_status('$xid');]),
'aborted', 'xid is aborted after crash');
$tx->kill_kill;

View File

@ -2556,7 +2556,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
where transactionid = txid_current()::integer)
where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@ -2719,7 +2719,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
where transactionid = txid_current()::integer)
where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'

View File

@ -4,9 +4,9 @@
-- hs_standby_functions.sql
--
-- should fail
select txid_current();
ERROR: cannot execute txid_current() during recovery
select length(txid_current_snapshot()::text) >= 4;
select pg_current_xact_id();
ERROR: cannot execute pg_current_xact_id() during recovery
select length(pg_current_snapshot()::text) >= 4;
?column?
----------
t

View File

@ -194,9 +194,11 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
prorettype | prorettype
------------+------------
20 | 9419
25 | 1043
1114 | 1184
(2 rows)
2970 | 8355
(4 rows)
SELECT DISTINCT p1.proargtypes[0], p2.proargtypes[0]
FROM pg_proc AS p1, pg_proc AS p2
@ -210,11 +212,13 @@ WHERE p1.oid != p2.oid AND
ORDER BY 1, 2;
proargtypes | proargtypes
-------------+-------------
20 | 9419
25 | 1042
25 | 1043
1114 | 1184
1560 | 1562
(4 rows)
2970 | 8355
(6 rows)
SELECT DISTINCT p1.proargtypes[1], p2.proargtypes[1]
FROM pg_proc AS p1, pg_proc AS p2
@ -231,7 +235,8 @@ ORDER BY 1, 2;
23 | 28
1114 | 1184
1560 | 1562
(3 rows)
2970 | 8355
(4 rows)
SELECT DISTINCT p1.proargtypes[2], p2.proargtypes[2]
FROM pg_proc AS p1, pg_proc AS p2

View File

@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
-- Note: these are backward-compatibility functions and types, and have been
-- replaced by new xid8-based variants. See xid.sql. The txid variants will
-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;
txid_snapshot
@ -20,19 +23,19 @@ select '12:16:14,14'::txid_snapshot;
-- errors
select '31:12:'::txid_snapshot;
ERROR: invalid input syntax for type txid_snapshot: "31:12:"
ERROR: invalid input syntax for type pg_snapshot: "31:12:"
LINE 1: select '31:12:'::txid_snapshot;
^
select '0:1:'::txid_snapshot;
ERROR: invalid input syntax for type txid_snapshot: "0:1:"
ERROR: invalid input syntax for type pg_snapshot: "0:1:"
LINE 1: select '0:1:'::txid_snapshot;
^
select '12:13:0'::txid_snapshot;
ERROR: invalid input syntax for type txid_snapshot: "12:13:0"
ERROR: invalid input syntax for type pg_snapshot: "12:13:0"
LINE 1: select '12:13:0'::txid_snapshot;
^
select '12:16:14,13'::txid_snapshot;
ERROR: invalid input syntax for type txid_snapshot: "12:16:14,13"
ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::txid_snapshot;
^
create temp table snapshot_test (
@ -235,7 +238,7 @@ SELECT txid_snapshot '1:9223372036854775807:3';
(1 row)
SELECT txid_snapshot '1:9223372036854775808:3';
ERROR: invalid input syntax for type txid_snapshot: "1:9223372036854775808:3"
ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT txid_snapshot '1:9223372036854775808:3';
^
-- test txid_current_if_assigned

View File

@ -230,10 +230,9 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
@ -243,13 +242,12 @@ INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
tableoid | xmin_correct | xmax_correct
-------------+--------------+--------------
upsert_test | t | t
(1 row)
DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;
---------------------------

View File

@ -134,3 +134,329 @@ create table xid8_t1 (x xid8);
create index on xid8_t1 using btree(x);
create index on xid8_t1 using hash(x);
drop table xid8_t1;
-- pg_snapshot data type and related functions
-- Note: another set of tests similar to this exists in txid.sql, for a limited
-- time (the relevant functions share C code)
-- i/o
select '12:13:'::pg_snapshot;
pg_snapshot
-------------
12:13:
(1 row)
select '12:18:14,16'::pg_snapshot;
pg_snapshot
-------------
12:18:14,16
(1 row)
select '12:16:14,14'::pg_snapshot;
pg_snapshot
-------------
12:16:14
(1 row)
-- errors
select '31:12:'::pg_snapshot;
ERROR: invalid input syntax for type pg_snapshot: "31:12:"
LINE 1: select '31:12:'::pg_snapshot;
^
select '0:1:'::pg_snapshot;
ERROR: invalid input syntax for type pg_snapshot: "0:1:"
LINE 1: select '0:1:'::pg_snapshot;
^
select '12:13:0'::pg_snapshot;
ERROR: invalid input syntax for type pg_snapshot: "12:13:0"
LINE 1: select '12:13:0'::pg_snapshot;
^
select '12:16:14,13'::pg_snapshot;
ERROR: invalid input syntax for type pg_snapshot: "12:16:14,13"
LINE 1: select '12:16:14,13'::pg_snapshot;
^
create temp table snapshot_test (
nr integer,
snap pg_snapshot
);
insert into snapshot_test values (1, '12:13:');
insert into snapshot_test values (2, '12:20:13,15,18');
insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
select snap from snapshot_test order by nr;
snap
-------------------------------------------------------------------------------------------------------------------------------------
12:13:
12:20:13,15,18
100001:100009:100005,100007,100008
100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131
(4 rows)
select pg_snapshot_xmin(snap),
pg_snapshot_xmax(snap),
pg_snapshot_xip(snap)
from snapshot_test order by nr;
pg_snapshot_xmin | pg_snapshot_xmax | pg_snapshot_xip
------------------+------------------+-----------------
12 | 20 | 13
12 | 20 | 15
12 | 20 | 18
100001 | 100009 | 100005
100001 | 100009 | 100007
100001 | 100009 | 100008
100 | 150 | 101
100 | 150 | 102
100 | 150 | 103
100 | 150 | 104
100 | 150 | 105
100 | 150 | 106
100 | 150 | 107
100 | 150 | 108
100 | 150 | 109
100 | 150 | 110
100 | 150 | 111
100 | 150 | 112
100 | 150 | 113
100 | 150 | 114
100 | 150 | 115
100 | 150 | 116
100 | 150 | 117
100 | 150 | 118
100 | 150 | 119
100 | 150 | 120
100 | 150 | 121
100 | 150 | 122
100 | 150 | 123
100 | 150 | 124
100 | 150 | 125
100 | 150 | 126
100 | 150 | 127
100 | 150 | 128
100 | 150 | 129
100 | 150 | 130
100 | 150 | 131
(37 rows)
select id, pg_visible_in_snapshot(id::text::xid8, snap)
from snapshot_test, generate_series(11, 21) id
where nr = 2;
id | pg_visible_in_snapshot
----+------------------------
11 | t
12 | t
13 | f
14 | t
15 | f
16 | t
17 | t
18 | f
19 | t
20 | f
21 | f
(11 rows)
-- test bsearch
select id, pg_visible_in_snapshot(id::text::xid8, snap)
from snapshot_test, generate_series(90, 160) id
where nr = 4;
id | pg_visible_in_snapshot
-----+------------------------
90 | t
91 | t
92 | t
93 | t
94 | t
95 | t
96 | t
97 | t
98 | t
99 | t
100 | t
101 | f
102 | f
103 | f
104 | f
105 | f
106 | f
107 | f
108 | f
109 | f
110 | f
111 | f
112 | f
113 | f
114 | f
115 | f
116 | f
117 | f
118 | f
119 | f
120 | f
121 | f
122 | f
123 | f
124 | f
125 | f
126 | f
127 | f
128 | f
129 | f
130 | f
131 | f
132 | t
133 | t
134 | t
135 | t
136 | t
137 | t
138 | t
139 | t
140 | t
141 | t
142 | t
143 | t
144 | t
145 | t
146 | t
147 | t
148 | t
149 | t
150 | f
151 | f
152 | f
153 | f
154 | f
155 | f
156 | f
157 | f
158 | f
159 | f
160 | f
(71 rows)
-- test current values also
select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot());
?column?
----------
t
(1 row)
-- we can't assume current is always less than xmax, however
select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot());
pg_visible_in_snapshot
------------------------
f
(1 row)
-- test 64bitness
select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
pg_snapshot
---------------------------------------------------------------------
1000100010001000:1000100010001100:1000100010001012,1000100010001013
(1 row)
select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
pg_visible_in_snapshot
------------------------
f
(1 row)
select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
pg_visible_in_snapshot
------------------------
t
(1 row)
-- test 64bit overflow
SELECT pg_snapshot '1:9223372036854775807:3';
pg_snapshot
-------------------------
1:9223372036854775807:3
(1 row)
SELECT pg_snapshot '1:9223372036854775808:3';
ERROR: invalid input syntax for type pg_snapshot: "1:9223372036854775808:3"
LINE 1: SELECT pg_snapshot '1:9223372036854775808:3';
^
-- test pg_current_xact_id_if_assigned
BEGIN;
SELECT pg_current_xact_id_if_assigned() IS NULL;
?column?
----------
t
(1 row)
SELECT pg_current_xact_id() \gset
SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id';
?column?
----------
t
(1 row)
COMMIT;
-- test xid status functions
BEGIN;
SELECT pg_current_xact_id() AS committed \gset
COMMIT;
BEGIN;
SELECT pg_current_xact_id() AS rolledback \gset
ROLLBACK;
BEGIN;
SELECT pg_current_xact_id() AS inprogress \gset
SELECT pg_xact_status(:committed::text::xid8) AS committed;
committed
-----------
committed
(1 row)
SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback;
rolledback
------------
aborted
(1 row)
SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress;
inprogress
-------------
in progress
(1 row)
SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed
pg_xact_status
----------------
committed
(1 row)
SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed
pg_xact_status
----------------
committed
(1 row)
SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
pg_xact_status
----------------
(1 row)
COMMIT;
BEGIN;
CREATE FUNCTION test_future_xid_status(xid8)
RETURNS void
LANGUAGE plpgsql
AS
$$
BEGIN
PERFORM pg_xact_status($1);
RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
EXCEPTION
WHEN invalid_parameter_value THEN
RAISE NOTICE 'Got expected error for xid in the future';
END;
$$;
SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
NOTICE: Got expected error for xid in the future
test_future_xid_status
------------------------
(1 row)
ROLLBACK;

View File

@ -1645,7 +1645,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
where transactionid = txid_current()::integer)
where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname != 'my_locks'
@ -1732,7 +1732,7 @@ from pg_locks l join pg_class c on l.relation = c.oid
where virtualtransaction = (
select virtualtransaction
from pg_locks
where transactionid = txid_current()::integer)
where transactionid = pg_current_xact_id()::xid)
and locktype = 'relation'
and relnamespace != (select oid from pg_namespace where nspname = 'pg_catalog')
and c.relname = 'my_locks'

View File

@ -5,9 +5,9 @@
--
-- should fail
select txid_current();
select pg_current_xact_id();
select length(txid_current_snapshot()::text) >= 4;
select length(pg_current_snapshot()::text) >= 4;
select pg_start_backup('should fail');
select pg_switch_wal();

View File

@ -1,4 +1,7 @@
-- txid_snapshot data type and related functions
-- Note: these are backward-compatibility functions and types, and have been
-- replaced by new xid8-based variants. See xid.sql. The txid variants will
-- be removed in a future release.
-- i/o
select '12:13:'::txid_snapshot;

View File

@ -117,18 +117,16 @@ INSERT INTO upsert_test VALUES (1, 'Bat') ON CONFLICT(a)
-- ON CONFLICT using system attributes in RETURNING, testing both the
-- inserting and updating paths. See bug report at:
-- https://www.postgresql.org/message-id/73436355-6432-49B1-92ED-1FE4F7E7E100%40finefun.com.au
CREATE FUNCTION xid_current() RETURNS xid LANGUAGE SQL AS $$SELECT (txid_current() % ((1::int8<<32)))::text::xid;$$;
INSERT INTO upsert_test VALUES (2, 'Beeble') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = 0 AS xmax_correct;
RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = 0 AS xmax_correct;
-- currently xmax is set after a conflict - that's probably not good,
-- but it seems worthwhile to have to be explicit if that changes.
INSERT INTO upsert_test VALUES (2, 'Brox') ON CONFLICT(a)
DO UPDATE SET (b, a) = (SELECT b || ', Excluded', a from upsert_test i WHERE i.a = excluded.a)
RETURNING tableoid::regclass, xmin = xid_current() AS xmin_correct, xmax = xid_current() AS xmax_correct;
RETURNING tableoid::regclass, xmin = pg_current_xact_id()::xid AS xmin_correct, xmax = pg_current_xact_id()::xid AS xmax_correct;
DROP FUNCTION xid_current();
DROP TABLE update_test;
DROP TABLE upsert_test;

View File

@ -46,3 +46,107 @@ create table xid8_t1 (x xid8);
create index on xid8_t1 using btree(x);
create index on xid8_t1 using hash(x);
drop table xid8_t1;
-- pg_snapshot data type and related functions
-- Note: another set of tests similar to this exists in txid.sql, for a limited
-- time (the relevant functions share C code)
-- i/o
select '12:13:'::pg_snapshot;
select '12:18:14,16'::pg_snapshot;
select '12:16:14,14'::pg_snapshot;
-- errors
select '31:12:'::pg_snapshot;
select '0:1:'::pg_snapshot;
select '12:13:0'::pg_snapshot;
select '12:16:14,13'::pg_snapshot;
create temp table snapshot_test (
nr integer,
snap pg_snapshot
);
insert into snapshot_test values (1, '12:13:');
insert into snapshot_test values (2, '12:20:13,15,18');
insert into snapshot_test values (3, '100001:100009:100005,100007,100008');
insert into snapshot_test values (4, '100:150:101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131');
select snap from snapshot_test order by nr;
select pg_snapshot_xmin(snap),
pg_snapshot_xmax(snap),
pg_snapshot_xip(snap)
from snapshot_test order by nr;
select id, pg_visible_in_snapshot(id::text::xid8, snap)
from snapshot_test, generate_series(11, 21) id
where nr = 2;
-- test bsearch
select id, pg_visible_in_snapshot(id::text::xid8, snap)
from snapshot_test, generate_series(90, 160) id
where nr = 4;
-- test current values also
select pg_current_xact_id() >= pg_snapshot_xmin(pg_current_snapshot());
-- we can't assume current is always less than xmax, however
select pg_visible_in_snapshot(pg_current_xact_id(), pg_current_snapshot());
-- test 64bitness
select pg_snapshot '1000100010001000:1000100010001100:1000100010001012,1000100010001013';
select pg_visible_in_snapshot('1000100010001012', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
select pg_visible_in_snapshot('1000100010001015', '1000100010001000:1000100010001100:1000100010001012,1000100010001013');
-- test 64bit overflow
SELECT pg_snapshot '1:9223372036854775807:3';
SELECT pg_snapshot '1:9223372036854775808:3';
-- test pg_current_xact_id_if_assigned
BEGIN;
SELECT pg_current_xact_id_if_assigned() IS NULL;
SELECT pg_current_xact_id() \gset
SELECT pg_current_xact_id_if_assigned() IS NOT DISTINCT FROM xid8 :'pg_current_xact_id';
COMMIT;
-- test xid status functions
BEGIN;
SELECT pg_current_xact_id() AS committed \gset
COMMIT;
BEGIN;
SELECT pg_current_xact_id() AS rolledback \gset
ROLLBACK;
BEGIN;
SELECT pg_current_xact_id() AS inprogress \gset
SELECT pg_xact_status(:committed::text::xid8) AS committed;
SELECT pg_xact_status(:rolledback::text::xid8) AS rolledback;
SELECT pg_xact_status(:inprogress::text::xid8) AS inprogress;
SELECT pg_xact_status('1'::xid8); -- BootstrapTransactionId is always committed
SELECT pg_xact_status('2'::xid8); -- FrozenTransactionId is always committed
SELECT pg_xact_status('3'::xid8); -- in regress testing FirstNormalTransactionId will always be behind oldestXmin
COMMIT;
BEGIN;
CREATE FUNCTION test_future_xid_status(xid8)
RETURNS void
LANGUAGE plpgsql
AS
$$
BEGIN
PERFORM pg_xact_status($1);
RAISE EXCEPTION 'didn''t ERROR at xid in the future as expected';
EXCEPTION
WHEN invalid_parameter_value THEN
RAISE NOTICE 'Got expected error for xid in the future';
END;
$$;
SELECT test_future_xid_status((:inprogress + 10000)::text::xid8);
ROLLBACK;

View File

@ -3099,6 +3099,7 @@ pg_sha224_ctx
pg_sha256_ctx
pg_sha384_ctx
pg_sha512_ctx
pg_snapshot
pg_stack_base_t
pg_time_t
pg_tz