diff --git a/contrib/pg_plan_advice/expected/join_order.out b/contrib/pg_plan_advice/expected/join_order.out index 7258c4f8964..76df9128703 100644 --- a/contrib/pg_plan_advice/expected/join_order.out +++ b/contrib/pg_plan_advice/expected/join_order.out @@ -249,6 +249,65 @@ SELECT * FROM jo_fact f NO_GATHER(f d1 d2) (18 rows) +COMMIT; +-- Test cases for sublists spanning all targets. +BEGIN; +SET LOCAL pg_plan_advice.advice = 'join_order((f d1 d2))'; +EXPLAIN (COSTS OFF, PLAN_ADVICE) +SELECT * FROM jo_fact f + LEFT JOIN jo_dim1 d1 ON f.dim1_id = d1.id + LEFT JOIN jo_dim2 d2 ON f.dim2_id = d2.id + WHERE val1 = 1 AND val2 = 1; + QUERY PLAN +------------------------------------------ + Hash Join + Hash Cond: (f.dim2_id = d2.id) + -> Hash Join + Hash Cond: (f.dim1_id = d1.id) + -> Seq Scan on jo_fact f + -> Hash + -> Seq Scan on jo_dim1 d1 + Filter: (val1 = 1) + -> Hash + -> Seq Scan on jo_dim2 d2 + Filter: (val2 = 1) + Supplied Plan Advice: + JOIN_ORDER((f d1 d2)) /* matched */ + Generated Plan Advice: + JOIN_ORDER(f d1 d2) + HASH_JOIN(d1 d2) + SEQ_SCAN(f d1 d2) + NO_GATHER(f d1 d2) +(18 rows) + +SET LOCAL pg_plan_advice.advice = 'join_order({f d1 d2})'; +EXPLAIN (COSTS OFF, PLAN_ADVICE) +SELECT * FROM jo_fact f + LEFT JOIN jo_dim1 d1 ON f.dim1_id = d1.id + LEFT JOIN jo_dim2 d2 ON f.dim2_id = d2.id + WHERE val1 = 1 AND val2 = 1; + QUERY PLAN +------------------------------------------ + Hash Join + Hash Cond: (f.dim1_id = d1.id) + -> Hash Join + Hash Cond: (f.dim2_id = d2.id) + -> Seq Scan on jo_fact f + -> Hash + -> Seq Scan on jo_dim2 d2 + Filter: (val2 = 1) + -> Hash + -> Seq Scan on jo_dim1 d1 + Filter: (val1 = 1) + Supplied Plan Advice: + JOIN_ORDER({f d1 d2}) /* matched */ + Generated Plan Advice: + JOIN_ORDER(f d2 d1) + HASH_JOIN(d2 d1) + SEQ_SCAN(f d2 d1) + NO_GATHER(f d1 d2) +(18 rows) + COMMIT; -- Test cases for single-element groupings. The extra grouping levels should -- be ignored. diff --git a/contrib/pg_plan_advice/pgpa_planner.c b/contrib/pg_plan_advice/pgpa_planner.c index c57df4aa7f0..9f05fa033ba 100644 --- a/contrib/pg_plan_advice/pgpa_planner.c +++ b/contrib/pg_plan_advice/pgpa_planner.c @@ -1241,14 +1241,33 @@ pgpa_join_order_permits_join(int outer_count, int inner_count, { if (child_target->ttype == PGPA_TARGET_ORDERED_LIST) { + /* + * JOIN_ORDER((...)) is treated as syntactic sugar; the + * extra level of parentheses does not change the + * interpretation. Hence, don't set sublist = true in that + * case. + */ + if (list_length(target->children) != 1) + sublist = true; target = child_target; - sublist = true; loop = true; break; } else { Assert(child_target->ttype == PGPA_TARGET_UNORDERED_LIST); + + /* + * If an unordered sublist is the entire advice target, + * for example because the user writes JOIN_ORDER({...}) + * or JOIN_ORDER(({...})), and if the join we're + * considering contains exactly the mentioned relations, + * we need to mark this advice as fully matched, because + * that won't happen anywhere else. + */ + if (itm == PGPA_ITM_EQUAL && !sublist && + list_length(target->children) == 1) + entry->flags |= PGPA_FB_MATCH_FULL; return PGPA_JO_INDIFFERENT; } } diff --git a/contrib/pg_plan_advice/sql/join_order.sql b/contrib/pg_plan_advice/sql/join_order.sql index dbc472cd3d0..48405c32ddf 100644 --- a/contrib/pg_plan_advice/sql/join_order.sql +++ b/contrib/pg_plan_advice/sql/join_order.sql @@ -82,6 +82,22 @@ SELECT * FROM jo_fact f WHERE val1 = 1 AND val2 = 1; COMMIT; +-- Test cases for sublists spanning all targets. +BEGIN; +SET LOCAL pg_plan_advice.advice = 'join_order((f d1 d2))'; +EXPLAIN (COSTS OFF, PLAN_ADVICE) +SELECT * FROM jo_fact f + LEFT JOIN jo_dim1 d1 ON f.dim1_id = d1.id + LEFT JOIN jo_dim2 d2 ON f.dim2_id = d2.id + WHERE val1 = 1 AND val2 = 1; +SET LOCAL pg_plan_advice.advice = 'join_order({f d1 d2})'; +EXPLAIN (COSTS OFF, PLAN_ADVICE) +SELECT * FROM jo_fact f + LEFT JOIN jo_dim1 d1 ON f.dim1_id = d1.id + LEFT JOIN jo_dim2 d2 ON f.dim2_id = d2.id + WHERE val1 = 1 AND val2 = 1; +COMMIT; + -- Test cases for single-element groupings. The extra grouping levels should -- be ignored. BEGIN;