| -- start_matchsubs | |
| -- m/\(actual rows=[^)]*\)/ | |
| -- s/\(actual rows=[^)]*\)/(actual rows=...)/ | |
| -- end_matchsubs | |
| -- start_matchignore | |
| -- m/^\s*Buckets: \d+ Batches: \d+ Memory Usage: \d+kB/ | |
| -- end_matchignore | |
| CREATE EXTENSION parallel_customscan; | |
| -- Set up data BEFORE enabling our hook so ANALYZE doesn't traverse it. | |
| CREATE TABLE pcs_t (a int) | |
| WITH (parallel_workers = 2) | |
| DISTRIBUTED BY (a); | |
| INSERT INTO pcs_t SELECT generate_series(1, 1000); | |
| ANALYZE pcs_t; | |
| SET optimizer = off; | |
| SET enable_parallel = on; | |
| SET enable_seqscan = 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; | |
| -- (1) Enabling the hook replaces the Parallel Seq Scan with a Custom Scan. | |
| EXPLAIN (COSTS OFF) SELECT count(*) FROM pcs_t; | |
| SET parallel_customscan.enabled = on; | |
| EXPLAIN (COSTS OFF) SELECT count(*) FROM pcs_t; | |
| -- (2) Correctness: results match only if every parallel worker runs our scan. | |
| SELECT count(*) FROM pcs_t; | |
| SELECT sum(a) FROM pcs_t; | |
| -- (3) Scan-level qualifier and projection through the custom scan. | |
| EXPLAIN (COSTS OFF) SELECT a FROM pcs_t WHERE a > 990; | |
| SELECT a FROM pcs_t WHERE a > 990 ORDER BY a; | |
| SELECT count(*) FROM pcs_t WHERE a % 2 = 0; | |
| -- (4) Join with both inputs scanned by the custom scan, plus a scan-level qual. | |
| CREATE TABLE pcs_t2 (a int) | |
| WITH (parallel_workers = 2) | |
| DISTRIBUTED BY (a); | |
| INSERT INTO pcs_t2 SELECT generate_series(1, 500); | |
| ANALYZE pcs_t2; | |
| EXPLAIN (COSTS OFF) | |
| SELECT count(*) FROM pcs_t x JOIN pcs_t2 y ON x.a = y.a WHERE x.a <= 100; | |
| SELECT count(*) FROM pcs_t x JOIN pcs_t2 y ON x.a = y.a WHERE x.a <= 100; | |
| -- (5) Empty relation: the custom scan must handle an immediate end-of-scan. | |
| CREATE TABLE pcs_empty (a int) | |
| WITH (parallel_workers = 2) | |
| DISTRIBUTED BY (a); | |
| ANALYZE pcs_empty; | |
| EXPLAIN (COSTS OFF) SELECT count(*) FROM pcs_empty; | |
| SELECT count(*) FROM pcs_empty; | |
| SELECT * FROM pcs_empty; | |
| -- (6) Serial path: with no workers, a non-parallel Custom Scan is used. | |
| SET max_parallel_workers_per_gather = 0; | |
| EXPLAIN (COSTS OFF) SELECT count(*) FROM pcs_t; | |
| SELECT count(*) FROM pcs_t; | |
| SELECT a FROM pcs_t WHERE a > 995 ORDER BY a; | |
| SET max_parallel_workers_per_gather = 2; | |
| -- (7) EXPLAIN ANALYZE: exercises planstate_walk_kids' custom_ps recursion. | |
| EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) | |
| SELECT count(*) FROM pcs_t; | |
| EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF, SUMMARY OFF) | |
| SELECT count(*) FROM pcs_t x JOIN pcs_t2 y ON x.a = y.a; | |
| -- cleanup | |
| DROP TABLE pcs_t2; | |
| DROP TABLE pcs_empty; | |
| DROP TABLE pcs_t; | |
| DROP EXTENSION parallel_customscan; |