diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index ae2efdc..654b502 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -3495,7 +3495,11 @@ StorePartitionBound(Relation rel, Relation parent, PartitionBoundSpec *bound) elog(ERROR, "cache lookup failed for relation %u", RelationGetRelid(rel)); -#ifdef USE_ASSERT_CHECKING + /* + * Assertion fails during partition getting multiple parents. Disable the + * assertion, to see what non-assert builds experience. + */ +#if 0 { Form_pg_class classForm; bool isnull; diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 8fcb188..48207f9 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -95,6 +95,7 @@ #include "utils/acl.h" #include "utils/builtins.h" #include "utils/fmgroids.h" +#include "utils/injection_point.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" @@ -15825,7 +15826,11 @@ MergeAttributesIntoExisting(Relation child_rel, Relation parent_rel, bool ispart */ if (parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) { - Assert(child_att->attinhcount == 1); + /* + * Assertion fails during partition getting multiple parents. + * Disable, to see what non-assert builds experience. + */ + /* Assert(child_att->attinhcount == 1); */ child_att->attislocal = false; } @@ -20388,7 +20393,14 @@ createPartitionTable(RangeVar *newPartName, Relation modelRel, * Open the new partition with no lock, because we already have * AccessExclusiveLock placed there after creation. */ - newRel = table_openrv(newPartName, NoLock); + INJECTION_POINT("merge-after-create"); + /* + * For testing, switch to taking a lock. This solves two problems. + * First, it gets an AcceptInvalidationMessages(), so we actually + * invalidate the search path. Second, it avoids an assertion failure + * from our lack of lock, so we see what non-assert builds experience. + */ + newRel = table_openrv(newPartName, AccessExclusiveLock); /* * We intended to create the partition with the same persistence as the diff --git a/src/test/modules/injection_points/Makefile b/src/test/modules/injection_points/Makefile index 2ffd2f7..9af45bf 100644 --- a/src/test/modules/injection_points/Makefile +++ b/src/test/modules/injection_points/Makefile @@ -9,7 +9,7 @@ PGFILEDESC = "injection_points - facility for injection points" REGRESS = injection_points REGRESS_OPTS = --dlpath=$(top_builddir)/src/test/regress -ISOLATION = inplace +ISOLATION = inplace merge # The injection points are cluster-wide, so disable installcheck NO_INSTALLCHECK = 1 diff --git a/src/test/modules/injection_points/expected/merge.out b/src/test/modules/injection_points/expected/merge.out new file mode 100644 index 0000000..04920e7 --- /dev/null +++ b/src/test/modules/injection_points/expected/merge.out @@ -0,0 +1,60 @@ +Parsed test spec with 3 sessions + +starting permutation: merge1 ct2 detach3 +injection_points_attach +----------------------- + +(1 row) + +step merge1: + SET search_path = target; + ALTER TABLE parted MERGE PARTITIONS (part1, part3) INTO part_all; + -- only one of *parted should have rows, but both do + SELECT a AS new_target_parted FROM target.parted ORDER BY 1; + SELECT a AS old_target_parted FROM old_target.parted ORDER BY 1; + SELECT a AS new_target_part_all FROM target.part_all ORDER BY 1; + SELECT a AS old_target_part_all FROM old_target.part_all ORDER BY 1; + +step ct2: + ALTER SCHEMA target RENAME TO old_target; + CREATE SCHEMA target + CREATE TABLE parted (a int) partition by list (a) + CREATE TABLE part_all PARTITION OF parted FOR VALUES IN (1, 2, 3, 4) + +step detach3: + SELECT injection_points_detach('merge-after-create'); + SELECT injection_points_wakeup('merge-after-create'); + +injection_points_detach +----------------------- + +(1 row) + +injection_points_wakeup +----------------------- + +(1 row) + +step merge1: <... completed> +new_target_parted +----------------- + 1 + 3 +(2 rows) + +old_target_parted +----------------- + 1 + 3 +(2 rows) + +new_target_part_all +------------------- + 1 + 3 +(2 rows) + +old_target_part_all +------------------- +(0 rows) + diff --git a/src/test/modules/injection_points/specs/merge.spec b/src/test/modules/injection_points/specs/merge.spec new file mode 100644 index 0000000..63bcd2b --- /dev/null +++ b/src/test/modules/injection_points/specs/merge.spec @@ -0,0 +1,51 @@ +setup +{ + CREATE EXTENSION injection_points; + CREATE SCHEMA target + CREATE TABLE parted (a int) partition by list (a) + CREATE TABLE part1 PARTITION OF parted FOR VALUES IN (1, 2) + CREATE TABLE part3 PARTITION OF parted FOR VALUES IN (3, 4); + INSERT INTO target.parted VALUES (1),(3); +} +teardown +{ + DROP SCHEMA target, old_target CASCADE; + DROP EXTENSION injection_points; +} + +# MERGE PARTITIONS +session s1 +setup { + SELECT injection_points_set_local(); + SELECT injection_points_attach('merge-after-create', 'wait'); +} +step merge1 { + SET search_path = target; + ALTER TABLE parted MERGE PARTITIONS (part1, part3) INTO part_all; + -- only one of *parted should have rows, but both do + SELECT a AS new_target_parted FROM target.parted ORDER BY 1; + SELECT a AS old_target_parted FROM old_target.parted ORDER BY 1; + SELECT a AS new_target_part_all FROM target.part_all ORDER BY 1; + SELECT a AS old_target_part_all FROM old_target.part_all ORDER BY 1; +} + + +# inject another table via ALTER SCHEMA RENAME +session s2 +step ct2 { + ALTER SCHEMA target RENAME TO old_target; + CREATE SCHEMA target + CREATE TABLE parted (a int) partition by list (a) + CREATE TABLE part_all PARTITION OF parted FOR VALUES IN (1, 2, 3, 4) +} + + +# injection release +session s3 +step detach3 { + SELECT injection_points_detach('merge-after-create'); + SELECT injection_points_wakeup('merge-after-create'); +} + + +permutation merge1(detach3) ct2 detach3