diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:46:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-16 19:46:48 +0000 |
commit | 311bcfc6b3acdd6fd152798c7f287ddf74fa2a98 (patch) | |
tree | 0ec307299b1dada3701e42f4ca6eda57d708261e /src/test/modules/commit_ts | |
parent | Initial commit. (diff) | |
download | postgresql-15-upstream.tar.xz postgresql-15-upstream.zip |
Adding upstream version 15.4.upstream/15.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/modules/commit_ts')
-rw-r--r-- | src/test/modules/commit_ts/.gitignore | 4 | ||||
-rw-r--r-- | src/test/modules/commit_ts/Makefile | 20 | ||||
-rw-r--r-- | src/test/modules/commit_ts/commit_ts.conf | 1 | ||||
-rw-r--r-- | src/test/modules/commit_ts/expected/commit_timestamp.out | 139 | ||||
-rw-r--r-- | src/test/modules/commit_ts/expected/commit_timestamp_1.out | 119 | ||||
-rw-r--r-- | src/test/modules/commit_ts/sql/commit_timestamp.sql | 64 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/001_base.pl | 38 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/002_standby.pl | 68 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/003_standby_2.pl | 69 | ||||
-rw-r--r-- | src/test/modules/commit_ts/t/004_restart.pl | 154 |
10 files changed, 676 insertions, 0 deletions
diff --git a/src/test/modules/commit_ts/.gitignore b/src/test/modules/commit_ts/.gitignore new file mode 100644 index 0000000..5dcb3ff --- /dev/null +++ b/src/test/modules/commit_ts/.gitignore @@ -0,0 +1,4 @@ +# Generated subdirectories +/log/ +/results/ +/tmp_check/ diff --git a/src/test/modules/commit_ts/Makefile b/src/test/modules/commit_ts/Makefile new file mode 100644 index 0000000..113bcfa --- /dev/null +++ b/src/test/modules/commit_ts/Makefile @@ -0,0 +1,20 @@ +# src/test/modules/commit_ts/Makefile + +REGRESS = commit_timestamp +REGRESS_OPTS = --temp-config=$(top_srcdir)/src/test/modules/commit_ts/commit_ts.conf +# Disabled because these tests require "track_commit_timestamp = on", +# which typical installcheck users do not have (e.g. buildfarm clients). +NO_INSTALLCHECK = 1 + +TAP_TESTS = 1 + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/commit_ts +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/commit_ts/commit_ts.conf b/src/test/modules/commit_ts/commit_ts.conf new file mode 100644 index 0000000..e9d3c35 --- /dev/null +++ b/src/test/modules/commit_ts/commit_ts.conf @@ -0,0 +1 @@ +track_commit_timestamp = on diff --git a/src/test/modules/commit_ts/expected/commit_timestamp.out b/src/test/modules/commit_ts/expected/commit_timestamp.out new file mode 100644 index 0000000..bb2fda2 --- /dev/null +++ b/src/test/modules/commit_ts/expected/commit_timestamp.out @@ -0,0 +1,139 @@ +-- +-- Commit Timestamp +-- +SHOW track_commit_timestamp; + track_commit_timestamp +------------------------ + on +(1 row) + +CREATE TABLE committs_test(id serial, ts timestamptz default now()); +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; +SELECT id, + pg_xact_commit_timestamp(xmin) >= ts, + pg_xact_commit_timestamp(xmin) <= now(), + pg_xact_commit_timestamp(xmin) - ts < '60s' -- 60s should give a lot of reserve +FROM committs_test +ORDER BY id; + id | ?column? | ?column? | ?column? +----+----------+----------+---------- + 1 | t | t | t + 2 | t | t | t + 3 | t | t | t +(3 rows) + +DROP TABLE committs_test; +SELECT pg_xact_commit_timestamp('0'::xid); +ERROR: cannot retrieve commit timestamp for transaction 0 +SELECT pg_xact_commit_timestamp('1'::xid); + pg_xact_commit_timestamp +-------------------------- + +(1 row) + +SELECT pg_xact_commit_timestamp('2'::xid); + pg_xact_commit_timestamp +-------------------------- + +(1 row) + +SELECT x.xid::text::bigint > 0 as xid_valid, + x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; + xid_valid | ts_low | ts_high | valid_roident +-----------+--------+---------+--------------- + t | t | t | f +(1 row) + +-- Test non-normal transaction ids. +SELECT * FROM pg_xact_commit_timestamp_origin(NULL); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +SELECT * FROM pg_xact_commit_timestamp_origin('0'::xid); -- error +ERROR: cannot retrieve commit timestamp for transaction 0 +SELECT * FROM pg_xact_commit_timestamp_origin('1'::xid); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +-- Test transaction without replication origin +SELECT txid_current() as txid_no_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; + ts_low | ts_high | valid_roident +--------+---------+--------------- + t | t | f +(1 row) + +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_xact_commit_timestamp_origin(:'txid_no_origin') x; + ts_low | ts_high | valid_roident +--------+---------+--------------- + t | t | f +(1 row) + +-- Test transaction with replication origin +SELECT pg_replication_origin_create('regress_commit_ts: get_origin') != 0 + AS valid_roident; + valid_roident +--------------- + t +(1 row) + +SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin'); + pg_replication_origin_session_setup +------------------------------------- + +(1 row) + +SELECT txid_current() as txid_with_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_last_committed_xact() x, pg_replication_origin r + WHERE r.roident = x.roident; + ts_low | ts_high | roname +--------+---------+------------------------------- + t | t | regress_commit_ts: get_origin +(1 row) + +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_xact_commit_timestamp_origin(:'txid_with_origin') x, pg_replication_origin r + WHERE r.roident = x.roident; + ts_low | ts_high | roname +--------+---------+------------------------------- + t | t | regress_commit_ts: get_origin +(1 row) + +SELECT pg_replication_origin_session_reset(); + pg_replication_origin_session_reset +------------------------------------- + +(1 row) + +SELECT pg_replication_origin_drop('regress_commit_ts: get_origin'); + pg_replication_origin_drop +---------------------------- + +(1 row) + diff --git a/src/test/modules/commit_ts/expected/commit_timestamp_1.out b/src/test/modules/commit_ts/expected/commit_timestamp_1.out new file mode 100644 index 0000000..f37e701 --- /dev/null +++ b/src/test/modules/commit_ts/expected/commit_timestamp_1.out @@ -0,0 +1,119 @@ +-- +-- Commit Timestamp +-- +SHOW track_commit_timestamp; + track_commit_timestamp +------------------------ + off +(1 row) + +CREATE TABLE committs_test(id serial, ts timestamptz default now()); +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; +SELECT id, + pg_xact_commit_timestamp(xmin) >= ts, + pg_xact_commit_timestamp(xmin) <= now(), + pg_xact_commit_timestamp(xmin) - ts < '60s' -- 60s should give a lot of reserve +FROM committs_test +ORDER BY id; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +DROP TABLE committs_test; +SELECT pg_xact_commit_timestamp('0'::xid); +ERROR: cannot retrieve commit timestamp for transaction 0 +SELECT pg_xact_commit_timestamp('1'::xid); + pg_xact_commit_timestamp +-------------------------- + +(1 row) + +SELECT pg_xact_commit_timestamp('2'::xid); + pg_xact_commit_timestamp +-------------------------- + +(1 row) + +SELECT x.xid::text::bigint > 0 as xid_valid, + x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +-- Test non-normal transaction ids. +SELECT * FROM pg_xact_commit_timestamp_origin(NULL); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +SELECT * FROM pg_xact_commit_timestamp_origin('0'::xid); -- error +ERROR: cannot retrieve commit timestamp for transaction 0 +SELECT * FROM pg_xact_commit_timestamp_origin('1'::xid); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL + timestamp | roident +-----------+--------- + | +(1 row) + +-- Test transaction without replication origin +SELECT txid_current() as txid_no_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_xact_commit_timestamp_origin(:'txid_no_origin') x; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +-- Test transaction with replication origin +SELECT pg_replication_origin_create('regress_commit_ts: get_origin') != 0 + AS valid_roident; + valid_roident +--------------- + t +(1 row) + +SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin'); + pg_replication_origin_session_setup +------------------------------------- + +(1 row) + +SELECT txid_current() as txid_with_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_last_committed_xact() x, pg_replication_origin r + WHERE r.roident = x.roident; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_xact_commit_timestamp_origin(:'txid_with_origin') x, pg_replication_origin r + WHERE r.roident = x.roident; +ERROR: could not get commit timestamp data +HINT: Make sure the configuration parameter "track_commit_timestamp" is set. +SELECT pg_replication_origin_session_reset(); + pg_replication_origin_session_reset +------------------------------------- + +(1 row) + +SELECT pg_replication_origin_drop('regress_commit_ts: get_origin'); + pg_replication_origin_drop +---------------------------- + +(1 row) + diff --git a/src/test/modules/commit_ts/sql/commit_timestamp.sql b/src/test/modules/commit_ts/sql/commit_timestamp.sql new file mode 100644 index 0000000..3bb7bb2 --- /dev/null +++ b/src/test/modules/commit_ts/sql/commit_timestamp.sql @@ -0,0 +1,64 @@ +-- +-- Commit Timestamp +-- +SHOW track_commit_timestamp; +CREATE TABLE committs_test(id serial, ts timestamptz default now()); + +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; +INSERT INTO committs_test DEFAULT VALUES; + +SELECT id, + pg_xact_commit_timestamp(xmin) >= ts, + pg_xact_commit_timestamp(xmin) <= now(), + pg_xact_commit_timestamp(xmin) - ts < '60s' -- 60s should give a lot of reserve +FROM committs_test +ORDER BY id; + +DROP TABLE committs_test; + +SELECT pg_xact_commit_timestamp('0'::xid); +SELECT pg_xact_commit_timestamp('1'::xid); +SELECT pg_xact_commit_timestamp('2'::xid); + +SELECT x.xid::text::bigint > 0 as xid_valid, + x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; + +-- Test non-normal transaction ids. +SELECT * FROM pg_xact_commit_timestamp_origin(NULL); -- ok, NULL +SELECT * FROM pg_xact_commit_timestamp_origin('0'::xid); -- error +SELECT * FROM pg_xact_commit_timestamp_origin('1'::xid); -- ok, NULL +SELECT * FROM pg_xact_commit_timestamp_origin('2'::xid); -- ok, NULL + +-- Test transaction without replication origin +SELECT txid_current() as txid_no_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_last_committed_xact() x; +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + roident != 0 AS valid_roident + FROM pg_xact_commit_timestamp_origin(:'txid_no_origin') x; + +-- Test transaction with replication origin +SELECT pg_replication_origin_create('regress_commit_ts: get_origin') != 0 + AS valid_roident; +SELECT pg_replication_origin_session_setup('regress_commit_ts: get_origin'); +SELECT txid_current() as txid_with_origin \gset +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_last_committed_xact() x, pg_replication_origin r + WHERE r.roident = x.roident; +SELECT x.timestamp > '-infinity'::timestamptz AS ts_low, + x.timestamp <= now() AS ts_high, + r.roname + FROM pg_xact_commit_timestamp_origin(:'txid_with_origin') x, pg_replication_origin r + WHERE r.roident = x.roident; + +SELECT pg_replication_origin_session_reset(); +SELECT pg_replication_origin_drop('regress_commit_ts: get_origin'); diff --git a/src/test/modules/commit_ts/t/001_base.pl b/src/test/modules/commit_ts/t/001_base.pl new file mode 100644 index 0000000..3f0bb9e --- /dev/null +++ b/src/test/modules/commit_ts/t/001_base.pl @@ -0,0 +1,38 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Single-node test: value can be set, and is still present after recovery + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $node = PostgreSQL::Test::Cluster->new('foxtrot'); +$node->init; +$node->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$node->start; + +# Create a table, compare "now()" to the commit TS of its xmin +$node->safe_psql('postgres', + 'create table t as select now from (select now(), pg_sleep(1)) f'); +my $true = $node->safe_psql('postgres', + 'select t.now - ts.* < \'1s\' from t, pg_class c, pg_xact_commit_timestamp(c.xmin) ts where relname = \'t\'' +); +is($true, 't', 'commit TS is set'); +my $ts = $node->safe_psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t\'' +); + +# Verify that we read the same TS after crash recovery +$node->stop('immediate'); +$node->start; + +my $recovered_ts = $node->safe_psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t\'' +); +is($recovered_ts, $ts, 'commit TS remains after crash recovery'); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/002_standby.pl b/src/test/modules/commit_ts/t/002_standby.pl new file mode 100644 index 0000000..ace3140 --- /dev/null +++ b/src/test/modules/commit_ts/t/002_standby.pl @@ -0,0 +1,68 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Test simple scenario involving a standby + +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $bkplabel = 'backup'; +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(allows_streaming => 1); + +$primary->append_conf( + 'postgresql.conf', qq{ + track_commit_timestamp = on + max_wal_senders = 5 + }); +$primary->start; +$primary->backup($bkplabel); + +my $standby = PostgreSQL::Test::Cluster->new('standby'); +$standby->init_from_backup($primary, $bkplabel, has_streaming => 1); +$standby->start; + +for my $i (1 .. 10) +{ + $primary->safe_psql('postgres', "create table t$i()"); +} +my $primary_ts = $primary->safe_psql('postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't10'} +); +my $primary_lsn = + $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; + +my $standby_ts = $standby->safe_psql('postgres', + qq{select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = 't10'} +); +is($primary_ts, $standby_ts, "standby gives same value as primary"); + +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; +$primary->safe_psql('postgres', 'checkpoint'); +$primary_lsn = $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; +$standby->safe_psql('postgres', 'checkpoint'); + +# This one should raise an error now +my ($ret, $standby_ts_stdout, $standby_ts_stderr) = $standby->psql('postgres', + 'select ts.* from pg_class, pg_xact_commit_timestamp(xmin) ts where relname = \'t10\'' +); +is($ret, 3, 'standby errors when primary turned feature off'); +is($standby_ts_stdout, '', + "standby gives no value when primary turned feature off"); +like( + $standby_ts_stderr, + qr/could not get commit timestamp data/, + 'expected error when primary turned feature off'); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/003_standby_2.pl b/src/test/modules/commit_ts/t/003_standby_2.pl new file mode 100644 index 0000000..16d5f13 --- /dev/null +++ b/src/test/modules/commit_ts/t/003_standby_2.pl @@ -0,0 +1,69 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Test primary/standby scenario where the track_commit_timestamp GUC is +# repeatedly toggled on and off. +use strict; +use warnings; + +use PostgreSQL::Test::Utils; +use Test::More; +use PostgreSQL::Test::Cluster; + +my $bkplabel = 'backup'; +my $primary = PostgreSQL::Test::Cluster->new('primary'); +$primary->init(allows_streaming => 1); +$primary->append_conf( + 'postgresql.conf', qq{ + track_commit_timestamp = on + max_wal_senders = 5 + }); +$primary->start; +$primary->backup($bkplabel); + +my $standby = PostgreSQL::Test::Cluster->new('standby'); +$standby->init_from_backup($primary, $bkplabel, has_streaming => 1); +$standby->start; + +for my $i (1 .. 10) +{ + $primary->safe_psql('postgres', "create table t$i()"); +} +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; +$primary->safe_psql('postgres', 'checkpoint'); +my $primary_lsn = + $primary->safe_psql('postgres', 'select pg_current_wal_lsn()'); +$standby->poll_query_until('postgres', + qq{SELECT '$primary_lsn'::pg_lsn <= pg_last_wal_replay_lsn()}) + or die "standby never caught up"; + +$standby->safe_psql('postgres', 'checkpoint'); +$standby->restart; + +my ($psql_ret, $standby_ts_stdout, $standby_ts_stderr) = $standby->psql( + 'postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't10'} +); +is($psql_ret, 3, 'expect error when getting commit timestamp after restart'); +is($standby_ts_stdout, '', "standby does not return a value after restart"); +like( + $standby_ts_stderr, + qr/could not get commit timestamp data/, + 'expected err msg after restart'); + +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$primary->restart; +$primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$primary->restart; + +system_or_bail('pg_ctl', '-D', $standby->data_dir, 'promote'); + +$standby->safe_psql('postgres', "create table t11()"); +my $standby_ts = $standby->safe_psql('postgres', + qq{SELECT ts.* FROM pg_class, pg_xact_commit_timestamp(xmin) AS ts WHERE relname = 't11'} +); +isnt($standby_ts, '', + "standby gives valid value ($standby_ts) after promotion"); + +done_testing(); diff --git a/src/test/modules/commit_ts/t/004_restart.pl b/src/test/modules/commit_ts/t/004_restart.pl new file mode 100644 index 0000000..808164c --- /dev/null +++ b/src/test/modules/commit_ts/t/004_restart.pl @@ -0,0 +1,154 @@ + +# Copyright (c) 2021-2022, PostgreSQL Global Development Group + +# Testing of commit timestamps preservation across restarts +use strict; +use warnings; +use PostgreSQL::Test::Cluster; +use PostgreSQL::Test::Utils; +use Test::More; + +my $node_primary = PostgreSQL::Test::Cluster->new('primary'); +$node_primary->init(allows_streaming => 1); +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); +$node_primary->start; + +my ($ret, $stdout, $stderr); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('0');]); +is($ret, 3, 'getting ts of InvalidTransactionId reports error'); +like( + $stderr, + qr/cannot retrieve commit timestamp for transaction/, + 'expected error from InvalidTransactionId'); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('1');]); +is($ret, 0, 'getting ts of BootstrapTransactionId succeeds'); +is($stdout, '', 'timestamp of BootstrapTransactionId is null'); + +($ret, $stdout, $stderr) = + $node_primary->psql('postgres', qq[SELECT pg_xact_commit_timestamp('2');]); +is($ret, 0, 'getting ts of FrozenTransactionId succeeds'); +is($stdout, '', 'timestamp of FrozenTransactionId is null'); + +# Since FirstNormalTransactionId will've occurred during initdb, long before we +# enabled commit timestamps, it'll be null since we have no cts data for it but +# cts are enabled. +is( $node_primary->safe_psql( + 'postgres', qq[SELECT pg_xact_commit_timestamp('3');]), + '', + 'committs for FirstNormalTransactionId is null'); + +$node_primary->safe_psql('postgres', + qq[CREATE TABLE committs_test(x integer, y timestamp with time zone);]); + +my $xid = $node_primary->safe_psql( + 'postgres', qq[ + BEGIN; + INSERT INTO committs_test(x, y) VALUES (1, current_timestamp); + SELECT pg_current_xact_id()::xid; + COMMIT; +]); + +my $before_restart_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +ok($before_restart_ts ne '' && $before_restart_ts ne 'null', + 'commit timestamp recorded'); + +$node_primary->stop('immediate'); +$node_primary->start; + +my $after_crash_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_crash_ts, $before_restart_ts, + 'timestamps before and after crash are equal'); + +$node_primary->stop('fast'); +$node_primary->start; + +my $after_restart_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_restart_ts, $before_restart_ts, + 'timestamps before and after restart are equal'); + +# Now disable commit timestamps +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = off'); +$node_primary->stop('fast'); + +# Start the server, which generates a XLOG_PARAMETER_CHANGE record where +# the parameter change is registered. +$node_primary->start; + +# Now restart again the server so as no XLOG_PARAMETER_CHANGE record are +# replayed with the follow-up immediate shutdown. +$node_primary->restart; + +# Move commit timestamps across page boundaries. Things should still +# be able to work across restarts with those transactions committed while +# track_commit_timestamp is disabled. +$node_primary->safe_psql( + 'postgres', + qq(CREATE PROCEDURE consume_xid(cnt int) +AS \$\$ +DECLARE + i int; + BEGIN + FOR i in 1..cnt LOOP + EXECUTE 'SELECT pg_current_xact_id()'; + COMMIT; + END LOOP; + END; +\$\$ +LANGUAGE plpgsql; +)); +$node_primary->safe_psql('postgres', 'CALL consume_xid(2000)'); + +($ret, $stdout, $stderr) = $node_primary->psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($ret, 3, 'no commit timestamp from enable tx when cts disabled'); +like( + $stderr, + qr/could not get commit timestamp data/, + 'expected error from enabled tx when committs disabled'); + +# Do a tx while cts disabled +my $xid_disabled = $node_primary->safe_psql( + 'postgres', qq[ + BEGIN; + INSERT INTO committs_test(x, y) VALUES (2, current_timestamp); + SELECT pg_current_xact_id(); + COMMIT; +]); + +# Should be inaccessible +($ret, $stdout, $stderr) = $node_primary->psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid_disabled');]); +is($ret, 3, 'no commit timestamp when disabled'); +like( + $stderr, + qr/could not get commit timestamp data/, + 'expected error from disabled tx when committs disabled'); + +# Re-enable, restart and ensure we can still get the old timestamps +$node_primary->append_conf('postgresql.conf', 'track_commit_timestamp = on'); + +# An immediate shutdown is used here. At next startup recovery will +# replay transactions which committed when track_commit_timestamp was +# disabled, and the facility should be able to work properly. +$node_primary->stop('immediate'); +$node_primary->start; + +my $after_enable_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid');]); +is($after_enable_ts, '', 'timestamp of enabled tx null after re-enable'); + +my $after_enable_disabled_ts = $node_primary->safe_psql('postgres', + qq[SELECT pg_xact_commit_timestamp('$xid_disabled');]); +is($after_enable_disabled_ts, '', + 'timestamp of disabled tx null after re-enable'); + +$node_primary->stop; + +done_testing(); |