diff --git a/src/test/regress/expected/btree_index.out b/src/test/regress/expected/btree_index.out index ff443a476c..acab8e0b11 100644 --- a/src/test/regress/expected/btree_index.out +++ b/src/test/regress/expected/btree_index.out @@ -244,3 +244,21 @@ select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; {vacuum_cleanup_index_scale_factor=70.0} (1 row) +-- +-- Test for multilevel page deletion +-- +CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); +INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; +ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); +-- Delete most entries, and vacuum, deleting internal pages and creating "fast +-- root" +DELETE FROM delete_test_table WHERE a < 79990; +VACUUM delete_test_table; +-- +-- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META +-- WAL record type). This happens when a "fast root" page is split. This +-- also creates coverage for nbtree FSM page recycling. +-- +-- The vacuum above should've turned the leaf page into a fast root. We just +-- need to insert some rows to cause the fast root page to split. +INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; diff --git a/src/test/regress/expected/create_index.out b/src/test/regress/expected/create_index.out index 388d709875..f9b4768aee 100644 --- a/src/test/regress/expected/create_index.out +++ b/src/test/regress/expected/create_index.out @@ -68,25 +68,7 @@ CREATE TEMP TABLE gcircle_tbl AS CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- --- SP-GiST --- -CREATE TABLE quad_point_tbl AS - SELECT point(unique1,unique2) AS p FROM tenk1; -INSERT INTO quad_point_tbl - SELECT '(333.0,400.0)'::point FROM generate_series(1,1000); -INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); -CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); -CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; -CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); -CREATE TABLE radix_text_tbl AS - SELECT name AS t FROM road WHERE name !~ '^[0-9]'; -INSERT INTO radix_text_tbl - SELECT 'P0123456789abcdef' FROM generate_series(1,1000); -INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); -INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); -CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); --- --- Test GiST and SP-GiST indexes +-- Test GiST indexes -- -- get non-indexed results for comparison purposes SET enable_seqscan = ON; @@ -241,159 +223,6 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0 (10,10) (5 rows) -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - count -------- - 3 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - count -------- - 11000 -(1 row) - -SELECT count(*) FROM quad_point_tbl; - count -------- - 11003 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - count -------- - 1057 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - count -------- - 1057 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - count -------- - 6000 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - count -------- - 4999 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - count -------- - 5000 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - count -------- - 5999 -(1 row) - -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - count -------- - 1 -(1 row) - -CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; -CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - count -------- - 1000 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - count -------- - 1 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - count -------- - 1 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - count -------- - 272 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - count -------- - 272 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - count -------- - 273 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - count -------- - 273 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - count -------- - 1 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - count -------- - 2 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - count -------- - 50 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - count -------- - 50 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - count -------- - 48 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - count -------- - 48 -(1 row) - -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - count -------- - 2 -(1 row) - SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; f1 ------------------------------------------------- @@ -762,600 +591,6 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0 (10,10) (5 rows) -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p IS NULL) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - count -------- - 3 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p IS NOT NULL) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - count -------- - 11000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl -(2 rows) - -SELECT count(*) FROM quad_point_tbl; - count -------- - 11003 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p << '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - count -------- - 6000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p >> '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - count -------- - 4999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p <^ '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - count -------- - 5000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p >^ '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - count -------- - 5999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p ~= '(4585,365)'::point) -(3 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; - QUERY PLAN ------------------------------------------------------------ - WindowAgg - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Order By: (p <-> '(0,0)'::point) -(3 rows) - -CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; -SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ------------------------------------------------------------ - WindowAgg - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) - Order By: (p <-> '(0,0)'::point) -(4 rows) - -CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; - QUERY PLAN ------------------------------------------------------------ - WindowAgg - -> Index Only Scan using sp_quad_ind on quad_point_tbl - Index Cond: (p IS NOT NULL) - Order By: (p <-> '(333,400)'::point) -(4 rows) - -CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; -SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - QUERY PLAN ---------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p << '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - count -------- - 6000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p >> '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - count -------- - 4999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p <^ '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - count -------- - 5000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p >^ '(5000,4000)'::point) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - count -------- - 5999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p ~= '(4585,365)'::point) -(3 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl; - QUERY PLAN -------------------------------------------------------- - WindowAgg - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Order By: (p <-> '(0,0)'::point) -(3 rows) - -CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl; -SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ---------------------------------------------------------- - WindowAgg - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p <@ '(1000,1000),(200,200)'::box) - Order By: (p <-> '(0,0)'::point) -(4 rows) - -CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM kd_point_tbl WHERE p IS NOT NULL; - QUERY PLAN -------------------------------------------------------- - WindowAgg - -> Index Only Scan using sp_kd_ind on kd_point_tbl - Index Cond: (p IS NOT NULL) - Order By: (p <-> '(333,400)'::point) -(4 rows) - -CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM kd_point_tbl WHERE p IS NOT NULL; -SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - n | dist | p | n | dist | p ----+------+---+---+------+--- -(0 rows) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t = 'P0123456789abcdef'::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - count -------- - 1000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t = 'P0123456789abcde'::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t = 'P0123456789abcdefF'::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - QUERY PLAN ----------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t < 'Aztec Ct '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - count -------- - 272 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - QUERY PLAN ------------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t ~<~ 'Aztec Ct '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - count -------- - 272 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - QUERY PLAN ------------------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t <= 'Aztec Ct '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - count -------- - 273 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - QUERY PLAN -------------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t ~<=~ 'Aztec Ct '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - count -------- - 273 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - QUERY PLAN ----------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t = 'Aztec Ct '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - QUERY PLAN ----------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t = 'Worth St '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - count -------- - 2 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - QUERY PLAN ------------------------------------------------------------------------ - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t >= 'Worth St '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - count -------- - 50 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - QUERY PLAN -------------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t ~>=~ 'Worth St '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - count -------- - 50 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - QUERY PLAN ----------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t > 'Worth St '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - count -------- - 48 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - QUERY PLAN ------------------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t ~>~ 'Worth St '::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - count -------- - 48 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Index Only Scan using sp_radix_ind on radix_text_tbl - Index Cond: (t ^@ 'Worth'::text) -(3 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - count -------- - 2 -(1 row) - EXPLAIN (COSTS OFF) SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; QUERY PLAN @@ -1430,531 +665,6 @@ SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0 (10,10) (5 rows) -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - QUERY PLAN ----------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p IS NULL) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p IS NULL) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - count -------- - 3 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - QUERY PLAN ----------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p IS NOT NULL) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p IS NOT NULL) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - count -------- - 11000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl; - QUERY PLAN ----------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - -> Bitmap Index Scan on sp_quad_ind -(3 rows) - -SELECT count(*) FROM quad_point_tbl; - count -------- - 11003 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ---------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p <@ '(1000,1000),(200,200)'::box) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - QUERY PLAN ---------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: ('(1000,1000),(200,200)'::box @> p) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p << '(5000,4000)'::point) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p << '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - count -------- - 6000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p >> '(5000,4000)'::point) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p >> '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - count -------- - 4999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p <^ '(5000,4000)'::point) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p <^ '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - count -------- - 5000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p >^ '(5000,4000)'::point) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p >^ '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - count -------- - 5999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on quad_point_tbl - Recheck Cond: (p ~= '(4585,365)'::point) - -> Bitmap Index Scan on sp_quad_ind - Index Cond: (p ~= '(4585,365)'::point) -(5 rows) - -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - QUERY PLAN ---------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p <@ '(1000,1000),(200,200)'::box) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - QUERY PLAN ---------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: ('(1000,1000),(200,200)'::box @> p) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p <@ '(1000,1000),(200,200)'::box) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - count -------- - 1057 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p << '(5000,4000)'::point) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p << '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - count -------- - 6000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p >> '(5000,4000)'::point) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p >> '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - count -------- - 4999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p <^ '(5000,4000)'::point) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p <^ '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - count -------- - 5000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - QUERY PLAN -------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p >^ '(5000,4000)'::point) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p >^ '(5000,4000)'::point) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - count -------- - 5999 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on kd_point_tbl - Recheck Cond: (p ~= '(4585,365)'::point) - -> Bitmap Index Scan on sp_kd_ind - Index Cond: (p ~= '(4585,365)'::point) -(5 rows) - -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - QUERY PLAN ------------------------------------------------------------ - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t = 'P0123456789abcdef'::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t = 'P0123456789abcdef'::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - count -------- - 1000 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - QUERY PLAN ----------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t = 'P0123456789abcde'::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t = 'P0123456789abcde'::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - QUERY PLAN ------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t = 'P0123456789abcdefF'::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t = 'P0123456789abcdefF'::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - QUERY PLAN ----------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t < 'Aztec Ct '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t < 'Aztec Ct '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - count -------- - 272 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - QUERY PLAN ------------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t ~<~ 'Aztec Ct '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t ~<~ 'Aztec Ct '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - count -------- - 272 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - QUERY PLAN ------------------------------------------------------------------------------ - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t <= 'Aztec Ct '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t <= 'Aztec Ct '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - count -------- - 273 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - QUERY PLAN -------------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t ~<=~ 'Aztec Ct '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t ~<=~ 'Aztec Ct '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - count -------- - 273 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - QUERY PLAN ----------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t = 'Aztec Ct '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t = 'Aztec Ct '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - count -------- - 1 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - QUERY PLAN ----------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t = 'Worth St '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t = 'Worth St '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - count -------- - 2 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - QUERY PLAN ------------------------------------------------------------------------------ - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t >= 'Worth St '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t >= 'Worth St '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - count -------- - 50 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - QUERY PLAN -------------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t ~>=~ 'Worth St '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t ~>=~ 'Worth St '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - count -------- - 50 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - QUERY PLAN ----------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t > 'Worth St '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t > 'Worth St '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - count -------- - 48 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - QUERY PLAN ------------------------------------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t ~>~ 'Worth St '::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t ~>~ 'Worth St '::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - count -------- - 48 -(1 row) - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - QUERY PLAN ------------------------------------------------- - Aggregate - -> Bitmap Heap Scan on radix_text_tbl - Recheck Cond: (t ^@ 'Worth'::text) - -> Bitmap Index Scan on sp_radix_ind - Index Cond: (t ^@ 'Worth'::text) -(5 rows) - -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - count -------- - 2 -(1 row) - RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; @@ -3219,24 +1929,6 @@ explain (costs off) Index Cond: (b = false) (3 rows) --- --- Test for multilevel page deletion --- -CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; -ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); --- Delete most entries, and vacuum, deleting internal pages and creating "fast --- root" -DELETE FROM delete_test_table WHERE a < 79990; -VACUUM delete_test_table; --- --- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META --- WAL record type). This happens when a "fast root" page is split. This --- also creates coverage for nbtree FSM page recycling. --- --- The vacuum above should've turned the leaf page into a fast root. We just --- need to insert some rows to cause the fast root page to split. -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; -- -- REINDEX (VERBOSE) -- diff --git a/src/test/regress/expected/create_index_spgist.out b/src/test/regress/expected/create_index_spgist.out new file mode 100644 index 0000000000..b422e928c0 --- /dev/null +++ b/src/test/regress/expected/create_index_spgist.out @@ -0,0 +1,1305 @@ +-- +-- SP-GiST index tests +-- +CREATE TABLE quad_point_tbl AS + SELECT point(unique1,unique2) AS p FROM tenk1; +INSERT INTO quad_point_tbl + SELECT '(333.0,400.0)'::point FROM generate_series(1,1000); +INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); +CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); +CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; +CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); +CREATE TABLE radix_text_tbl AS + SELECT name AS t FROM road WHERE name !~ '^[0-9]'; +INSERT INTO radix_text_tbl + SELECT 'P0123456789abcdef' FROM generate_series(1,1000); +INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); +INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); +CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); +-- get non-indexed results for comparison purposes +SET enable_seqscan = ON; +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + count +------- + 3 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + count +------- + 11000 +(1 row) + +SELECT count(*) FROM quad_point_tbl; + count +------- + 11003 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + count +------- + 1057 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + count +------- + 1057 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + count +------- + 6000 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + count +------- + 4999 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + count +------- + 5000 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + count +------- + 5999 +(1 row) + +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + count +------- + 1 +(1 row) + +CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; +CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + count +------- + 1000 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + count +------- + 1 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + count +------- + 1 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + count +------- + 272 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + count +------- + 272 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + count +------- + 273 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + count +------- + 273 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + count +------- + 1 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + count +------- + 2 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + count +------- + 50 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + count +------- + 50 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + count +------- + 48 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + count +------- + 48 +(1 row) + +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + count +------- + 2 +(1 row) + +-- Now check the results from plain indexscan +SET enable_seqscan = OFF; +SET enable_indexscan = ON; +SET enable_bitmapscan = OFF; +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p IS NULL) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + count +------- + 3 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p IS NOT NULL) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + count +------- + 11000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl +(2 rows) + +SELECT count(*) FROM quad_point_tbl; + count +------- + 11003 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p << '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + count +------- + 6000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p >> '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + count +------- + 4999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p <^ '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + count +------- + 5000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p >^ '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + count +------- + 5999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p ~= '(4585,365)'::point) +(3 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; + QUERY PLAN +----------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Order By: (p <-> '(0,0)'::point) +(3 rows) + +CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; +SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +----------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) + Order By: (p <-> '(0,0)'::point) +(4 rows) + +CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; + QUERY PLAN +----------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_quad_ind on quad_point_tbl + Index Cond: (p IS NOT NULL) + Order By: (p <-> '(333,400)'::point) +(4 rows) + +CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; +SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +--------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + QUERY PLAN +--------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p << '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + count +------- + 6000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p >> '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + count +------- + 4999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p <^ '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + count +------- + 5000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p >^ '(5000,4000)'::point) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + count +------- + 5999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p ~= '(4585,365)'::point) +(3 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl; + QUERY PLAN +------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Order By: (p <-> '(0,0)'::point) +(3 rows) + +CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl; +SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +--------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p <@ '(1000,1000),(200,200)'::box) + Order By: (p <-> '(0,0)'::point) +(4 rows) + +CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM kd_point_tbl WHERE p IS NOT NULL; + QUERY PLAN +------------------------------------------------------- + WindowAgg + -> Index Only Scan using sp_kd_ind on kd_point_tbl + Index Cond: (p IS NOT NULL) + Order By: (p <-> '(333,400)'::point) +(4 rows) + +CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM kd_point_tbl WHERE p IS NOT NULL; +SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + n | dist | p | n | dist | p +---+------+---+---+------+--- +(0 rows) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + QUERY PLAN +------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t = 'P0123456789abcdef'::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + count +------- + 1000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + QUERY PLAN +------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t = 'P0123456789abcde'::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + QUERY PLAN +------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t = 'P0123456789abcdefF'::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + QUERY PLAN +---------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t < 'Aztec Ct '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + count +------- + 272 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t ~<~ 'Aztec Ct '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + count +------- + 272 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + QUERY PLAN +----------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t <= 'Aztec Ct '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + count +------- + 273 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + QUERY PLAN +------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t ~<=~ 'Aztec Ct '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + count +------- + 273 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + QUERY PLAN +---------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t = 'Aztec Ct '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + QUERY PLAN +---------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t = 'Worth St '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + count +------- + 2 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + QUERY PLAN +----------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t >= 'Worth St '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + count +------- + 50 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + QUERY PLAN +------------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t ~>=~ 'Worth St '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + count +------- + 50 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + QUERY PLAN +---------------------------------------------------------------------- + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t > 'Worth St '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + count +------- + 48 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + QUERY PLAN +------------------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t ~>~ 'Worth St '::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + count +------- + 48 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + QUERY PLAN +------------------------------------------------------------ + Aggregate + -> Index Only Scan using sp_radix_ind on radix_text_tbl + Index Cond: (t ^@ 'Worth'::text) +(3 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + count +------- + 2 +(1 row) + +-- Now check the results from bitmap indexscan +SET enable_seqscan = OFF; +SET enable_indexscan = OFF; +SET enable_bitmapscan = ON; +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + QUERY PLAN +---------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p IS NULL) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p IS NULL) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + count +------- + 3 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + QUERY PLAN +---------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p IS NOT NULL) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p IS NOT NULL) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + count +------- + 11000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl; + QUERY PLAN +---------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + -> Bitmap Index Scan on sp_quad_ind +(3 rows) + +SELECT count(*) FROM quad_point_tbl; + count +------- + 11003 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +--------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p <@ '(1000,1000),(200,200)'::box) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + QUERY PLAN +--------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: ('(1000,1000),(200,200)'::box @> p) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p << '(5000,4000)'::point) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p << '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + count +------- + 6000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p >> '(5000,4000)'::point) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p >> '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + count +------- + 4999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p <^ '(5000,4000)'::point) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p <^ '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + count +------- + 5000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p >^ '(5000,4000)'::point) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p >^ '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + count +------- + 5999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + QUERY PLAN +------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on quad_point_tbl + Recheck Cond: (p ~= '(4585,365)'::point) + -> Bitmap Index Scan on sp_quad_ind + Index Cond: (p ~= '(4585,365)'::point) +(5 rows) + +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + QUERY PLAN +--------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p <@ '(1000,1000),(200,200)'::box) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + QUERY PLAN +--------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: ('(1000,1000),(200,200)'::box @> p) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p <@ '(1000,1000),(200,200)'::box) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + count +------- + 1057 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p << '(5000,4000)'::point) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p << '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + count +------- + 6000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p >> '(5000,4000)'::point) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p >> '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + count +------- + 4999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p <^ '(5000,4000)'::point) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p <^ '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + count +------- + 5000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + QUERY PLAN +------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p >^ '(5000,4000)'::point) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p >^ '(5000,4000)'::point) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + count +------- + 5999 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + QUERY PLAN +------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on kd_point_tbl + Recheck Cond: (p ~= '(4585,365)'::point) + -> Bitmap Index Scan on sp_kd_ind + Index Cond: (p ~= '(4585,365)'::point) +(5 rows) + +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + QUERY PLAN +----------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t = 'P0123456789abcdef'::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t = 'P0123456789abcdef'::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + count +------- + 1000 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + QUERY PLAN +---------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t = 'P0123456789abcde'::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t = 'P0123456789abcde'::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + QUERY PLAN +------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t = 'P0123456789abcdefF'::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t = 'P0123456789abcdefF'::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + QUERY PLAN +---------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t < 'Aztec Ct '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t < 'Aztec Ct '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + count +------- + 272 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + QUERY PLAN +------------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t ~<~ 'Aztec Ct '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t ~<~ 'Aztec Ct '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + count +------- + 272 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t <= 'Aztec Ct '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t <= 'Aztec Ct '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + count +------- + 273 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t ~<=~ 'Aztec Ct '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t ~<=~ 'Aztec Ct '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + count +------- + 273 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + QUERY PLAN +---------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t = 'Aztec Ct '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t = 'Aztec Ct '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + count +------- + 1 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + QUERY PLAN +---------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t = 'Worth St '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t = 'Worth St '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + count +------- + 2 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + QUERY PLAN +----------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t >= 'Worth St '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t >= 'Worth St '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + count +------- + 50 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + QUERY PLAN +------------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t ~>=~ 'Worth St '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t ~>=~ 'Worth St '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + count +------- + 50 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + QUERY PLAN +---------------------------------------------------------------------------- + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t > 'Worth St '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t > 'Worth St '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + count +------- + 48 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + QUERY PLAN +------------------------------------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t ~>~ 'Worth St '::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t ~>~ 'Worth St '::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + count +------- + 48 +(1 row) + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + QUERY PLAN +------------------------------------------------ + Aggregate + -> Bitmap Heap Scan on radix_text_tbl + Recheck Cond: (t ^@ 'Worth'::text) + -> Bitmap Index Scan on sp_radix_ind + Index Cond: (t ^@ 'Worth'::text) +(5 rows) + +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + count +------- + 2 +(1 row) + +RESET enable_seqscan; +RESET enable_indexscan; +RESET enable_bitmapscan; diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out index 88fcd52ae1..07e631d45e 100644 --- a/src/test/regress/expected/join.out +++ b/src/test/regress/expected/join.out @@ -6051,883 +6051,3 @@ where exists (select 1 from j3 (13 rows) drop table j3; --- --- exercises for the hash join code --- -begin; -set local min_parallel_table_scan_size = 0; -set local parallel_setup_cost = 0; --- Extract bucket and batch counts from an explain analyze plan. In --- general we can't make assertions about how many batches (or --- buckets) will be required because it can vary, but we can in some --- special cases and we can check for growth. -create or replace function find_hash(node json) -returns json language plpgsql -as -$$ -declare - x json; - child json; -begin - if node->>'Node Type' = 'Hash' then - return node; - else - for child in select json_array_elements(node->'Plans') - loop - x := find_hash(child); - if x is not null then - return x; - end if; - end loop; - return null; - end if; -end; -$$; -create or replace function hash_join_batches(query text) -returns table (original int, final int) language plpgsql -as -$$ -declare - whole_plan json; - hash_node json; -begin - for whole_plan in - execute 'explain (analyze, format ''json'') ' || query - loop - hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); - original := hash_node->>'Original Hash Batches'; - final := hash_node->>'Hash Batches'; - return next; - end loop; -end; -$$; --- Make a simple relation with well distributed keys and correctly --- estimated size. -create table simple as - select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table simple set (parallel_workers = 2); -analyze simple; --- Make a relation whose size we will under-estimate. We want stats --- to say 1000 rows, but actually there are 20,000 rows. -create table bigger_than_it_looks as - select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); -alter table bigger_than_it_looks set (parallel_workers = 2); -analyze bigger_than_it_looks; -update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; --- Make a relation whose size we underestimate and that also has a --- kind of skew that breaks our batching scheme. We want stats to say --- 2 rows, but actually there are 20,000 rows with the same key. -create table extremely_skewed (id int, t text); -alter table extremely_skewed set (autovacuum_enabled = 'false'); -alter table extremely_skewed set (parallel_workers = 2); -analyze extremely_skewed; -insert into extremely_skewed - select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - from generate_series(1, 20000); -update pg_class - set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 - where relname = 'extremely_skewed'; --- Make a relation with a couple of enormous tuples. -create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; -alter table wide set (parallel_workers = 2); --- The "optimal" case: the hash table fits in memory; we plan for 1 --- batch, we stick to that number, and peak memory usage stays within --- our work_mem budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '4MB'; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | f -(1 row) - -rollback to settings; --- The "good" case: batches required, but we plan the right number; we --- plan for some number of batches, and we stick to that number, and --- peak memory usage says within our work_mem budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); - QUERY PLAN -------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on simple s -(9 rows) - -select count(*) from simple r join simple s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - t | f -(1 row) - -rollback to settings; --- The "bad" case: during execution we need to increase number of --- batches; in this case we plan for 1 batch, and increase at least a --- couple of times, and peak memory usage stays within our work_mem --- budget --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); - QUERY PLAN ------------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on bigger_than_it_looks s -(6 rows) - -select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); - QUERY PLAN ------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on bigger_than_it_looks s -(9 rows) - -select count(*) from simple r join bigger_than_it_looks s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); - QUERY PLAN ---------------------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 1 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on bigger_than_it_looks s -(9 rows) - -select count(*) from simple r join bigger_than_it_looks s using (id); - count -------- - 20000 -(1 row) - -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); - initially_multibatch | increased_batches -----------------------+------------------- - f | t -(1 row) - -rollback to settings; --- The "ugly" case: increasing the number of batches during execution --- doesn't help, so stop trying to fit in work_mem and hope for the --- best; in this case we plan for 1 batch, increases just once and --- then stop increasing because that didn't help at all, so we blow --- right through the work_mem budget and hope for the best... --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN --------------------------------------------------- - Aggregate - -> Hash Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on extremely_skewed s -(6 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 2 -(1 row) - -rollback to settings; --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN --------------------------------------------------------- - Aggregate - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Hash - -> Seq Scan on extremely_skewed s -(8 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 2 -(1 row) - -rollback to settings; --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '128kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); - QUERY PLAN ------------------------------------------------------------------------ - Finalize Aggregate - -> Gather - Workers Planned: 1 - -> Partial Aggregate - -> Parallel Hash Join - Hash Cond: (r.id = s.id) - -> Parallel Seq Scan on simple r - -> Parallel Hash - -> Parallel Seq Scan on extremely_skewed s -(9 rows) - -select count(*) from simple r join extremely_skewed s using (id); - count -------- - 20000 -(1 row) - -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); - original | final -----------+------- - 1 | 4 -(1 row) - -rollback to settings; --- A couple of other hash join tests unrelated to work_mem management. --- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local parallel_leader_participation = off; -select * from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); - original | final -----------+------- - 1 | 1 -(1 row) - -rollback to settings; --- Exercise rescans. We'll turn off parallel_leader_participation so --- that we can check that instrumentation comes back correctly. -create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; -alter table join_foo set (parallel_workers = 0); -create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; -alter table join_bar set (parallel_workers = 2); --- multi-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Hash - -> Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; --- single-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Hash - -> Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - f -(1 row) - -rollback to settings; --- multi-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Parallel Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Parallel Hash - -> Parallel Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; --- single-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - QUERY PLAN ------------------------------------------------------------------------------------- - Aggregate - -> Nested Loop Left Join - Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) - -> Seq Scan on join_foo - -> Gather - Workers Planned: 2 - -> Parallel Hash Join - Hash Cond: (b1.id = b2.id) - -> Parallel Seq Scan on join_bar b1 - -> Parallel Hash - -> Parallel Seq Scan on join_bar b2 -(11 rows) - -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; - count -------- - 3 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); - multibatch ------------- - f -(1 row) - -rollback to settings; --- A full outer join where every record is matched. --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r full outer join simple s using (id); - count -------- - 20000 -(1 row) - -rollback to settings; --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: (r.id = s.id) - -> Seq Scan on simple r - -> Hash - -> Seq Scan on simple s -(6 rows) - -select count(*) from simple r full outer join simple s using (id); - count -------- - 20000 -(1 row) - -rollback to settings; --- An full outer join where every record is not matched. --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: ((0 - s.id) = r.id) - -> Seq Scan on simple s - -> Hash - -> Seq Scan on simple r -(6 rows) - -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - count -------- - 40000 -(1 row) - -rollback to settings; --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - QUERY PLAN ----------------------------------------- - Aggregate - -> Hash Full Join - Hash Cond: ((0 - s.id) = r.id) - -> Seq Scan on simple s - -> Hash - -> Seq Scan on simple r -(6 rows) - -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); - count -------- - 40000 -(1 row) - -rollback to settings; --- exercise special code paths for huge tuples (note use of non-strict --- expression and left join required to get the detoasted tuple into --- the hash table) --- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and --- sts_puttuple oversized tuple cases because it's multi-batch) -savepoint settings; -set max_parallel_workers_per_gather = 2; -set enable_parallel_hash = on; -set work_mem = '128kB'; -explain (costs off) - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); - QUERY PLAN ----------------------------------------------------------------- - Finalize Aggregate - -> Gather - Workers Planned: 2 - -> Partial Aggregate - -> Parallel Hash Left Join - Hash Cond: (wide.id = wide_1.id) - -> Parallel Seq Scan on wide - -> Parallel Hash - -> Parallel Seq Scan on wide wide_1 -(9 rows) - -select length(max(s.t)) -from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); - length --------- - 320000 -(1 row) - -select final > 1 as multibatch - from hash_join_batches( -$$ - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); -$$); - multibatch ------------- - t -(1 row) - -rollback to settings; -rollback; diff --git a/src/test/regress/expected/join_hash.out b/src/test/regress/expected/join_hash.out new file mode 100644 index 0000000000..9eee39bdd3 --- /dev/null +++ b/src/test/regress/expected/join_hash.out @@ -0,0 +1,880 @@ +-- +-- exercises for the hash join code +-- +begin; +set local min_parallel_table_scan_size = 0; +set local parallel_setup_cost = 0; +-- Extract bucket and batch counts from an explain analyze plan. In +-- general we can't make assertions about how many batches (or +-- buckets) will be required because it can vary, but we can in some +-- special cases and we can check for growth. +create or replace function find_hash(node json) +returns json language plpgsql +as +$$ +declare + x json; + child json; +begin + if node->>'Node Type' = 'Hash' then + return node; + else + for child in select json_array_elements(node->'Plans') + loop + x := find_hash(child); + if x is not null then + return x; + end if; + end loop; + return null; + end if; +end; +$$; +create or replace function hash_join_batches(query text) +returns table (original int, final int) language plpgsql +as +$$ +declare + whole_plan json; + hash_node json; +begin + for whole_plan in + execute 'explain (analyze, format ''json'') ' || query + loop + hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); + original := hash_node->>'Original Hash Batches'; + final := hash_node->>'Hash Batches'; + return next; + end loop; +end; +$$; +-- Make a simple relation with well distributed keys and correctly +-- estimated size. +create table simple as + select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +alter table simple set (parallel_workers = 2); +analyze simple; +-- Make a relation whose size we will under-estimate. We want stats +-- to say 1000 rows, but actually there are 20,000 rows. +create table bigger_than_it_looks as + select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); +alter table bigger_than_it_looks set (parallel_workers = 2); +analyze bigger_than_it_looks; +update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; +-- Make a relation whose size we underestimate and that also has a +-- kind of skew that breaks our batching scheme. We want stats to say +-- 2 rows, but actually there are 20,000 rows with the same key. +create table extremely_skewed (id int, t text); +alter table extremely_skewed set (autovacuum_enabled = 'false'); +alter table extremely_skewed set (parallel_workers = 2); +analyze extremely_skewed; +insert into extremely_skewed + select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + from generate_series(1, 20000); +update pg_class + set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 + where relname = 'extremely_skewed'; +-- Make a relation with a couple of enormous tuples. +create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; +alter table wide set (parallel_workers = 2); +-- The "optimal" case: the hash table fits in memory; we plan for 1 +-- batch, we stick to that number, and peak memory usage stays within +-- our work_mem budget +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '4MB'; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(6 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | f +(1 row) + +rollback to settings; +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(9 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | f +(1 row) + +rollback to settings; +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +------------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Parallel Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Parallel Hash + -> Parallel Seq Scan on simple s +(9 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | f +(1 row) + +rollback to settings; +-- The "good" case: batches required, but we plan the right number; we +-- plan for some number of batches, and we stick to that number, and +-- peak memory usage says within our work_mem budget +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(6 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + t | f +(1 row) + +rollback to settings; +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(9 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + t | f +(1 row) + +rollback to settings; +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '192kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join simple s using (id); + QUERY PLAN +------------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Parallel Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Parallel Hash + -> Parallel Seq Scan on simple s +(9 rows) + +select count(*) from simple r join simple s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + t | f +(1 row) + +rollback to settings; +-- The "bad" case: during execution we need to increase number of +-- batches; in this case we plan for 1 batch, and increase at least a +-- couple of times, and peak memory usage stays within our work_mem +-- budget +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); + QUERY PLAN +------------------------------------------------------ + Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on bigger_than_it_looks s +(6 rows) + +select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | t +(1 row) + +rollback to settings; +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join bigger_than_it_looks s using (id); + QUERY PLAN +------------------------------------------------------------------ + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Hash + -> Seq Scan on bigger_than_it_looks s +(9 rows) + +select count(*) from simple r join bigger_than_it_looks s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join bigger_than_it_looks s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | t +(1 row) + +rollback to settings; +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 1; +set local work_mem = '192kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join bigger_than_it_looks s using (id); + QUERY PLAN +--------------------------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 1 + -> Partial Aggregate + -> Parallel Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Parallel Hash + -> Parallel Seq Scan on bigger_than_it_looks s +(9 rows) + +select count(*) from simple r join bigger_than_it_looks s using (id); + count +------- + 20000 +(1 row) + +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join bigger_than_it_looks s using (id); +$$); + initially_multibatch | increased_batches +----------------------+------------------- + f | t +(1 row) + +rollback to settings; +-- The "ugly" case: increasing the number of batches during execution +-- doesn't help, so stop trying to fit in work_mem and hope for the +-- best; in this case we plan for 1 batch, increases just once and +-- then stop increasing because that didn't help at all, so we blow +-- right through the work_mem budget and hope for the best... +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); + QUERY PLAN +-------------------------------------------------- + Aggregate + -> Hash Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on extremely_skewed s +(6 rows) + +select count(*) from simple r join extremely_skewed s using (id); + count +------- + 20000 +(1 row) + +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); + original | final +----------+------- + 1 | 2 +(1 row) + +rollback to settings; +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); + QUERY PLAN +-------------------------------------------------------- + Aggregate + -> Gather + Workers Planned: 2 + -> Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Hash + -> Seq Scan on extremely_skewed s +(8 rows) + +select count(*) from simple r join extremely_skewed s using (id); + count +------- + 20000 +(1 row) + +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); + original | final +----------+------- + 1 | 2 +(1 row) + +rollback to settings; +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 1; +set local work_mem = '128kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); + QUERY PLAN +----------------------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 1 + -> Partial Aggregate + -> Parallel Hash Join + Hash Cond: (r.id = s.id) + -> Parallel Seq Scan on simple r + -> Parallel Hash + -> Parallel Seq Scan on extremely_skewed s +(9 rows) + +select count(*) from simple r join extremely_skewed s using (id); + count +------- + 20000 +(1 row) + +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); + original | final +----------+------- + 1 | 4 +(1 row) + +rollback to settings; +-- A couple of other hash join tests unrelated to work_mem management. +-- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local parallel_leader_participation = off; +select * from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); + original | final +----------+------- + 1 | 1 +(1 row) + +rollback to settings; +-- Exercise rescans. We'll turn off parallel_leader_participation so +-- that we can check that instrumentation comes back correctly. +create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; +alter table join_foo set (parallel_workers = 0); +create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; +alter table join_bar set (parallel_workers = 2); +-- multi-batch with rescan, parallel-oblivious +savepoint settings; +set enable_parallel_hash = off; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '64kB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Aggregate + -> Nested Loop Left Join + Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) + -> Seq Scan on join_foo + -> Gather + Workers Planned: 2 + -> Hash Join + Hash Cond: (b1.id = b2.id) + -> Parallel Seq Scan on join_bar b1 + -> Hash + -> Seq Scan on join_bar b2 +(11 rows) + +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + count +------- + 3 +(1 row) + +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); + multibatch +------------ + t +(1 row) + +rollback to settings; +-- single-batch with rescan, parallel-oblivious +savepoint settings; +set enable_parallel_hash = off; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '4MB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Aggregate + -> Nested Loop Left Join + Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) + -> Seq Scan on join_foo + -> Gather + Workers Planned: 2 + -> Hash Join + Hash Cond: (b1.id = b2.id) + -> Parallel Seq Scan on join_bar b1 + -> Hash + -> Seq Scan on join_bar b2 +(11 rows) + +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + count +------- + 3 +(1 row) + +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); + multibatch +------------ + f +(1 row) + +rollback to settings; +-- multi-batch with rescan, parallel-aware +savepoint settings; +set enable_parallel_hash = on; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '64kB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Aggregate + -> Nested Loop Left Join + Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) + -> Seq Scan on join_foo + -> Gather + Workers Planned: 2 + -> Parallel Hash Join + Hash Cond: (b1.id = b2.id) + -> Parallel Seq Scan on join_bar b1 + -> Parallel Hash + -> Parallel Seq Scan on join_bar b2 +(11 rows) + +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + count +------- + 3 +(1 row) + +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); + multibatch +------------ + t +(1 row) + +rollback to settings; +-- single-batch with rescan, parallel-aware +savepoint settings; +set enable_parallel_hash = on; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '4MB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + QUERY PLAN +------------------------------------------------------------------------------------ + Aggregate + -> Nested Loop Left Join + Join Filter: ((join_foo.id < (b1.id + 1)) AND (join_foo.id > (b1.id - 1))) + -> Seq Scan on join_foo + -> Gather + Workers Planned: 2 + -> Parallel Hash Join + Hash Cond: (b1.id = b2.id) + -> Parallel Seq Scan on join_bar b1 + -> Parallel Hash + -> Parallel Seq Scan on join_bar b2 +(11 rows) + +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; + count +------- + 3 +(1 row) + +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); + multibatch +------------ + f +(1 row) + +rollback to settings; +-- A full outer join where every record is matched. +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +explain (costs off) + select count(*) from simple r full outer join simple s using (id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Full Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(6 rows) + +select count(*) from simple r full outer join simple s using (id); + count +------- + 20000 +(1 row) + +rollback to settings; +-- parallelism not possible with parallel-oblivious outer hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +explain (costs off) + select count(*) from simple r full outer join simple s using (id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Full Join + Hash Cond: (r.id = s.id) + -> Seq Scan on simple r + -> Hash + -> Seq Scan on simple s +(6 rows) + +select count(*) from simple r full outer join simple s using (id); + count +------- + 20000 +(1 row) + +rollback to settings; +-- An full outer join where every record is not matched. +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +explain (costs off) + select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Full Join + Hash Cond: ((0 - s.id) = r.id) + -> Seq Scan on simple s + -> Hash + -> Seq Scan on simple r +(6 rows) + +select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); + count +------- + 40000 +(1 row) + +rollback to settings; +-- parallelism not possible with parallel-oblivious outer hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +explain (costs off) + select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); + QUERY PLAN +---------------------------------------- + Aggregate + -> Hash Full Join + Hash Cond: ((0 - s.id) = r.id) + -> Seq Scan on simple s + -> Hash + -> Seq Scan on simple r +(6 rows) + +select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); + count +------- + 40000 +(1 row) + +rollback to settings; +-- exercise special code paths for huge tuples (note use of non-strict +-- expression and left join required to get the detoasted tuple into +-- the hash table) +-- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and +-- sts_puttuple oversized tuple cases because it's multi-batch) +savepoint settings; +set max_parallel_workers_per_gather = 2; +set enable_parallel_hash = on; +set work_mem = '128kB'; +explain (costs off) + select length(max(s.t)) + from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); + QUERY PLAN +---------------------------------------------------------------- + Finalize Aggregate + -> Gather + Workers Planned: 2 + -> Partial Aggregate + -> Parallel Hash Left Join + Hash Cond: (wide.id = wide_1.id) + -> Parallel Seq Scan on wide + -> Parallel Hash + -> Parallel Seq Scan on wide wide_1 +(9 rows) + +select length(max(s.t)) +from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); + length +-------- + 320000 +(1 row) + +select final > 1 as multibatch + from hash_join_batches( +$$ + select length(max(s.t)) + from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); +$$); + multibatch +------------ + t +(1 row) + +rollback to settings; +rollback; diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 3afe39d2d9..a77a8db93f 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -38,7 +38,6 @@ d_star|f date_tbl|f default_tbl|f defaultexpr_tbl|f -delete_test_table|t dept|f dupindexcols|t e_star|f diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index f320fb6ef3..4c6dad8b5a 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -55,7 +55,7 @@ test: copy copyselect copydml # ---------- test: create_misc create_operator create_procedure # These depend on the above two -test: create_index create_view index_including index_including_gist +test: create_index create_index_spgist create_view index_including index_including_gist # ---------- # Another group of parallel tests @@ -84,7 +84,7 @@ test: select_into select_distinct select_distinct_on select_implicit select_havi # ---------- # Another group of parallel tests # ---------- -test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated +test: brin gin gist spgist privileges init_privs security_label collate matview lock replica_identity rowsecurity object_address tablesample groupingsets drop_operator password identity generated join_hash # ---------- # Another group of parallel tests diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 36644aa963..296e4cf843 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -63,6 +63,7 @@ test: create_misc test: create_operator test: create_procedure test: create_index +test: create_index_spgist test: index_including test: index_including_gist test: create_view @@ -123,6 +124,7 @@ test: drop_operator test: password test: identity test: generated +test: join_hash test: create_table_like test: alter_generic test: alter_operator diff --git a/src/test/regress/sql/btree_index.sql b/src/test/regress/sql/btree_index.sql index 19fbfa8b72..48eaf4fe42 100644 --- a/src/test/regress/sql/btree_index.sql +++ b/src/test/regress/sql/btree_index.sql @@ -120,3 +120,23 @@ create index btree_idx_err on btree_test(a) with (vacuum_cleanup_index_scale_fac -- Simple ALTER INDEX alter index btree_idx1 set (vacuum_cleanup_index_scale_factor = 70.0); select reloptions from pg_class WHERE oid = 'btree_idx1'::regclass; + +-- +-- Test for multilevel page deletion +-- +CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); +INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; +ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); +-- Delete most entries, and vacuum, deleting internal pages and creating "fast +-- root" +DELETE FROM delete_test_table WHERE a < 79990; +VACUUM delete_test_table; + +-- +-- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META +-- WAL record type). This happens when a "fast root" page is split. This +-- also creates coverage for nbtree FSM page recycling. +-- +-- The vacuum above should've turned the leaf page into a fast root. We just +-- need to insert some rows to cause the fast root page to split. +INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; diff --git a/src/test/regress/sql/create_index.sql b/src/test/regress/sql/create_index.sql index 4d2535b482..2f0e9a63e6 100644 --- a/src/test/regress/sql/create_index.sql +++ b/src/test/regress/sql/create_index.sql @@ -97,35 +97,7 @@ CREATE INDEX ggpolygonind ON gpolygon_tbl USING gist (f1); CREATE INDEX ggcircleind ON gcircle_tbl USING gist (f1); -- --- SP-GiST --- - -CREATE TABLE quad_point_tbl AS - SELECT point(unique1,unique2) AS p FROM tenk1; - -INSERT INTO quad_point_tbl - SELECT '(333.0,400.0)'::point FROM generate_series(1,1000); - -INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); - -CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); - -CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; - -CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); - -CREATE TABLE radix_text_tbl AS - SELECT name AS t FROM road WHERE name !~ '^[0-9]'; - -INSERT INTO radix_text_tbl - SELECT 'P0123456789abcdef' FROM generate_series(1,1000); -INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); -INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); - -CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); - --- --- Test GiST and SP-GiST indexes +-- Test GiST indexes -- -- get non-indexed results for comparison purposes @@ -178,66 +150,6 @@ SELECT * FROM point_tbl WHERE f1 IS NOT NULL ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - -SELECT count(*) FROM quad_point_tbl; - -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - -CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; - -CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT circle_center(f1), round(radius(f1)) as radius FROM gcircle_tbl ORDER BY f1 <-> '(200,300)'::point LIMIT 10; @@ -335,196 +247,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl; -SELECT count(*) FROM quad_point_tbl; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; -CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl; -SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; -CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM quad_point_tbl WHERE p IS NOT NULL; -SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx -ON seq.n = idx.n -AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl; -CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl; -SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS -SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p -FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM kd_point_tbl WHERE p IS NOT NULL; -CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS -SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p -FROM kd_point_tbl WHERE p IS NOT NULL; -SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx -ON seq.n = idx.n AND -(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) -WHERE seq.n IS NULL OR idx.n IS NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - EXPLAIN (COSTS OFF) SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; SELECT * FROM gpolygon_tbl ORDER BY f1 <-> '(0,0)'::point LIMIT 10; @@ -542,130 +264,6 @@ EXPLAIN (COSTS OFF) SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; SELECT * FROM point_tbl WHERE f1 <@ '(-10,-10),(10,10)':: box ORDER BY f1 <-> '0,1'; -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; -SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; -SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl; -SELECT count(*) FROM quad_point_tbl; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; -SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; -SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; -SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; -SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; -SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; -SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; -SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; -SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; -SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; -SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; - -EXPLAIN (COSTS OFF) -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; -SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; - RESET enable_seqscan; RESET enable_indexscan; RESET enable_bitmapscan; @@ -1140,26 +738,6 @@ explain (costs off) explain (costs off) select * from boolindex where b is false order by i desc limit 10; --- --- Test for multilevel page deletion --- -CREATE TABLE delete_test_table (a bigint, b bigint, c bigint, d bigint); -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,80000) i; -ALTER TABLE delete_test_table ADD PRIMARY KEY (a,b,c,d); --- Delete most entries, and vacuum, deleting internal pages and creating "fast --- root" -DELETE FROM delete_test_table WHERE a < 79990; -VACUUM delete_test_table; - --- --- Test B-tree insertion with a metapage update (XLOG_BTREE_INSERT_META --- WAL record type). This happens when a "fast root" page is split. This --- also creates coverage for nbtree FSM page recycling. --- --- The vacuum above should've turned the leaf page into a fast root. We just --- need to insert some rows to cause the fast root page to split. -INSERT INTO delete_test_table SELECT i, 1, 2, 3 FROM generate_series(1,1000) i; - -- -- REINDEX (VERBOSE) -- diff --git a/src/test/regress/sql/create_index_spgist.sql b/src/test/regress/sql/create_index_spgist.sql new file mode 100644 index 0000000000..6ada702280 --- /dev/null +++ b/src/test/regress/sql/create_index_spgist.sql @@ -0,0 +1,421 @@ +-- +-- SP-GiST index tests +-- + +CREATE TABLE quad_point_tbl AS + SELECT point(unique1,unique2) AS p FROM tenk1; + +INSERT INTO quad_point_tbl + SELECT '(333.0,400.0)'::point FROM generate_series(1,1000); + +INSERT INTO quad_point_tbl VALUES (NULL), (NULL), (NULL); + +CREATE INDEX sp_quad_ind ON quad_point_tbl USING spgist (p); + +CREATE TABLE kd_point_tbl AS SELECT * FROM quad_point_tbl; + +CREATE INDEX sp_kd_ind ON kd_point_tbl USING spgist (p kd_point_ops); + +CREATE TABLE radix_text_tbl AS + SELECT name AS t FROM road WHERE name !~ '^[0-9]'; + +INSERT INTO radix_text_tbl + SELECT 'P0123456789abcdef' FROM generate_series(1,1000); +INSERT INTO radix_text_tbl VALUES ('P0123456789abcde'); +INSERT INTO radix_text_tbl VALUES ('P0123456789abcdefF'); + +CREATE INDEX sp_radix_ind ON radix_text_tbl USING spgist (t); + +-- get non-indexed results for comparison purposes + +SET enable_seqscan = ON; +SET enable_indexscan = OFF; +SET enable_bitmapscan = OFF; + +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + +SELECT count(*) FROM quad_point_tbl; + +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + +CREATE TEMP TABLE quad_point_tbl_ord_seq1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; + +CREATE TEMP TABLE quad_point_tbl_ord_seq2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +CREATE TEMP TABLE quad_point_tbl_ord_seq3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + +-- Now check the results from plain indexscan +SET enable_seqscan = OFF; +SET enable_indexscan = ON; +SET enable_bitmapscan = OFF; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl; +SELECT count(*) FROM quad_point_tbl; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; +CREATE TEMP TABLE quad_point_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl; +SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN quad_point_tbl_ord_idx1 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +CREATE TEMP TABLE quad_point_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN quad_point_tbl_ord_idx2 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; +CREATE TEMP TABLE quad_point_tbl_ord_idx3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM quad_point_tbl WHERE p IS NOT NULL; +SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN quad_point_tbl_ord_idx3 idx +ON seq.n = idx.n +AND (seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl; +CREATE TEMP TABLE kd_point_tbl_ord_idx1 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl; +SELECT * FROM quad_point_tbl_ord_seq1 seq FULL JOIN kd_point_tbl_ord_idx1 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +CREATE TEMP TABLE kd_point_tbl_ord_idx2 AS +SELECT rank() OVER (ORDER BY p <-> '0,0') n, p <-> '0,0' dist, p +FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT * FROM quad_point_tbl_ord_seq2 seq FULL JOIN kd_point_tbl_ord_idx2 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM kd_point_tbl WHERE p IS NOT NULL; +CREATE TEMP TABLE kd_point_tbl_ord_idx3 AS +SELECT rank() OVER (ORDER BY p <-> '333,400') n, p <-> '333,400' dist, p +FROM kd_point_tbl WHERE p IS NOT NULL; +SELECT * FROM quad_point_tbl_ord_seq3 seq FULL JOIN kd_point_tbl_ord_idx3 idx +ON seq.n = idx.n AND +(seq.dist = idx.dist AND seq.p ~= idx.p OR seq.p IS NULL AND idx.p IS NULL) +WHERE seq.n IS NULL OR idx.n IS NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + +-- Now check the results from bitmap indexscan +SET enable_seqscan = OFF; +SET enable_indexscan = OFF; +SET enable_bitmapscan = ON; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; +SELECT count(*) FROM quad_point_tbl WHERE p IS NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; +SELECT count(*) FROM quad_point_tbl WHERE p IS NOT NULL; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl; +SELECT count(*) FROM quad_point_tbl; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT count(*) FROM quad_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; +SELECT count(*) FROM quad_point_tbl WHERE box '(200,200,1000,1000)' @> p; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p << '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p >> '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p <^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; +SELECT count(*) FROM quad_point_tbl WHERE p >^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; +SELECT count(*) FROM quad_point_tbl WHERE p ~= '(4585, 365)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; +SELECT count(*) FROM kd_point_tbl WHERE p <@ box '(200,200,1000,1000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; +SELECT count(*) FROM kd_point_tbl WHERE box '(200,200,1000,1000)' @> p; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p << '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p >> '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p <^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; +SELECT count(*) FROM kd_point_tbl WHERE p >^ '(5000, 4000)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; +SELECT count(*) FROM kd_point_tbl WHERE p ~= '(4585, 365)'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdef'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcde'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; +SELECT count(*) FROM radix_text_tbl WHERE t = 'P0123456789abcdefF'; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t < 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t ~<~ 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t <= 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t ~<=~ 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; +SELECT count(*) FROM radix_text_tbl WHERE t = 'Aztec Ct '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t = 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t >= 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t ~>=~ 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t > 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; +SELECT count(*) FROM radix_text_tbl WHERE t ~>~ 'Worth St '; + +EXPLAIN (COSTS OFF) +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; +SELECT count(*) FROM radix_text_tbl WHERE t ^@ 'Worth'; + +RESET enable_seqscan; +RESET enable_indexscan; +RESET enable_bitmapscan; diff --git a/src/test/regress/sql/join.sql b/src/test/regress/sql/join.sql index c247509a56..bf6d5c3ae4 100644 --- a/src/test/regress/sql/join.sql +++ b/src/test/regress/sql/join.sql @@ -2067,473 +2067,3 @@ where exists (select 1 from j3 and t1.unique1 < 1; drop table j3; - --- --- exercises for the hash join code --- - -begin; - -set local min_parallel_table_scan_size = 0; -set local parallel_setup_cost = 0; - --- Extract bucket and batch counts from an explain analyze plan. In --- general we can't make assertions about how many batches (or --- buckets) will be required because it can vary, but we can in some --- special cases and we can check for growth. -create or replace function find_hash(node json) -returns json language plpgsql -as -$$ -declare - x json; - child json; -begin - if node->>'Node Type' = 'Hash' then - return node; - else - for child in select json_array_elements(node->'Plans') - loop - x := find_hash(child); - if x is not null then - return x; - end if; - end loop; - return null; - end if; -end; -$$; -create or replace function hash_join_batches(query text) -returns table (original int, final int) language plpgsql -as -$$ -declare - whole_plan json; - hash_node json; -begin - for whole_plan in - execute 'explain (analyze, format ''json'') ' || query - loop - hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); - original := hash_node->>'Original Hash Batches'; - final := hash_node->>'Hash Batches'; - return next; - end loop; -end; -$$; - --- Make a simple relation with well distributed keys and correctly --- estimated size. -create table simple as - select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table simple set (parallel_workers = 2); -analyze simple; - --- Make a relation whose size we will under-estimate. We want stats --- to say 1000 rows, but actually there are 20,000 rows. -create table bigger_than_it_looks as - select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; -alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); -alter table bigger_than_it_looks set (parallel_workers = 2); -analyze bigger_than_it_looks; -update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; - --- Make a relation whose size we underestimate and that also has a --- kind of skew that breaks our batching scheme. We want stats to say --- 2 rows, but actually there are 20,000 rows with the same key. -create table extremely_skewed (id int, t text); -alter table extremely_skewed set (autovacuum_enabled = 'false'); -alter table extremely_skewed set (parallel_workers = 2); -analyze extremely_skewed; -insert into extremely_skewed - select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' - from generate_series(1, 20000); -update pg_class - set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 - where relname = 'extremely_skewed'; - --- Make a relation with a couple of enormous tuples. -create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; -alter table wide set (parallel_workers = 2); - --- The "optimal" case: the hash table fits in memory; we plan for 1 --- batch, we stick to that number, and peak memory usage stays within --- our work_mem budget - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '4MB'; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- The "good" case: batches required, but we plan the right number; we --- plan for some number of batches, and we stick to that number, and --- peak memory usage says within our work_mem budget - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join simple s using (id); -select count(*) from simple r join simple s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- The "bad" case: during execution we need to increase number of --- batches; in this case we plan for 1 batch, and increase at least a --- couple of times, and peak memory usage stays within our work_mem --- budget - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); -select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); -$$); -rollback to settings; - --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); -select count(*) from simple r join bigger_than_it_looks s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); -rollback to settings; - --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '192kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join bigger_than_it_looks s using (id); -select count(*) from simple r join bigger_than_it_looks s using (id); -select original > 1 as initially_multibatch, final > original as increased_batches - from hash_join_batches( -$$ - select count(*) from simple r join bigger_than_it_looks s using (id); -$$); -rollback to settings; - --- The "ugly" case: increasing the number of batches during execution --- doesn't help, so stop trying to fit in work_mem and hope for the --- best; in this case we plan for 1 batch, increases just once and --- then stop increasing because that didn't help at all, so we blow --- right through the work_mem budget and hope for the best... - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -set local work_mem = '128kB'; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); -select count(*) from simple r join extremely_skewed s using (id); -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); -rollback to settings; - --- parallel with parallel-oblivious hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '128kB'; -set local enable_parallel_hash = off; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); -select count(*) from simple r join extremely_skewed s using (id); -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); -rollback to settings; - --- parallel with parallel-aware hash join -savepoint settings; -set local max_parallel_workers_per_gather = 1; -set local work_mem = '128kB'; -set local enable_parallel_hash = on; -explain (costs off) - select count(*) from simple r join extremely_skewed s using (id); -select count(*) from simple r join extremely_skewed s using (id); -select * from hash_join_batches( -$$ - select count(*) from simple r join extremely_skewed s using (id); -$$); -rollback to settings; - --- A couple of other hash join tests unrelated to work_mem management. - --- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate -savepoint settings; -set local max_parallel_workers_per_gather = 2; -set local work_mem = '4MB'; -set local parallel_leader_participation = off; -select * from hash_join_batches( -$$ - select count(*) from simple r join simple s using (id); -$$); -rollback to settings; - --- Exercise rescans. We'll turn off parallel_leader_participation so --- that we can check that instrumentation comes back correctly. - -create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; -alter table join_foo set (parallel_workers = 0); -create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; -alter table join_bar set (parallel_workers = 2); - --- multi-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); -rollback to settings; - --- single-batch with rescan, parallel-oblivious -savepoint settings; -set enable_parallel_hash = off; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); -rollback to settings; - --- multi-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '64kB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); -rollback to settings; - --- single-batch with rescan, parallel-aware -savepoint settings; -set enable_parallel_hash = on; -set parallel_leader_participation = off; -set min_parallel_table_scan_size = 0; -set parallel_setup_cost = 0; -set parallel_tuple_cost = 0; -set max_parallel_workers_per_gather = 2; -set enable_material = off; -set enable_mergejoin = off; -set work_mem = '4MB'; -explain (costs off) - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -select final > 1 as multibatch - from hash_join_batches( -$$ - select count(*) from join_foo - left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss - on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; -$$); -rollback to settings; - --- A full outer join where every record is matched. - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); -select count(*) from simple r full outer join simple s using (id); -rollback to settings; - --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s using (id); -select count(*) from simple r full outer join simple s using (id); -rollback to settings; - --- An full outer join where every record is not matched. - --- non-parallel -savepoint settings; -set local max_parallel_workers_per_gather = 0; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); -rollback to settings; - --- parallelism not possible with parallel-oblivious outer hash join -savepoint settings; -set local max_parallel_workers_per_gather = 2; -explain (costs off) - select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); -select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); -rollback to settings; - --- exercise special code paths for huge tuples (note use of non-strict --- expression and left join required to get the detoasted tuple into --- the hash table) - --- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and --- sts_puttuple oversized tuple cases because it's multi-batch) -savepoint settings; -set max_parallel_workers_per_gather = 2; -set enable_parallel_hash = on; -set work_mem = '128kB'; -explain (costs off) - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); -select length(max(s.t)) -from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); -select final > 1 as multibatch - from hash_join_batches( -$$ - select length(max(s.t)) - from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); -$$); -rollback to settings; - -rollback; diff --git a/src/test/regress/sql/join_hash.sql b/src/test/regress/sql/join_hash.sql new file mode 100644 index 0000000000..ae352e9b0b --- /dev/null +++ b/src/test/regress/sql/join_hash.sql @@ -0,0 +1,469 @@ +-- +-- exercises for the hash join code +-- + +begin; + +set local min_parallel_table_scan_size = 0; +set local parallel_setup_cost = 0; + +-- Extract bucket and batch counts from an explain analyze plan. In +-- general we can't make assertions about how many batches (or +-- buckets) will be required because it can vary, but we can in some +-- special cases and we can check for growth. +create or replace function find_hash(node json) +returns json language plpgsql +as +$$ +declare + x json; + child json; +begin + if node->>'Node Type' = 'Hash' then + return node; + else + for child in select json_array_elements(node->'Plans') + loop + x := find_hash(child); + if x is not null then + return x; + end if; + end loop; + return null; + end if; +end; +$$; +create or replace function hash_join_batches(query text) +returns table (original int, final int) language plpgsql +as +$$ +declare + whole_plan json; + hash_node json; +begin + for whole_plan in + execute 'explain (analyze, format ''json'') ' || query + loop + hash_node := find_hash(json_extract_path(whole_plan, '0', 'Plan')); + original := hash_node->>'Original Hash Batches'; + final := hash_node->>'Hash Batches'; + return next; + end loop; +end; +$$; + +-- Make a simple relation with well distributed keys and correctly +-- estimated size. +create table simple as + select generate_series(1, 20000) AS id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +alter table simple set (parallel_workers = 2); +analyze simple; + +-- Make a relation whose size we will under-estimate. We want stats +-- to say 1000 rows, but actually there are 20,000 rows. +create table bigger_than_it_looks as + select generate_series(1, 20000) as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +alter table bigger_than_it_looks set (autovacuum_enabled = 'false'); +alter table bigger_than_it_looks set (parallel_workers = 2); +analyze bigger_than_it_looks; +update pg_class set reltuples = 1000 where relname = 'bigger_than_it_looks'; + +-- Make a relation whose size we underestimate and that also has a +-- kind of skew that breaks our batching scheme. We want stats to say +-- 2 rows, but actually there are 20,000 rows with the same key. +create table extremely_skewed (id int, t text); +alter table extremely_skewed set (autovacuum_enabled = 'false'); +alter table extremely_skewed set (parallel_workers = 2); +analyze extremely_skewed; +insert into extremely_skewed + select 42 as id, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' + from generate_series(1, 20000); +update pg_class + set reltuples = 2, relpages = pg_relation_size('extremely_skewed') / 8192 + where relname = 'extremely_skewed'; + +-- Make a relation with a couple of enormous tuples. +create table wide as select generate_series(1, 2) as id, rpad('', 320000, 'x') as t; +alter table wide set (parallel_workers = 2); + +-- The "optimal" case: the hash table fits in memory; we plan for 1 +-- batch, we stick to that number, and peak memory usage stays within +-- our work_mem budget + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '4MB'; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- The "good" case: batches required, but we plan the right number; we +-- plan for some number of batches, and we stick to that number, and +-- peak memory usage says within our work_mem budget + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '192kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join simple s using (id); +select count(*) from simple r join simple s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- The "bad" case: during execution we need to increase number of +-- batches; in this case we plan for 1 batch, and increase at least a +-- couple of times, and peak memory usage stays within our work_mem +-- budget + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); +select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) FROM simple r JOIN bigger_than_it_looks s USING (id); +$$); +rollback to settings; + +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join bigger_than_it_looks s using (id); +select count(*) from simple r join bigger_than_it_looks s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join bigger_than_it_looks s using (id); +$$); +rollback to settings; + +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 1; +set local work_mem = '192kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join bigger_than_it_looks s using (id); +select count(*) from simple r join bigger_than_it_looks s using (id); +select original > 1 as initially_multibatch, final > original as increased_batches + from hash_join_batches( +$$ + select count(*) from simple r join bigger_than_it_looks s using (id); +$$); +rollback to settings; + +-- The "ugly" case: increasing the number of batches during execution +-- doesn't help, so stop trying to fit in work_mem and hope for the +-- best; in this case we plan for 1 batch, increases just once and +-- then stop increasing because that didn't help at all, so we blow +-- right through the work_mem budget and hope for the best... + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +set local work_mem = '128kB'; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); +select count(*) from simple r join extremely_skewed s using (id); +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); +rollback to settings; + +-- parallel with parallel-oblivious hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '128kB'; +set local enable_parallel_hash = off; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); +select count(*) from simple r join extremely_skewed s using (id); +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); +rollback to settings; + +-- parallel with parallel-aware hash join +savepoint settings; +set local max_parallel_workers_per_gather = 1; +set local work_mem = '128kB'; +set local enable_parallel_hash = on; +explain (costs off) + select count(*) from simple r join extremely_skewed s using (id); +select count(*) from simple r join extremely_skewed s using (id); +select * from hash_join_batches( +$$ + select count(*) from simple r join extremely_skewed s using (id); +$$); +rollback to settings; + +-- A couple of other hash join tests unrelated to work_mem management. + +-- Check that EXPLAIN ANALYZE has data even if the leader doesn't participate +savepoint settings; +set local max_parallel_workers_per_gather = 2; +set local work_mem = '4MB'; +set local parallel_leader_participation = off; +select * from hash_join_batches( +$$ + select count(*) from simple r join simple s using (id); +$$); +rollback to settings; + +-- Exercise rescans. We'll turn off parallel_leader_participation so +-- that we can check that instrumentation comes back correctly. + +create table join_foo as select generate_series(1, 3) as id, 'xxxxx'::text as t; +alter table join_foo set (parallel_workers = 0); +create table join_bar as select generate_series(1, 10000) as id, 'xxxxx'::text as t; +alter table join_bar set (parallel_workers = 2); + +-- multi-batch with rescan, parallel-oblivious +savepoint settings; +set enable_parallel_hash = off; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '64kB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); +rollback to settings; + +-- single-batch with rescan, parallel-oblivious +savepoint settings; +set enable_parallel_hash = off; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '4MB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); +rollback to settings; + +-- multi-batch with rescan, parallel-aware +savepoint settings; +set enable_parallel_hash = on; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '64kB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); +rollback to settings; + +-- single-batch with rescan, parallel-aware +savepoint settings; +set enable_parallel_hash = on; +set parallel_leader_participation = off; +set min_parallel_table_scan_size = 0; +set parallel_setup_cost = 0; +set parallel_tuple_cost = 0; +set max_parallel_workers_per_gather = 2; +set enable_material = off; +set enable_mergejoin = off; +set work_mem = '4MB'; +explain (costs off) + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +select final > 1 as multibatch + from hash_join_batches( +$$ + select count(*) from join_foo + left join (select b1.id, b1.t from join_bar b1 join join_bar b2 using (id)) ss + on join_foo.id < ss.id + 1 and join_foo.id > ss.id - 1; +$$); +rollback to settings; + +-- A full outer join where every record is matched. + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +explain (costs off) + select count(*) from simple r full outer join simple s using (id); +select count(*) from simple r full outer join simple s using (id); +rollback to settings; + +-- parallelism not possible with parallel-oblivious outer hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +explain (costs off) + select count(*) from simple r full outer join simple s using (id); +select count(*) from simple r full outer join simple s using (id); +rollback to settings; + +-- An full outer join where every record is not matched. + +-- non-parallel +savepoint settings; +set local max_parallel_workers_per_gather = 0; +explain (costs off) + select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); +select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); +rollback to settings; + +-- parallelism not possible with parallel-oblivious outer hash join +savepoint settings; +set local max_parallel_workers_per_gather = 2; +explain (costs off) + select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); +select count(*) from simple r full outer join simple s on (r.id = 0 - s.id); +rollback to settings; + +-- exercise special code paths for huge tuples (note use of non-strict +-- expression and left join required to get the detoasted tuple into +-- the hash table) + +-- parallel with parallel-aware hash join (hits ExecParallelHashLoadTuple and +-- sts_puttuple oversized tuple cases because it's multi-batch) +savepoint settings; +set max_parallel_workers_per_gather = 2; +set enable_parallel_hash = on; +set work_mem = '128kB'; +explain (costs off) + select length(max(s.t)) + from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); +select length(max(s.t)) +from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); +select final > 1 as multibatch + from hash_join_batches( +$$ + select length(max(s.t)) + from wide left join (select id, coalesce(t, '') || '' as t from wide) s using (id); +$$); +rollback to settings; + +rollback;