LOAD 'pg_plan_advice';
SET max_parallel_workers_per_gather = 0;
SET seq_page_cost = 0.1;
SET random_page_cost = 0.1;
SET cpu_tuple_cost = 0;
SET cpu_index_tuple_cost = 0;

CREATE TABLE scan_table (a int primary key, b text)
	WITH (autovacuum_enabled = false);
INSERT INTO scan_table
	SELECT g, 'some text ' || g FROM generate_series(1, 100000) g;
CREATE INDEX scan_table_b ON scan_table USING brin (b);
VACUUM ANALYZE scan_table;

-- Sequential scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;

-- Index scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;

-- Index-only scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;

-- Bitmap heap scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE b > 'some text 8';

-- TID scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE ctid = '(0,1)';

-- TID range scan
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE ctid > '(1,1)' AND ctid < '(2,1)';

-- Try forcing each of our test queries to use the scan type they
-- wanted to use anyway. This should succeed.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'BITMAP_HEAP_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE b > 'some text 8';
SET LOCAL pg_plan_advice.advice = 'TID_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE ctid = '(0,1)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE ctid > '(1,1)' AND ctid < '(2,1)';
COMMIT;

-- Try to force a full scan of the table to use some other scan type. All
-- of these will fail. An index scan or bitmap heap scan could potentially
-- generate the correct answer, but the planner does not even consider these
-- possibilities due to the lack of a WHERE clause.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;
SET LOCAL pg_plan_advice.advice = 'BITMAP_HEAP_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;
SET LOCAL pg_plan_advice.advice = 'TID_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table;
COMMIT;

-- Try again to force index use. This should now succeed for the INDEX_SCAN
-- and BITMAP_HEAP_SCAN, but the INDEX_ONLY_SCAN can't be forced because the
-- query fetches columns not included in the index.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a > 0;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a > 0;
SET LOCAL pg_plan_advice.advice = 'BITMAP_HEAP_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a > 0;
COMMIT;

-- We can force a primary key lookup to use a sequential scan, but we
-- can't force it to use an index-only scan (due to the column list)
-- or a TID scan (due to the absence of a TID qual). If we apply DO_NOT_SCAN
-- here, we should get a valid plan anyway, but with the scan disabled.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'TID_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'DO_NOT_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
COMMIT;

-- We can forcibly downgrade an index-only scan to an index scan, but we can't
-- force the use of an index that the planner thinks is inapplicable.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table public.scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_b)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
COMMIT;

-- We can force the use of a sequential scan in place of a bitmap heap scan,
-- but a plain index scan on a BRIN index is not possible.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE b > 'some text 8';
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_b)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
COMMIT;

-- We can force the use of a sequential scan rather than a TID scan or
-- TID range scan.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(scan_table)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE ctid = '(0,1)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table
	WHERE ctid > '(1,1)' AND ctid < '(2,1)';
COMMIT;

-- Test more complex scenarios with index scans.
BEGIN;
-- Should still work if we mention the schema.
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table public.scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
-- But not if we mention the wrong schema.
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table cilbup.scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
-- It's OK to repeat the same advice.
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey scan_table scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
-- But it doesn't work if the index target is even notionally different.
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table scan_table_pkey scan_table public.scan_table_pkey)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT * FROM scan_table WHERE a = 1;
COMMIT;

-- Test assorted incorrect advice.
BEGIN;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(nothing)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(nothing whatsoever)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_SCAN(scan_table bogus)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(nothing whatsoever)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
SET LOCAL pg_plan_advice.advice = 'INDEX_ONLY_SCAN(scan_table bogus)';
EXPLAIN (COSTS OFF, PLAN_ADVICE) SELECT a FROM scan_table WHERE a = 1;
COMMIT;

-- Test our ability to refer to multiple instances of the same alias.
BEGIN;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (generate_series(1,10) g LEFT JOIN scan_table s ON g = s.a) x
    LEFT JOIN scan_table s ON g = s.a;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (generate_series(1,10) g LEFT JOIN scan_table s ON g = s.a) x
    LEFT JOIN scan_table s ON g = s.a;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s#2)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (generate_series(1,10) g LEFT JOIN scan_table s ON g = s.a) x
    LEFT JOIN scan_table s ON g = s.a;
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s) SEQ_SCAN(s#2)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (generate_series(1,10) g LEFT JOIN scan_table s ON g = s.a) x
    LEFT JOIN scan_table s ON g = s.a;
COMMIT;

-- Test our ability to refer to scans within a subquery.
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0) x;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0);
BEGIN;
-- Should not match.
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0) x;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0);
-- Should match first query only.
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s@x)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0) x;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0);
-- Should match second query only.
SET LOCAL pg_plan_advice.advice = 'SEQ_SCAN(s@unnamed_subquery)';
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0) x;
EXPLAIN (COSTS OFF, PLAN_ADVICE)
SELECT * FROM (SELECT * FROM scan_table s WHERE a = 1 OFFSET 0);
COMMIT;
