syscache-update-pruned \
heap_lock_update
-# Temporarily disabled because of flakiness
-#ISOLATION =+
-# index-concurrently-upsert \
-# index-concurrently-upsert-predicate \
-# reindex-concurrently-upsert \
-# reindex-concurrently-upsert-on-constraint \
-# reindex-concurrently-upsert-partitioned
-
# The injection points are cluster-wide, so disable installcheck
NO_INSTALLCHECK = 1
+++ /dev/null
-Parsed test spec with 5 sessions
-
-starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot:
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s4_wakeup_s1_setup:
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-
-case
-----
-
-(1 row)
-
-step s3_start_create_index:
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_define_index_before_set_valid:
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
- <waiting ...>
-step s5_wakeup_s1_from_invalidate_catalog_snapshot:
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_create_index: <... completed>
+++ /dev/null
-Parsed test spec with 5 sessions
-
-starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot:
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
- <waiting ...>
-step s4_wakeup_s1_setup:
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-
-case
-----
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot: <... completed>
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_create_index:
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_define_index_before_set_valid:
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
- <waiting ...>
-step s5_wakeup_s1_from_invalidate_catalog_snapshot:
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_create_index: <... completed>
+++ /dev/null
-Parsed test spec with 5 sessions
-
-starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot:
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s4_wakeup_s1_setup:
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-
-case
-----
-
-(1 row)
-
-step s3_start_create_index:
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i);
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_define_index_before_set_valid:
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s5_wakeup_s1_from_invalidate_catalog_snapshot:
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_create_index: <... completed>
+++ /dev/null
-Parsed test spec with 5 sessions
-
-starting permutation: s1_attach_invalidate_catalog_snapshot s4_wakeup_s1_setup s3_start_create_index s1_start_upsert s4_wakeup_define_index_before_set_valid s2_start_upsert s5_wakeup_s1_from_invalidate_catalog_snapshot s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot:
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
- <waiting ...>
-step s4_wakeup_s1_setup:
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-
-case
-----
-
-(1 row)
-
-step s1_attach_invalidate_catalog_snapshot: <... completed>
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_create_index:
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i);
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_define_index_before_set_valid:
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s5_wakeup_s1_from_invalidate_catalog_snapshot:
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_create_index: <... completed>
+++ /dev/null
-Parsed test spec with 4 sessions
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_swap:
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_swap:
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
+++ /dev/null
-Parsed test spec with 4 sessions
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_swap:
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_swap:
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
+++ /dev/null
-Parsed test spec with 4 sessions
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s4_wakeup_to_set_dead s2_start_upsert s4_wakeup_s1 s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_swap s3_start_reindex s1_start_upsert s4_wakeup_to_swap s2_start_upsert s4_wakeup_s2 s4_wakeup_s1
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_swap:
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_to_swap:
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
-
-starting permutation: s3_setup_wait_before_set_dead s3_start_reindex s1_start_upsert s2_start_upsert s4_wakeup_s1 s4_wakeup_to_set_dead s4_wakeup_s2
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_attach
------------------------
-
-(1 row)
-
-injection_points_set_local
---------------------------
-
-(1 row)
-
-step s3_setup_wait_before_set_dead:
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-
-injection_points_attach
------------------------
-
-(1 row)
-
-step s3_start_reindex:
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
- <waiting ...>
-step s1_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s2_start_upsert:
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
- <waiting ...>
-step s4_wakeup_s1:
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s1_start_upsert: <... completed>
-step s4_wakeup_to_set_dead:
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s4_wakeup_s2:
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-
-injection_points_detach
------------------------
-
-(1 row)
-
-injection_points_wakeup
------------------------
-
-(1 row)
-
-step s2_start_upsert: <... completed>
-step s3_start_reindex: <... completed>
'inplace',
'syscache-update-pruned',
'heap_lock_update',
- # temporarily disabled because of flakiness
- # 'index-concurrently-upsert',
- # 'index-concurrently-upsert-predicate',
- # 'reindex-concurrently-upsert',
- # 'reindex-concurrently-upsert-on-constraint',
- # 'reindex-concurrently-upsert-partitioned',
],
'runningcheck': false, # see syscache-update-pruned
# Some tests wait for all snapshots, so avoid parallel execution
+++ /dev/null
-# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with
-# CREATE INDEX CONCURRENTLY a partial index.
-#
-# - s1: UPSERT a tuple
-# - s2: UPSERT the same tuple
-# - s3: CREATE UNIQUE INDEX CONCURRENTLY (with a predicate)
-#
-# - s4 and s5: control concurrency via injection points
-
-setup
-{
- CREATE EXTENSION injection_points;
- CREATE SCHEMA test;
- CREATE UNLOGGED TABLE test.tbl(i int, updated_at timestamp);
- CREATE UNIQUE INDEX tbl_pkey_special ON test.tbl(abs(i)) WHERE i < 1000;
- ALTER TABLE test.tbl SET (parallel_workers=0);
-}
-
-teardown
-{
- DROP SCHEMA test CASCADE;
- DROP EXTENSION injection_points;
-}
-
-session s1
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
-}
-step s1_attach_invalidate_catalog_snapshot
-{
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
-}
-step s1_start_upsert
-{
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
-}
-
-session s2
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
-}
-step s2_start_upsert
-{
- INSERT INTO test.tbl VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
-}
-
-session s3
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('define-index-before-set-valid', 'wait');
-}
-step s3_start_create_index
-{
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tbl(abs(i)) WHERE i < 10000;
-}
-
-session s4
-# Step s1_attach_invalidate_catalog_snapshot sleeps or not depending on
-# build conditions (CATCACHE_FORCE_RELEASE). Here we send a wakeup signal if
-# it's sleeping or do nothing otherwise, and print a null value in either
-# case.
-step s4_wakeup_s1_setup
-{
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-}
-step s4_wakeup_s1
-{
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-}
-step s4_wakeup_s2
-{
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-}
-step s4_wakeup_define_index_before_set_valid
-{
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-}
-
-session s5
-step s5_wakeup_s1_from_invalidate_catalog_snapshot
-{
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-}
-
-permutation
- s1_attach_invalidate_catalog_snapshot
- s4_wakeup_s1_setup
- s3_start_create_index(s1_start_upsert, s2_start_upsert)
- s1_start_upsert
- s4_wakeup_define_index_before_set_valid
- s2_start_upsert(s1_start_upsert)
- s5_wakeup_s1_from_invalidate_catalog_snapshot
- s4_wakeup_s2
- s4_wakeup_s1
+++ /dev/null
-# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with
-# CREATE INDEX CONCURRENTLY.
-#
-# - s1: UPSERT a tuple
-# - s2: UPSERT the same tuple
-# - s3: CREATE UNIQUE INDEX CONCURRENTLY
-#
-# - s4: Control concurrency using injection points
-
-setup
-{
- CREATE EXTENSION injection_points;
- CREATE SCHEMA test;
- CREATE UNLOGGED TABLE test.tbl(i int primary key, updated_at timestamp);
- ALTER TABLE test.tbl SET (parallel_workers=0);
-}
-
-teardown
-{
- DROP SCHEMA test CASCADE;
- DROP EXTENSION injection_points;
-}
-
-session s1
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
-}
-step s1_attach_invalidate_catalog_snapshot
-{
- SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
-}
-step s1_start_upsert
-{
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s2
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
-}
-step s2_start_upsert
-{
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s3
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('define-index-before-set-valid', 'wait');
-}
-step s3_start_create_index
-{
- CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tbl(i);
-}
-
-session s4
-# Step s1_attach_invalidate_catalog_snapshot sleeps or not depending on
-# build conditions (CATCACHE_FORCE_RELEASE). Here we send a wakeup signal if
-# it's sleeping or do nothing otherwise, and print a null value in either
-# case.
-step s4_wakeup_s1_setup
-{
- SELECT CASE WHEN
- (SELECT pid FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint' AND
- wait_event = 'invalidate-catalog-snapshot-end') IS NOT NULL
- THEN injection_points_wakeup('invalidate-catalog-snapshot-end')
- END;
-}
-step s4_wakeup_s1
-{
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-}
-step s4_wakeup_s2
-{
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-}
-step s4_wakeup_define_index_before_set_valid
-{
- SELECT injection_points_detach('define-index-before-set-valid');
- SELECT injection_points_wakeup('define-index-before-set-valid');
-}
-
-session s5
-step s5_wakeup_s1_from_invalidate_catalog_snapshot
-{
- DO $$
- DECLARE
- v_waiting_pid INTEGER;
- BEGIN
- LOOP
- SELECT pid INTO v_waiting_pid
- FROM pg_stat_activity
- WHERE wait_event_type = 'InjectionPoint'
- AND wait_event = 'invalidate-catalog-snapshot-end'
- LIMIT 1;
- EXIT WHEN v_waiting_pid IS NOT NULL;
- PERFORM pg_sleep(100);
- END LOOP;
- END
- $$;
-
- SELECT injection_points_detach('invalidate-catalog-snapshot-end');
- SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
-}
-
-permutation
- s1_attach_invalidate_catalog_snapshot
- s4_wakeup_s1_setup
- s3_start_create_index(s1_start_upsert, s2_start_upsert)
- s1_start_upsert
- s4_wakeup_define_index_before_set_valid
- s2_start_upsert(s1_start_upsert)
- s5_wakeup_s1_from_invalidate_catalog_snapshot
- s4_wakeup_s2
- s4_wakeup_s1
+++ /dev/null
-# Test race conditions involving:
-#
-# - s1: UPSERT a tuple
-# - s2: UPSERT the same tuple
-# - s3: concurrently REINDEX the primary key
-#
-# - s4: operations with injection points
-
-setup
-{
- CREATE EXTENSION injection_points;
- CREATE SCHEMA test;
- CREATE UNLOGGED TABLE test.tbl(i int primary key, updated_at timestamp);
- ALTER TABLE test.tbl SET (parallel_workers=0);
-}
-
-teardown
-{
- DROP SCHEMA test CASCADE;
- DROP EXTENSION injection_points;
-}
-
-session s1
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
-}
-step s1_start_upsert
-{
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
-}
-
-session s2
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
-}
-step s2_start_upsert
-{
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT ON CONSTRAINT tbl_pkey DO UPDATE SET updated_at = now();
-}
-
-session s3
-setup
-{
- SELECT injection_points_set_local();
-}
-step s3_setup_wait_before_set_dead
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-}
-step s3_setup_wait_before_swap
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-}
-step s3_start_reindex
-{
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
-}
-
-session s4
-step s4_wakeup_to_swap
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-}
-step s4_wakeup_s1
-{
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-}
-step s4_wakeup_s2
-{
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-}
-step s4_wakeup_to_set_dead
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-}
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_set_dead
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_s2
-
-permutation
- s3_setup_wait_before_swap
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_swap
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s2
- s4_wakeup_s1
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_to_set_dead
- s4_wakeup_s2
+++ /dev/null
-# This test verifies INSERT ON CONFLICT DO UPDATE behavior on partitioned
-# tables concurrent with REINDEX CONCURRENTLY.
-#
-# - s1: UPSERT a tuple
-# - s2: UPSERT the same tuple
-# - s3: concurrently REINDEX the primary key index
-#
-# - s4: controls concurrency via injection points
-
-setup
-{
- CREATE EXTENSION injection_points;
- CREATE SCHEMA test;
- CREATE TABLE test.tbl(i int primary key, updated_at timestamp) PARTITION BY RANGE (i);
- CREATE TABLE test.tbl_partition PARTITION OF test.tbl
- FOR VALUES FROM (0) TO (10000)
- WITH (parallel_workers = 0);
-}
-
-teardown
-{
- DROP SCHEMA test CASCADE;
- DROP EXTENSION injection_points;
-}
-
-session s1
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
-}
-step s1_start_upsert
-{
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s2
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
-}
-step s2_start_upsert
-{
- INSERT INTO test.tbl VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s3
-setup
-{
- SELECT injection_points_set_local();
-}
-step s3_setup_wait_before_set_dead
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-}
-step s3_setup_wait_before_swap
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-}
-step s3_start_reindex
-{
- REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
-}
-
-session s4
-step s4_wakeup_to_swap
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-}
-step s4_wakeup_s1
-{
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-}
-step s4_wakeup_s2
-{
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-}
-step s4_wakeup_to_set_dead
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-}
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_set_dead
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_s2
-
-permutation
- s3_setup_wait_before_swap
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_swap
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s2
- s4_wakeup_s1
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_to_set_dead
- s4_wakeup_s2
+++ /dev/null
-# This test verifies INSERT ON CONFLICT DO UPDATE behavior concurrent with
-# REINDEX CONCURRENTLY.
-#
-# - s1: UPSERT a tuple
-# - s2: UPSERT the same tuple
-# - s3: REINDEX concurrent primary key index
-#
-# - s4: controls concurrency via injection points
-
-setup
-{
- CREATE EXTENSION injection_points;
- CREATE SCHEMA test;
- CREATE UNLOGGED TABLE test.tbl (i int PRIMARY KEY, updated_at timestamp);
- ALTER TABLE test.tbl SET (parallel_workers=0);
-}
-
-teardown
-{
- DROP SCHEMA test CASCADE;
- DROP EXTENSION injection_points;
-}
-
-session s1
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
-}
-step s1_start_upsert
-{
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s2
-setup
-{
- SELECT injection_points_set_local();
- SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
-}
-step s2_start_upsert
-{
- INSERT INTO test.tbl VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
-}
-
-session s3
-setup
-{
- SELECT injection_points_set_local();
-}
-step s3_setup_wait_before_set_dead
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
-}
-step s3_setup_wait_before_swap
-{
- SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
-}
-step s3_start_reindex
-{
- REINDEX INDEX CONCURRENTLY test.tbl_pkey;
-}
-
-session s4
-step s4_wakeup_to_swap
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-swap');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-swap');
-}
-step s4_wakeup_s1
-{
- SELECT injection_points_detach('check-exclusion-or-unique-constraint-no-conflict');
- SELECT injection_points_wakeup('check-exclusion-or-unique-constraint-no-conflict');
-}
-step s4_wakeup_s2
-{
- SELECT injection_points_detach('exec-insert-before-insert-speculative');
- SELECT injection_points_wakeup('exec-insert-before-insert-speculative');
-}
-step s4_wakeup_to_set_dead
-{
- SELECT injection_points_detach('reindex-relation-concurrently-before-set-dead');
- SELECT injection_points_wakeup('reindex-relation-concurrently-before-set-dead');
-}
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_set_dead
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_s2
-
-permutation
- s3_setup_wait_before_swap
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s4_wakeup_to_swap
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s2
- s4_wakeup_s1
-
-permutation
- s3_setup_wait_before_set_dead
- s3_start_reindex(s1_start_upsert, s2_start_upsert)
- s1_start_upsert(s4_wakeup_s2)
- s2_start_upsert(s1_start_upsert)
- s4_wakeup_s1
- s4_wakeup_to_set_dead
- s4_wakeup_s2
EXTRA_INSTALL=src/test/modules/injection_points \
contrib/test_decoding
+# The injection points are cluster-wide, so disable installcheck
+NO_INSTALLCHECK = 1
+
export enable_injection_points
ifdef USE_PGXS
't/007_catcache_inval.pl',
't/008_replslot_single_user.pl',
't/009_log_temp_files.pl',
+ 't/010_index_concurrently_upsert.pl',
],
+ # The injection points are cluster-wide, so disable installcheck
+ 'runningcheck': false,
},
}
--- /dev/null
+
+# Copyright (c) 2025, PostgreSQL Global Development Group
+
+# Test INSERT ON CONFLICT DO UPDATE behavior concurrent with
+# CREATE INDEX CONCURRENTLY and REINDEX CONCURRENTLY.
+#
+# These tests verify the fix for "duplicate key value violates unique
+# constraint" errors that occurred when infer_arbiter_indexes() only considered
+# indisvalid indexes, causing different transactions to use different arbiter
+# indexes.
+
+use strict;
+use warnings FATAL => 'all';
+
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+plan skip_all => 'Injection points not supported by this build'
+ unless $ENV{enable_injection_points} eq 'yes';
+
+# Node initialization
+my $node = PostgreSQL::Test::Cluster->new('node');
+$node->init();
+$node->start();
+
+# Check if the extension injection_points is available
+plan skip_all => 'Extension injection_points not installed'
+ unless $node->check_extension('injection_points');
+
+$node->safe_psql('postgres', 'CREATE EXTENSION injection_points;');
+
+$node->safe_psql(
+ 'postgres', q[
+CREATE SCHEMA test;
+CREATE UNLOGGED TABLE test.tblpk (i int PRIMARY KEY, updated_at timestamp);
+ALTER TABLE test.tblpk SET (parallel_workers=0);
+
+CREATE TABLE test.tblparted(i int primary key, updated_at timestamp) PARTITION BY RANGE (i);
+CREATE TABLE test.tbl_partition PARTITION OF test.tblparted
+ FOR VALUES FROM (0) TO (10000)
+ WITH (parallel_workers = 0);
+
+CREATE UNLOGGED TABLE test.tblexpr(i int, updated_at timestamp);
+CREATE UNIQUE INDEX tbl_pkey_special ON test.tblexpr(abs(i)) WHERE i < 1000;
+ALTER TABLE test.tblexpr SET (parallel_workers=0);
+
+]);
+
+############################################################################
+note('Test: REINDEX CONCURRENTLY + UPSERT (wakeup at set-dead phase)');
+
+# Create sessions with on_error_stop => 0 so psql doesn't exit on SQL errors.
+# This allows us to collect stderr and detect errors after the test completes.
+my $s1 = $node->background_psql('postgres', on_error_stop => 0);
+my $s2 = $node->background_psql('postgres', on_error_stop => 0);
+my $s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+# Setup injection points for each session
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+# s3 starts REINDEX (will block on reindex-relation-concurrently-before-set-dead)
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+# Wait for s3 to hit injection point
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+# s1 starts UPSERT (will block on check-exclusion-or-unique-constraint-no-conflict)
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+# Wait for s1 to hit injection point
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+# Wakeup s3 to continue (reindex-relation-concurrently-before-set-dead)
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+
+# s2 starts UPSERT (will block on exec-insert-before-insert-speculative)
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+# Wait for s2 to hit injection point
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+# Wakeup s1 (check-exclusion-or-unique-constraint-no-conflict)
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+# Wakeup s2 (exec-insert-before-insert-speculative)
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+# Cleanup test 1
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX CONCURRENTLY + UPSERT (wakeup at swap phase)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX CONCURRENTLY + UPSERT (s1 wakes before reindex)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+# Start s2 BEFORE waking reindex (key difference from permutation 1)
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+# Wake s1 first, then reindex, then s2
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX + UPSERT ON CONSTRAINT (set-dead phase)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX + UPSERT ON CONSTRAINT (swap phase)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX + UPSERT ON CONSTRAINT (s1 wakes before reindex)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tblpk_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+# Start s2 BEFORE waking reindex
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13, now()) ON CONFLICT ON CONSTRAINT tblpk_pkey DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+# Wake s1 first, then reindex, then s2
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblpk');
+
+############################################################################
+note('Test: REINDEX on partitioned table (set-dead phase)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
+
+############################################################################
+note('Test: REINDEX on partitioned table (swap phase)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-swap', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node, 'reindex-relation-concurrently-before-swap');
+
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
+
+############################################################################
+note('Test: REINDEX on partitioned table (s1 wakes before reindex)');
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('reindex-relation-concurrently-before-set-dead', 'wait');
+]);
+
+$s3->query_until(
+ qr/starting_reindex/, q[
+\echo starting_reindex
+REINDEX INDEX CONCURRENTLY test.tbl_partition_pkey;
+]);
+
+ok_injection_point($node, 'reindex-relation-concurrently-before-set-dead');
+
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+# Start s2 BEFORE waking reindex
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblparted VALUES (13, now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+# Wake s1 first, then reindex, then s2
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node,
+ 'reindex-relation-concurrently-before-set-dead');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
+
+############################################################################
+note('Test: CREATE INDEX CONCURRENTLY + UPSERT');
+# Uses invalidate-catalog-snapshot-end to test catalog invalidation
+# during UPSERT
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+my $s1_pid = $s1->query_safe('SELECT pg_backend_pid()');
+
+# s1 attaches BOTH injection points - the unique constraint check AND catalog snapshot
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s1->query_until(
+ qr/attaching_injection_point/, q[
+\echo attaching_injection_point
+SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
+]);
+# In case of CLOBBER_CACHE_ALWAYS - s1 may hit the injection point during attach.
+# Wait for s1 to become idle (attach completed) or wakeup if stuck on injection point.
+if (!wait_for_idle($node, $s1_pid))
+{
+ ok_injection_point(
+ $node,
+ 'invalidate-catalog-snapshot-end',
+ 's1 hit injection point during attach (CLOBBER_CACHE_ALWAYS)');
+ $node->safe_psql(
+ 'postgres', q[
+ SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
+ ]);
+}
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('define-index-before-set-valid', 'wait');
+]);
+
+# s3: Start CREATE INDEX CONCURRENTLY (blocks on define-index-before-set-valid)
+$s3->query_until(
+ qr/starting_create_index/, q[
+\echo starting_create_index
+CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_duplicate ON test.tblpk(i);
+]);
+
+ok_injection_point($node, 'define-index-before-set-valid');
+
+# s1: Start UPSERT (blocks on invalidate-catalog-snapshot-end)
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'invalidate-catalog-snapshot-end');
+
+# Wakeup s3 (CREATE INDEX continues, triggers catalog invalidation)
+wakeup_injection_point($node, 'define-index-before-set-valid');
+
+# s2: Start UPSERT (blocks on exec-insert-before-insert-speculative)
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblpk VALUES (13,now()) ON CONFLICT (i) DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node, 'invalidate-catalog-snapshot-end');
+
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblparted');
+
+############################################################################
+note('Test: CREATE INDEX CONCURRENTLY on partial index + UPSERT');
+# Uses invalidate-catalog-snapshot-end to test catalog invalidation during UPSERT
+
+$s1 = $node->background_psql('postgres', on_error_stop => 0);
+$s2 = $node->background_psql('postgres', on_error_stop => 0);
+$s3 = $node->background_psql('postgres', on_error_stop => 0);
+
+$s1_pid = $s1->query_safe('SELECT pg_backend_pid()');
+
+# s1 attaches BOTH injection points - the unique constraint check AND catalog snapshot
+$s1->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('check-exclusion-or-unique-constraint-no-conflict', 'wait');
+]);
+
+$s1->query_until(
+ qr/attaching_injection_point/, q[
+\echo attaching_injection_point
+SELECT injection_points_attach('invalidate-catalog-snapshot-end', 'wait');
+]);
+# In case of CLOBBER_CACHE_ALWAYS - s1 may hit the injection point during attach.
+# Wait for s1 to become idle (attach completed) or wakeup if stuck on injection point.
+if (!wait_for_idle($node, $s1_pid))
+{
+ ok_injection_point($node, 'invalidate-catalog-snapshot-end',
+ 'Test 8: s1 hit injection point during attach (CLOBBER_CACHE_ALWAYS)'
+ );
+ $node->safe_psql(
+ 'postgres', q[
+ SELECT injection_points_wakeup('invalidate-catalog-snapshot-end');
+ ]);
+}
+
+$s2->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('exec-insert-before-insert-speculative', 'wait');
+]);
+
+$s3->query_safe(
+ q[
+SELECT injection_points_set_local();
+SELECT injection_points_attach('define-index-before-set-valid', 'wait');
+]);
+
+# s3: Start CREATE INDEX CONCURRENTLY (blocks on define-index-before-set-valid)
+$s3->query_until(
+ qr/starting_create_index/, q[
+\echo starting_create_index
+CREATE UNIQUE INDEX CONCURRENTLY tbl_pkey_special_duplicate ON test.tblexpr(abs(i)) WHERE i < 10000;
+]);
+
+ok_injection_point($node, 'define-index-before-set-valid');
+
+# s1: Start UPSERT (blocks on invalidate-catalog-snapshot-end)
+$s1->query_until(
+ qr/starting_upsert_s1/, q[
+\echo starting_upsert_s1
+INSERT INTO test.tblexpr VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'invalidate-catalog-snapshot-end');
+
+# Wakeup s3 (CREATE INDEX continues, triggers catalog invalidation)
+wakeup_injection_point($node, 'define-index-before-set-valid');
+
+# s2: Start UPSERT (blocks on exec-insert-before-insert-speculative)
+$s2->query_until(
+ qr/starting_upsert_s2/, q[
+\echo starting_upsert_s2
+INSERT INTO test.tblexpr VALUES(13,now()) ON CONFLICT (abs(i)) WHERE i < 100 DO UPDATE SET updated_at = now();
+]);
+
+ok_injection_point($node, 'exec-insert-before-insert-speculative');
+wakeup_injection_point($node, 'invalidate-catalog-snapshot-end');
+ok_injection_point($node, 'check-exclusion-or-unique-constraint-no-conflict');
+wakeup_injection_point($node, 'exec-insert-before-insert-speculative');
+wakeup_injection_point($node,
+ 'check-exclusion-or-unique-constraint-no-conflict');
+
+clean_safe_quit_ok($s1, $s2, $s3);
+
+$node->safe_psql('postgres', 'TRUNCATE TABLE test.tblexpr');
+
+done_testing();
+
+############################################################################
+# Helper functions
+#
+############################################################################
+
+# Helper: Wait for a session to hit an injection point.
+# Optional second argument is timeout in seconds.
+# Returns true if found, false if timeout.
+# On timeout, logs diagnostic information about all active queries.
+sub wait_for_injection_point
+{
+ my ($node, $point_name, $timeout) = @_;
+ $timeout //= $PostgreSQL::Test::Utils::timeout_default;
+
+ for (my $elapsed = 0; $elapsed < $timeout * 10; $elapsed++)
+ {
+ my $pid = $node->safe_psql(
+ 'postgres', qq[
+ SELECT pid FROM pg_stat_activity
+ WHERE wait_event_type = 'InjectionPoint'
+ AND wait_event = '$point_name'
+ LIMIT 1;
+ ]);
+ return 1 if $pid ne '';
+ sleep(0.1);
+ }
+
+ # Timeout - report diagnostic information
+ my $activity = $node->safe_psql(
+ 'postgres', q[
+ SELECT format('pid=%s, state=%s, wait_event_type=%s, wait_event=%s, backend_xmin=%s, backend_xid=%s, query=%s',
+ pid, state, wait_event_type, wait_event, backend_xmin, backend_xid, left(query, 100))
+ FROM pg_stat_activity
+ ORDER BY pid;
+ ]);
+ diag( "wait_for_injection_point timeout waiting for: $point_name\n"
+ . "Current queries in pg_stat_activity:\n$activity");
+
+ return 0;
+}
+
+# Test helper: ok() a wait for the given injection point
+# Third argument is an optional test name.
+sub ok_injection_point
+{
+ my ($node, $injection_point, $testname) = @_;
+ $testname //= "hit injection point $injection_point";
+
+ ok(wait_for_injection_point($node, $injection_point), $testname);
+}
+
+# Helper: Wait for a specific backend to become idle.
+# Returns true if idle, false if timeout.
+sub wait_for_idle
+{
+ my ($node, $pid, $timeout) = @_;
+ $timeout //= $PostgreSQL::Test::Utils::timeout_default;
+
+ for (my $elapsed = 0; $elapsed < $timeout * 10; $elapsed++)
+ {
+ my $state = $node->safe_psql(
+ 'postgres', qq[
+ SELECT state FROM pg_stat_activity WHERE pid = $pid;
+ ]);
+ return 1 if $state eq 'idle';
+ sleep(0.1);
+ }
+ return 0;
+}
+
+# Helper: Detach and wakeup an injection point
+sub wakeup_injection_point
+{
+ my ($node, $point_name) = @_;
+ $node->safe_psql(
+ 'postgres', qq[
+SELECT injection_points_detach('$point_name');
+SELECT injection_points_wakeup('$point_name');
+]);
+}
+
+# Wait for any pending query to complete, capture stderr, and close the session.
+# Returns the stderr output (excluding internal markers).
+sub safe_quit
+{
+ my ($session) = @_;
+
+ # Send a marker and wait for it to ensure any pending query completes
+ my $banner = "safe_quit_marker";
+ my $banner_match = qr/(^|\n)$banner\r?\n/;
+
+ $session->{stdin} .= "\\echo $banner\n\\warn $banner\n";
+
+ pump_until(
+ $session->{run}, $session->{timeout},
+ \$session->{stdout}, $banner_match);
+ pump_until(
+ $session->{run}, $session->{timeout},
+ \$session->{stderr}, $banner_match);
+
+ # Capture stderr (excluding the banner)
+ my $stderr = $session->{stderr};
+ $stderr =~ s/$banner_match//;
+
+ # Close the session
+ $session->quit;
+
+ return $stderr;
+}
+
+# Helper function: verify that the given sessions exit cleanly.
+sub clean_safe_quit_ok
+{
+ my $i = 1;
+ foreach my $session (@_)
+ {
+ is(safe_quit($session), '', "session " . $i++ . " quit cleanly");
+ }
+}