summaryrefslogtreecommitdiffstats
path: root/src/test/modules/delay_execution
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 12:17:33 +0000
commit5e45211a64149b3c659b90ff2de6fa982a5a93ed (patch)
tree739caf8c461053357daa9f162bef34516c7bf452 /src/test/modules/delay_execution
parentInitial commit. (diff)
downloadpostgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.tar.xz
postgresql-15-5e45211a64149b3c659b90ff2de6fa982a5a93ed.zip
Adding upstream version 15.5.upstream/15.5
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/modules/delay_execution')
-rw-r--r--src/test/modules/delay_execution/.gitignore3
-rw-r--r--src/test/modules/delay_execution/Makefile22
-rw-r--r--src/test/modules/delay_execution/delay_execution.c98
-rw-r--r--src/test/modules/delay_execution/expected/partition-addition.out27
-rw-r--r--src/test/modules/delay_execution/expected/partition-removal-1.out233
-rw-r--r--src/test/modules/delay_execution/specs/partition-addition.spec38
-rw-r--r--src/test/modules/delay_execution/specs/partition-removal-1.spec58
7 files changed, 479 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..407ebc0
--- /dev/null
+++ b/src/test/modules/delay_execution/delay_execution.c
@@ -0,0 +1,98 @@
+/*-------------------------------------------------------------------------
+ *
+ * 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-2022, 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;
+
+/* Module load function */
+void _PG_init(void);
+
+
+/* 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/specs/partition-addition.spec b/src/test/modules/delay_execution/specs/partition-addition.spec
new file mode 100644
index 0000000..2a09482
--- /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 ExecCreatePartitionPruneState'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"