1--
2-- CREATE_INDEX
3-- Create ancillary data structures (i.e. indices)
4--
5
6--
7-- BTREE
8--
9CREATE INDEX onek_unique1 ON onek USING btree(unique1 int4_ops);
10
11CREATE INDEX IF NOT EXISTS onek_unique1 ON onek USING btree(unique1 int4_ops);
12
13CREATE INDEX IF NOT EXISTS ON onek USING btree(unique1 int4_ops);
14
15CREATE INDEX onek_unique2 ON onek USING btree(unique2 int4_ops);
16
17CREATE INDEX onek_hundred ON onek USING btree(hundred int4_ops);
18
19CREATE INDEX onek_stringu1 ON onek USING btree(stringu1 name_ops);
20
21CREATE INDEX tenk1_unique1 ON tenk1 USING btree(unique1 int4_ops);
22
23CREATE INDEX tenk1_unique2 ON tenk1 USING btree(unique2 int4_ops);
24
25CREATE INDEX tenk1_hundred ON tenk1 USING btree(hundred int4_ops);
26
27CREATE INDEX tenk1_thous_tenthous ON tenk1 (thousand, tenthous);
28
29CREATE INDEX tenk2_unique1 ON tenk2 USING btree(unique1 int4_ops);
30
31CREATE INDEX tenk2_unique2 ON tenk2 USING btree(unique2 int4_ops);
32
33CREATE INDEX tenk2_hundred ON tenk2 USING btree(hundred int4_ops);
34
35CREATE INDEX rix ON road USING btree (name text_ops);
36
37CREATE INDEX iix ON ihighway USING btree (name text_ops);
38
39CREATE INDEX six ON shighway USING btree (name text_ops);
40
41-- test comments
42COMMENT ON INDEX six_wrong IS 'bad index';
43COMMENT ON INDEX six IS 'good index';
44COMMENT ON INDEX six IS NULL;
45
46--
47-- BTREE ascending/descending cases
48--
49-- we load int4/text from pure descending data (each key is a new
50-- low key) and name/f8 from pure ascending data (each key is a new
51-- high key).  we had a bug where new low keys would sometimes be
52-- "lost".
53--
54CREATE INDEX bt_i4_index ON bt_i4_heap USING btree (seqno int4_ops);
55
56CREATE INDEX bt_name_index ON bt_name_heap USING btree (seqno name_ops);
57
58CREATE INDEX bt_txt_index ON bt_txt_heap USING btree (seqno text_ops);
59
60CREATE INDEX bt_f8_index ON bt_f8_heap USING btree (seqno float8_ops);
61
62--
63-- BTREE partial indices
64--
65CREATE INDEX onek2_u1_prtl ON onek2 USING btree(unique1 int4_ops)
66	where unique1 < 20 or unique1 > 980;
67
68CREATE INDEX onek2_u2_prtl ON onek2 USING btree(unique2 int4_ops)
69	where stringu1 < 'B';
70
71CREATE INDEX onek2_stu1_prtl ON onek2 USING btree(stringu1 name_ops)
72	where onek2.stringu1 >= 'J' and onek2.stringu1 < 'K';
73
74--
75-- GiST (rtree-equivalent opclasses only)
76--
77CREATE INDEX grect2ind ON fast_emp4000 USING gist (home_base);
78
79CREATE INDEX gpolygonind ON polygon_tbl USING gist (f1);
80
81CREATE INDEX gcircleind ON circle_tbl USING gist (f1);
82
83INSERT INTO POINT_TBL(f1) VALUES (NULL);
84
85CREATE INDEX gpointind ON point_tbl USING gist (f1);
86
87CREATE TEMP TABLE gpolygon_tbl AS
88    SELECT polygon(home_base) AS f1 FROM slow_emp4000;
89INSERT INTO gpolygon_tbl VALUES ( '(1000,0,0,1000)' );
90INSERT INTO gpolygon_tbl VALUES ( '(0,1000,1000,1000)' );
91
92CREATE TEMP TABLE gcircle_tbl AS
93    SELECT circle(home_base) AS f1 FROM slow_emp4000;
94
95CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1);
96
97CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1);
98
99--
100-- Test GiST indexes
101--
102
103-- get non-indexed results for comparison purposes
104
105SET enable_seqscan = ON;
106SET enable_indexscan = OFF;
107SET enable_bitmapscan = OFF;
108
109SELECT * FROM fast_emp4000
110    WHERE home_base @ '(200,200),(2000,1000)'::box
111    ORDER BY (home_base[0])[0];
112
113SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
114
115SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
116
117SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
118    ORDER BY (poly_center(f1))[0];
119
120SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1)
121    ORDER BY area(f1);
122
123SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon;
124
125SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle;
126
127SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
128
129SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
130
131SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)';
132
133SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
134
135SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
136
137SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
138
139SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
140
141SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
142
143SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
144
145SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
146
147SELECT * FROM point_tbl WHERE f1 IS NULL;
148
149SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
150
151SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
152
153SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10;
154
155SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10;
156
157-- Now check the results from plain indexscan
158SET enable_seqscan = OFF;
159SET enable_indexscan = ON;
160SET enable_bitmapscan = OFF;
161
162EXPLAIN (COSTS OFF)
163SELECT * FROM fast_emp4000
164    WHERE home_base @ '(200,200),(2000,1000)'::box
165    ORDER BY (home_base[0])[0];
166SELECT * FROM fast_emp4000
167    WHERE home_base @ '(200,200),(2000,1000)'::box
168    ORDER BY (home_base[0])[0];
169
170EXPLAIN (COSTS OFF)
171SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
172SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box;
173
174EXPLAIN (COSTS OFF)
175SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
176SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL;
177
178EXPLAIN (COSTS OFF)
179SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
180    ORDER BY (poly_center(f1))[0];
181SELECT * FROM polygon_tbl WHERE f1 ~ '((1,1),(2,2),(2,1))'::polygon
182    ORDER BY (poly_center(f1))[0];
183
184EXPLAIN (COSTS OFF)
185SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1)
186    ORDER BY area(f1);
187SELECT * FROM circle_tbl WHERE f1 && circle(point(1,-2), 1)
188    ORDER BY area(f1);
189
190EXPLAIN (COSTS OFF)
191SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon;
192SELECT count(*) FROM gpolygon_tbl WHERE f1 && '(1000,1000,0,0)'::polygon;
193
194EXPLAIN (COSTS OFF)
195SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle;
196SELECT count(*) FROM gcircle_tbl WHERE f1 && '<(500,500),500>'::circle;
197
198EXPLAIN (COSTS OFF)
199SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
200SELECT count(*) FROM point_tbl WHERE f1 <@ box '(0,0,100,100)';
201
202EXPLAIN (COSTS OFF)
203SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
204SELECT count(*) FROM point_tbl WHERE box '(0,0,100,100)' @> f1;
205
206EXPLAIN (COSTS OFF)
207SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)';
208SELECT count(*) FROM point_tbl WHERE f1 <@ polygon '(0,0),(0,100),(100,100),(50,50),(100,0),(0,0)';
209
210EXPLAIN (COSTS OFF)
211SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
212SELECT count(*) FROM point_tbl WHERE f1 <@ circle '<(50,50),50>';
213
214EXPLAIN (COSTS OFF)
215SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
216SELECT count(*) FROM point_tbl p WHERE p.f1 << '(0.0, 0.0)';
217
218EXPLAIN (COSTS OFF)
219SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
220SELECT count(*) FROM point_tbl p WHERE p.f1 >> '(0.0, 0.0)';
221
222EXPLAIN (COSTS OFF)
223SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
224SELECT count(*) FROM point_tbl p WHERE p.f1 <^ '(0.0, 0.0)';
225
226EXPLAIN (COSTS OFF)
227SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
228SELECT count(*) FROM point_tbl p WHERE p.f1 >^ '(0.0, 0.0)';
229
230EXPLAIN (COSTS OFF)
231SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
232SELECT count(*) FROM point_tbl p WHERE p.f1 ~= '(-5, -12)';
233
234EXPLAIN (COSTS OFF)
235SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
236SELECT * FROM point_tbl ORDER BY f1 <-> '0,1';
237
238EXPLAIN (COSTS OFF)
239SELECT * FROM point_tbl WHERE f1 IS NULL;
240SELECT * FROM point_tbl WHERE f1 IS NULL;
241
242EXPLAIN (COSTS OFF)
243SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
244SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1';
245
246EXPLAIN (COSTS OFF)
247SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
248SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
249
250EXPLAIN (COSTS OFF)
251SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10;
252SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10;
253
254EXPLAIN (COSTS OFF)
255SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10;
256SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10;
257
258-- Now check the results from bitmap indexscan
259SET enable_seqscan = OFF;
260SET enable_indexscan = OFF;
261SET enable_bitmapscan = ON;
262
263EXPLAIN (COSTS OFF)
264SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
265SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1';
266
267RESET enable_seqscan;
268RESET enable_indexscan;
269RESET enable_bitmapscan;
270
271--
272-- GIN over int[] and text[]
273--
274-- Note: GIN currently supports only bitmap scans, not plain indexscans
275--
276
277SET enable_seqscan = OFF;
278SET enable_indexscan = OFF;
279SET enable_bitmapscan = ON;
280
281CREATE INDEX intarrayidx ON array_index_op_test USING gin (i);
282
283explain (costs off)
284SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
285
286SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
287SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
288SELECT * FROM array_index_op_test WHERE i @> '{17}' ORDER BY seqno;
289SELECT * FROM array_index_op_test WHERE i && '{17}' ORDER BY seqno;
290SELECT * FROM array_index_op_test WHERE i @> '{32,17}' ORDER BY seqno;
291SELECT * FROM array_index_op_test WHERE i && '{32,17}' ORDER BY seqno;
292SELECT * FROM array_index_op_test WHERE i <@ '{38,34,32,89}' ORDER BY seqno;
293SELECT * FROM array_index_op_test WHERE i = '{47,77}' ORDER BY seqno;
294SELECT * FROM array_index_op_test WHERE i = '{}' ORDER BY seqno;
295SELECT * FROM array_index_op_test WHERE i @> '{}' ORDER BY seqno;
296SELECT * FROM array_index_op_test WHERE i && '{}' ORDER BY seqno;
297SELECT * FROM array_index_op_test WHERE i <@ '{}' ORDER BY seqno;
298SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
299SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
300SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
301SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
302
303CREATE INDEX textarrayidx ON array_index_op_test USING gin (t);
304
305explain (costs off)
306SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
307
308SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
309SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
310SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
311SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
312SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
313SELECT * FROM array_index_op_test WHERE t && '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
314SELECT * FROM array_index_op_test WHERE t <@ '{AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}' ORDER BY seqno;
315SELECT * FROM array_index_op_test WHERE t = '{AAAAAAAAAA646,A87088}' ORDER BY seqno;
316SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
317SELECT * FROM array_index_op_test WHERE t @> '{}' ORDER BY seqno;
318SELECT * FROM array_index_op_test WHERE t && '{}' ORDER BY seqno;
319SELECT * FROM array_index_op_test WHERE t <@ '{}' ORDER BY seqno;
320
321-- And try it with a multicolumn GIN index
322
323DROP INDEX intarrayidx, textarrayidx;
324
325CREATE INDEX botharrayidx ON array_index_op_test USING gin (i, t);
326
327SELECT * FROM array_index_op_test WHERE i @> '{32}' ORDER BY seqno;
328SELECT * FROM array_index_op_test WHERE i && '{32}' ORDER BY seqno;
329SELECT * FROM array_index_op_test WHERE t @> '{AAAAAAA80240}' ORDER BY seqno;
330SELECT * FROM array_index_op_test WHERE t && '{AAAAAAA80240}' ORDER BY seqno;
331SELECT * FROM array_index_op_test WHERE i @> '{32}' AND t && '{AAAAAAA80240}' ORDER BY seqno;
332SELECT * FROM array_index_op_test WHERE i && '{32}' AND t @> '{AAAAAAA80240}' ORDER BY seqno;
333SELECT * FROM array_index_op_test WHERE t = '{}' ORDER BY seqno;
334SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
335SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
336
337RESET enable_seqscan;
338RESET enable_indexscan;
339RESET enable_bitmapscan;
340
341--
342-- Try a GIN index with a lot of items with same key. (GIN creates a posting
343-- tree when there are enough duplicates)
344--
345CREATE TABLE array_gin_test (a int[]);
346
347INSERT INTO array_gin_test SELECT ARRAY[1, g%5, g] FROM generate_series(1, 10000) g;
348
349CREATE INDEX array_gin_test_idx ON array_gin_test USING gin (a);
350
351SELECT COUNT(*) FROM array_gin_test WHERE a @> '{2}';
352
353DROP TABLE array_gin_test;
354
355--
356-- Test GIN index's reloptions
357--
358CREATE INDEX gin_relopts_test ON array_index_op_test USING gin (i)
359  WITH (FASTUPDATE=on, GIN_PENDING_LIST_LIMIT=128);
360\d+ gin_relopts_test
361
362--
363-- HASH
364--
365CREATE INDEX hash_i4_index ON hash_i4_heap USING hash (random int4_ops);
366
367CREATE INDEX hash_name_index ON hash_name_heap USING hash (random name_ops);
368
369CREATE INDEX hash_txt_index ON hash_txt_heap USING hash (random text_ops);
370
371CREATE INDEX hash_f8_index ON hash_f8_heap USING hash (random float8_ops) WITH (fillfactor=60);
372
373CREATE UNLOGGED TABLE unlogged_hash_table (id int4);
374CREATE INDEX unlogged_hash_index ON unlogged_hash_table USING hash (id int4_ops);
375DROP TABLE unlogged_hash_table;
376
377-- CREATE INDEX hash_ovfl_index ON hash_ovfl_heap USING hash (x int4_ops);
378
379-- Test hash index build tuplesorting.  Force hash tuplesort using low
380-- maintenance_work_mem setting and fillfactor:
381SET maintenance_work_mem = '1MB';
382CREATE INDEX hash_tuplesort_idx ON tenk1 USING hash (stringu1 name_ops) WITH (fillfactor = 10);
383EXPLAIN (COSTS OFF)
384SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA';
385SELECT count(*) FROM tenk1 WHERE stringu1 = 'TVAAAA';
386DROP INDEX hash_tuplesort_idx;
387RESET maintenance_work_mem;
388
389
390--
391-- Test functional index
392--
393CREATE TABLE func_index_heap (f1 text, f2 text);
394CREATE UNIQUE INDEX func_index_index on func_index_heap (textcat(f1,f2));
395
396INSERT INTO func_index_heap VALUES('ABC','DEF');
397INSERT INTO func_index_heap VALUES('AB','CDEFG');
398INSERT INTO func_index_heap VALUES('QWE','RTY');
399-- this should fail because of unique index:
400INSERT INTO func_index_heap VALUES('ABCD', 'EF');
401-- but this shouldn't:
402INSERT INTO func_index_heap VALUES('QWERTY');
403
404-- while we're here, see that the metadata looks sane
405\d func_index_heap
406\d func_index_index
407
408
409--
410-- Same test, expressional index
411--
412DROP TABLE func_index_heap;
413CREATE TABLE func_index_heap (f1 text, f2 text);
414CREATE UNIQUE INDEX func_index_index on func_index_heap ((f1 || f2) text_ops);
415
416INSERT INTO func_index_heap VALUES('ABC','DEF');
417INSERT INTO func_index_heap VALUES('AB','CDEFG');
418INSERT INTO func_index_heap VALUES('QWE','RTY');
419-- this should fail because of unique index:
420INSERT INTO func_index_heap VALUES('ABCD', 'EF');
421-- but this shouldn't:
422INSERT INTO func_index_heap VALUES('QWERTY');
423
424-- while we're here, see that the metadata looks sane
425\d func_index_heap
426\d func_index_index
427
428-- this should fail because of unsafe column type (anonymous record)
429create index on func_index_heap ((f1 || f2), (row(f1, f2)));
430
431
432--
433-- Test unique index with included columns
434--
435CREATE TABLE covering_index_heap (f1 int, f2 int, f3 text);
436CREATE UNIQUE INDEX covering_index_index on covering_index_heap (f1,f2) INCLUDE(f3);
437
438INSERT INTO covering_index_heap VALUES(1,1,'AAA');
439INSERT INTO covering_index_heap VALUES(1,2,'AAA');
440-- this should fail because of unique index on f1,f2:
441INSERT INTO covering_index_heap VALUES(1,2,'BBB');
442-- and this shouldn't:
443INSERT INTO covering_index_heap VALUES(1,4,'AAA');
444-- Try to build index on table that already contains data
445CREATE UNIQUE INDEX covering_pkey on covering_index_heap (f1,f2) INCLUDE(f3);
446-- Try to use existing covering index as primary key
447ALTER TABLE covering_index_heap ADD CONSTRAINT covering_pkey PRIMARY KEY USING INDEX
448covering_pkey;
449DROP TABLE covering_index_heap;
450
451
452--
453-- Also try building functional, expressional, and partial indexes on
454-- tables that already contain data.
455--
456create unique index hash_f8_index_1 on hash_f8_heap(abs(random));
457create unique index hash_f8_index_2 on hash_f8_heap((seqno + 1), random);
458create unique index hash_f8_index_3 on hash_f8_heap(random) where seqno > 1000;
459
460--
461-- Try some concurrent index builds
462--
463-- Unfortunately this only tests about half the code paths because there are
464-- no concurrent updates happening to the table at the same time.
465
466CREATE TABLE concur_heap (f1 text, f2 text);
467-- empty table
468CREATE INDEX CONCURRENTLY concur_index1 ON concur_heap(f2,f1);
469CREATE INDEX CONCURRENTLY IF NOT EXISTS concur_index1 ON concur_heap(f2,f1);
470INSERT INTO concur_heap VALUES  ('a','b');
471INSERT INTO concur_heap VALUES  ('b','b');
472-- unique index
473CREATE UNIQUE INDEX CONCURRENTLY concur_index2 ON concur_heap(f1);
474CREATE UNIQUE INDEX CONCURRENTLY IF NOT EXISTS concur_index2 ON concur_heap(f1);
475-- check if constraint is set up properly to be enforced
476INSERT INTO concur_heap VALUES ('b','x');
477-- check if constraint is enforced properly at build time
478CREATE UNIQUE INDEX CONCURRENTLY concur_index3 ON concur_heap(f2);
479-- test that expression indexes and partial indexes work concurrently
480CREATE INDEX CONCURRENTLY concur_index4 on concur_heap(f2) WHERE f1='a';
481CREATE INDEX CONCURRENTLY concur_index5 on concur_heap(f2) WHERE f1='x';
482-- here we also check that you can default the index name
483CREATE INDEX CONCURRENTLY on concur_heap((f2||f1));
484-- You can't do a concurrent index build in a transaction
485BEGIN;
486CREATE INDEX CONCURRENTLY concur_index7 ON concur_heap(f1);
487COMMIT;
488-- test where predicate is able to do a transactional update during
489-- a concurrent build before switching pg_index state flags.
490CREATE FUNCTION predicate_stable() RETURNS bool IMMUTABLE
491LANGUAGE plpgsql AS $$
492BEGIN
493  EXECUTE 'SELECT txid_current()';
494  RETURN true;
495END; $$;
496CREATE INDEX CONCURRENTLY concur_index8 ON concur_heap (f1)
497  WHERE predicate_stable();
498DROP INDEX concur_index8;
499DROP FUNCTION predicate_stable();
500
501-- But you can do a regular index build in a transaction
502BEGIN;
503CREATE INDEX std_index on concur_heap(f2);
504COMMIT;
505
506-- Failed builds are left invalid by VACUUM FULL, fixed by REINDEX
507VACUUM FULL concur_heap;
508REINDEX TABLE concur_heap;
509DELETE FROM concur_heap WHERE f1 = 'b';
510VACUUM FULL concur_heap;
511\d concur_heap
512REINDEX TABLE concur_heap;
513\d concur_heap
514
515-- Temporary tables with concurrent builds and on-commit actions
516-- CONCURRENTLY used with CREATE INDEX and DROP INDEX is ignored.
517-- PRESERVE ROWS, the default.
518CREATE TEMP TABLE concur_temp (f1 int, f2 text)
519  ON COMMIT PRESERVE ROWS;
520INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
521CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
522DROP INDEX CONCURRENTLY concur_temp_ind;
523DROP TABLE concur_temp;
524-- ON COMMIT DROP
525BEGIN;
526CREATE TEMP TABLE concur_temp (f1 int, f2 text)
527  ON COMMIT DROP;
528INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
529-- Fails when running in a transaction.
530CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
531COMMIT;
532-- ON COMMIT DELETE ROWS
533CREATE TEMP TABLE concur_temp (f1 int, f2 text)
534  ON COMMIT DELETE ROWS;
535INSERT INTO concur_temp VALUES (1, 'foo'), (2, 'bar');
536CREATE INDEX CONCURRENTLY concur_temp_ind ON concur_temp(f1);
537DROP INDEX CONCURRENTLY concur_temp_ind;
538DROP TABLE concur_temp;
539
540--
541-- Try some concurrent index drops
542--
543DROP INDEX CONCURRENTLY "concur_index2";				-- works
544DROP INDEX CONCURRENTLY IF EXISTS "concur_index2";		-- notice
545
546-- failures
547DROP INDEX CONCURRENTLY "concur_index2", "concur_index3";
548BEGIN;
549DROP INDEX CONCURRENTLY "concur_index5";
550ROLLBACK;
551
552-- successes
553DROP INDEX CONCURRENTLY IF EXISTS "concur_index3";
554DROP INDEX CONCURRENTLY "concur_index4";
555DROP INDEX CONCURRENTLY "concur_index5";
556DROP INDEX CONCURRENTLY "concur_index1";
557DROP INDEX CONCURRENTLY "concur_heap_expr_idx";
558
559\d concur_heap
560
561DROP TABLE concur_heap;
562
563--
564-- Test ADD CONSTRAINT USING INDEX
565--
566
567CREATE TABLE cwi_test( a int , b varchar(10), c char);
568
569-- add some data so that all tests have something to work with.
570
571INSERT INTO cwi_test VALUES(1, 2), (3, 4), (5, 6);
572
573CREATE UNIQUE INDEX cwi_uniq_idx ON cwi_test(a , b);
574ALTER TABLE cwi_test ADD primary key USING INDEX cwi_uniq_idx;
575
576\d cwi_test
577\d cwi_uniq_idx
578
579CREATE UNIQUE INDEX cwi_uniq2_idx ON cwi_test(b , a);
580ALTER TABLE cwi_test DROP CONSTRAINT cwi_uniq_idx,
581	ADD CONSTRAINT cwi_replaced_pkey PRIMARY KEY
582		USING INDEX cwi_uniq2_idx;
583
584\d cwi_test
585\d cwi_replaced_pkey
586
587DROP INDEX cwi_replaced_pkey;	-- Should fail; a constraint depends on it
588
589DROP TABLE cwi_test;
590
591-- ADD CONSTRAINT USING INDEX is forbidden on partitioned tables
592CREATE TABLE cwi_test(a int) PARTITION BY hash (a);
593create unique index on cwi_test (a);
594alter table cwi_test add primary key using index cwi_test_a_idx ;
595DROP TABLE cwi_test;
596
597--
598-- Check handling of indexes on system columns
599--
600CREATE TABLE syscol_table (a INT);
601
602-- System columns cannot be indexed
603CREATE INDEX ON syscolcol_table (ctid);
604
605-- nor used in expressions
606CREATE INDEX ON syscol_table ((ctid >= '(1000,0)'));
607
608-- nor used in predicates
609CREATE INDEX ON syscol_table (a) WHERE ctid >= '(1000,0)';
610
611DROP TABLE syscol_table;
612
613--
614-- Tests for IS NULL/IS NOT NULL with b-tree indexes
615--
616
617SELECT unique1, unique2 INTO onek_with_null FROM onek;
618INSERT INTO onek_with_null (unique1,unique2) VALUES (NULL, -1), (NULL, NULL);
619CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2,unique1);
620
621SET enable_seqscan = OFF;
622SET enable_indexscan = ON;
623SET enable_bitmapscan = ON;
624
625SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
626SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
627SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
628SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
629SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
630SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
631
632DROP INDEX onek_nulltest;
633
634CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc,unique1);
635
636SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
637SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
638SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
639SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
640SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
641SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
642
643DROP INDEX onek_nulltest;
644
645CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2 desc nulls last,unique1);
646
647SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
648SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
649SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
650SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
651SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
652SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
653
654DROP INDEX onek_nulltest;
655
656CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2  nulls first,unique1);
657
658SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL;
659SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NULL;
660SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL;
661SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique2 IS NOT NULL;
662SELECT count(*) FROM onek_with_null WHERE unique1 IS NOT NULL AND unique1 > 500;
663SELECT count(*) FROM onek_with_null WHERE unique1 IS NULL AND unique1 > 500;
664
665DROP INDEX onek_nulltest;
666
667-- Check initial-positioning logic too
668
669CREATE UNIQUE INDEX onek_nulltest ON onek_with_null (unique2);
670
671SET enable_seqscan = OFF;
672SET enable_indexscan = ON;
673SET enable_bitmapscan = OFF;
674
675SELECT unique1, unique2 FROM onek_with_null
676  ORDER BY unique2 LIMIT 2;
677SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1
678  ORDER BY unique2 LIMIT 2;
679SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= 0
680  ORDER BY unique2 LIMIT 2;
681
682SELECT unique1, unique2 FROM onek_with_null
683  ORDER BY unique2 DESC LIMIT 2;
684SELECT unique1, unique2 FROM onek_with_null WHERE unique2 >= -1
685  ORDER BY unique2 DESC LIMIT 2;
686SELECT unique1, unique2 FROM onek_with_null WHERE unique2 < 999
687  ORDER BY unique2 DESC LIMIT 2;
688
689RESET enable_seqscan;
690RESET enable_indexscan;
691RESET enable_bitmapscan;
692
693DROP TABLE onek_with_null;
694
695--
696-- Check bitmap index path planning
697--
698
699EXPLAIN (COSTS OFF)
700SELECT * FROM tenk1
701  WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
702SELECT * FROM tenk1
703  WHERE thousand = 42 AND (tenthous = 1 OR tenthous = 3 OR tenthous = 42);
704
705EXPLAIN (COSTS OFF)
706SELECT count(*) FROM tenk1
707  WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
708SELECT count(*) FROM tenk1
709  WHERE hundred = 42 AND (thousand = 42 OR thousand = 99);
710
711--
712-- Check behavior with duplicate index column contents
713--
714
715CREATE TABLE dupindexcols AS
716  SELECT unique1 as id, stringu2::text as f1 FROM tenk1;
717CREATE INDEX dupindexcols_i ON dupindexcols (f1, id, f1 text_pattern_ops);
718ANALYZE dupindexcols;
719
720EXPLAIN (COSTS OFF)
721  SELECT count(*) FROM dupindexcols
722    WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX';
723SELECT count(*) FROM dupindexcols
724  WHERE f1 BETWEEN 'WA' AND 'ZZZ' and id < 1000 and f1 ~<~ 'YX';
725
726--
727-- Check ordering of =ANY indexqual results (bug in 9.2.0)
728--
729
730vacuum tenk1;		-- ensure we get consistent plans here
731
732explain (costs off)
733SELECT unique1 FROM tenk1
734WHERE unique1 IN (1,42,7)
735ORDER BY unique1;
736
737SELECT unique1 FROM tenk1
738WHERE unique1 IN (1,42,7)
739ORDER BY unique1;
740
741explain (costs off)
742SELECT thousand, tenthous FROM tenk1
743WHERE thousand < 2 AND tenthous IN (1001,3000)
744ORDER BY thousand;
745
746SELECT thousand, tenthous FROM tenk1
747WHERE thousand < 2 AND tenthous IN (1001,3000)
748ORDER BY thousand;
749
750SET enable_indexonlyscan = OFF;
751
752explain (costs off)
753SELECT thousand, tenthous FROM tenk1
754WHERE thousand < 2 AND tenthous IN (1001,3000)
755ORDER BY thousand;
756
757SELECT thousand, tenthous FROM tenk1
758WHERE thousand < 2 AND tenthous IN (1001,3000)
759ORDER BY thousand;
760
761RESET enable_indexonlyscan;
762
763--
764-- Check elimination of constant-NULL subexpressions
765--
766
767explain (costs off)
768  select * from tenk1 where (thousand, tenthous) in ((1,1001), (null,null));
769
770--
771-- Check matching of boolean index columns to WHERE conditions and sort keys
772--
773
774create temp table boolindex (b bool, i int, unique(b, i), junk float);
775
776explain (costs off)
777  select * from boolindex order by b, i limit 10;
778explain (costs off)
779  select * from boolindex where b order by i limit 10;
780explain (costs off)
781  select * from boolindex where b = true order by i desc limit 10;
782explain (costs off)
783  select * from boolindex where not b order by i limit 10;
784explain (costs off)
785  select * from boolindex where b is true order by i desc limit 10;
786explain (costs off)
787  select * from boolindex where b is false order by i desc limit 10;
788
789--
790-- REINDEX (VERBOSE)
791--
792CREATE TABLE reindex_verbose(id integer primary key);
793\set VERBOSITY terse \\ -- suppress machine-dependent details
794REINDEX (VERBOSE) TABLE reindex_verbose;
795\set VERBOSITY default
796DROP TABLE reindex_verbose;
797
798--
799-- REINDEX CONCURRENTLY
800--
801CREATE TABLE concur_reindex_tab (c1 int);
802-- REINDEX
803REINDEX TABLE concur_reindex_tab; -- notice
804REINDEX TABLE CONCURRENTLY concur_reindex_tab; -- notice
805ALTER TABLE concur_reindex_tab ADD COLUMN c2 text; -- add toast index
806-- Normal index with integer column
807CREATE UNIQUE INDEX concur_reindex_ind1 ON concur_reindex_tab(c1);
808-- Normal index with text column
809CREATE INDEX concur_reindex_ind2 ON concur_reindex_tab(c2);
810-- UNIQUE index with expression
811CREATE UNIQUE INDEX concur_reindex_ind3 ON concur_reindex_tab(abs(c1));
812-- Duplicate column names
813CREATE INDEX concur_reindex_ind4 ON concur_reindex_tab(c1, c1, c2);
814-- Create table for check on foreign key dependence switch with indexes swapped
815ALTER TABLE concur_reindex_tab ADD PRIMARY KEY USING INDEX concur_reindex_ind1;
816CREATE TABLE concur_reindex_tab2 (c1 int REFERENCES concur_reindex_tab);
817INSERT INTO concur_reindex_tab VALUES  (1, 'a');
818INSERT INTO concur_reindex_tab VALUES  (2, 'a');
819-- Reindex concurrently of exclusion constraint currently not supported
820CREATE TABLE concur_reindex_tab3 (c1 int, c2 int4range, EXCLUDE USING gist (c2 WITH &&));
821INSERT INTO concur_reindex_tab3 VALUES  (3, '[1,2]');
822REINDEX INDEX CONCURRENTLY  concur_reindex_tab3_c2_excl;  -- error
823REINDEX TABLE CONCURRENTLY concur_reindex_tab3;  -- succeeds with warning
824INSERT INTO concur_reindex_tab3 VALUES  (4, '[2,4]');
825-- Check materialized views
826CREATE MATERIALIZED VIEW concur_reindex_matview AS SELECT * FROM concur_reindex_tab;
827-- Dependency lookup before and after the follow-up REINDEX commands.
828-- These should remain consistent.
829SELECT pg_describe_object(classid, objid, objsubid) as obj,
830       pg_describe_object(refclassid,refobjid,refobjsubid) as objref,
831       deptype
832FROM pg_depend
833WHERE classid = 'pg_class'::regclass AND
834  objid in ('concur_reindex_tab'::regclass,
835            'concur_reindex_ind1'::regclass,
836	    'concur_reindex_ind2'::regclass,
837	    'concur_reindex_ind3'::regclass,
838	    'concur_reindex_ind4'::regclass,
839	    'concur_reindex_matview'::regclass)
840  ORDER BY 1, 2;
841REINDEX INDEX CONCURRENTLY concur_reindex_ind1;
842REINDEX TABLE CONCURRENTLY concur_reindex_tab;
843REINDEX TABLE CONCURRENTLY concur_reindex_matview;
844SELECT pg_describe_object(classid, objid, objsubid) as obj,
845       pg_describe_object(refclassid,refobjid,refobjsubid) as objref,
846       deptype
847FROM pg_depend
848WHERE classid = 'pg_class'::regclass AND
849  objid in ('concur_reindex_tab'::regclass,
850            'concur_reindex_ind1'::regclass,
851	    'concur_reindex_ind2'::regclass,
852	    'concur_reindex_ind3'::regclass,
853	    'concur_reindex_ind4'::regclass,
854	    'concur_reindex_matview'::regclass)
855  ORDER BY 1, 2;
856-- Check that comments are preserved
857CREATE TABLE testcomment (i int);
858CREATE INDEX testcomment_idx1 ON testcomment (i);
859COMMENT ON INDEX testcomment_idx1 IS 'test comment';
860SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
861REINDEX TABLE testcomment;
862SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
863REINDEX TABLE CONCURRENTLY testcomment ;
864SELECT obj_description('testcomment_idx1'::regclass, 'pg_class');
865DROP TABLE testcomment;
866-- Check that indisclustered updates are preserved
867CREATE TABLE concur_clustered(i int);
868CREATE INDEX concur_clustered_i_idx ON concur_clustered(i);
869ALTER TABLE concur_clustered CLUSTER ON concur_clustered_i_idx;
870REINDEX TABLE CONCURRENTLY concur_clustered;
871SELECT indexrelid::regclass, indisclustered FROM pg_index
872  WHERE indrelid = 'concur_clustered'::regclass;
873DROP TABLE concur_clustered;
874-- Check that indisreplident updates are preserved.
875CREATE TABLE concur_replident(i int NOT NULL);
876CREATE UNIQUE INDEX concur_replident_i_idx ON concur_replident(i);
877ALTER TABLE concur_replident REPLICA IDENTITY
878  USING INDEX concur_replident_i_idx;
879SELECT indexrelid::regclass, indisreplident FROM pg_index
880  WHERE indrelid = 'concur_replident'::regclass;
881REINDEX TABLE CONCURRENTLY concur_replident;
882SELECT indexrelid::regclass, indisreplident FROM pg_index
883  WHERE indrelid = 'concur_replident'::regclass;
884DROP TABLE concur_replident;
885
886-- Partitions
887-- Create some partitioned tables
888CREATE TABLE concur_reindex_part (c1 int, c2 int) PARTITION BY RANGE (c1);
889CREATE TABLE concur_reindex_part_0 PARTITION OF concur_reindex_part
890  FOR VALUES FROM (0) TO (10) PARTITION BY list (c2);
891CREATE TABLE concur_reindex_part_0_1 PARTITION OF concur_reindex_part_0
892  FOR VALUES IN (1);
893CREATE TABLE concur_reindex_part_0_2 PARTITION OF concur_reindex_part_0
894  FOR VALUES IN (2);
895-- This partitioned table will have no partitions.
896CREATE TABLE concur_reindex_part_10 PARTITION OF concur_reindex_part
897  FOR VALUES FROM (10) TO (20) PARTITION BY list (c2);
898-- Create some partitioned indexes
899CREATE INDEX concur_reindex_part_index ON ONLY concur_reindex_part (c1);
900CREATE INDEX concur_reindex_part_index_0 ON ONLY concur_reindex_part_0 (c1);
901ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_0;
902-- This partitioned index will have no partitions.
903CREATE INDEX concur_reindex_part_index_10 ON ONLY concur_reindex_part_10 (c1);
904ALTER INDEX concur_reindex_part_index ATTACH PARTITION concur_reindex_part_index_10;
905CREATE INDEX concur_reindex_part_index_0_1 ON ONLY concur_reindex_part_0_1 (c1);
906ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_1;
907CREATE INDEX concur_reindex_part_index_0_2 ON ONLY concur_reindex_part_0_2 (c1);
908ALTER INDEX concur_reindex_part_index_0 ATTACH PARTITION concur_reindex_part_index_0_2;
909SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
910  ORDER BY relid, level;
911-- REINDEX fails for partitioned indexes
912REINDEX INDEX concur_reindex_part_index_10;
913REINDEX INDEX CONCURRENTLY concur_reindex_part_index_10;
914-- REINDEX is a no-op for partitioned tables
915REINDEX TABLE concur_reindex_part_10;
916REINDEX TABLE CONCURRENTLY concur_reindex_part_10;
917SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
918  ORDER BY relid, level;
919-- REINDEX should preserve dependencies of partition tree.
920SELECT pg_describe_object(classid, objid, objsubid) as obj,
921       pg_describe_object(refclassid,refobjid,refobjsubid) as objref,
922       deptype
923FROM pg_depend
924WHERE classid = 'pg_class'::regclass AND
925  objid in ('concur_reindex_part'::regclass,
926            'concur_reindex_part_0'::regclass,
927            'concur_reindex_part_0_1'::regclass,
928            'concur_reindex_part_0_2'::regclass,
929            'concur_reindex_part_index'::regclass,
930            'concur_reindex_part_index_0'::regclass,
931            'concur_reindex_part_index_0_1'::regclass,
932            'concur_reindex_part_index_0_2'::regclass)
933  ORDER BY 1, 2;
934REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_1;
935REINDEX INDEX CONCURRENTLY concur_reindex_part_index_0_2;
936SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
937  ORDER BY relid, level;
938REINDEX TABLE CONCURRENTLY concur_reindex_part_0_1;
939REINDEX TABLE CONCURRENTLY concur_reindex_part_0_2;
940SELECT pg_describe_object(classid, objid, objsubid) as obj,
941       pg_describe_object(refclassid,refobjid,refobjsubid) as objref,
942       deptype
943FROM pg_depend
944WHERE classid = 'pg_class'::regclass AND
945  objid in ('concur_reindex_part'::regclass,
946            'concur_reindex_part_0'::regclass,
947            'concur_reindex_part_0_1'::regclass,
948            'concur_reindex_part_0_2'::regclass,
949            'concur_reindex_part_index'::regclass,
950            'concur_reindex_part_index_0'::regclass,
951            'concur_reindex_part_index_0_1'::regclass,
952            'concur_reindex_part_index_0_2'::regclass)
953  ORDER BY 1, 2;
954SELECT relid, parentrelid, level FROM pg_partition_tree('concur_reindex_part_index')
955  ORDER BY relid, level;
956DROP TABLE concur_reindex_part;
957
958-- Check errors
959-- Cannot run inside a transaction block
960BEGIN;
961REINDEX TABLE CONCURRENTLY concur_reindex_tab;
962COMMIT;
963REINDEX TABLE CONCURRENTLY pg_class; -- no catalog relation
964REINDEX INDEX CONCURRENTLY pg_class_oid_index; -- no catalog index
965-- These are the toast table and index of pg_authid.
966REINDEX TABLE CONCURRENTLY pg_toast.pg_toast_1260; -- no catalog toast table
967REINDEX INDEX CONCURRENTLY pg_toast.pg_toast_1260_index; -- no catalog toast index
968REINDEX SYSTEM CONCURRENTLY postgres; -- not allowed for SYSTEM
969-- Warns about catalog relations
970REINDEX SCHEMA CONCURRENTLY pg_catalog;
971
972-- Check the relation status, there should not be invalid indexes
973\d concur_reindex_tab
974DROP MATERIALIZED VIEW concur_reindex_matview;
975DROP TABLE concur_reindex_tab, concur_reindex_tab2, concur_reindex_tab3;
976
977-- Check handling of invalid indexes
978CREATE TABLE concur_reindex_tab4 (c1 int);
979INSERT INTO concur_reindex_tab4 VALUES (1), (1), (2);
980-- This trick creates an invalid index.
981CREATE UNIQUE INDEX CONCURRENTLY concur_reindex_ind5 ON concur_reindex_tab4 (c1);
982-- Reindexing concurrently this index fails with the same failure.
983-- The extra index created is itself invalid, and can be dropped.
984REINDEX INDEX CONCURRENTLY concur_reindex_ind5;
985\d concur_reindex_tab4
986DROP INDEX concur_reindex_ind5_ccnew;
987-- This makes the previous failure go away, so the index can become valid.
988DELETE FROM concur_reindex_tab4 WHERE c1 = 1;
989-- The invalid index is not processed when running REINDEX TABLE.
990REINDEX TABLE CONCURRENTLY concur_reindex_tab4;
991\d concur_reindex_tab4
992-- But it is fixed with REINDEX INDEX.
993REINDEX INDEX CONCURRENTLY concur_reindex_ind5;
994\d concur_reindex_tab4
995DROP TABLE concur_reindex_tab4;
996
997-- Check handling of indexes with expressions and predicates.  The
998-- definitions of the rebuilt indexes should match the original
999-- definitions.
1000CREATE TABLE concur_exprs_tab (c1 int , c2 boolean);
1001INSERT INTO concur_exprs_tab (c1, c2) VALUES (1369652450, FALSE),
1002  (414515746, TRUE),
1003  (897778963, FALSE);
1004CREATE UNIQUE INDEX concur_exprs_index_expr
1005  ON concur_exprs_tab ((c1::text COLLATE "C"));
1006CREATE UNIQUE INDEX concur_exprs_index_pred ON concur_exprs_tab (c1)
1007  WHERE (c1::text > 500000000::text COLLATE "C");
1008CREATE UNIQUE INDEX concur_exprs_index_pred_2
1009  ON concur_exprs_tab ((1 / c1))
1010  WHERE ('-H') >= (c2::TEXT) COLLATE "C";
1011ALTER INDEX concur_exprs_index_expr ALTER COLUMN 1 SET STATISTICS 100;
1012ANALYZE concur_exprs_tab;
1013SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
1014  'concur_exprs_index_expr'::regclass,
1015  'concur_exprs_index_pred'::regclass,
1016  'concur_exprs_index_pred_2'::regclass)
1017  GROUP BY starelid ORDER BY starelid::regclass::text;
1018SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass);
1019SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass);
1020SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass);
1021REINDEX TABLE CONCURRENTLY concur_exprs_tab;
1022SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass);
1023SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass);
1024SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass);
1025-- ALTER TABLE recreates the indexes, which should keep their collations.
1026ALTER TABLE concur_exprs_tab ALTER c2 TYPE TEXT;
1027SELECT pg_get_indexdef('concur_exprs_index_expr'::regclass);
1028SELECT pg_get_indexdef('concur_exprs_index_pred'::regclass);
1029SELECT pg_get_indexdef('concur_exprs_index_pred_2'::regclass);
1030-- Statistics should remain intact.
1031SELECT starelid::regclass, count(*) FROM pg_statistic WHERE starelid IN (
1032  'concur_exprs_index_expr'::regclass,
1033  'concur_exprs_index_pred'::regclass,
1034  'concur_exprs_index_pred_2'::regclass)
1035  GROUP BY starelid ORDER BY starelid::regclass::text;
1036-- attstattarget should remain intact
1037SELECT attrelid::regclass, attnum, attstattarget
1038  FROM pg_attribute WHERE attrelid IN (
1039    'concur_exprs_index_expr'::regclass,
1040    'concur_exprs_index_pred'::regclass,
1041    'concur_exprs_index_pred_2'::regclass)
1042  ORDER BY attrelid::regclass::text, attnum;
1043DROP TABLE concur_exprs_tab;
1044
1045-- Temporary tables and on-commit actions, where CONCURRENTLY is ignored.
1046-- ON COMMIT PRESERVE ROWS, the default.
1047CREATE TEMP TABLE concur_temp_tab_1 (c1 int, c2 text)
1048  ON COMMIT PRESERVE ROWS;
1049INSERT INTO concur_temp_tab_1 VALUES (1, 'foo'), (2, 'bar');
1050CREATE INDEX concur_temp_ind_1 ON concur_temp_tab_1(c2);
1051REINDEX TABLE CONCURRENTLY concur_temp_tab_1;
1052REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
1053-- Still fails in transaction blocks
1054BEGIN;
1055REINDEX INDEX CONCURRENTLY concur_temp_ind_1;
1056COMMIT;
1057-- ON COMMIT DELETE ROWS
1058CREATE TEMP TABLE concur_temp_tab_2 (c1 int, c2 text)
1059  ON COMMIT DELETE ROWS;
1060CREATE INDEX concur_temp_ind_2 ON concur_temp_tab_2(c2);
1061REINDEX TABLE CONCURRENTLY concur_temp_tab_2;
1062REINDEX INDEX CONCURRENTLY concur_temp_ind_2;
1063-- ON COMMIT DROP
1064BEGIN;
1065CREATE TEMP TABLE concur_temp_tab_3 (c1 int, c2 text)
1066  ON COMMIT PRESERVE ROWS;
1067INSERT INTO concur_temp_tab_3 VALUES (1, 'foo'), (2, 'bar');
1068CREATE INDEX concur_temp_ind_3 ON concur_temp_tab_3(c2);
1069-- Fails when running in a transaction
1070REINDEX INDEX CONCURRENTLY concur_temp_ind_3;
1071COMMIT;
1072-- REINDEX SCHEMA processes all temporary relations
1073CREATE TABLE reindex_temp_before AS
1074SELECT oid, relname, relfilenode, relkind, reltoastrelid
1075  FROM pg_class
1076  WHERE relname IN ('concur_temp_ind_1', 'concur_temp_ind_2');
1077SELECT pg_my_temp_schema()::regnamespace as temp_schema_name \gset
1078REINDEX SCHEMA CONCURRENTLY :temp_schema_name;
1079SELECT  b.relname,
1080        b.relkind,
1081        CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged'
1082        ELSE 'relfilenode has changed' END
1083  FROM reindex_temp_before b JOIN pg_class a ON b.oid = a.oid
1084  ORDER BY 1;
1085DROP TABLE concur_temp_tab_1, concur_temp_tab_2, reindex_temp_before;
1086
1087--
1088-- REINDEX SCHEMA
1089--
1090REINDEX SCHEMA schema_to_reindex; -- failure, schema does not exist
1091CREATE SCHEMA schema_to_reindex;
1092SET search_path = 'schema_to_reindex';
1093CREATE TABLE table1(col1 SERIAL PRIMARY KEY);
1094INSERT INTO table1 SELECT generate_series(1,400);
1095CREATE TABLE table2(col1 SERIAL PRIMARY KEY, col2 TEXT NOT NULL);
1096INSERT INTO table2 SELECT generate_series(1,400), 'abc';
1097CREATE INDEX ON table2(col2);
1098CREATE MATERIALIZED VIEW matview AS SELECT col1 FROM table2;
1099CREATE INDEX ON matview(col1);
1100CREATE VIEW view AS SELECT col2 FROM table2;
1101CREATE TABLE reindex_before AS
1102SELECT oid, relname, relfilenode, relkind, reltoastrelid
1103	FROM pg_class
1104	where relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex');
1105INSERT INTO reindex_before
1106SELECT oid, 'pg_toast_TABLE', relfilenode, relkind, reltoastrelid
1107FROM pg_class WHERE oid IN
1108	(SELECT reltoastrelid FROM reindex_before WHERE reltoastrelid > 0);
1109INSERT INTO reindex_before
1110SELECT oid, 'pg_toast_TABLE_index', relfilenode, relkind, reltoastrelid
1111FROM pg_class where oid in
1112	(select indexrelid from pg_index where indrelid in
1113		(select reltoastrelid from reindex_before where reltoastrelid > 0));
1114REINDEX SCHEMA schema_to_reindex;
1115CREATE TABLE reindex_after AS SELECT oid, relname, relfilenode, relkind
1116	FROM pg_class
1117	where relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'schema_to_reindex');
1118SELECT  b.relname,
1119        b.relkind,
1120        CASE WHEN a.relfilenode = b.relfilenode THEN 'relfilenode is unchanged'
1121        ELSE 'relfilenode has changed' END
1122  FROM reindex_before b JOIN pg_class a ON b.oid = a.oid
1123  ORDER BY 1;
1124REINDEX SCHEMA schema_to_reindex;
1125BEGIN;
1126REINDEX SCHEMA schema_to_reindex; -- failure, cannot run in a transaction
1127END;
1128
1129-- concurrently
1130REINDEX SCHEMA CONCURRENTLY schema_to_reindex;
1131
1132-- Failure for unauthorized user
1133CREATE ROLE regress_reindexuser NOLOGIN;
1134SET SESSION ROLE regress_reindexuser;
1135REINDEX SCHEMA schema_to_reindex;
1136-- Permission failures with toast tables and indexes (pg_authid here)
1137RESET ROLE;
1138GRANT USAGE ON SCHEMA pg_toast TO regress_reindexuser;
1139SET SESSION ROLE regress_reindexuser;
1140REINDEX TABLE pg_toast.pg_toast_1260;
1141REINDEX INDEX pg_toast.pg_toast_1260_index;
1142
1143-- Clean up
1144RESET ROLE;
1145REVOKE USAGE ON SCHEMA pg_toast FROM regress_reindexuser;
1146DROP ROLE regress_reindexuser;
1147DROP SCHEMA schema_to_reindex CASCADE;
1148