static bool pgpa_join_order_permits_join(int outer_count, int inner_count,
pgpa_trove_key *tkeys,
char *plan_name,
- pgpa_advice_target *target);
+ pgpa_trove_entry *entry);
static bool pgpa_join_method_permits_join(int outer_count, int inner_count,
pgpa_trove_key *tkeys,
char *plan_name,
- pgpa_advice_target *target,
+ pgpa_trove_entry *entry,
bool *restrict_method);
+static void pgpa_set_entry_flags(pgpa_trove_entry *entries, Bitmapset *indexes,
+ int flags);
static inline void pgpa_ri_checker_save(pgpa_planner_state *pps,
PlannerInfo *root,
RelOptInfo *rel);
-static void pgpa_ri_checker_validate(pgpa_planner_state *pps, PlannedStmt *pstmt);
+static void pgpa_ri_checker_validate(pgpa_planner_state *pps,
+ PlannedStmt *pstmt);
/*
* Install planner-related hooks.
}
}
+/*
+ * XXX obviously not the right thing
+ *
+ * XXX note that any overlap between troves is a real problem here -- we need
+ * one trove entry per tag/target combo
+ */
+static void
+pgpa_planner_stupid_warning(pgpa_trove *trove, pgpa_trove_lookup_type type)
+{
+ pgpa_trove_entry *entries;
+ int nentries;
+
+ pgpa_trove_lookup_all(trove, type, &entries, &nentries);
+ for (int i = 0; i < nentries; ++i)
+ {
+ pgpa_trove_entry *entry = &entries[i];
+
+ elog(WARNING, "%s -> %d", pgpa_cstring_trove_entry(entry),
+ entry->flags);
+ }
+}
+
/*
* Carry out whatever work we want to do after planning is complete.
*/
/* Fetch our private state, set up by pgpa_planner_setup(). */
pps = GetPlannerGlobalExtensionState(glob, planner_extension_id);
+ if (pps != NULL && pps->trove != NULL)
+ {
+ pgpa_planner_stupid_warning(pps->trove, PGPA_TROVE_LOOKUP_SCAN);
+ pgpa_planner_stupid_warning(pps->trove, PGPA_TROVE_LOOKUP_JOIN);
+ }
+
/*
* If assertions are enabled, cross-check the generated range table
* identifiers.
pgpa_join_state *pjs)
{
int i = -1;
- bool join_ok = true;
+ Bitmapset *jo_permit_indexes = NULL;
+ Bitmapset *jo_deny_indexes = NULL;
+ Bitmapset *jm_indexes = NULL;
+ bool jm_conflict = false;
uint32 pgs_mask = 0;
/* Iterate over all possibly-relevant advice. */
while ((i = bms_next_member(pjs->indexes, i)) >= 0)
{
pgpa_trove_entry *entry = &pjs->entries[i];
- uint32 my_pgs_mask = 0;
+ uint32 my_pgs_mask;
/* Handle join order advice. */
if (entry->tag == PGPA_TAG_JOIN_ORDER)
{
- if (!join_ok)
- continue;
- if (!pgpa_join_order_permits_join(pjs->outer_count,
- pjs->inner_count,
- pjs->tkeys,
- plan_name,
- entry->target))
- join_ok = false;
+ if (pgpa_join_order_permits_join(pjs->outer_count,
+ pjs->inner_count,
+ pjs->tkeys,
+ plan_name,
+ entry))
+ jo_permit_indexes = bms_add_member(jo_permit_indexes, i);
+ else
+ jo_deny_indexes = bms_add_member(jo_deny_indexes, i);
continue;
}
pjs->inner_count,
pjs->tkeys,
plan_name,
- entry->target,
+ entry,
&restrict_method))
- join_ok = false;
+ jo_deny_indexes = bms_add_member(jo_deny_indexes, i);
else if (restrict_method)
{
- /* XXX wrong way to report problems */
+ jo_permit_indexes = bms_add_member(jo_permit_indexes, i);
+ jm_indexes = bms_add_member(jo_permit_indexes, i);
if (pgs_mask != 0 && pgs_mask != my_pgs_mask)
- elog(WARNING,
- "conflicting masks: %u vs. %u", pgs_mask, my_pgs_mask);
+ jm_conflict = true;
pgs_mask = my_pgs_mask;
}
continue;
pjs->inner_count,
pjs->tkeys,
plan_name,
- entry->target,
+ entry,
&restrict_method))
- join_ok = false;
+ jo_deny_indexes = bms_add_member(jo_deny_indexes, i);
else if (restrict_method)
{
+ jo_permit_indexes = bms_add_member(jo_permit_indexes, i);
if (!jt_unique && !jt_non_unique)
{
- /* XXX wrong way to report problems */
- elog(WARNING, "%s not a semijoin jointype=%d",
- pgpa_cstring_trove_entry(entry), jointype);
+ /*
+ * This doesn't seem to be a semijoin to which SJ_UNIQUE
+ * or SJ_NON_UNIQUE can be applied.
+ */
+ entry->flags |= PGPA_TE_INAPPLICABLE;
}
else if (advice_unique != jt_unique)
- join_ok = false;
+ jo_deny_indexes = bms_add_member(jo_deny_indexes, i);
}
continue;
}
}
- /* Apply computed pgs_mask. */
- if (!join_ok)
+ /*
+ * If the advice indicates both that this join order is permissible and
+ * also that it isn't, then mark advice related to the join order as
+ * conflicting.
+ */
+ if (jo_permit_indexes != NULL && jo_deny_indexes != NULL)
+ {
+ pgpa_set_entry_flags(pjs->entries, jo_permit_indexes,
+ PGPA_TE_CONFLICTING);
+ pgpa_set_entry_flags(pjs->entries, jo_deny_indexes,
+ PGPA_TE_CONFLICTING);
+ }
+
+ /*
+ * If more than one join method specification is relevant here and they
+ * differ, mark them all as conflicting.
+ */
+ if (jm_conflict)
+ pgpa_set_entry_flags(pjs->entries, jm_indexes,
+ PGPA_TE_CONFLICTING);
+
+ /*
+ * If we were advised to deny this join order, then do so. However, if we
+ * were also advised to permit it, then do nothing, since the advice
+ * conflicts.
+ */
+ if (jo_deny_indexes != NULL && jo_permit_indexes == NULL)
*pgs_mask_p = 0;
- else if (pgs_mask != 0)
+
+ /*
+ * If we were advised to restrict the join method, then do so. However,
+ * if we got conflicting join method advice or were also advised to reject
+ * this join order completely, then instead do nothing.
+ */
+ if (pgs_mask != 0 && !jm_conflict && jo_deny_indexes == NULL)
*pgs_mask_p = pgs_mask;
}
pgpa_join_order_permits_join(int outer_count, int inner_count,
pgpa_trove_key *tkeys,
char *plan_name,
- pgpa_advice_target *target)
+ pgpa_trove_entry *entry)
{
bool loop = true;
bool sublist = false;
int length;
int outer_length;
+ pgpa_advice_target *target = entry->target;
pgpa_advice_target *prefix_target;
+ /* We definitely have at least a partial match for this trove entry. */
+ entry->flags |= PGPA_TE_MATCH_PARTIAL;
+
/*
* Find the innermost sublist that contains all keys; if no sublist does,
* then continue processing with the toplevel list.
tkm = pgpa_trove_keys_match_target(inner_count, tkeys + outer_count,
plan_name, inner_target);
+
+ /*
+ * Before returning, consider whether we need to mark this entry as
+ * fully matched. If we found every item but one on the lefthand side
+ * of the join and the last item on the righthand side of the join,
+ * then the answer is yes.
+ */
+ if (outer_length + 1 == length && tkm == PGPA_TKM_EQUAL)
+ entry->flags |= PGPA_TE_MATCH_FULL;
+
return (tkm == PGPA_TKM_EQUAL);
}
pgpa_join_method_permits_join(int outer_count, int inner_count,
pgpa_trove_key *tkeys,
char *plan_name,
- pgpa_advice_target *target,
+ pgpa_trove_entry *entry,
bool *restrict_method)
{
+ pgpa_advice_target *target = entry->target;
pgpa_tkm_type inner_tkm;
pgpa_tkm_type outer_tkm;
pgpa_tkm_type join_tkm;
+ /* We definitely have at least a partial match for this trove entry. */
+ entry->flags |= PGPA_TE_MATCH_PARTIAL;
+
*restrict_method = false;
/*
target);
if (inner_tkm == PGPA_TKM_EQUAL)
{
+ entry->flags |= PGPA_TE_MATCH_FULL;
*restrict_method = true;
return true;
}
#endif
+/*
+ * Set PGPA_TE_* flags on a set of trove entries.
+ */
+static void
+pgpa_set_entry_flags(pgpa_trove_entry *entries, Bitmapset *indexes, int flags)
+{
+ int i = -1;
+
+ while ((i = bms_next_member(indexes, i)) >= 0)
+ {
+ pgpa_trove_entry *entry = &entries[i];
+
+ entry->flags |= flags;
+ }
+}
+
/*
* Save the range table identifier for one relation for future cross-checking.
*/
result->indexes = indexes;
}
+/*
+ * Return all entries in a trove slice to the caller.
+ *
+ * The first two arguments are input arguments, and the remainder are output
+ * arguments.
+ */
+void
+pgpa_trove_lookup_all(pgpa_trove *trove, pgpa_trove_lookup_type type,
+ pgpa_trove_entry **entries, int *nentries)
+{
+ pgpa_trove_slice *tslice;
+
+ if (type == PGPA_TROVE_LOOKUP_SCAN)
+ tslice = &trove->scan;
+ else
+ tslice = &trove->join;
+
+ *entries = tslice->entries;
+ *nentries = tslice->nused;
+}
+
/*
* Match trove keys to advice targets and return an enum value indicating
* the relationship between the set of keys and the set of targets.
StringInfoData buf;
initStringInfo(&buf);
- appendStringInfo(&buf, "%s(", pgpa_cstring_advice_tag(entry->tag));
+ appendStringInfo(&buf, "%s", pgpa_cstring_advice_tag(entry->tag));
+
+ /* JOIN_ORDER tags are transformed by pgpa_build_trove; undo that here */
+ if (entry->tag != PGPA_TAG_JOIN_ORDER)
+ appendStringInfoChar(&buf, '(');
+ else
+ Assert(entry->target->ttype == PGPA_TARGET_ORDERED_LIST);
+
pgpa_format_advice_target(&buf, entry->target);
- appendStringInfoChar(&buf, ')');
+
+ if (entry->tag != PGPA_TAG_JOIN_ORDER)
+ appendStringInfoChar(&buf, ')');
return buf.data;
}
entry = &tslice->entries[tslice->nused];
entry->tag = tag;
entry->target = target;
+ entry->flags = 0;
pgpa_trove_add_to_hash(tslice->hash, target, tslice->nused);
typedef struct pgpa_trove pgpa_trove;
+/*
+ * Flags that can be set on a pgpa_trove_entry to indicate what happened when
+ * trying to plan using advice.
+ *
+ * PGPA_TE_MATCH_PARTIAL means that we found some part of the query that at
+ * least partially matched the target; e.g. given JOIN_ORDER(a b), this would
+ * be set if we ever saw any joinrel including either "a" or "b".
+ *
+ * PGPA_TE_MATCH_FULL means that we found an exact match for the target; e.g.
+ * given JOIN_ORDER(a b), this would be set if we saw a joinrel containing
+ * exactly "a" and "b" and nothing else.
+ *
+ * PGPA_TE_INAPPLICABLE means that the advice doesn't properly apply to the
+ * target; e.g. INDEX_SCAN(foo bar_idx) would be so marked if bar_idx does not
+ * exist on foo. The fact that this bit has been set does not mean that the
+ * advice had no effect.
+ *
+ * PGPA_TE_CONFLICTING means that a conflict was detected between what this
+ * advice wants and what some other plan advice wants; e.g. JOIN_ORDER(a b)
+ * would conflict with HASH_JOIN(a), because the former requires "a" to be the
+ * outer table while the latter requires it to be the inner table.
+ */
+#define PGPA_TE_MATCH_PARTIAL 0x0001
+#define PGPA_TE_MATCH_FULL 0x0002
+#define PGPA_TE_INAPPLICABLE 0x0004
+#define PGPA_TE_CONFLICTING 0x0008
+
/*
* Each entry in a trove of advice represents the application of a tag to
* a single target.
{
pgpa_advice_tag_type tag;
pgpa_advice_target *target;
+ int flags;
} pgpa_trove_entry;
/*
pgpa_trove_key *tkeys,
char *plan_name,
pgpa_trove_result *result);
+extern void pgpa_trove_lookup_all(pgpa_trove *trove,
+ pgpa_trove_lookup_type type,
+ pgpa_trove_entry **entries,
+ int *nentries);
extern pgpa_tkm_type pgpa_trove_keys_match_target(int nkeys,
pgpa_trove_key *tkeys,
char *plan_name,