mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-11-27 07:21:09 +08:00
Support testing of cases where table schemas change after planning.
We have various cases where we allow DDL on tables to be performed with
less than full AccessExclusiveLock. This requires concurrent queries
to be able to cope with the DDL change mid-flight, but up to now we had
no repeatable way to test such cases. To improve that, invent a test
module that allows halting a backend after planning and then resuming
execution once we've done desired actions in another session. (The same
approach could be used to inject delays in other places, if there's a
suitable hook available.)
This commit includes a single test case, which is meant to exercise the
previously-untestable ExecCreatePartitionPruneState code repaired by
commit 7a980dfc6
. We'd probably not bother with this if that were the
only foreseen benefit, but I expect additional test cases will use this
infrastructure in the future.
Test module by Andy Fan, partition-addition test case by me.
Discussion: https://postgr.es/m/20200802181131.GA27754@telsasoft.com
This commit is contained in:
parent
3df92bbd1d
commit
6f0b632f08
@ -7,6 +7,7 @@ include $(top_builddir)/src/Makefile.global
|
||||
SUBDIRS = \
|
||||
brin \
|
||||
commit_ts \
|
||||
delay_execution \
|
||||
dummy_index_am \
|
||||
dummy_seclabel \
|
||||
snapshot_too_old \
|
||||
|
3
src/test/modules/delay_execution/.gitignore
vendored
Normal file
3
src/test/modules/delay_execution/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
# Generated subdirectories
|
||||
/output_iso/
|
||||
/tmp_check_iso/
|
21
src/test/modules/delay_execution/Makefile
Normal file
21
src/test/modules/delay_execution/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# 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
|
||||
|
||||
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
|
104
src/test/modules/delay_execution/delay_execution.c
Normal file
104
src/test/modules/delay_execution/delay_execution.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* 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, 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/unload functions */
|
||||
void _PG_init(void);
|
||||
void _PG_fini(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);
|
||||
|
||||
/* Install our hook */
|
||||
prev_planner_hook = planner_hook;
|
||||
planner_hook = delay_execution_planner;
|
||||
}
|
||||
|
||||
/* Module unload function (pro forma, not used currently) */
|
||||
void
|
||||
_PG_fini(void)
|
||||
{
|
||||
planner_hook = prev_planner_hook;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
Parsed test spec with 2 sessions
|
||||
|
||||
starting permutation: s2lock s1exec s2addp s2unlock
|
||||
step s2lock: SELECT pg_advisory_lock(12345);
|
||||
pg_advisory_lock
|
||||
|
||||
|
||||
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
|
||||
step s1exec: <... completed>
|
||||
a b
|
||||
|
||||
4 GHI
|
@ -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"
|
Loading…
Reference in New Issue
Block a user