bool generate_advice_string;
pgpa_trove *trove;
MemoryContext trove_cxt;
+ List *sj_unique_rels;
#ifdef USE_ASSERT_CHECKING
pgpa_ri_check_hash *ri_check_hash;
Assert(bms_membership(joinrel->relids) == BMS_MULTIPLE);
+ /*
+ * If we're considering implementing a semijoin by making one side unique,
+ * make a note of it in the pgpa_planner_state. See comments for
+ * pgpa_sj_unique_rel for why we do this.
+ */
+ if (jointype == JOIN_UNIQUE_OUTER || jointype == JOIN_UNIQUE_INNER)
+ {
+ pgpa_planner_state *pps;
+ RelOptInfo *uniquerel;
+
+ uniquerel = jointype == JOIN_UNIQUE_OUTER ? outerrel : innerrel;
+ pps = GetPlannerGlobalExtensionState(root->glob, planner_extension_id);
+ if (pps->generate_advice_string)
+ {
+ bool found = false;
+
+ /* Avoid adding duplicates. */
+ foreach_ptr(pgpa_sj_unique_rel, ur, pps->sj_unique_rels)
+ {
+ /*
+ * We should always use the same pointer for the same plan
+ * name, so we need not use strcmp() here.
+ */
+ if (root->plan_name == ur->plan_name &&
+ bms_equal(uniquerel->relids, ur->relids))
+ {
+ found = true;
+ break;
+ }
+ }
+
+ /* If not a duplicate, append to the list. */
+ if (!found)
+ {
+ pgpa_sj_unique_rel *ur = palloc_object(pgpa_sj_unique_rel);
+
+ ur->plan_name = root->plan_name;
+ ur->relids = uniquerel->relids;
+ pps->sj_unique_rels = lappend(pps->sj_unique_rels, ur);
+ }
+ }
+ }
+
/* Get our private state information for this join. */
pjs = pgpa_get_join_state(root, joinrel, outerrel, innerrel);
do_advice_feedback = (trove != NULL && es != NULL);
if (generate_advice_string || do_advice_feedback)
{
- pgpa_plan_walker(&walker, pstmt);
+ pgpa_plan_walker(&walker, pstmt, pps->sj_unique_rels);
rt_identifiers = pgpa_create_identifiers_for_planned_stmt(pstmt);
}
* Top-level entrypoint for the plan tree walk.
*
* Populates walker based on a traversal of the Plan trees in pstmt.
+ *
+ * sj_unique_rels is a list of pgpa_sj_unique_rel objects, one for each
+ * relation we considered making unique as part of semijoin planning.
*/
void
-pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt)
+pgpa_plan_walker(pgpa_plan_walker_context *walker, PlannedStmt *pstmt,
+ List *sj_unique_rels)
{
ListCell *lc;
#include "pgpa_join.h"
#include "pgpa_scan.h"
+/*
+ * When generating advice, we should emit either SEMIJOIN_UNIQUE advice or
+ * SEMIJOIN_NON_UNIQUE advice for each semijoin depending on whether we chose
+ * to implement it as a semijoin or whether we instead chose to make the
+ * nullable side unique and then perform an inner join. When the make-unique
+ * strategy is not chosen, it's not easy to tell from the final plan tree
+ * whether it was considered. That's awkward, because we don't want to emit
+ * useless SEMIJOIN_NON_UNIQUE advice when there was no decision to be made.
+ *
+ * To avoid that, during planning, we create a pgpa_sj_unique_rel for each
+ * relation that we considered making unique for purposes of semijoin planning.
+ */
+typedef struct pgpa_sj_unique_rel
+{
+ char *plan_name;
+ Bitmapset *relids;
+} pgpa_sj_unique_rel;
+
/*
* We use the term "query feature" to refer to plan nodes that are interesting
* in the following way: to generate advice, we'll need to know the set of
} pgpa_plan_walker_context;
extern void pgpa_plan_walker(pgpa_plan_walker_context *walker,
- PlannedStmt *pstmt);
+ PlannedStmt *pstmt,
+ List *sj_unique_rels);
extern void pgpa_add_future_feature(pgpa_plan_walker_context *walker,
pgpa_qf_type type,