Provide a better error message for misplaced dispatch options.
[pgsql.git] / src / test / modules / injection_points / specs / inplace.spec
blob86539a5bd2f119fa413194e4b0bb85e5cb976c0d
1 # Test race conditions involving:
2 # - s1: VACUUM inplace-updating a pg_class row
3 # - s2: GRANT/REVOKE making pg_class rows dead
4 # - s3: "VACUUM pg_class" making dead rows LP_UNUSED; DDL reusing them
6 # Need GRANT to make a non-HOT update. Otherwise, "VACUUM pg_class" would
7 # leave an LP_REDIRECT that persists. To get non-HOT, make rels so the
8 # pg_class row for vactest.orig50 is on a filled page (assuming BLCKSZ=8192).
9 # Just to save on filesystem syscalls, use relkind=c for every other rel.
10 setup
12 CREATE EXTENSION injection_points;
13 CREATE SCHEMA vactest;
14 CREATE FUNCTION vactest.mkrels(text, int, int) RETURNS void
15 LANGUAGE plpgsql SET search_path = vactest AS $$
16 DECLARE
17 tname text;
18 BEGIN
19 FOR i in $2 .. $3 LOOP
20 tname := $1 || i;
21 EXECUTE FORMAT('CREATE TYPE ' || tname || ' AS ()');
22 RAISE DEBUG '% at %', tname, ctid
23 FROM pg_class WHERE oid = tname::regclass;
24 END LOOP;
25 END
26 $$;
28 setup { VACUUM FULL pg_class; -- reduce free space }
29 setup
31 SELECT vactest.mkrels('orig', 1, 49);
32 CREATE TABLE vactest.orig50 ();
33 SELECT vactest.mkrels('orig', 51, 100);
35 teardown
37 DROP SCHEMA vactest CASCADE;
38 DROP EXTENSION injection_points;
41 # Wait during inplace update, in a VACUUM of vactest.orig50.
42 session s1
43 setup {
44 SELECT injection_points_set_local();
45 SELECT injection_points_attach('inplace-before-pin', 'wait');
47 step vac1 { VACUUM vactest.orig50; -- wait during inplace update }
48 # One bug scenario leaves two live pg_class tuples for vactest.orig50 and zero
49 # live tuples for one of the "intruder" rels. REINDEX observes the duplicate.
50 step read1 {
51 REINDEX TABLE pg_class; -- look for duplicates
52 SELECT reltuples = -1 AS reltuples_unknown
53 FROM pg_class WHERE oid = 'vactest.orig50'::regclass;
56 # Transactional updates of the tuple vac1 is waiting to inplace-update.
57 session s2
58 step grant2 { GRANT SELECT ON TABLE vactest.orig50 TO PUBLIC; }
59 step revoke2 { REVOKE SELECT ON TABLE vactest.orig50 FROM PUBLIC; }
60 step begin2 { BEGIN; }
61 step c2 { COMMIT; }
62 step r2 { ROLLBACK; }
64 # Non-blocking actions.
65 session s3
66 step vac3 { VACUUM pg_class; }
67 # Reuse the lp that vac1 is waiting to change. I've observed reuse at the 1st
68 # or 18th CREATE, so create excess.
69 step mkrels3 {
70 SELECT vactest.mkrels('intruder', 1, 100); -- repopulate LP_UNUSED
71 SELECT injection_points_detach('inplace-before-pin');
72 SELECT injection_points_wakeup('inplace-before-pin');
76 # target gains a successor at the last moment
77 permutation
78 vac1(mkrels3) # reads pg_class tuple T0 for vactest.orig50, xmax invalid
79 grant2 # T0 becomes eligible for pruning, T1 is successor
80 vac3 # T0 becomes LP_UNUSED
81 mkrels3 # vac1 wakes, scans to T1
82 read1
84 # target already has a successor, which commits
85 permutation
86 begin2
87 grant2 # T0.t_ctid = T1
88 vac1(mkrels3) # reads T0 for vactest.orig50
89 c2 # T0 becomes eligible for pruning
90 vac3 # T0 becomes LP_UNUSED
91 mkrels3 # vac1 wakes, scans to T1
92 read1
94 # target already has a successor, which becomes LP_UNUSED at the last moment
95 permutation
96 begin2
97 grant2 # T0.t_ctid = T1
98 vac1(mkrels3) # reads T0 for vactest.orig50
99 r2 # T1 becomes eligible for pruning
100 vac3 # T1 becomes LP_UNUSED
101 mkrels3 # reuse T1; vac1 scans to T0
102 read1
104 # target already has a successor, which becomes LP_REDIRECT at the last moment
105 permutation
106 begin2
107 grant2 # T0.t_ctid = T1, non-HOT due to filled page
108 vac1(mkrels3) # reads T0
110 revoke2 # HOT update to T2
111 grant2 # HOT update to T3
112 vac3 # T1 becomes LP_REDIRECT
113 mkrels3 # reuse T2; vac1 scans to T3
114 read1
116 # waiting for updater to end
117 permutation
118 vac1(c2) # reads pg_class tuple T0 for vactest.orig50, xmax invalid
119 begin2
120 grant2 # T0.t_ctid = T1, non-HOT due to filled page
121 revoke2 # HOT update to T2
122 mkrels3 # vac1 awakes briefly, then waits for s2
124 read1
126 # Another LP_UNUSED. This time, do change the live tuple. Final live tuple
127 # body is identical to original, at a different TID.
128 permutation
129 begin2
130 grant2 # T0.t_ctid = T1, non-HOT due to filled page
131 vac1(mkrels3) # reads T0
132 r2 # T1 becomes eligible for pruning
133 grant2 # T0.t_ctid = T2; T0 becomes eligible for pruning
134 revoke2 # T2.t_ctid = T3; T2 becomes eligible for pruning
135 vac3 # T0, T1 & T2 become LP_UNUSED
136 mkrels3 # reuse T0, T1 & T2; vac1 scans to T3
137 read1
139 # Another LP_REDIRECT. Compared to the earlier test, omit the last grant2.
140 # Hence, final live tuple body is identical to original, at a different TID.
141 permutation begin2 grant2 vac1(mkrels3) c2 revoke2 vac3 mkrels3 read1