From 4c04be9b05ad2ec5acd27c3417bf075c13cab134 Mon Sep 17 00:00:00 2001 From: Thomas Munro Date: Tue, 7 Apr 2020 11:33:56 +1200 Subject: [PATCH] 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 Reviewed-by: Takao Fujii Reviewed-by: Yoshikazu Imai Reviewed-by: Mark Dilger Discussion: https://postgr.es/m/20190725000636.666m5mad25wfbrri%40alap3.anarazel.de --- contrib/test_decoding/expected/ddl.out | 2 +- .../expected/decoding_in_xact.out | 6 +- .../test_decoding/expected/oldest_xmin.out | 2 +- .../test_decoding/expected/ondisk_startup.out | 6 +- .../expected/snapshot_transfer.out | 4 +- contrib/test_decoding/specs/oldest_xmin.spec | 2 +- .../test_decoding/specs/ondisk_startup.spec | 4 +- .../specs/snapshot_transfer.spec | 2 +- contrib/test_decoding/sql/ddl.sql | 2 +- .../test_decoding/sql/decoding_in_xact.sql | 6 +- doc/src/sgml/datatype.sgml | 8 +- doc/src/sgml/func.sgml | 166 +++++-- doc/src/sgml/logicaldecoding.sgml | 2 +- doc/src/sgml/monitoring.sgml | 2 +- src/backend/utils/adt/Makefile | 2 +- src/backend/utils/adt/{txid.c => xid8funcs.c} | 424 ++++++++---------- src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_proc.dat | 69 ++- src/include/catalog/pg_type.dat | 5 + src/test/modules/commit_ts/t/004_restart.pl | 6 +- .../expected/create_table.out | 2 +- .../test_ddl_deparse/sql/create_table.sql | 2 +- src/test/recovery/t/003_recovery_targets.pl | 2 +- src/test/recovery/t/011_crash_recovery.pl | 10 +- src/test/regress/expected/alter_table.out | 4 +- .../regress/expected/hs_standby_functions.out | 6 +- src/test/regress/expected/opr_sanity.out | 11 +- src/test/regress/expected/txid.out | 13 +- src/test/regress/expected/update.out | 6 +- src/test/regress/expected/xid.out | 326 ++++++++++++++ src/test/regress/sql/alter_table.sql | 4 +- src/test/regress/sql/hs_standby_functions.sql | 4 +- src/test/regress/sql/txid.sql | 3 + src/test/regress/sql/update.sql | 6 +- src/test/regress/sql/xid.sql | 104 +++++ src/tools/pgindent/typedefs.list | 1 + 36 files changed, 871 insertions(+), 355 deletions(-) rename src/backend/utils/adt/{txid.c => xid8funcs.c} (54%) diff --git a/contrib/test_decoding/expected/ddl.out b/contrib/test_decoding/expected/ddl.out index cf0318f697..d79cd316b7 100644 --- a/contrib/test_decoding/expected/ddl.out +++ b/contrib/test_decoding/expected/ddl.out @@ -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 diff --git a/contrib/test_decoding/expected/decoding_in_xact.out b/contrib/test_decoding/expected/decoding_in_xact.out index ab4d3aee72..b65253f463 100644 --- a/contrib/test_decoding/expected/decoding_in_xact.out +++ b/contrib/test_decoding/expected/decoding_in_xact.out @@ -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 diff --git a/contrib/test_decoding/expected/oldest_xmin.out b/contrib/test_decoding/expected/oldest_xmin.out index d1b4f17e3a..02a091398f 100644 --- a/contrib/test_decoding/expected/oldest_xmin.out +++ b/contrib/test_decoding/expected/oldest_xmin.out @@ -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 diff --git a/contrib/test_decoding/expected/ondisk_startup.out b/contrib/test_decoding/expected/ondisk_startup.out index c7b1f45b46..586b03d75d 100644 --- a/contrib/test_decoding/expected/ondisk_startup.out +++ b/contrib/test_decoding/expected/ondisk_startup.out @@ -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'); 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 diff --git a/contrib/test_decoding/expected/snapshot_transfer.out b/contrib/test_decoding/expected/snapshot_transfer.out index 87bed03f76..c3a0000994 100644 --- a/contrib/test_decoding/expected/snapshot_transfer.out +++ b/contrib/test_decoding/expected/snapshot_transfer.out @@ -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 diff --git a/contrib/test_decoding/specs/oldest_xmin.spec b/contrib/test_decoding/specs/oldest_xmin.spec index 6cb13e85ce..da3a8cd512 100644 --- a/contrib/test_decoding/specs/oldest_xmin.spec +++ b/contrib/test_decoding/specs/oldest_xmin.spec @@ -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; } diff --git a/contrib/test_decoding/specs/ondisk_startup.spec b/contrib/test_decoding/specs/ondisk_startup.spec index 12c57a813d..96ce87f1a4 100644 --- a/contrib/test_decoding/specs/ondisk_startup.spec +++ b/contrib/test_decoding/specs/ondisk_startup.spec @@ -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 diff --git a/contrib/test_decoding/specs/snapshot_transfer.spec b/contrib/test_decoding/specs/snapshot_transfer.spec index ae81e8f102..152f2fd03d 100644 --- a/contrib/test_decoding/specs/snapshot_transfer.spec +++ b/contrib/test_decoding/specs/snapshot_transfer.spec @@ -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); } diff --git a/contrib/test_decoding/sql/ddl.sql b/contrib/test_decoding/sql/ddl.sql index 0f2b9992f7..2c4823e578 100644 --- a/contrib/test_decoding/sql/ddl.sql +++ b/contrib/test_decoding/sql/ddl.sql @@ -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; diff --git a/contrib/test_decoding/sql/decoding_in_xact.sql b/contrib/test_decoding/sql/decoding_in_xact.sql index b524eb9a6e..108782dc2e 100644 --- a/contrib/test_decoding/sql/decoding_in_xact.sql +++ b/contrib/test_decoding/sql/decoding_in_xact.sql @@ -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'); diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 89f3a7c119..c2e42f31c0 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -198,6 +198,12 @@ PostgreSQL Log Sequence Number + + pg_snapshot + + user-level transaction ID snapshot + + point @@ -279,7 +285,7 @@ txid_snapshot - user-level transaction ID snapshot + user-level transaction ID snapshot (deprecated; see pg_snapshot) diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index 4d88b45e72..cc4a7bf8df 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -18998,6 +18998,38 @@ SELECT collation for ('foo' COLLATE "de_DE"); are stored globally as well. + + pg_current_xact_id + + + + pg_current_xact_id_if_assigned + + + + pg_current_snapshot + + + + pg_snapshot_xip + + + + pg_snapshot_xmax + + + + pg_snapshot_xmin + + + + pg_visible_in_snapshot + + + + pg_xact_status + + txid_current @@ -19031,76 +19063,136 @@ SELECT collation for ('foo' COLLATE "de_DE"); - The functions shown in + The functions shown in provide server transaction information in an exportable form. The main use of these functions is to determine which transactions were committed between two snapshots. - +
Transaction IDs and Snapshots Name Return Type Description + + + pg_current_xact_id() + xid8 + get current transaction ID, assigning a new one if the current transaction does not have one + + + pg_current_xact_id_if_assigned() + xid8 + same as pg_current_xact_id() but returns null instead of assigning a new transaction ID if none is already assigned + + + pg_xact_status(xid8) + text + report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old + + + pg_current_snapshot() + pg_snapshot + get current snapshot + + + pg_snapshot_xip(pg_snapshot) + setof xid8 + get in-progress transaction IDs in snapshot + + + pg_snapshot_xmax(pg_snapshot) + xid8 + get xmax of snapshot + + + pg_snapshot_xmin(pg_snapshot) + xid8 + get xmin of snapshot + + + pg_visible_in_snapshot(xid8, pg_snapshot) + boolean + is transaction ID visible in snapshot? (do not use with subtransaction IDs) + + + +
+ + + The internal transaction ID type xid is 32 bits wide and + wraps around every 4 billion transactions. However, these functions use a + 64-bit variant xid8 that does not wrap around during the life + of an installation, and can be converted to xid by casting if + required. The data type pg_snapshot stores information about + transaction ID visibility at a particular moment in time. Its components + are described in . + + + + In releases of PostgreSQL before 13 there was + no xid8 type, so variants of these functions were provided + that used bigint. These older functions with + txid in their names are still supported for backward + compatibility, but may be removed from a future + release. See . + + + + Transaction IDs and Snapshots (Deprecated Functions) + + + Name Return Type Description + + txid_current() bigint - get current transaction ID, assigning a new one if the current transaction does not have one + see pg_current_xact_id() txid_current_if_assigned() bigint - same as txid_current() but returns null instead of assigning a new transaction ID if none is already assigned + see pg_current_xact_id_if_assigned() txid_current_snapshot() txid_snapshot - get current snapshot + see pg_current_snapshot() txid_snapshot_xip(txid_snapshot) setof bigint - get in-progress transaction IDs in snapshot + see pg_snapshot_xip() txid_snapshot_xmax(txid_snapshot) bigint - get xmax of snapshot + see pg_snapshot_xmax() txid_snapshot_xmin(txid_snapshot) bigint - get xmin of snapshot + see pg_snapshot_xmin() txid_visible_in_snapshot(bigint, txid_snapshot) boolean - is transaction ID visible in snapshot? (do not use with subtransaction ids) + see pg_visible_in_snapshot() txid_status(bigint) text - report the status of the given transaction: committed, aborted, in progress, or null if the transaction ID is too old + see pg_xact_status()
- - The internal transaction ID type (xid) is 32 bits wide and - wraps around every 4 billion transactions. However, these functions - export a 64-bit format that is extended with an epoch counter - so it will not wrap around during the life of an installation. - The data type used by these functions, txid_snapshot, - stores information about transaction ID - visibility at a particular moment in time. Its components are - described in . - - - +
Snapshot Components @@ -19115,31 +19207,29 @@ SELECT collation for ('foo' COLLATE "de_DE"); xmin - 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 xmin are either committed and visible, + or rolled back and dead. xmax - 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 xmax had not yet + completed as of the time of the snapshot, and thus are invisible. xip_list - Active txids at the time of the snapshot. The list - includes only those active txids between xmin - and xmax; there might be active txids higher - than xmax. A txid that is xmin <= txid < - xmax 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 xmin <= X < xmax 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. @@ -19148,14 +19238,14 @@ SELECT collation for ('foo' COLLATE "de_DE");
- txid_snapshot's textual representation is + pg_snapshot's textual representation is xmin:xmax:xip_list. For example 10:20:10,14,15 means xmin=10, xmax=20, xip_list=10, 14, 15. - txid_status(bigint) reports the commit status of a recent + pg_xact_status(xid8) 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 COMMIT is in progress. @@ -19169,7 +19259,7 @@ SELECT collation for ('foo' COLLATE "de_DE"); transactions are reported as in progress; applications must check pg_prepared_xacts if they - need to determine whether the txid is a prepared transaction. + need to determine whether the transaction ID belongs to a prepared transaction. diff --git a/doc/src/sgml/logicaldecoding.sgml b/doc/src/sgml/logicaldecoding.sgml index bce6d379bf..bad3bfe620 100644 --- a/doc/src/sgml/logicaldecoding.sgml +++ b/doc/src/sgml/logicaldecoding.sgml @@ -418,7 +418,7 @@ CREATE TABLE another_catalog_table(data text) WITH (user_catalog_table = true); Any actions leading to transaction ID assignment are prohibited. That, among others, includes writing to tables, performing DDL changes, and - calling txid_current(). + calling pg_current_xact_id(). diff --git a/doc/src/sgml/monitoring.sgml b/doc/src/sgml/monitoring.sgml index fd8b17ef8f..c50b72137f 100644 --- a/doc/src/sgml/monitoring.sgml +++ b/doc/src/sgml/monitoring.sgml @@ -1112,7 +1112,7 @@ postgres 27093 0.0 0.0 30096 2752 ? Ss 11:34 0:00 postgres: ser
CLogTruncationLock - Waiting to execute txid_status or update + Waiting to execute pg_xact_status or update the oldest transaction id available to it. diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index 13efa9338c..5d2aca8cfe 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -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 diff --git a/src/backend/utils/adt/txid.c b/src/backend/utils/adt/xid8funcs.c similarity index 54% rename from src/backend/utils/adt/txid.c rename to src/backend/utils/adt/xid8funcs.c index 33272f8030..616f187ad4 100644 --- a/src/backend/utils/adt/txid.c +++ b/src/backend/utils/adt/xid8funcs.c @@ -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)); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 4201790ec4..498992ff84 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202004061 +#define CATALOG_VERSION_NO 202004062 #endif diff --git a/src/include/catalog/pg_proc.dat b/src/include/catalog/pg_proc.dat index 6be7e4f157..2d1862a9d8 100644 --- a/src/include/catalog/pg_proc.dat +++ b/src/include/catalog/pg_proc.dat @@ -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', diff --git a/src/include/catalog/pg_type.dat b/src/include/catalog/pg_type.dat index a1f441b8da..78f44af5e7 100644 --- a/src/include/catalog/pg_type.dat +++ b/src/include/catalog/pg_type.dat @@ -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', diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl index bd4b943305..39ca25a06b 100644 --- a/src/test/modules/commit_ts/t/004_restart.pl +++ b/src/test/modules/commit_ts/t/004_restart.pl @@ -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; ]); diff --git a/src/test/modules/test_ddl_deparse/expected/create_table.out b/src/test/modules/test_ddl_deparse/expected/create_table.out index 523c996093..c7c9bf8971 100644 --- a/src/test/modules/test_ddl_deparse/expected/create_table.out +++ b/src/test/modules/test_ddl_deparse/expected/create_table.out @@ -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, diff --git a/src/test/modules/test_ddl_deparse/sql/create_table.sql b/src/test/modules/test_ddl_deparse/sql/create_table.sql index dd3a908638..39cdb9df03 100644 --- a/src/test/modules/test_ddl_deparse/sql/create_table.sql +++ b/src/test/modules/test_ddl_deparse/sql/create_table.sql @@ -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, diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl index fd14bab208..2a520e6db1 100644 --- a/src/test/recovery/t/003_recovery_targets.pl +++ b/src/test/recovery/t/003_recovery_targets.pl @@ -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 diff --git a/src/test/recovery/t/011_crash_recovery.pl b/src/test/recovery/t/011_crash_recovery.pl index 526a3481fb..ca6e92b50d 100644 --- a/src/test/recovery/t/011_crash_recovery.pl +++ b/src/test/recovery/t/011_crash_recovery.pl @@ -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; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index e46775afbf..f343f9b42e 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -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' diff --git a/src/test/regress/expected/hs_standby_functions.out b/src/test/regress/expected/hs_standby_functions.out index e0af677ea1..ce846b758b 100644 --- a/src/test/regress/expected/hs_standby_functions.out +++ b/src/test/regress/expected/hs_standby_functions.out @@ -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 diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index 0c03afe53d..8d350ab61b 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -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 diff --git a/src/test/regress/expected/txid.out b/src/test/regress/expected/txid.out index 015dae3051..95ba66e95e 100644 --- a/src/test/regress/expected/txid.out +++ b/src/test/regress/expected/txid.out @@ -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 diff --git a/src/test/regress/expected/update.out b/src/test/regress/expected/update.out index b7f90de52b..bf939d79f6 100644 --- a/src/test/regress/expected/update.out +++ b/src/test/regress/expected/update.out @@ -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; --------------------------- diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out index e9e1fa8259..b7a1ed0f9e 100644 --- a/src/test/regress/expected/xid.out +++ b/src/test/regress/expected/xid.out @@ -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; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index 8eca21823d..fb5c9139d1 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -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' diff --git a/src/test/regress/sql/hs_standby_functions.sql b/src/test/regress/sql/hs_standby_functions.sql index 251bac0a43..b57f67ff8b 100644 --- a/src/test/regress/sql/hs_standby_functions.sql +++ b/src/test/regress/sql/hs_standby_functions.sql @@ -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(); diff --git a/src/test/regress/sql/txid.sql b/src/test/regress/sql/txid.sql index bd6decf0ef..8d5ac98a89 100644 --- a/src/test/regress/sql/txid.sql +++ b/src/test/regress/sql/txid.sql @@ -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; diff --git a/src/test/regress/sql/update.sql b/src/test/regress/sql/update.sql index bb9c24e40f..8c558a7bc7 100644 --- a/src/test/regress/sql/update.sql +++ b/src/test/regress/sql/update.sql @@ -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; diff --git a/src/test/regress/sql/xid.sql b/src/test/regress/sql/xid.sql index a4fbca5176..565714d462 100644 --- a/src/test/regress/sql/xid.sql +++ b/src/test/regress/sql/xid.sql @@ -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; diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list index 34623523a7..525d58e7f0 100644 --- a/src/tools/pgindent/typedefs.list +++ b/src/tools/pgindent/typedefs.list @@ -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