LOAD 'pg_plan_advice'; -- An empty string is allowed. Empty target lists are allowed for most advice -- tags, but not for JOIN_ORDER. "Supplied Plan Advice" should be omitted in -- text format when there is no actual advice, but not in non-text format. SET pg_plan_advice.advice = ''; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------ Result (1 row) SET pg_plan_advice.advice = 'SEQ_SCAN()'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------ Result (1 row) SET pg_plan_advice.advice = 'NESTED_LOOP_PLAIN()'; EXPLAIN (COSTS OFF, FORMAT JSON) SELECT 1; QUERY PLAN -------------------------------- [ + { + "Plan": { + "Node Type": "Result", + "Parallel Aware": false,+ "Async Capable": false, + "Disabled": false + }, + "Supplied Plan Advice": ""+ } + ] (1 row) SET pg_plan_advice.advice = 'JOIN_ORDER()'; ERROR: invalid value for parameter "pg_plan_advice.advice": "JOIN_ORDER()" DETAIL: Could not parse advice: JOIN_ORDER must have at least one target at or near ")" -- Test assorted variations in capitalization, whitespace, and which parts of -- the relation identifier are included. These should all work. SET pg_plan_advice.advice = 'SEQ_SCAN(x)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN --------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'seq_scan(x@y)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ----------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x@y) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_scan(x#2)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ----------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x#2) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_SCAN (x/y)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ----------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x/y) /* not matched */ (3 rows) SET pg_plan_advice.advice = ' SEQ_SCAN ( x / y . z ) '; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x/y.z) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_SCAN("x"#2/"y"."z"@"t")'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ----------------------------------------- Result Supplied Plan Advice: SEQ_SCAN(x#2/y.z@t) /* not matched */ (3 rows) -- Syntax errors. SET pg_plan_advice.advice = 'SEQUENTIAL_SCAN(x)'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQUENTIAL_SCAN(x)" DETAIL: Could not parse advice: syntax error at or near "SEQUENTIAL_SCAN" SET pg_plan_advice.advice = 'SEQ_SCAN'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN" DETAIL: Could not parse advice: syntax error at end of input SET pg_plan_advice.advice = 'SEQ_SCAN('; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN(" DETAIL: Could not parse advice: syntax error at end of input SET pg_plan_advice.advice = 'SEQ_SCAN("'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN("" DETAIL: Could not parse advice: unterminated quoted identifier at end of input SET pg_plan_advice.advice = 'SEQ_SCAN("")'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN("")" DETAIL: Could not parse advice: zero-length delimited identifier at or near """ SET pg_plan_advice.advice = 'SEQ_SCAN("a"'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN("a"" DETAIL: Could not parse advice: syntax error at end of input SET pg_plan_advice.advice = 'SEQ_SCAN(#'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN(#" DETAIL: Could not parse advice: syntax error at or near "#" SET pg_plan_advice.advice = '()'; ERROR: invalid value for parameter "pg_plan_advice.advice": "()" DETAIL: Could not parse advice: syntax error at or near "(" SET pg_plan_advice.advice = '123'; ERROR: invalid value for parameter "pg_plan_advice.advice": "123" DETAIL: Could not parse advice: syntax error at or near "123" -- Tags like SEQ_SCAN and NO_GATHER don't allow sublists at all; other tags, -- except for JOIN_ORDER, allow at most one level of sublist. Hence, these -- examples should error out. SET pg_plan_advice.advice = 'SEQ_SCAN((x))'; ERROR: invalid value for parameter "pg_plan_advice.advice": "SEQ_SCAN((x))" DETAIL: Could not parse advice: syntax error at or near "(" SET pg_plan_advice.advice = 'GATHER(((x)))'; ERROR: invalid value for parameter "pg_plan_advice.advice": "GATHER(((x)))" DETAIL: Could not parse advice: syntax error at or near "(" -- Legal comments. SET pg_plan_advice.advice = '/**/'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------ Result (1 row) SET pg_plan_advice.advice = 'HASH_JOIN(_)/***/'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ---------------------------------- Result Supplied Plan Advice: HASH_JOIN(_) /* not matched */ (3 rows) SET pg_plan_advice.advice = '/* comment */ HASH_JOIN(/*x*/y)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ---------------------------------- Result Supplied Plan Advice: HASH_JOIN(y) /* not matched */ (3 rows) SET pg_plan_advice.advice = '/* comment */ HASH_JOIN(y//*x*/z)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------------------------------ Result Supplied Plan Advice: HASH_JOIN(y/z) /* not matched */ (3 rows) -- Unterminated comments. SET pg_plan_advice.advice = '/*'; ERROR: invalid value for parameter "pg_plan_advice.advice": "/*" DETAIL: Could not parse advice: unterminated comment at end of input SET pg_plan_advice.advice = 'JOIN_ORDER("fOO") /* oops'; ERROR: invalid value for parameter "pg_plan_advice.advice": "JOIN_ORDER("fOO") /* oops" DETAIL: Could not parse advice: unterminated comment at end of input -- Nested comments are not supported, so the first of these is legal and -- the second is not. SET pg_plan_advice.advice = '/*/*/'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------ Result (1 row) SET pg_plan_advice.advice = '/*/* stuff */*/'; ERROR: invalid value for parameter "pg_plan_advice.advice": "/*/* stuff */*/" DETAIL: Could not parse advice: syntax error at or near "*" -- Foreign join requires multiple relation identifiers. SET pg_plan_advice.advice = 'FOREIGN_JOIN(a)'; ERROR: invalid value for parameter "pg_plan_advice.advice": "FOREIGN_JOIN(a)" DETAIL: Could not parse advice: FOREIGN_JOIN targets must contain more than one relation identifier at or near ")" SET pg_plan_advice.advice = 'FOREIGN_JOIN((a))'; ERROR: invalid value for parameter "pg_plan_advice.advice": "FOREIGN_JOIN((a))" DETAIL: Could not parse advice: FOREIGN_JOIN targets must contain more than one relation identifier at or near ")" -- Tag keywords used as alias names work fine, because the 'identifier' -- nonterminal accepts all token types. SET pg_plan_advice.advice = 'SEQ_SCAN(hash_join)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ----------------------------------------- Result Supplied Plan Advice: SEQ_SCAN(hash_join) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_SCAN(seq_scan)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ---------------------------------------- Result Supplied Plan Advice: SEQ_SCAN(seq_scan) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_SCAN(gather)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN -------------------------------------- Result Supplied Plan Advice: SEQ_SCAN(gather) /* not matched */ (3 rows) SET pg_plan_advice.advice = 'SEQ_SCAN(join_order)'; EXPLAIN (COSTS OFF) SELECT 1; QUERY PLAN ------------------------------------------ Result Supplied Plan Advice: SEQ_SCAN(join_order) /* not matched */ (3 rows) -- Tag keywords used as partition names or plan names should also work, -- since pgpa_identifier_string() can generate these from real partition -- and subquery names. SET pg_plan_advice.advice = 'SEQ_SCAN(t/public.hash_join)'; SET pg_plan_advice.advice = 'SEQ_SCAN(t/hash_join.foo)'; SET pg_plan_advice.advice = 'SEQ_SCAN(t@hash_join)'; SET pg_plan_advice.advice = 'SEQ_SCAN(t@seq_scan)';