@@ -108,3 +108,104 @@ SELECT (COUNT(*) = (SELECT relpages FROM pg_class WHERE relname = 'test2')) AS r
108
108
109
109
DROP TABLE test1;
110
110
DROP 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