From: Robert Haas Date: Tue, 15 Jul 2025 17:13:53 +0000 (-0400) Subject: PGPAQF_SEMIJOIN_(NON_)UNIQUE X-Git-Url: http://git.postgresql.org/gitweb/static/gitweb.js?a=commitdiff_plain;h=ba9f3debdbabbd02dab74de2df88af167b017bb6;p=users%2Frhaas%2Fpostgres.git PGPAQF_SEMIJOIN_(NON_)UNIQUE --- diff --git a/contrib/pg_plan_advice/pgpa_join.c b/contrib/pg_plan_advice/pgpa_join.c index d7a5c99ffd..14c0a444da 100644 --- a/contrib/pg_plan_advice/pgpa_join.c +++ b/contrib/pg_plan_advice/pgpa_join.c @@ -402,6 +402,7 @@ pgpa_decompose_join(pgpa_plan_walker_context *context, Plan *plan, ElidedNode **elidedrealouter, ElidedNode **elidedrealinner) { PlannedStmt *pstmt = context->pstmt; + JoinType jointype = ((Join *) plan)->jointype; Plan *outerplan = plan->lefttree; Plan *innerplan = plan->righttree; ElidedNode *elidedouter; @@ -517,16 +518,31 @@ pgpa_decompose_join(pgpa_plan_walker_context *context, Plan *plan, elidedinner = pgpa_descend_node(pstmt, &innerplan); /* - * If we descended through a unique node, make a note that the identified - * inner or outer subplan should be treated as a query plan feature when - * the main tree traversal reaches it. (We could designate the Agg or - * Unique node itself as the feature, but there shouldn't be any - * RTI-bearing nodes between that node and this one.) + * If this is a semijoin that was converted to an inner join by making + * one side or the other unique, make a note that the inner or outer + * subplan, as appropriate, should be treated as a query plan feature + * when the main tree traversal reaches it. + * + * Conversely, if the planner could have made one side of the join unique + * and thereby converted it to an inner join, and chose not to do so, + * that is also worth noting. (XXX: I'm not sure that we need to emit + * non-unique advice in every case where these join-type tests pass.) + * + * NB: This code could appear slightly higher up in in this function, + * but none of the nodes through which we just descended should be have + * associated RTIs. + * + * NB: This seems like a somewhat hacky way of passing information up + * to the main tree walk, but I don't currently have a better idea. */ if (uniqueouter) - pgpa_add_future_feature(context, PGPAQF_SJ_UNIQUE, outerplan); + pgpa_add_future_feature(context, PGPAQF_SEMIJOIN_UNIQUE, outerplan); + else if (jointype == JOIN_RIGHT_SEMI) + pgpa_add_future_feature(context, PGPAQF_SEMIJOIN_NON_UNIQUE, outerplan); if (uniqueinner) - pgpa_add_future_feature(context, PGPAQF_SJ_UNIQUE, innerplan); + pgpa_add_future_feature(context, PGPAQF_SEMIJOIN_UNIQUE, innerplan); + else if (jointype == JOIN_SEMI) + pgpa_add_future_feature(context, PGPAQF_SEMIJOIN_NON_UNIQUE, innerplan); /* Set output parameters. */ *realouter = outerplan; diff --git a/contrib/pg_plan_advice/pgpa_output.c b/contrib/pg_plan_advice/pgpa_output.c index a8c121d9c8..69ca0c36f4 100644 --- a/contrib/pg_plan_advice/pgpa_output.c +++ b/contrib/pg_plan_advice/pgpa_output.c @@ -147,8 +147,11 @@ pgpa_output_advice(StringInfo buf, pgpa_plan_walker_context *walker, case PGPAQF_GATHER_MERGE: appendStringInfo(buf, "GATHER_MERGE("); break; - case PGPAQF_SJ_UNIQUE: - appendStringInfo(buf, "SJ_UNIQUE("); + case PGPAQF_SEMIJOIN_NON_UNIQUE: + appendStringInfo(buf, "SEMIJOIN_NON_UNIQUE("); + break; + case PGPAQF_SEMIJOIN_UNIQUE: + appendStringInfo(buf, "SEMIJOIN_UNIQUE("); break; } pgpa_output_relations(&context, buf, qf->relids); diff --git a/contrib/pg_plan_advice/pgpa_walker.h b/contrib/pg_plan_advice/pgpa_walker.h index 65a366ee06..223d42d746 100644 --- a/contrib/pg_plan_advice/pgpa_walker.h +++ b/contrib/pg_plan_advice/pgpa_walker.h @@ -23,11 +23,12 @@ * For example, Gather nodes, desiginated by PGPAQF_GATHER, and Gather Merge * nodes, designated by PGPAQF_GATHER_MERGE, are query features, because we'll * want to admit some kind of advice that describes the portion of the plan - * tree that appears beneath those nodes. Whenever we decide to implement a - * semijoin by making one side unique and then performing a normal join, we - * create a feature of PGPAQF_SG_UNIQUE, so that we can describe what RTIs need - * to be made unique to perform the join. XXX We should also probably emit - * advice for the decision NOT to unique-ify a semijoin. + * tree that appears beneath those nodes. + * + * Each semijoin can be implemented either by directly performing a semijoin, + * or by making one side unique and then performing a normal join. Either way, + * we use a query feature to notice what decision was made, so that we can + * describe it by enumerating the RTIs on that side of the join. * * To elaborate on the "no admixture of parent and child RTIs" rule, in all of * these cases, if the entirety of an inheritance hierarchy appears beneath @@ -41,7 +42,8 @@ typedef enum pgpa_qf_type { PGPAQF_GATHER, PGPAQF_GATHER_MERGE, - PGPAQF_SJ_UNIQUE + PGPAQF_SEMIJOIN_NON_UNIQUE, + PGPAQF_SEMIJOIN_UNIQUE } pgpa_qf_type; /*