-- -- Create access method tests -- -- Make gist2 over gisthandler. In fact, it would be a synonym to gist. CREATE ACCESS METHOD gist2 TYPE INDEX HANDLER gisthandler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE INDEX HANDLER int4in; ERROR: function int4in(internal) does not exist CREATE ACCESS METHOD bogus TYPE INDEX HANDLER heap_tableam_handler; ERROR: function heap_tableam_handler must return type index_am_handler -- Try to create gist2 index on fast_emp4000: fail because opclass doesn't exist CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); ERROR: data type box has no default operator class for access method "gist2" HINT: You must specify an operator class for the index or define a default operator class for the data type. -- Make operator class for boxes using gist2 CREATE OPERATOR CLASS box_ops DEFAULT FOR TYPE box USING gist2 AS OPERATOR 1 <<, OPERATOR 2 &<, OPERATOR 3 &&, OPERATOR 4 &>, OPERATOR 5 >>, OPERATOR 6 ~=, OPERATOR 7 @>, OPERATOR 8 <@, OPERATOR 9 &<|, OPERATOR 10 <<|, OPERATOR 11 |>>, OPERATOR 12 |&>, OPERATOR 13 ~, OPERATOR 14 @, FUNCTION 1 gist_box_consistent(internal, box, smallint, oid, internal), FUNCTION 2 gist_box_union(internal, internal), -- don't need compress, decompress, or fetch functions FUNCTION 5 gist_box_penalty(internal, internal, internal), FUNCTION 6 gist_box_picksplit(internal, internal), FUNCTION 7 gist_box_same(box, box, internal); -- Create gist2 index on fast_emp4000 CREATE INDEX grect2ind2 ON fast_emp4000 USING gist2 (home_base); -- Now check the results from plain indexscan; temporarily drop existing -- index grect2ind to ensure it doesn't capture the plan BEGIN; DROP INDEX grect2ind; SET enable_seqscan = OFF; SET enable_indexscan = ON; SET enable_bitmapscan = OFF; EXPLAIN (COSTS OFF) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; QUERY PLAN ---------------------------------------------------------------- Sort Sort Key: ((home_base[0])[0]) -> Index Only Scan using grect2ind2 on fast_emp4000 Index Cond: (home_base @ '(2000,1000),(200,200)'::box) (4 rows) SELECT * FROM fast_emp4000 WHERE home_base @ '(200,200),(2000,1000)'::box ORDER BY (home_base[0])[0]; home_base ----------------------- (337,455),(240,359) (1444,403),(1346,344) (2 rows) EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; QUERY PLAN ------------------------------------------------------------- Aggregate -> Index Only Scan using grect2ind2 on fast_emp4000 Index Cond: (home_base && '(1000,1000),(0,0)'::box) (3 rows) SELECT count(*) FROM fast_emp4000 WHERE home_base && '(1000,1000,0,0)'::box; count ------- 2 (1 row) EXPLAIN (COSTS OFF) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; QUERY PLAN -------------------------------------------------------- Aggregate -> Index Only Scan using grect2ind2 on fast_emp4000 Index Cond: (home_base IS NULL) (3 rows) SELECT count(*) FROM fast_emp4000 WHERE home_base IS NULL; count ------- 278 (1 row) ROLLBACK; -- Try to drop access method: fail because of dependent objects DROP ACCESS METHOD gist2; ERROR: cannot drop access method gist2 because other objects depend on it DETAIL: index grect2ind2 depends on operator class box_ops for access method gist2 HINT: Use DROP ... CASCADE to drop the dependent objects too. -- Drop access method cascade -- To prevent a (rare) deadlock against autovacuum, -- we must lock the table that owns the index that will be dropped BEGIN; LOCK TABLE fast_emp4000; DROP ACCESS METHOD gist2 CASCADE; NOTICE: drop cascades to index grect2ind2 COMMIT; -- -- Test table access methods -- -- prevent empty values SET default_table_access_method = ''; ERROR: invalid value for parameter "default_table_access_method": "" DETAIL: default_table_access_method cannot be empty. -- prevent nonexistent values SET default_table_access_method = 'I do not exist AM'; ERROR: invalid value for parameter "default_table_access_method": "I do not exist AM" DETAIL: Table access method "I do not exist AM" does not exist. -- prevent setting it to an index AM SET default_table_access_method = 'btree'; ERROR: access method "btree" is not of type TABLE -- Create a heap2 table am handler with heapam handler CREATE ACCESS METHOD heap2 TYPE TABLE HANDLER heap_tableam_handler; -- Verify return type checks for handlers CREATE ACCESS METHOD bogus TYPE TABLE HANDLER int4in; ERROR: function int4in(internal) does not exist CREATE ACCESS METHOD bogus TYPE TABLE HANDLER bthandler; ERROR: function bthandler must return type table_am_handler SELECT amname, amhandler, amtype FROM pg_am where amtype = 't' ORDER BY 1, 2; amname | amhandler | amtype --------+----------------------+-------- heap | heap_tableam_handler | t heap2 | heap_tableam_handler | t (2 rows) -- First create tables employing the new AM using USING -- plain CREATE TABLE CREATE TABLE tableam_tbl_heap2(f1 int) USING heap2; INSERT INTO tableam_tbl_heap2 VALUES(1); SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; f1 ---- 1 (1 row) -- CREATE TABLE AS CREATE TABLE tableam_tblas_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tbl_heap2 ORDER BY f1; f1 ---- 1 (1 row) -- SELECT INTO doesn't support USING SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tableam_tbl_heap2; ERROR: syntax error at or near "USING" LINE 1: SELECT INTO tableam_tblselectinto_heap2 USING heap2 FROM tab... ^ -- CREATE VIEW doesn't support USING CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; ERROR: syntax error at or near "USING" LINE 1: CREATE VIEW tableam_view_heap2 USING heap2 AS SELECT * FROM ... ^ -- CREATE SEQUENCE doesn't support USING CREATE SEQUENCE tableam_seq_heap2 USING heap2; ERROR: syntax error at or near "USING" LINE 1: CREATE SEQUENCE tableam_seq_heap2 USING heap2; ^ -- CREATE MATERIALIZED VIEW does support USING CREATE MATERIALIZED VIEW tableam_tblmv_heap2 USING heap2 AS SELECT * FROM tableam_tbl_heap2; SELECT f1 FROM tableam_tblmv_heap2 ORDER BY f1; f1 ---- 1 (1 row) -- CREATE TABLE .. PARTITION BY doesn't not support USING CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a) USING heap2; ERROR: specifying a table access method is not supported on a partitioned table CREATE TABLE tableam_parted_heap2 (a text, b int) PARTITION BY list (a); -- new partitions will inherit from the current default, rather the partition root SET default_table_access_method = 'heap'; CREATE TABLE tableam_parted_a_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('a'); SET default_table_access_method = 'heap2'; CREATE TABLE tableam_parted_b_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('b'); RESET default_table_access_method; -- but the method can be explicitly specified CREATE TABLE tableam_parted_c_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('c') USING heap; CREATE TABLE tableam_parted_d_heap2 PARTITION OF tableam_parted_heap2 FOR VALUES IN ('d') USING heap2; -- List all objects in AM SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc, pg_am AS pa WHERE pa.oid = pc.relam AND pa.amname = 'heap2' ORDER BY 3, 1, 2; relkind | amname | relname ---------+--------+---------------------------------- r | heap2 | tableam_parted_b_heap2 r | heap2 | tableam_parted_d_heap2 r | heap2 | tableam_tbl_heap2 r | heap2 | tableam_tblas_heap2 m | heap2 | tableam_tblmv_heap2 t | heap2 | toast for tableam_parted_b_heap2 t | heap2 | toast for tableam_parted_d_heap2 (7 rows) -- Show dependencies onto AM - there shouldn't be any for toast SELECT pg_describe_object(classid,objid,objsubid) AS obj FROM pg_depend, pg_am WHERE pg_depend.refclassid = 'pg_am'::regclass AND pg_am.oid = pg_depend.refobjid AND pg_am.amname = 'heap2' ORDER BY classid, objid, objsubid; obj --------------------------------------- table tableam_tbl_heap2 table tableam_tblas_heap2 materialized view tableam_tblmv_heap2 table tableam_parted_b_heap2 table tableam_parted_d_heap2 (5 rows) -- Second, create objects in the new AM by changing the default AM BEGIN; SET LOCAL default_table_access_method = 'heap2'; -- following tests should all respect the default AM CREATE TABLE tableam_tbl_heapx(f1 int); CREATE TABLE tableam_tblas_heapx AS SELECT * FROM tableam_tbl_heapx; SELECT INTO tableam_tblselectinto_heapx FROM tableam_tbl_heapx; CREATE MATERIALIZED VIEW tableam_tblmv_heapx USING heap2 AS SELECT * FROM tableam_tbl_heapx; CREATE TABLE tableam_parted_heapx (a text, b int) PARTITION BY list (a); CREATE TABLE tableam_parted_1_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('a', 'b'); -- but an explicitly set AM overrides it CREATE TABLE tableam_parted_2_heapx PARTITION OF tableam_parted_heapx FOR VALUES IN ('c', 'd') USING heap; -- sequences, views and foreign servers shouldn't have an AM CREATE VIEW tableam_view_heapx AS SELECT * FROM tableam_tbl_heapx; CREATE SEQUENCE tableam_seq_heapx; CREATE FOREIGN DATA WRAPPER fdw_heap2 VALIDATOR postgresql_fdw_validator; CREATE SERVER fs_heap2 FOREIGN DATA WRAPPER fdw_heap2 ; CREATE FOREIGN table tableam_fdw_heapx () SERVER fs_heap2; -- Verify that new AM was used for tables, matviews, but not for sequences, views and fdws SELECT pc.relkind, pa.amname, CASE WHEN relkind = 't' THEN (SELECT 'toast for ' || relname::regclass FROM pg_class pcm WHERE pcm.reltoastrelid = pc.oid) ELSE relname::regclass::text END COLLATE "C" AS relname FROM pg_class AS pc LEFT JOIN pg_am AS pa ON (pa.oid = pc.relam) WHERE pc.relname LIKE 'tableam_%_heapx' ORDER BY 3, 1, 2; relkind | amname | relname ---------+--------+----------------------------- f | | tableam_fdw_heapx r | heap2 | tableam_parted_1_heapx r | heap | tableam_parted_2_heapx p | | tableam_parted_heapx S | | tableam_seq_heapx r | heap2 | tableam_tbl_heapx r | heap2 | tableam_tblas_heapx m | heap2 | tableam_tblmv_heapx r | heap2 | tableam_tblselectinto_heapx v | | tableam_view_heapx (10 rows) -- don't want to keep those tables, nor the default ROLLBACK; -- Third, check that we can neither create a table using a nonexistent -- AM, nor using an index AM CREATE TABLE i_am_a_failure() USING ""; ERROR: zero-length delimited identifier at or near """" LINE 1: CREATE TABLE i_am_a_failure() USING ""; ^ CREATE TABLE i_am_a_failure() USING i_do_not_exist_am; ERROR: access method "i_do_not_exist_am" does not exist CREATE TABLE i_am_a_failure() USING "I do not exist AM"; ERROR: access method "I do not exist AM" does not exist CREATE TABLE i_am_a_failure() USING "btree"; ERROR: access method "btree" is not of type TABLE -- Drop table access method, which fails as objects depends on it DROP ACCESS METHOD heap2; ERROR: cannot drop access method heap2 because other objects depend on it DETAIL: table tableam_tbl_heap2 depends on access method heap2 table tableam_tblas_heap2 depends on access method heap2 materialized view tableam_tblmv_heap2 depends on access method heap2 table tableam_parted_b_heap2 depends on access method heap2 table tableam_parted_d_heap2 depends on access method heap2 HINT: Use DROP ... CASCADE to drop the dependent objects too. -- we intentionally leave the objects created above alive, to verify pg_dump support