diff options
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 184 |
1 files changed, 121 insertions, 63 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index e978b491f6c..aae5df09f9b 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -62,14 +62,12 @@ static SpecialJoinInfo *make_outerjoininfo(PlannerInfo *root, JoinType jointype, List *clause); static void compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause); static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, - bool is_deduced, bool below_outer_join, JoinType jointype, Index security_level, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, - Relids deduced_nullable_relids, List **postponed_qual_list); static bool check_outerjoin_delay(PlannerInfo *root, Relids *relids_p, Relids *nullable_relids_p, bool is_pushed_down); @@ -815,9 +813,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, if (bms_is_subset(pq->relids, *qualscope)) distribute_qual_to_rels(root, pq->qual, - false, below_outer_join, JOIN_INNER, + below_outer_join, JOIN_INNER, root->qual_security_level, - *qualscope, NULL, NULL, NULL, + *qualscope, NULL, NULL, NULL); else *postponed_qual_list = lappend(*postponed_qual_list, pq); @@ -831,9 +829,9 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, Node *qual = (Node *) lfirst(l); distribute_qual_to_rels(root, qual, - false, below_outer_join, JOIN_INNER, + below_outer_join, JOIN_INNER, root->qual_security_level, - *qualscope, NULL, NULL, NULL, + *qualscope, NULL, NULL, postponed_qual_list); } } @@ -1008,10 +1006,10 @@ deconstruct_recurse(PlannerInfo *root, Node *jtnode, bool below_outer_join, Node *qual = (Node *) lfirst(l); distribute_qual_to_rels(root, qual, - false, below_outer_join, j->jointype, + below_outer_join, j->jointype, root->qual_security_level, *qualscope, - ojscope, nonnullable_rels, NULL, + ojscope, nonnullable_rels, postponed_qual_list); } @@ -1110,14 +1108,12 @@ process_security_barrier_quals(PlannerInfo *root, * than being pushed up to top of tree, which we don't want. */ distribute_qual_to_rels(root, qual, - false, below_outer_join, JOIN_INNER, security_level, qualscope, qualscope, NULL, - NULL, NULL); } security_level++; @@ -1581,7 +1577,6 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause) * as belonging to a higher join level, just add it to postponed_qual_list. * * 'clause': the qual clause to be distributed - * 'is_deduced': true if the qual came from implied-equality deduction * 'below_outer_join': true if the qual is from a JOIN/ON that is below the * nullable side of a higher-level outer join * 'jointype': type of join the qual is from (JOIN_INNER for a WHERE clause) @@ -1593,8 +1588,6 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause) * baserels appearing on the outer (nonnullable) side of the join * (for FULL JOIN this includes both sides of the join, and must in fact * equal qualscope) - * 'deduced_nullable_relids': if is_deduced is true, the nullable relids to - * impute to the clause; otherwise NULL * 'postponed_qual_list': list of PostponedQual structs, which we can add * this qual to if it turns out to belong to a higher join level. * Can be NULL if caller knows postponement is impossible. @@ -1603,23 +1596,17 @@ compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause) * 'ojscope' is needed if we decide to force the qual up to the outer-join * level, which will be ojscope not necessarily qualscope. * - * In normal use (when is_deduced is false), at the time this is called, - * root->join_info_list must contain entries for all and only those special - * joins that are syntactically below this qual. But when is_deduced is true, - * we are adding new deduced clauses after completion of deconstruct_jointree, - * so it cannot be assumed that root->join_info_list has anything to do with - * qual placement. + * At the time this is called, root->join_info_list must contain entries for + * all and only those special joins that are syntactically below this qual. */ static void distribute_qual_to_rels(PlannerInfo *root, Node *clause, - bool is_deduced, bool below_outer_join, JoinType jointype, Index security_level, Relids qualscope, Relids ojscope, Relids outerjoin_nonnullable, - Relids deduced_nullable_relids, List **postponed_qual_list) { Relids relids; @@ -1653,7 +1640,6 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, Assert(root->hasLateralRTEs); /* shouldn't happen otherwise */ Assert(jointype == JOIN_INNER); /* mustn't postpone past outer join */ - Assert(!is_deduced); /* shouldn't be deduced, either */ pq->qual = clause; pq->relids = relids; *postponed_qual_list = lappend(*postponed_qual_list, pq); @@ -1754,24 +1740,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, * This seems like another reason why it should perhaps be rethought. *---------- */ - if (is_deduced) - { - /* - * If the qual came from implied-equality deduction, it should not be - * outerjoin-delayed, else deducer blew it. But we can't check this - * because the join_info_list may now contain OJs above where the qual - * belongs. For the same reason, we must rely on caller to supply the - * correct nullable_relids set. - */ - Assert(!ojscope); - is_pushed_down = true; - outerjoin_delayed = false; - nullable_relids = deduced_nullable_relids; - /* Don't feed it back for more deductions */ - maybe_equivalence = false; - maybe_outer_join = false; - } - else if (bms_overlap(relids, outerjoin_nonnullable)) + if (bms_overlap(relids, outerjoin_nonnullable)) { /* * The qual is attached to an outer join and mentions (some of the) @@ -2277,14 +2246,18 @@ distribute_restrictinfo_to_rels(PlannerInfo *root, * can produce constant TRUE or constant FALSE. (Otherwise it's not, * because the expressions went through eval_const_expressions already.) * + * Returns the generated RestrictInfo, if any. The result will be NULL + * if both_const is true and we successfully reduced the clause to + * constant TRUE. + * * Note: this function will copy item1 and item2, but it is caller's * responsibility to make sure that the Relids parameters are fresh copies * not shared with other uses. * - * This is currently used only when an EquivalenceClass is found to - * contain pseudoconstants. See path/pathkeys.c for more details. + * Note: we do not do initialize_mergeclause_eclasses() here. It is + * caller's responsibility that left_ec/right_ec be set as necessary. */ -void +RestrictInfo * process_implied_equality(PlannerInfo *root, Oid opno, Oid collation, @@ -2296,24 +2269,27 @@ process_implied_equality(PlannerInfo *root, bool below_outer_join, bool both_const) { - Expr *clause; + RestrictInfo *restrictinfo; + Node *clause; + Relids relids; + bool pseudoconstant = false; /* * Build the new clause. Copy to ensure it shares no substructure with * original (this is necessary in case there are subselects in there...) */ - clause = make_opclause(opno, - BOOLOID, /* opresulttype */ - false, /* opretset */ - copyObject(item1), - copyObject(item2), - InvalidOid, - collation); + clause = (Node *) make_opclause(opno, + BOOLOID, /* opresulttype */ + false, /* opretset */ + copyObject(item1), + copyObject(item2), + InvalidOid, + collation); /* If both constant, try to reduce to a boolean constant. */ if (both_const) { - clause = (Expr *) eval_const_expressions(root, (Node *) clause); + clause = eval_const_expressions(root, clause); /* If we produced const TRUE, just drop the clause */ if (clause && IsA(clause, Const)) @@ -2322,25 +2298,106 @@ process_implied_equality(PlannerInfo *root, Assert(cclause->consttype == BOOLOID); if (!cclause->constisnull && DatumGetBool(cclause->constvalue)) - return; + return NULL; + } + } + + /* + * The rest of this is a very cut-down version of distribute_qual_to_rels. + * We can skip most of the work therein, but there are a couple of special + * cases we still have to handle. + * + * Retrieve all relids mentioned within the possibly-simplified clause. + */ + relids = pull_varnos(clause); + Assert(bms_is_subset(relids, qualscope)); + + /* + * If the clause is variable-free, our normal heuristic for pushing it + * down to just the mentioned rels doesn't work, because there are none. + * Apply at the given qualscope, or at the top of tree if it's nonvolatile + * (which it very likely is, but we'll check, just to be sure). + */ + if (bms_is_empty(relids)) + { + /* eval at original syntactic level */ + relids = bms_copy(qualscope); + if (!contain_volatile_functions(clause)) + { + /* mark as gating qual */ + pseudoconstant = true; + /* tell createplan.c to check for gating quals */ + root->hasPseudoConstantQuals = true; + /* if not below outer join, push it to top of tree */ + if (!below_outer_join) + { + relids = + get_relids_in_jointree((Node *) root->parse->jointree, + false); + } } } /* + * Build the RestrictInfo node itself. + */ + restrictinfo = make_restrictinfo((Expr *) clause, + true, /* is_pushed_down */ + false, /* outerjoin_delayed */ + pseudoconstant, + security_level, + relids, + NULL, /* outer_relids */ + nullable_relids); + + /* + * If it's a join clause, add vars used in the clause to targetlists of + * their relations, so that they will be emitted by the plan nodes that + * scan those relations (else they won't be available at the join node!). + * + * Typically, we'd have already done this when the component expressions + * were first seen by distribute_qual_to_rels; but it is possible that + * some of the Vars could have missed having that done because they only + * appeared in single-relation clauses originally. So do it here for + * safety. + */ + if (bms_membership(relids) == BMS_MULTIPLE) + { + List *vars = pull_var_clause(clause, + PVC_RECURSE_AGGREGATES | + PVC_RECURSE_WINDOWFUNCS | + PVC_INCLUDE_PLACEHOLDERS); + + add_vars_to_targetlist(root, vars, relids, false); + list_free(vars); + } + + /* + * Check mergejoinability. This will usually succeed, since the op came + * from an EquivalenceClass; but we could have reduced the original clause + * to a constant. + */ + check_mergejoinable(restrictinfo); + + /* + * Note we don't do initialize_mergeclause_eclasses(); the caller can + * handle that much more cheaply than we can. It's okay to call + * distribute_restrictinfo_to_rels() before that happens. + */ + + /* * Push the new clause into all the appropriate restrictinfo lists. */ - distribute_qual_to_rels(root, (Node *) clause, - true, below_outer_join, JOIN_INNER, - security_level, - qualscope, NULL, NULL, nullable_relids, - NULL); + distribute_restrictinfo_to_rels(root, restrictinfo); + + return restrictinfo; } /* * build_implied_join_equality --- build a RestrictInfo for a derived equality * * This overlaps the functionality of process_implied_equality(), but we - * must return the RestrictInfo, not push it into the joininfo tree. + * must not push the RestrictInfo into the joininfo tree. * * Note: this function will copy item1 and item2, but it is caller's * responsibility to make sure that the Relids parameters are fresh copies @@ -2455,18 +2512,19 @@ match_foreign_keys_to_quals(PlannerInfo *root) */ for (colno = 0; colno < fkinfo->nkeys; colno++) { + EquivalenceClass *ec; AttrNumber con_attno, ref_attno; Oid fpeqop; ListCell *lc2; - fkinfo->eclass[colno] = match_eclasses_to_foreign_key_col(root, - fkinfo, - colno); + ec = match_eclasses_to_foreign_key_col(root, fkinfo, colno); /* Don't bother looking for loose quals if we got an EC match */ - if (fkinfo->eclass[colno] != NULL) + if (ec != NULL) { fkinfo->nmatched_ec++; + if (ec->ec_has_const) + fkinfo->nconst_ec++; continue; } |