diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index f0d9e94..995bc6c 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -1340,9 +1340,11 @@ ExplainNode(PlanState *planstate, List *ancestors, if (((NestLoop *) plan)->join.joinqual) show_instrumentation_count("Rows Removed by Join Filter", 1, planstate, es); - show_upper_qual(plan->qual, "Filter", planstate, ancestors, es); - if (plan->qual) - show_instrumentation_count("Rows Removed by Filter", 2, + show_upper_qual(plan->qual, "Other Filter", planstate, ancestors, es); + show_upper_qual(((NestLoop *) plan)->join.filterqual, + "Inner Filter", planstate, ancestors, es); + if (plan->qual || ((NestLoop *) plan)->join.filterqual) + show_instrumentation_count("Rows Removed by Inner/Other Filter", 2, planstate, es); break; case T_MergeJoin: @@ -1353,9 +1355,11 @@ ExplainNode(PlanState *planstate, List *ancestors, if (((MergeJoin *) plan)->join.joinqual) show_instrumentation_count("Rows Removed by Join Filter", 1, planstate, es); - show_upper_qual(plan->qual, "Filter", planstate, ancestors, es); - if (plan->qual) - show_instrumentation_count("Rows Removed by Filter", 2, + show_upper_qual(plan->qual, "Other Filter", planstate, ancestors, es); + show_upper_qual(((MergeJoin *) plan)->join.filterqual, + "Inner Filter", planstate, ancestors, es); + if (plan->qual || ((MergeJoin *) plan)->join.filterqual) + show_instrumentation_count("Rows Removed by Inner/Other Filters", 2, planstate, es); break; case T_HashJoin: @@ -1366,9 +1370,9 @@ ExplainNode(PlanState *planstate, List *ancestors, if (((HashJoin *) plan)->join.joinqual) show_instrumentation_count("Rows Removed by Join Filter", 1, planstate, es); - show_upper_qual(plan->qual, "Filter", planstate, ancestors, es); + show_upper_qual(plan->qual, "Other Filter", planstate, ancestors, es); if (plan->qual) - show_instrumentation_count("Rows Removed by Filter", 2, + show_instrumentation_count("Rows Removed by Other Filter", 2, planstate, es); break; case T_Agg: @@ -1407,6 +1411,11 @@ ExplainNode(PlanState *planstate, List *ancestors, break; case T_Hash: show_hash_info((HashState *) planstate, es); + show_upper_qual(((Hash *) plan)->filterqual, "Inner Filter", + planstate, ancestors, es); + if (((Hash *) plan)->filterqual) + show_instrumentation_count("Rows Removed by Inner Filter", 2, + planstate, es); break; default: break; diff --git a/src/backend/executor/nodeHash.c b/src/backend/executor/nodeHash.c index 0b2c139..87a71a0 100644 --- a/src/backend/executor/nodeHash.c +++ b/src/backend/executor/nodeHash.c @@ -75,6 +75,7 @@ MultiExecHash(HashState *node) { PlanState *outerNode; List *hashkeys; + List *filterqual; HashJoinTable hashtable; TupleTableSlot *slot; ExprContext *econtext; @@ -95,6 +96,7 @@ MultiExecHash(HashState *node) */ hashkeys = node->hashkeys; econtext = node->ps.ps_ExprContext; + filterqual = node->filterqual; /* * get all inner tuples and insert into the hash table (or temp files) @@ -104,8 +106,31 @@ MultiExecHash(HashState *node) slot = ExecProcNode(outerNode); if (TupIsNull(slot)) break; + + /* + * Sub node is connected to this node as "OUTER", + * so we temporary specify slot as outer tuple during ExecQual. + */ + econtext->ecxt_outertuple = slot; + + /* + * Now, we filter with filterqual. + */ + if (filterqual == NIL || ExecQual(filterqual, econtext, false)) + { + /* Nothing to do. No-op */ + } + else + { + /* filterqual is neither scanqual nor joinqual. */ + InstrCountFiltered2(node, 1); + continue; + } + /* We have to compute the hash value */ econtext->ecxt_innertuple = slot; + econtext->ecxt_outertuple = NULL; + if (ExecHashGetHashValue(hashtable, econtext, hashkeys, false, hashtable->keepNulls, &hashvalue)) @@ -206,6 +231,9 @@ ExecInitHash(Hash *node, EState *estate, int eflags) hashstate->ps.qual = (List *) ExecInitExpr((Expr *) node->plan.qual, (PlanState *) hashstate); + hashstate->filterqual = (List *) + ExecInitExpr((Expr *) node->filterqual, + (PlanState *) hashstate); /* * initialize child nodes @@ -273,7 +301,7 @@ ExecHashTableCreate(Hash *node, List *hashOperators, bool keepNulls) */ outerNode = outerPlan(node); - ExecChooseHashTableSize(outerNode->plan_rows, outerNode->plan_width, + ExecChooseHashTableSize(node->plan.plan_rows, outerNode->plan_width, OidIsValid(node->skewTable), &nbuckets, &nbatch, &num_skew_mcvs); diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c index 1d78cdf..eb2f250 100644 --- a/src/backend/executor/nodeHashjoin.c +++ b/src/backend/executor/nodeHashjoin.c @@ -472,6 +472,8 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags) hjstate->js.joinqual = (List *) ExecInitExpr((Expr *) node->join.joinqual, (PlanState *) hjstate); + /* filterqual is not needed here, needed in Hash instead*/ + hjstate->js.filterqual = NIL; hjstate->hashclauses = (List *) ExecInitExpr((Expr *) node->hashclauses, (PlanState *) hjstate); diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c index 34b6cf6..cd858f5 100644 --- a/src/backend/executor/nodeMergejoin.c +++ b/src/backend/executor/nodeMergejoin.c @@ -355,6 +355,22 @@ MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot) econtext->ecxt_innertuple = innerslot; + /* + * We filter inner tuple with filterqual here. + */ + if (mergestate->js.filterqual == NIL || + ExecQual(mergestate->js.filterqual, econtext, false)) + { + /* Nothing to do. No-op */ + } + else + { + /* Filtered. */ + MemoryContextSwitchTo(oldContext); + InstrCountFiltered2(mergestate, 1); + return MJEVAL_NONMATCHABLE; + } + for (i = 0; i < mergestate->mj_NumClauses; i++) { MergeJoinClause clause = &mergestate->mj_Clauses[i]; @@ -1514,6 +1530,9 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags) mergestate->js.joinqual = (List *) ExecInitExpr((Expr *) node->join.joinqual, (PlanState *) mergestate); + mergestate->js.filterqual = (List *) + ExecInitExpr((Expr *) node->join.filterqual, + (PlanState *) mergestate); mergestate->mj_ConstFalseJoin = false; /* mergeclauses are handled below */ diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c index e66bcda..93ed171 100644 --- a/src/backend/executor/nodeNestloop.c +++ b/src/backend/executor/nodeNestloop.c @@ -65,6 +65,7 @@ ExecNestLoop(NestLoopState *node) TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *joinqual; + List *filterqual; List *otherqual; ExprContext *econtext; ListCell *lc; @@ -76,6 +77,7 @@ ExecNestLoop(NestLoopState *node) nl = (NestLoop *) node->js.ps.plan; joinqual = node->js.joinqual; + filterqual = node->js.filterqual; otherqual = node->js.ps.qual; outerPlan = outerPlanState(node); innerPlan = innerPlanState(node); @@ -174,6 +176,20 @@ ExecNestLoop(NestLoopState *node) innerTupleSlot = ExecProcNode(innerPlan); econtext->ecxt_innertuple = innerTupleSlot; + /* + * We filter inner tuple with filterqual here. + */ + if (filterqual == NIL || ExecQual(filterqual, econtext, false)) + { + /* Nothing to do. No-op */ + } + else + { + /* Filtered. */ + InstrCountFiltered2(node, 1); + continue; + } + if (TupIsNull(innerTupleSlot)) { ENL1_printf("no inner tuple, need new outer tuple"); @@ -330,6 +346,9 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags) nlstate->js.joinqual = (List *) ExecInitExpr((Expr *) node->join.joinqual, (PlanState *) nlstate); + nlstate->js.filterqual = (List *) + ExecInitExpr((Expr *) node->join.filterqual, + (PlanState *) nlstate); /* * initialize child nodes diff --git a/src/backend/optimizer/geqo/geqo_eval.c b/src/backend/optimizer/geqo/geqo_eval.c index d9a20da..faadb69 100644 --- a/src/backend/optimizer/geqo/geqo_eval.c +++ b/src/backend/optimizer/geqo/geqo_eval.c @@ -261,7 +261,8 @@ merge_clump(PlannerInfo *root, List *clumps, Clump *new_clump, bool force) */ joinrel = make_join_rel(root, old_clump->joinrel, - new_clump->joinrel); + new_clump->joinrel, + NIL); /* Keep searching if join order is not valid */ if (joinrel) diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index d107d76..e9bd7ec 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1995,6 +1995,7 @@ final_cost_nestloop(PlannerInfo *root, NestPath *path, /* CPU costs */ cost_qual_eval(&restrict_qual_cost, path->joinrestrictinfo, root); + cost_qual_eval(&restrict_qual_cost, path->filterrestrictinfo, root); startup_cost += restrict_qual_cost.startup; cpu_per_tuple = cpu_tuple_cost + restrict_qual_cost.per_tuple; run_cost += cpu_per_tuple * ntuples; @@ -2306,6 +2307,7 @@ final_cost_mergejoin(PlannerInfo *root, MergePath *path, */ cost_qual_eval(&merge_qual_cost, mergeclauses, root); cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); + cost_qual_eval(&qp_qual_cost, path->jpath.filterrestrictinfo, root); qp_qual_cost.startup -= merge_qual_cost.startup; qp_qual_cost.per_tuple -= merge_qual_cost.per_tuple; @@ -2547,6 +2549,7 @@ void initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, List *hashclauses, + List *added_restrictinfo, Path *outer_path, Path *inner_path, SpecialJoinInfo *sjinfo, SemiAntiJoinFactors *semifactors) @@ -2565,6 +2568,18 @@ initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace, run_cost += outer_path->total_cost - outer_path->startup_cost; startup_cost += inner_path->total_cost; + /* estimate nrows of inner_path filtered by added_restrictlist */ + if (added_restrictinfo != NIL) + { + inner_path_rows *= + clauselist_selectivity(root, + added_restrictinfo, + 0, + JOIN_INNER, + NULL); + inner_path_rows = clamp_row_est(inner_path_rows); + } + /* * Cost of computing hash function: must do it once per input tuple. We * charge one cpu_operator_cost for each column's hash function. Also, @@ -2655,6 +2670,7 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path, Cost cpu_per_tuple; QualCost hash_qual_cost; QualCost qp_qual_cost; + QualCost filter_qual_cost; double hashjointuples; double virtualbuckets; Selectivity innerbucketsize; @@ -2674,6 +2690,18 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path, if (!enable_hashjoin) startup_cost += disable_cost; + /* estimate nrows of inner_path filtered by filter restrict info */ + if (path->jpath.filterrestrictinfo != NIL) + { + inner_path_rows *= + clauselist_selectivity(root, + path->jpath.filterrestrictinfo, + 0, + JOIN_INNER, + NULL); + inner_path_rows = clamp_row_est(inner_path_rows); + } + /* mark the path with estimated # of batches */ path->num_batches = numbatches; @@ -2753,6 +2781,7 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path, */ cost_qual_eval(&hash_qual_cost, hashclauses, root); cost_qual_eval(&qp_qual_cost, path->jpath.joinrestrictinfo, root); + cost_qual_eval(&filter_qual_cost, path->jpath.filterrestrictinfo, root); qp_qual_cost.startup -= hash_qual_cost.startup; qp_qual_cost.per_tuple -= hash_qual_cost.per_tuple; @@ -2835,6 +2864,8 @@ final_cost_hashjoin(PlannerInfo *root, HashPath *path, * not all of the quals may get evaluated at each tuple.) */ startup_cost += qp_qual_cost.startup; + startup_cost += filter_qual_cost.startup + + filter_qual_cost.per_tuple * inner_path_rows; cpu_per_tuple = cpu_tuple_cost + qp_qual_cost.per_tuple; run_cost += cpu_per_tuple * hashjointuples; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index a35c881..23de7f2 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -18,9 +18,22 @@ #include "executor/executor.h" #include "foreign/fdwapi.h" +#include "nodes/nodeFuncs.h" +#include "nodes/nodes.h" +#include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/plancat.h" +#include "optimizer/restrictinfo.h" +#include "rewrite/rewriteManip.h" +#include "utils/lsyscache.h" + +typedef struct +{ + List *joininfo; + bool is_mutated; +} check_constraint_mutator_context; /* Hook for plugins to get control in add_paths_to_joinrel() */ set_join_pathlist_hook_type set_join_pathlist_hook = NULL; @@ -45,6 +58,11 @@ static List *select_mergejoin_clauses(PlannerInfo *root, JoinType jointype, bool *mergejoin_allowed); +static void try_join_pushdown(PlannerInfo *root, + RelOptInfo *joinrel, RelOptInfo *outer_rel, + RelOptInfo *inner_rel, + List *restrictlist); + /* * add_paths_to_joinrel @@ -76,13 +94,33 @@ add_paths_to_joinrel(PlannerInfo *root, RelOptInfo *innerrel, JoinType jointype, SpecialJoinInfo *sjinfo, - List *restrictlist) + List *restrictlist, + List *added_restrictlist, + bool added_rinfo_for_outer) { JoinPathExtraData extra; bool mergejoin_allowed = true; ListCell *lc; - extra.restrictlist = restrictlist; + /* + * Try to push Join down under Append + */ + if (!IS_OUTER_JOIN(jointype)) + { + try_join_pushdown(root, joinrel, outerrel, innerrel, restrictlist); + } + + if (added_restrictlist != NIL && added_rinfo_for_outer) + { + extra.restrictlist = + list_concat(list_copy(restrictlist), added_restrictlist); + extra.added_restrictlist = NIL; + } + else + { + extra.restrictlist = restrictlist; + extra.added_restrictlist = added_restrictlist; + } extra.mergeclause_list = NIL; extra.sjinfo = sjinfo; extra.param_source_rels = NULL; @@ -417,6 +455,7 @@ try_nestloop_path(PlannerInfo *root, outer_path, inner_path, extra->restrictlist, + extra->added_restrictlist, pathkeys, required_outer)); } @@ -499,6 +538,7 @@ try_mergejoin_path(PlannerInfo *root, outer_path, inner_path, extra->restrictlist, + extra->added_restrictlist, pathkeys, required_outer, mergeclauses, @@ -554,6 +594,7 @@ try_hashjoin_path(PlannerInfo *root, * never have any output pathkeys, per comments in create_hashjoin_path. */ initial_cost_hashjoin(root, &workspace, jointype, hashclauses, + extra->added_restrictlist, outer_path, inner_path, extra->sjinfo, &extra->semifactors); @@ -571,6 +612,7 @@ try_hashjoin_path(PlannerInfo *root, outer_path, inner_path, extra->restrictlist, + extra->added_restrictlist, required_outer, hashclauses)); } @@ -1474,3 +1516,250 @@ select_mergejoin_clauses(PlannerInfo *root, return result_list; } + +static Node * +check_constraint_mutator(Node *node, check_constraint_mutator_context *context) +{ + /* Failed to mutate. Abort. */ + if (!context->is_mutated) + return (Node *) copyObject(node); + + if (node == NULL) + return NULL; + + if (IsA(node, Var)) + { + List *l = context->joininfo; + ListCell *lc; + + Assert(list_length(l) > 0); + + foreach (lc, l) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + Expr *expr = rinfo->clause; + + if (!rinfo->can_join || + !IsA(expr, OpExpr) || + !op_hashjoinable(((OpExpr *) expr)->opno, + exprType(get_leftop(expr)))) + continue; + + if (equal(get_leftop(expr), node)) + { + /* + * This node is equal to LEFT of join clause, + * thus will be replaced with RIGHT clause. + */ + return (Node *) copyObject(get_rightop(expr)); + } + else + if (equal(get_rightop(expr), node)) + { + /* + * This node is equal to RIGHT of join clause, + * thus will be replaced with LEFT clause. + */ + return (Node *) copyObject(get_leftop(expr)); + } + } + + /* Unfortunately, mutating is failed. */ + context->is_mutated = false; + return (Node *) copyObject(node); + } + + return expression_tree_mutator(node, check_constraint_mutator, context); +} + +/* + * Make RestrictInfo_List from CHECK() constraints. + */ +static List * +make_restrictinfos_from_check_constr(PlannerInfo *root, + List *joininfo, RelOptInfo *outer_rel) +{ + List *result = NIL; + RangeTblEntry *childRTE = root->simple_rte_array[outer_rel->relid]; + List *check_constr = + get_relation_constraints(root, childRTE->relid, + outer_rel, false); + ListCell *lc; + + check_constraint_mutator_context context; + + context.joininfo = joininfo; + context.is_mutated = true; + + /* + * Try to change CHECK() constraints to filter expressions. + */ + foreach(lc, check_constr) + { + Node *mutated = + expression_tree_mutator((Node *) lfirst(lc), + check_constraint_mutator, + (void *) &context); + + if (context.is_mutated) + result = lappend(result, mutated); + } + + Assert(list_length(check_constr) == list_length(result)); + list_free_deep(check_constr); + + return make_restrictinfos_from_actual_clauses(root, result); +} + +/* + * Mutate parent's relid to child one. + */ +static List * +mutate_parent_relid_to_child(PlannerInfo *root, List *join_clauses, + RelOptInfo *outer_rel) +{ + Index parent_relid = + find_childrel_appendrelinfo(root, outer_rel)->parent_relid; + List *old_clauses = get_actual_clauses(join_clauses); + List *new_clauses = NIL; + ListCell *lc; + + foreach(lc, old_clauses) + { + Node *new_clause = (Node *) copyObject(lfirst(lc)); + + ChangeVarNodes(new_clause, parent_relid, outer_rel->relid, 0); + new_clauses = lappend(new_clauses, new_clause); + } + + return make_restrictinfos_from_actual_clauses(root, new_clauses); +} + +static inline List * +extract_join_clauses(List *restrictlist, RelOptInfo *outer_prel, + RelOptInfo *inner_rel) +{ + List *result = NIL; + ListCell *lc; + + foreach (lc, restrictlist) + { + RestrictInfo *rinfo = (RestrictInfo *) lfirst(lc); + + if (clause_sides_match_join(rinfo, outer_prel, inner_rel)) + result = lappend(result, rinfo); + } + + return result; +} + +/* + Try to push JoinPath down under AppendPath. +*/ +static void +try_join_pushdown(PlannerInfo *root, + RelOptInfo *joinrel, RelOptInfo *outer_rel, + RelOptInfo *inner_rel, + List *restrictlist) +{ + AppendPath *outer_path; + ListCell *lc; + List *old_joinclauses; + List *new_append_subpaths = NIL; + + Assert(outer_rel->cheapest_total_path != NULL); + + /* When specified outer path is not an AppendPath, nothing to do here. */ + if (!IsA(outer_rel->cheapest_total_path, AppendPath)) + { + elog(DEBUG1, "Outer path is not an AppendPath. Do nothing."); + return; + } + + outer_path = (AppendPath *) outer_rel->cheapest_total_path; + + /* + * Extract join clauses to mutate CHECK() constraints. + * We don't have to clobber this list to mutate CHECK() constraints, + * so we need to do only once. + */ + old_joinclauses = extract_join_clauses(restrictlist, outer_rel, inner_rel); + + /* + * Make new joinrel between each of outer path's sub-paths and inner path. + */ + foreach(lc, outer_path->subpaths) + { + RelOptInfo *old_outer_rel = ((Path *) lfirst(lc))->parent; + RelOptInfo *new_outer_rel; + List *new_joinclauses; + List *added_restrictlist; + List **join_rel_level; + + Assert(!IS_DUMMY_REL(old_outer_rel)); + + /* + * Join clause points parent's relid, + * so we must change it to child's one. + */ + new_joinclauses = mutate_parent_relid_to_child(root, old_joinclauses, + old_outer_rel); + + /* + * Make RestrictInfo list from CHECK() constraints of outer table. + */ + added_restrictlist = + make_restrictinfos_from_check_constr(root, new_joinclauses, + old_outer_rel); + + /* XXX This is workaround for failing assertion at allpaths.c */ + join_rel_level = root->join_rel_level; + root->join_rel_level = NULL; + + /* + * Create new joinrel with restriction made above. + */ + new_outer_rel = + make_join_rel(root, old_outer_rel, inner_rel, + added_restrictlist); + + root->join_rel_level = join_rel_level; + + Assert(new_outer_rel != NULL); + + if (IS_DUMMY_REL(new_outer_rel)) + { + pfree(new_outer_rel); + continue; + } + + /* + * We must check if each of all new joinrels have one path at least. + * add_path() sometime rejects to add new path to parent RelOptInfo. + */ + if (list_length(new_outer_rel->pathlist) <= 0) + { + /* + * Sadly, No paths added. This means that pushdown is failed, + * thus clean up here. + */ + list_free_deep(new_append_subpaths); + pfree(new_outer_rel); + list_free(old_joinclauses); + elog(DEBUG1, "Join pushdown failed."); + return; + } + + set_cheapest(new_outer_rel); + Assert(new_outer_rel->cheapest_total_path != NULL); + new_append_subpaths = lappend(new_append_subpaths, + new_outer_rel->cheapest_total_path); + } + + /* Join Pushdown is succeeded. Add path to original joinrel. */ + add_path(joinrel, + (Path *) create_append_path(joinrel, new_append_subpaths, NULL)); + + list_free(old_joinclauses); + elog(DEBUG1, "Join pushdown succeeded."); +} diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index b2cc9f0..7075552 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -33,7 +33,6 @@ static void mark_dummy_rel(RelOptInfo *rel); static bool restriction_is_constant_false(List *restrictlist, bool only_pushed_down); - /* * join_search_one_level * Consider ways to produce join relations containing exactly 'level' @@ -170,7 +169,7 @@ join_search_one_level(PlannerInfo *root, int level) if (have_relevant_joinclause(root, old_rel, new_rel) || have_join_order_restriction(root, old_rel, new_rel)) { - (void) make_join_rel(root, old_rel, new_rel); + (void) make_join_rel(root, old_rel, new_rel, NIL); } } } @@ -271,7 +270,7 @@ make_rels_by_clause_joins(PlannerInfo *root, (have_relevant_joinclause(root, old_rel, other_rel) || have_join_order_restriction(root, old_rel, other_rel))) { - (void) make_join_rel(root, old_rel, other_rel); + (void) make_join_rel(root, old_rel, other_rel, NIL); } } } @@ -303,7 +302,7 @@ make_rels_by_clauseless_joins(PlannerInfo *root, if (!bms_overlap(other_rel->relids, old_rel->relids)) { - (void) make_join_rel(root, old_rel, other_rel); + (void) make_join_rel(root, old_rel, other_rel, NIL); } } } @@ -589,7 +588,8 @@ join_is_legal(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, * turned into joins. */ RelOptInfo * -make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) +make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2, + List *added_restrictlist) { Relids joinrelids; SpecialJoinInfo *sjinfo; @@ -691,10 +691,14 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_INNER, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_INNER, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + true); break; case JOIN_LEFT: if (is_dummy_rel(rel1) || @@ -708,10 +712,14 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) mark_dummy_rel(rel2); add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_LEFT, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_RIGHT, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + true); break; case JOIN_FULL: if ((is_dummy_rel(rel1) && is_dummy_rel(rel2)) || @@ -722,10 +730,14 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_FULL, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_FULL, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + true); /* * If there are join quals that aren't mergeable or hashable, we @@ -758,7 +770,9 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_SEMI, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); } /* @@ -781,10 +795,14 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) } add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + true); } break; case JOIN_ANTI: @@ -799,7 +817,9 @@ make_join_rel(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2) mark_dummy_rel(rel2); add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_ANTI, sjinfo, - restrictlist); + restrictlist, + added_restrictlist, + false); break; default: /* other values not expected here */ diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 404c6f5..8cbf86e 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -135,7 +135,8 @@ static WorkTableScan *make_worktablescan(List *qptlist, List *qpqual, static BitmapAnd *make_bitmap_and(List *bitmapplans); static BitmapOr *make_bitmap_or(List *bitmapplans); static NestLoop *make_nestloop(List *tlist, - List *joinclauses, List *otherclauses, List *nestParams, + List *joinclauses, List *filterclauses, + List *otherclauses, List *nestParams, Plan *lefttree, Plan *righttree, JoinType jointype); static HashJoin *make_hashjoin(List *tlist, @@ -144,14 +145,15 @@ static HashJoin *make_hashjoin(List *tlist, Plan *lefttree, Plan *righttree, JoinType jointype); static Hash *make_hash(Plan *lefttree, + List *filterclauses, Oid skewTable, AttrNumber skewColumn, bool skewInherit, Oid skewColType, int32 skewColTypmod); static MergeJoin *make_mergejoin(List *tlist, - List *joinclauses, List *otherclauses, - List *mergeclauses, + List *joinclauses, List *filterclauses, + List *otherclauses, List *mergeclauses, Oid *mergefamilies, Oid *mergecollations, int *mergestrategies, @@ -2239,6 +2241,7 @@ create_nestloop_plan(PlannerInfo *root, List *tlist = build_path_tlist(root, &best_path->path); List *joinrestrictclauses = best_path->joinrestrictinfo; List *joinclauses; + List *filterclauses; List *otherclauses; Relids outerrelids; List *nestParams; @@ -2248,6 +2251,7 @@ create_nestloop_plan(PlannerInfo *root, /* Sort join qual clauses into best execution order */ joinrestrictclauses = order_qual_clauses(root, joinrestrictclauses); + filterclauses = order_qual_clauses(root, best_path->filterrestrictinfo); /* Get the join qual clauses (in plain expression form) */ /* Any pseudoconstant clauses are ignored here */ @@ -2263,6 +2267,8 @@ create_nestloop_plan(PlannerInfo *root, otherclauses = NIL; } + filterclauses = extract_actual_clauses(filterclauses, false); + /* Replace any outer-relation variables with nestloop params */ if (best_path->path.param_info) { @@ -2309,6 +2315,7 @@ create_nestloop_plan(PlannerInfo *root, join_plan = make_nestloop(tlist, joinclauses, + filterclauses, otherclauses, nestParams, outer_plan, @@ -2328,6 +2335,7 @@ create_mergejoin_plan(PlannerInfo *root, { List *tlist = build_path_tlist(root, &best_path->jpath.path); List *joinclauses; + List *filterclauses; List *otherclauses; List *mergeclauses; List *outerpathkeys; @@ -2346,6 +2354,7 @@ create_mergejoin_plan(PlannerInfo *root, /* Sort join qual clauses into best execution order */ /* NB: do NOT reorder the mergeclauses */ joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); + filterclauses = order_qual_clauses(root, best_path->jpath.filterrestrictinfo); /* Get the join qual clauses (in plain expression form) */ /* Any pseudoconstant clauses are ignored here */ @@ -2361,6 +2370,7 @@ create_mergejoin_plan(PlannerInfo *root, otherclauses = NIL; } + filterclauses = extract_actual_clauses(filterclauses, false); /* * Remove the mergeclauses from the list of join qual clauses, leaving the * list of quals that must be checked as qpquals. @@ -2599,6 +2609,7 @@ create_mergejoin_plan(PlannerInfo *root, */ join_plan = make_mergejoin(tlist, joinclauses, + filterclauses, otherclauses, mergeclauses, mergefamilies, @@ -2623,6 +2634,7 @@ create_hashjoin_plan(PlannerInfo *root, { List *tlist = build_path_tlist(root, &best_path->jpath.path); List *joinclauses; + List *filterclauses; List *otherclauses; List *hashclauses; Oid skewTable = InvalidOid; @@ -2635,6 +2647,7 @@ create_hashjoin_plan(PlannerInfo *root, /* Sort join qual clauses into best execution order */ joinclauses = order_qual_clauses(root, best_path->jpath.joinrestrictinfo); + filterclauses = order_qual_clauses(root, best_path->jpath.filterrestrictinfo); /* There's no point in sorting the hash clauses ... */ /* Get the join qual clauses (in plain expression form) */ @@ -2720,8 +2733,11 @@ create_hashjoin_plan(PlannerInfo *root, /* * Build the hash node and hash join node. + * + * In HashJoin, filterclauses is needed by Hash, not HashJoin. */ hash_plan = make_hash(inner_plan, + filterclauses, skewTable, skewColumn, skewInherit, @@ -3862,6 +3878,7 @@ make_bitmap_or(List *bitmapplans) static NestLoop * make_nestloop(List *tlist, List *joinclauses, + List *filterclauses, List *otherclauses, List *nestParams, Plan *lefttree, @@ -3878,6 +3895,7 @@ make_nestloop(List *tlist, plan->righttree = righttree; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->join.filterqual = filterclauses; node->nestParams = nestParams; return node; @@ -3903,12 +3921,15 @@ make_hashjoin(List *tlist, node->hashclauses = hashclauses; node->join.jointype = jointype; node->join.joinqual = joinclauses; + /* filterqual is not needed for HashJoin node */ + node->join.filterqual = NIL; return node; } static Hash * make_hash(Plan *lefttree, + List *filterclauses, Oid skewTable, AttrNumber skewColumn, bool skewInherit, @@ -3919,6 +3940,27 @@ make_hash(Plan *lefttree, Plan *plan = &node->plan; copy_plan_costsize(plan, lefttree); + /* + * estimate nrows of 'lefttree' rel filtered by 'filterclauses'. + * All selectivity values maybe cached, because clauselist_selectivity() + * had already been called at this timing. + * Thus, clauselist_selectivity() need not to specify real PlannerInfo here. + */ + if (filterclauses != NIL) + { +#ifdef USE_ASSERT_CHECKING + ListCell *lc; + + foreach (lc, filterclauses) + { + Assert(IsA(lfirst(lc), RestrictInfo)); + Assert(((RestrictInfo *)lfirst(lc))->norm_selec != -1); + } +#endif + plan->plan_rows *= + clauselist_selectivity(NULL, filterclauses, 0, JOIN_INNER, NULL); + plan->plan_rows = clamp_row_est(plan->plan_rows); + } /* * For plausibility, make startup & total costs equal total cost of input @@ -3930,6 +3972,7 @@ make_hash(Plan *lefttree, plan->lefttree = lefttree; plan->righttree = NULL; + node->filterqual = extract_actual_clauses(filterclauses, false); node->skewTable = skewTable; node->skewColumn = skewColumn; node->skewInherit = skewInherit; @@ -3942,6 +3985,7 @@ make_hash(Plan *lefttree, static MergeJoin * make_mergejoin(List *tlist, List *joinclauses, + List *filterclauses, List *otherclauses, List *mergeclauses, Oid *mergefamilies, @@ -3967,6 +4011,7 @@ make_mergejoin(List *tlist, node->mergeNullsFirst = mergenullsfirst; node->join.jointype = jointype; node->join.joinqual = joinclauses; + node->join.filterqual = filterclauses; return node; } @@ -4011,6 +4056,21 @@ make_sort(PlannerInfo *root, Plan *lefttree, int numCols, return node; } +static inline bool +should_ignore_ec_member(EquivalenceMember *em, Relids relids) +{ + /* + * If this is called from make_sort_from_pathkeys, relids may be NULL. + * In this case, we must not ignore child members because inner/outer plan + * of pushed-down merge join is always child table. + */ + if (!relids) + return false; + + return (em->em_is_child && + !bms_equal(em->em_relids, relids)); +} + /* * prepare_sort_from_pathkeys * Prepare to sort according to given pathkeys @@ -4190,8 +4250,7 @@ prepare_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, * Ignore child members unless they match the rel being * sorted. */ - if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + if (should_ignore_ec_member(em, relids)) continue; sortexpr = em->em_expr; @@ -4304,8 +4363,7 @@ find_ec_member_for_tle(EquivalenceClass *ec, /* * Ignore child members unless they match the rel being sorted. */ - if (em->em_is_child && - !bms_equal(em->em_relids, relids)) + if (should_ignore_ec_member(em, relids)) continue; /* Match if same expression (after stripping relabel) */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index daeb584..3d9eb00 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -104,6 +104,7 @@ static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context); static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context); static void set_join_references(PlannerInfo *root, Join *join, int rtoffset); static void set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset); +static void set_hash_references(PlannerInfo *root, Hash *hash, int rtoffset); static void set_dummy_tlist_references(Plan *plan, int rtoffset); static indexed_tlist *build_tlist_index(List *tlist); static Var *search_indexed_tlist_for_var(Var *var, @@ -598,6 +599,12 @@ set_plan_refs(PlannerInfo *root, Plan *plan, int rtoffset) break; case T_Hash: + /* + * For Hash node, we need to set INNER_VAR to varno of filterqual. + */ + set_hash_references(root, (Hash *) plan, rtoffset); + /* FALL THRU */ + case T_Material: case T_Sort: case T_Unique: @@ -1493,6 +1500,14 @@ set_join_references(PlannerInfo *root, Join *join, int rtoffset) (Index) 0, rtoffset); + /* We need not to do for HashJoin */ + if (!IsA(join, HashJoin) || join->filterqual != NIL) + join->filterqual = fix_upper_expr(root, + (Node *) join->filterqual, + inner_itlist, + INNER_VAR, + rtoffset); + /* Now do join-type-specific stuff */ if (IsA(join, NestLoop)) { @@ -1655,6 +1670,30 @@ set_upper_references(PlannerInfo *root, Plan *plan, int rtoffset) pfree(subplan_itlist); } +static void +set_hash_references(PlannerInfo *root, Hash *hash, int rtoffset) +{ + Plan *subplan = hash->plan.lefttree; + indexed_tlist *subplan_itlist; + List *output_targetlist; + ListCell *l; + + subplan_itlist = build_tlist_index(subplan->targetlist); + + /* + * Sub plan node is connected to this plan node as "OUTER", + * so we specify OUTER_VAR instead of INNER_VAR. + */ + hash->filterqual = + (List *) fix_upper_expr(root, + (Node *) hash->filterqual, + subplan_itlist, + OUTER_VAR, + rtoffset); + + pfree(subplan_itlist); +} + /* * set_dummy_tlist_references * Replace the targetlist of an upper-level plan node with a simple diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c index 935bc2b..4d8fe4b 100644 --- a/src/backend/optimizer/util/pathnode.c +++ b/src/backend/optimizer/util/pathnode.c @@ -1562,6 +1562,7 @@ create_nestloop_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, List *pathkeys, Relids required_outer) { @@ -1609,6 +1610,7 @@ create_nestloop_path(PlannerInfo *root, pathnode->outerjoinpath = outer_path; pathnode->innerjoinpath = inner_path; pathnode->joinrestrictinfo = restrict_clauses; + pathnode->filterrestrictinfo = filtering_clauses; final_cost_nestloop(root, pathnode, workspace, sjinfo, semifactors); @@ -1643,6 +1645,7 @@ create_mergejoin_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, List *pathkeys, Relids required_outer, List *mergeclauses, @@ -1666,6 +1669,7 @@ create_mergejoin_path(PlannerInfo *root, pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->jpath.filterrestrictinfo = filtering_clauses; pathnode->path_mergeclauses = mergeclauses; pathnode->outersortkeys = outersortkeys; pathnode->innersortkeys = innersortkeys; @@ -1702,6 +1706,7 @@ create_hashjoin_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, Relids required_outer, List *hashclauses) { @@ -1734,6 +1739,7 @@ create_hashjoin_path(PlannerInfo *root, pathnode->jpath.outerjoinpath = outer_path; pathnode->jpath.innerjoinpath = inner_path; pathnode->jpath.joinrestrictinfo = restrict_clauses; + pathnode->jpath.filterrestrictinfo = filtering_clauses; pathnode->path_hashclauses = hashclauses; /* final_cost_hashjoin will fill in pathnode->num_batches */ diff --git a/src/backend/optimizer/util/plancat.c b/src/backend/optimizer/util/plancat.c index 9442e5f..c137b09 100644 --- a/src/backend/optimizer/util/plancat.c +++ b/src/backend/optimizer/util/plancat.c @@ -54,9 +54,6 @@ get_relation_info_hook_type get_relation_info_hook = NULL; static bool infer_collation_opclass_match(InferenceElem *elem, Relation idxRel, List *idxExprs); static int32 get_rel_data_width(Relation rel, int32 *attr_widths); -static List *get_relation_constraints(PlannerInfo *root, - Oid relationObjectId, RelOptInfo *rel, - bool include_notnull); static List *build_index_tlist(PlannerInfo *root, IndexOptInfo *index, Relation heapRelation); @@ -1022,7 +1019,7 @@ get_relation_data_width(Oid relid, int32 *attr_widths) * run, and in many cases it won't be invoked at all, so there seems no * point in caching the data in RelOptInfo. */ -static List * +List * get_relation_constraints(PlannerInfo *root, Oid relationObjectId, RelOptInfo *rel, bool include_notnull) diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 68a93a1..d6717db 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -496,19 +496,24 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, { Relids relids = joinrel->relids; ListCell *vars; + int nth = 0; foreach(vars, input_rel->reltargetlist) { Var *var = (Var *) lfirst(vars); RelOptInfo *baserel; int ndx; + bool is_needed = false; /* * Ignore PlaceHolderVars in the input tlists; we'll make our own * decisions about whether to copy them. */ if (IsA(var, PlaceHolderVar)) + { + nth++; continue; + } /* * Otherwise, anything in a baserel or joinrel targetlist ought to be @@ -521,15 +526,83 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, /* Get the Var's original base rel */ baserel = find_base_rel(root, var->varno); + ndx = var->varattno - baserel->min_attr; + + /* + * We must handle case of join pushdown. + */ + if (input_rel->reloptkind == RELOPT_OTHER_MEMBER_REL) + { + /* Get the Var's PARENT base rel */ + Index parent_relid = + find_childrel_appendrelinfo(root, input_rel)->parent_relid; + RelOptInfo *parent_rel = find_base_rel(root, parent_relid); + Var *parent_var = + (Var *) list_nth(parent_rel->reltargetlist, nth); + int parent_ndx = parent_var->varattno - parent_rel->min_attr; + /* Relids have included parent_rel's instead of input_rel's. */ + Relids relids_tmp = + bms_del_members(bms_copy(relids), input_rel->relids); + + relids_tmp = bms_union(relids_tmp, parent_rel->relids); + + Assert(ndx == parent_ndx); + is_needed = + (bms_nonempty_difference( + parent_rel->attr_needed[parent_ndx], + relids_tmp)); + + bms_free(relids_tmp); + } + else + { + Relids relids_tmp = + bms_del_members(bms_copy(relids), input_rel->relids); + Index another_relid = -1; + + /* Try to detect Inner relation of pushed-down join. */ + if (bms_get_singleton_member(relids_tmp, &another_relid)) + { + RelOptInfo *another_rel = + find_base_rel(root, another_relid); + + if (another_rel->reloptkind == RELOPT_OTHER_MEMBER_REL) + { + /* This may be inner relation of pushed-down join. */ + Index parent_relid = + find_childrel_appendrelinfo(root, another_rel)->parent_relid; + RelOptInfo *parent_rel = find_base_rel(root, parent_relid); + + bms_free(relids_tmp); + relids_tmp = + bms_union(input_rel->relids, parent_rel->relids); + } + } + + if (!bms_is_subset(input_rel->relids, relids_tmp)) + { + /* Can't detect inner relation of pushed-down join */ + bms_free(relids_tmp); + relids_tmp = bms_copy(relids); + } + + is_needed = + (bms_nonempty_difference( + baserel->attr_needed[ndx], + relids_tmp)); + + bms_free(relids_tmp); + } /* Is it still needed above this joinrel? */ - ndx = var->varattno - baserel->min_attr; - if (bms_nonempty_difference(baserel->attr_needed[ndx], relids)) + if (is_needed) { /* Yup, add it to the output */ joinrel->reltargetlist = lappend(joinrel->reltargetlist, var); joinrel->width += baserel->attr_widths[ndx]; } + + nth++; } } diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index 4ae2f3e..f2b56d0 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -1644,6 +1644,7 @@ typedef struct JoinState PlanState ps; JoinType jointype; List *joinqual; /* JOIN quals (in addition to ps.qual) */ + List *filterqual; /* FILTER quals (in addition to ps.qual) */ } JoinState; /* ---------------- @@ -1957,6 +1958,7 @@ typedef struct UniqueState typedef struct HashState { PlanState ps; /* its first field is NodeTag */ + List *filterqual; /* FILTER quals (in addition to ps.qual) */ HashJoinTable hashtable; /* hash table for the hashjoin */ List *hashkeys; /* list of ExprState nodes */ /* hashkeys is same as parent's hj_InnerHashKeys */ diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index cc259f1..37cef10 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -593,6 +593,7 @@ typedef struct Join Plan plan; JoinType jointype; List *joinqual; /* JOIN quals (in addition to plan.qual) */ + List *filterqual; /* FILTER quals (in addition to plan.qual) */ } Join; /* ---------------- @@ -764,6 +765,7 @@ typedef struct Unique typedef struct Hash { Plan plan; + List *filterqual; /* FILTER quals (in addition to plan.qual) */ Oid skewTable; /* outer join key's table OID, or InvalidOid */ AttrNumber skewColumn; /* outer join key's column #, or zero */ bool skewInherit; /* is outer join rel an inheritance tree? */ diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 79bed33..83d41de 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -1058,6 +1058,7 @@ typedef struct JoinPath Path *innerjoinpath; /* path for the inner side of the join */ List *joinrestrictinfo; /* RestrictInfos to apply to join */ + List *filterrestrictinfo; /* RestrictInfos to filter at join */ /* * See the notes for RelOptInfo and ParamPathInfo to understand why @@ -1706,6 +1707,7 @@ typedef struct JoinPathExtraData { List *restrictlist; List *mergeclause_list; + List *added_restrictlist; SpecialJoinInfo *sjinfo; SemiAntiJoinFactors semifactors; Relids param_source_rels; diff --git a/src/include/optimizer/cost.h b/src/include/optimizer/cost.h index dd43e45..e685a8d 100644 --- a/src/include/optimizer/cost.h +++ b/src/include/optimizer/cost.h @@ -137,6 +137,7 @@ extern void initial_cost_hashjoin(PlannerInfo *root, JoinCostWorkspace *workspace, JoinType jointype, List *hashclauses, + List *added_restrictlist, Path *outer_path, Path *inner_path, SpecialJoinInfo *sjinfo, SemiAntiJoinFactors *semifactors); diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h index 161644c..9d3718d 100644 --- a/src/include/optimizer/pathnode.h +++ b/src/include/optimizer/pathnode.h @@ -97,6 +97,7 @@ extern NestPath *create_nestloop_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, List *pathkeys, Relids required_outer); @@ -108,6 +109,7 @@ extern MergePath *create_mergejoin_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, List *pathkeys, Relids required_outer, List *mergeclauses, @@ -123,6 +125,7 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root, Path *outer_path, Path *inner_path, List *restrict_clauses, + List *filtering_clauses, Relids required_outer, List *hashclauses); diff --git a/src/include/optimizer/paths.h b/src/include/optimizer/paths.h index 87123a5..f038f5d 100644 --- a/src/include/optimizer/paths.h +++ b/src/include/optimizer/paths.h @@ -87,7 +87,8 @@ extern void create_tidscan_paths(PlannerInfo *root, RelOptInfo *rel); extern void add_paths_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel, JoinType jointype, SpecialJoinInfo *sjinfo, - List *restrictlist); + List *restrictlist, List *added_restrictlist, + bool added_rinfo_for_outer); /* * joinrels.c @@ -95,7 +96,7 @@ extern void add_paths_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel, */ extern void join_search_one_level(PlannerInfo *root, int level); extern RelOptInfo *make_join_rel(PlannerInfo *root, - RelOptInfo *rel1, RelOptInfo *rel2); + RelOptInfo *rel1, RelOptInfo *rel2, List *added_restrictlist); extern bool have_join_order_restriction(PlannerInfo *root, RelOptInfo *rel1, RelOptInfo *rel2); diff --git a/src/include/optimizer/plancat.h b/src/include/optimizer/plancat.h index 11e7d4d..f799a5b 100644 --- a/src/include/optimizer/plancat.h +++ b/src/include/optimizer/plancat.h @@ -28,6 +28,10 @@ extern PGDLLIMPORT get_relation_info_hook_type get_relation_info_hook; extern void get_relation_info(PlannerInfo *root, Oid relationObjectId, bool inhparent, RelOptInfo *rel); +extern List *get_relation_constraints(PlannerInfo *root, + Oid relationObjectId, RelOptInfo *rel, + bool include_notnull); + extern List *infer_arbiter_indexes(PlannerInfo *root); extern void estimate_rel_size(Relation rel, int32 *attr_widths,