diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-13 13:44:03 +0000 |
commit | 293913568e6a7a86fd1479e1cff8e2ecb58d6568 (patch) | |
tree | fc3b469a3ec5ab71b36ea97cc7aaddb838423a0c /src/test/modules/delay_execution | |
parent | Initial commit. (diff) | |
download | postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.tar.xz postgresql-16-293913568e6a7a86fd1479e1cff8e2ecb58d6568.zip |
Adding upstream version 16.2.upstream/16.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/modules/delay_execution')
8 files changed, 505 insertions, 0 deletions
diff --git a/src/test/modules/delay_execution/.gitignore b/src/test/modules/delay_execution/.gitignore new file mode 100644 index 0000000..ba2160b --- /dev/null +++ b/src/test/modules/delay_execution/.gitignore @@ -0,0 +1,3 @@ +# Generated subdirectories +/output_iso/ +/tmp_check_iso/ diff --git a/src/test/modules/delay_execution/Makefile b/src/test/modules/delay_execution/Makefile new file mode 100644 index 0000000..70f24e8 --- /dev/null +++ b/src/test/modules/delay_execution/Makefile @@ -0,0 +1,22 @@ +# src/test/modules/delay_execution/Makefile + +PGFILEDESC = "delay_execution - allow delay between parsing and execution" + +MODULE_big = delay_execution +OBJS = \ + $(WIN32RES) \ + delay_execution.o + +ISOLATION = partition-addition \ + partition-removal-1 + +ifdef USE_PGXS +PG_CONFIG = pg_config +PGXS := $(shell $(PG_CONFIG) --pgxs) +include $(PGXS) +else +subdir = src/test/modules/delay_execution +top_builddir = ../../../.. +include $(top_builddir)/src/Makefile.global +include $(top_srcdir)/contrib/contrib-global.mk +endif diff --git a/src/test/modules/delay_execution/delay_execution.c b/src/test/modules/delay_execution/delay_execution.c new file mode 100644 index 0000000..7cd76eb --- /dev/null +++ b/src/test/modules/delay_execution/delay_execution.c @@ -0,0 +1,95 @@ +/*------------------------------------------------------------------------- + * + * delay_execution.c + * Test module to allow delay between parsing and execution of a query. + * + * The delay is implemented by taking and immediately releasing a specified + * advisory lock. If another process has previously taken that lock, the + * current process will be blocked until the lock is released; otherwise, + * there's no effect. This allows an isolationtester script to reliably + * test behaviors where some specified action happens in another backend + * between parsing and execution of any desired query. + * + * Copyright (c) 2020-2023, PostgreSQL Global Development Group + * + * IDENTIFICATION + * src/test/modules/delay_execution/delay_execution.c + * + *------------------------------------------------------------------------- + */ + +#include "postgres.h" + +#include <limits.h> + +#include "optimizer/planner.h" +#include "utils/builtins.h" +#include "utils/guc.h" +#include "utils/inval.h" + + +PG_MODULE_MAGIC; + +/* GUC: advisory lock ID to use. Zero disables the feature. */ +static int post_planning_lock_id = 0; + +/* Save previous planner hook user to be a good citizen */ +static planner_hook_type prev_planner_hook = NULL; + + +/* planner_hook function to provide the desired delay */ +static PlannedStmt * +delay_execution_planner(Query *parse, const char *query_string, + int cursorOptions, ParamListInfo boundParams) +{ + PlannedStmt *result; + + /* Invoke the planner, possibly via a previous hook user */ + if (prev_planner_hook) + result = prev_planner_hook(parse, query_string, cursorOptions, + boundParams); + else + result = standard_planner(parse, query_string, cursorOptions, + boundParams); + + /* If enabled, delay by taking and releasing the specified lock */ + if (post_planning_lock_id != 0) + { + DirectFunctionCall1(pg_advisory_lock_int8, + Int64GetDatum((int64) post_planning_lock_id)); + DirectFunctionCall1(pg_advisory_unlock_int8, + Int64GetDatum((int64) post_planning_lock_id)); + + /* + * Ensure that we notice any pending invalidations, since the advisory + * lock functions don't do this. + */ + AcceptInvalidationMessages(); + } + + return result; +} + +/* Module load function */ +void +_PG_init(void) +{ + /* Set up the GUC to control which lock is used */ + DefineCustomIntVariable("delay_execution.post_planning_lock_id", + "Sets the advisory lock ID to be locked/unlocked after planning.", + "Zero disables the delay.", + &post_planning_lock_id, + 0, + 0, INT_MAX, + PGC_USERSET, + 0, + NULL, + NULL, + NULL); + + MarkGUCPrefixReserved("delay_execution"); + + /* Install our hook */ + prev_planner_hook = planner_hook; + planner_hook = delay_execution_planner; +} diff --git a/src/test/modules/delay_execution/expected/partition-addition.out b/src/test/modules/delay_execution/expected/partition-addition.out new file mode 100644 index 0000000..7d6572b --- /dev/null +++ b/src/test/modules/delay_execution/expected/partition-addition.out @@ -0,0 +1,27 @@ +Parsed test spec with 2 sessions + +starting permutation: s2lock s1exec s2addp s2unlock +step s2lock: SELECT pg_advisory_lock(12345); +pg_advisory_lock +---------------- + +(1 row) + +step s1exec: LOAD 'delay_execution'; + SET delay_execution.post_planning_lock_id = 12345; + SELECT * FROM foo WHERE a <> 1 AND a <> (SELECT 3); <waiting ...> +step s2addp: CREATE TABLE foo2 (LIKE foo); + ALTER TABLE foo ATTACH PARTITION foo2 FOR VALUES IN (2); + INSERT INTO foo VALUES (2, 'ADD2'); +step s2unlock: SELECT pg_advisory_unlock(12345); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1exec: <... completed> +a|b +-+--- +4|GHI +(1 row) + diff --git a/src/test/modules/delay_execution/expected/partition-removal-1.out b/src/test/modules/delay_execution/expected/partition-removal-1.out new file mode 100644 index 0000000..b81b999 --- /dev/null +++ b/src/test/modules/delay_execution/expected/partition-removal-1.out @@ -0,0 +1,233 @@ +Parsed test spec with 3 sessions + +starting permutation: s3lock s1b s1exec s2remp s3check s3unlock s3check s1c +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1b: BEGIN; +step s1exec: SELECT * FROM partrem WHERE a <> 1 AND a <> (SELECT 3); <waiting ...> +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s3check: SELECT * FROM partrem; +a|b +-+--- +1|ABC +3|DEF +(2 rows) + +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1exec: <... completed> +a|b +-+--- +2|JKL +(1 row) + +step s3check: SELECT * FROM partrem; +a|b +-+--- +1|ABC +3|DEF +(2 rows) + +step s1c: COMMIT; +step s2remp: <... completed> + +starting permutation: s3lock s1brr s1exec s2remp s3check s3unlock s3check s1c +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1exec: SELECT * FROM partrem WHERE a <> 1 AND a <> (SELECT 3); <waiting ...> +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s3check: SELECT * FROM partrem; +a|b +-+--- +1|ABC +3|DEF +(2 rows) + +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1exec: <... completed> +a|b +-+--- +2|JKL +(1 row) + +step s3check: SELECT * FROM partrem; +a|b +-+--- +1|ABC +3|DEF +(2 rows) + +step s1c: COMMIT; +step s2remp: <... completed> + +starting permutation: s3lock s1b s1exec2 s2remp s3unlock s1c +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1b: BEGIN; +step s1exec2: SELECT * FROM partrem WHERE a <> (SELECT 2) AND a <> 1; <waiting ...> +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1exec2: <... completed> +a|b +-+--- +3|DEF +(1 row) + +step s1c: COMMIT; +step s2remp: <... completed> + +starting permutation: s3lock s1brr s1exec2 s2remp s3unlock s1c +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1exec2: SELECT * FROM partrem WHERE a <> (SELECT 2) AND a <> 1; <waiting ...> +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1exec2: <... completed> +a|b +-+--- +3|DEF +(1 row) + +step s1c: COMMIT; +step s2remp: <... completed> + +starting permutation: s3lock s1brr s1prepare s2remp s1execprep s3unlock s1check s1c s1check s1dealloc +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1prepare: PREPARE ins AS INSERT INTO partrem VALUES ($1, 'GHI'); +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s1execprep: EXECUTE ins(2); <waiting ...> +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1execprep: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+--- +2|GHI +(1 row) + +step s1c: COMMIT; +step s2remp: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+- +(0 rows) + +step s1dealloc: DEALLOCATE ins; + +starting permutation: s1brr s1prepare s2remp s3lock s1execprep s3unlock s1check s1c s1check s1dealloc +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1prepare: PREPARE ins AS INSERT INTO partrem VALUES ($1, 'GHI'); +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s1execprep: EXECUTE ins(2); <waiting ...> +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1execprep: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+--- +2|GHI +(1 row) + +step s1c: COMMIT; +step s2remp: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+- +(0 rows) + +step s1dealloc: DEALLOCATE ins; + +starting permutation: s1brr s1check s3lock s2remp s1prepare s1execprep s3unlock s1check s1c s1check s1dealloc +step s1brr: BEGIN ISOLATION LEVEL REPEATABLE READ; +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+- +(0 rows) + +step s3lock: SELECT pg_advisory_lock(12543); +pg_advisory_lock +---------------- + +(1 row) + +step s2remp: ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; <waiting ...> +step s1prepare: PREPARE ins AS INSERT INTO partrem VALUES ($1, 'GHI'); +step s1execprep: EXECUTE ins(2); <waiting ...> +step s3unlock: SELECT pg_advisory_unlock(12543); +pg_advisory_unlock +------------------ +t +(1 row) + +step s1execprep: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+--- +2|GHI +(1 row) + +step s1c: COMMIT; +step s2remp: <... completed> +step s1check: SELECT * FROM partrem WHERE b = 'GHI'; +a|b +-+- +(0 rows) + +step s1dealloc: DEALLOCATE ins; diff --git a/src/test/modules/delay_execution/meson.build b/src/test/modules/delay_execution/meson.build new file mode 100644 index 0000000..9f33b19 --- /dev/null +++ b/src/test/modules/delay_execution/meson.build @@ -0,0 +1,29 @@ +# Copyright (c) 2022-2023, PostgreSQL Global Development Group + +delay_execution_sources = files( + 'delay_execution.c', +) + +if host_system == 'windows' + delay_execution_sources += rc_lib_gen.process(win32ver_rc, extra_args: [ + '--NAME', 'delay_execution', + '--FILEDESC', 'delay_execution - allow delay between parsing and execution',]) +endif + +delay_execution = shared_module('delay_execution', + delay_execution_sources, + kwargs: pg_test_mod_args, +) +test_install_libs += delay_execution + +tests += { + 'name': 'delay_execution', + 'sd': meson.current_source_dir(), + 'bd': meson.current_build_dir(), + 'isolation': { + 'specs': [ + 'partition-addition', + 'partition-removal-1', + ], + }, +} diff --git a/src/test/modules/delay_execution/specs/partition-addition.spec b/src/test/modules/delay_execution/specs/partition-addition.spec new file mode 100644 index 0000000..99010df --- /dev/null +++ b/src/test/modules/delay_execution/specs/partition-addition.spec @@ -0,0 +1,38 @@ +# Test addition of a partition with less-than-exclusive locking. + +setup +{ + CREATE TABLE foo (a int, b text) PARTITION BY LIST(a); + CREATE TABLE foo1 PARTITION OF foo FOR VALUES IN (1); + CREATE TABLE foo3 PARTITION OF foo FOR VALUES IN (3); + CREATE TABLE foo4 PARTITION OF foo FOR VALUES IN (4); + INSERT INTO foo VALUES (1, 'ABC'); + INSERT INTO foo VALUES (3, 'DEF'); + INSERT INTO foo VALUES (4, 'GHI'); +} + +teardown +{ + DROP TABLE foo; +} + +# The SELECT will be planned with just the three partitions shown above, +# of which we expect foo1 to be pruned at planning and foo3 at execution. +# Then we'll block, and by the time the query is actually executed, +# partition foo2 will also exist. We expect that not to be scanned. +# This test is specifically designed to check CreatePartitionPruneState's +# code for matching up the partition lists in such cases. + +session "s1" +step "s1exec" { LOAD 'delay_execution'; + SET delay_execution.post_planning_lock_id = 12345; + SELECT * FROM foo WHERE a <> 1 AND a <> (SELECT 3); } + +session "s2" +step "s2lock" { SELECT pg_advisory_lock(12345); } +step "s2unlock" { SELECT pg_advisory_unlock(12345); } +step "s2addp" { CREATE TABLE foo2 (LIKE foo); + ALTER TABLE foo ATTACH PARTITION foo2 FOR VALUES IN (2); + INSERT INTO foo VALUES (2, 'ADD2'); } + +permutation "s2lock" "s1exec" "s2addp" "s2unlock" diff --git a/src/test/modules/delay_execution/specs/partition-removal-1.spec b/src/test/modules/delay_execution/specs/partition-removal-1.spec new file mode 100644 index 0000000..5ee2750 --- /dev/null +++ b/src/test/modules/delay_execution/specs/partition-removal-1.spec @@ -0,0 +1,58 @@ +# Test removal of a partition with less-than-exclusive locking. + +setup +{ + CREATE TABLE partrem (a int, b text) PARTITION BY LIST(a); + CREATE TABLE partrem1 PARTITION OF partrem FOR VALUES IN (1); + CREATE TABLE partrem2 PARTITION OF partrem FOR VALUES IN (2); + CREATE TABLE partrem3 PARTITION OF partrem FOR VALUES IN (3); + INSERT INTO partrem VALUES (1, 'ABC'); + INSERT INTO partrem VALUES (2, 'JKL'); + INSERT INTO partrem VALUES (3, 'DEF'); +} + +teardown +{ + DROP TABLE IF EXISTS partrem, partrem2; +} + +session "s1" +setup { LOAD 'delay_execution'; + SET delay_execution.post_planning_lock_id = 12543; } +step "s1b" { BEGIN; } +step "s1brr" { BEGIN ISOLATION LEVEL REPEATABLE READ; } +step "s1exec" { SELECT * FROM partrem WHERE a <> 1 AND a <> (SELECT 3); } +step "s1exec2" { SELECT * FROM partrem WHERE a <> (SELECT 2) AND a <> 1; } +step "s1prepare" { PREPARE ins AS INSERT INTO partrem VALUES ($1, 'GHI'); } +step "s1execprep" { EXECUTE ins(2); } +step "s1check" { SELECT * FROM partrem WHERE b = 'GHI'; } +step "s1c" { COMMIT; } +step "s1dealloc" { DEALLOCATE ins; } + +session "s2" +step "s2remp" { ALTER TABLE partrem DETACH PARTITION partrem2 CONCURRENTLY; } + +session "s3" +step "s3lock" { SELECT pg_advisory_lock(12543); } +step "s3unlock" { SELECT pg_advisory_unlock(12543); } +step "s3check" { SELECT * FROM partrem; } + +# The SELECT will be planned with all three partitions shown above, +# of which we expect partrem1 to be pruned at planning and partrem3 at +# execution. Then we'll block, and by the time the query is actually +# executed, detach of partrem2 is already underway (so its row doesn't +# show up in s3's result), but we expect its row to still appear in the +# result for s1. +permutation "s3lock" "s1b" "s1exec" "s2remp" "s3check" "s3unlock" "s3check" "s1c" +permutation "s3lock" "s1brr" "s1exec" "s2remp" "s3check" "s3unlock" "s3check" "s1c" + +# In this case we're testing that after pruning partrem2 at runtime, the +# query still works correctly. +permutation "s3lock" "s1b" "s1exec2" "s2remp" "s3unlock" "s1c" +permutation "s3lock" "s1brr" "s1exec2" "s2remp" "s3unlock" "s1c" + +# In this case we test that an insert that's prepared in repeatable read +# mode still works after detaching. +permutation "s3lock" "s1brr" "s1prepare" "s2remp" "s1execprep" "s3unlock" "s1check" "s1c" "s1check" "s1dealloc" +permutation "s1brr" "s1prepare" "s2remp" "s3lock" "s1execprep" "s3unlock" "s1check" "s1c" "s1check" "s1dealloc" +permutation "s1brr" "s1check" "s3lock" "s2remp" "s1prepare" "s1execprep" "s3unlock" "s1check" "s1c" "s1check" "s1dealloc" |