@@ -108,3 +108,104 @@ SELECT (COUNT(*) = (SELECT relpages FROM pg_class WHERE relname = 'test2')) AS r
108108
109109DROP TABLE test1;
110110DROP TABLE test2;
111+ -- Test that parallel index build produces the same BRIN index as serial build.
112+ CREATE TABLE brin_parallel_test (a int, b text, c bigint) WITH (fillfactor=40);
113+ -- Generate a table with a mix of NULLs and non-NULL values (and data suitable
114+ -- for the different opclasses we build later).
115+ INSERT INTO brin_parallel_test
116+ SELECT (CASE WHEN (mod(i,231) = 0) OR (i BETWEEN 3500 AND 4000) THEN NULL ELSE i END),
117+ (CASE WHEN (mod(i,233) = 0) OR (i BETWEEN 3750 AND 4250) THEN NULL ELSE md5(i::text) END),
118+ (CASE WHEN (mod(i,233) = 0) OR (i BETWEEN 3850 AND 4500) THEN NULL ELSE (i/100) + mod(i,8) END)
119+ FROM generate_series(1,5000) S(i);
120+ -- Delete a couple pages, to make the ranges empty.
121+ DELETE FROM brin_parallel_test WHERE a BETWEEN 1000 and 1500;
122+ -- Vacuum to remove the tuples and make the ranges actually empty.
123+ VACUUM brin_parallel_test;
124+ -- Build an index with different opclasses - minmax, bloom and minmax-multi.
125+ --
126+ -- For minmax and opclass this is simple, but for minmax-multi we need to be
127+ -- careful, because the result depends on the order in which values are added
128+ -- to the summary, which in turn affects how are values merged etc. The order
129+ -- of merging results from workers has similar effect. All those summaries
130+ -- should produce correct query results, but it means we can't compare them
131+ -- using equality (which is what EXCEPT does). To work around this issue, we
132+ -- generated the data to only have very small number of distinct values per
133+ -- range, so that no merging is needed. This makes the results deterministic.
134+ -- build index without parallelism
135+ SET max_parallel_maintenance_workers = 0;
136+ CREATE INDEX brin_test_serial_idx ON brin_parallel_test
137+ USING brin (a int4_minmax_ops, a int4_bloom_ops, b, c int8_minmax_multi_ops)
138+ WITH (pages_per_range=7);
139+ -- build index using parallelism
140+ --
141+ -- Set a couple parameters to force parallel build for small table. There's a
142+ -- requirement for table size, so disable that. Also, plan_create_index_workers
143+ -- assumes each worker will use work_mem=32MB for sorting (which works for btree,
144+ -- but not really for BRIN), so we set maintenance_work_mem for 4 workers.
145+ SET min_parallel_table_scan_size = 0;
146+ SET max_parallel_maintenance_workers = 4;
147+ SET maintenance_work_mem = '128MB';
148+ CREATE INDEX brin_test_parallel_idx ON brin_parallel_test
149+ USING brin (a int4_minmax_ops, a int4_bloom_ops, b, c int8_minmax_multi_ops)
150+ WITH (pages_per_range=7);
151+ SELECT relname, relpages
152+ FROM pg_class
153+ WHERE relname IN ('brin_test_serial_idx', 'brin_test_parallel_idx')
154+ ORDER BY relname;
155+ relname | relpages
156+ ------------------------+----------
157+ brin_test_parallel_idx | 3
158+ brin_test_serial_idx | 3
159+ (2 rows)
160+
161+ -- Check that (A except B) and (B except A) is empty, which means the indexes
162+ -- are the same.
163+ SELECT * FROM brin_page_items(get_raw_page('brin_test_parallel_idx', 2), 'brin_test_parallel_idx')
164+ EXCEPT
165+ SELECT * FROM brin_page_items(get_raw_page('brin_test_serial_idx', 2), 'brin_test_serial_idx');
166+ itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | empty | value
167+ ------------+--------+--------+----------+----------+-------------+-------+-------
168+ (0 rows)
169+
170+ SELECT * FROM brin_page_items(get_raw_page('brin_test_serial_idx', 2), 'brin_test_serial_idx')
171+ EXCEPT
172+ SELECT * FROM brin_page_items(get_raw_page('brin_test_parallel_idx', 2), 'brin_test_parallel_idx');
173+ itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | empty | value
174+ ------------+--------+--------+----------+----------+-------------+-------+-------
175+ (0 rows)
176+
177+ DROP INDEX brin_test_parallel_idx;
178+ -- force parallel build, but don't allow starting parallel workers to force
179+ -- fallback to serial build, and repeat the checks
180+ SET max_parallel_workers = 0;
181+ CREATE INDEX brin_test_parallel_idx ON brin_parallel_test
182+ USING brin (a int4_minmax_ops, a int4_bloom_ops, b, c int8_minmax_multi_ops)
183+ WITH (pages_per_range=7);
184+ SELECT relname, relpages
185+ FROM pg_class
186+ WHERE relname IN ('brin_test_serial_idx', 'brin_test_parallel_idx')
187+ ORDER BY relname;
188+ relname | relpages
189+ ------------------------+----------
190+ brin_test_parallel_idx | 3
191+ brin_test_serial_idx | 3
192+ (2 rows)
193+
194+ SELECT * FROM brin_page_items(get_raw_page('brin_test_parallel_idx', 2), 'brin_test_parallel_idx')
195+ EXCEPT
196+ SELECT * FROM brin_page_items(get_raw_page('brin_test_serial_idx', 2), 'brin_test_serial_idx');
197+ itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | empty | value
198+ ------------+--------+--------+----------+----------+-------------+-------+-------
199+ (0 rows)
200+
201+ SELECT * FROM brin_page_items(get_raw_page('brin_test_serial_idx', 2), 'brin_test_serial_idx')
202+ EXCEPT
203+ SELECT * FROM brin_page_items(get_raw_page('brin_test_parallel_idx', 2), 'brin_test_parallel_idx');
204+ itemoffset | blknum | attnum | allnulls | hasnulls | placeholder | empty | value
205+ ------------+--------+--------+----------+----------+-------------+-------+-------
206+ (0 rows)
207+
208+ DROP TABLE brin_parallel_test;
209+ RESET min_parallel_table_scan_size;
210+ RESET max_parallel_maintenance_workers;
211+ RESET maintenance_work_mem;
0 commit comments