* RestrictInfos, so we must make our own.
*/
Assert(!IsA(expr, RestrictInfo));
- rinfo = make_restrictinfo(expr,
+ rinfo = make_restrictinfo(root,
+ expr,
true,
false,
false,
}
else
{
- ok = (NumRelids(clause) == 1) &&
+ ok = (NumRelids(root, clause) == 1) &&
(is_pseudo_constant_clause(lsecond(expr->args)) ||
(varonleft = false,
is_pseudo_constant_clause(linitial(expr->args))));
* restriction or join estimator. Subroutine for clause_selectivity().
*/
static inline bool
-treat_as_join_clause(Node *clause, RestrictInfo *rinfo,
+treat_as_join_clause(PlannerInfo *root, Node *clause, RestrictInfo *rinfo,
int varRelid, SpecialJoinInfo *sjinfo)
{
if (varRelid != 0)
if (rinfo)
return (bms_membership(rinfo->clause_relids) == BMS_MULTIPLE);
else
- return (NumRelids(clause) > 1);
+ return (NumRelids(root, clause) > 1);
}
}
OpExpr *opclause = (OpExpr *) clause;
Oid opno = opclause->opno;
- if (treat_as_join_clause(clause, rinfo, varRelid, sjinfo))
+ if (treat_as_join_clause(root, clause, rinfo, varRelid, sjinfo))
{
/* Estimate selectivity for a join clause. */
s1 = join_selectivity(root, opno,
funcclause->funcid,
funcclause->args,
funcclause->inputcollid,
- treat_as_join_clause(clause, rinfo,
+ treat_as_join_clause(root, clause, rinfo,
varRelid, sjinfo),
varRelid,
jointype,
/* Use node specific selectivity calculation function */
s1 = scalararraysel(root,
(ScalarArrayOpExpr *) clause,
- treat_as_join_clause(clause, rinfo,
+ treat_as_join_clause(root, clause, rinfo,
varRelid, sjinfo),
varRelid,
jointype,
* Check if the expression contains Var with "varno 0" so that we
* don't call estimate_num_groups in that case.
*/
- if (bms_is_member(0, pull_varnos((Node *) member->em_expr)))
+ if (bms_is_member(0, pull_varnos(root, (Node *) member->em_expr)))
{
unknown_varno = true;
break;
ntest->location = -1;
*p_restrictinfo =
- make_restrictinfo((Expr *) ntest,
+ make_restrictinfo(root,
+ (Expr *) ntest,
restrictinfo->is_pushed_down,
restrictinfo->outerjoin_delayed,
restrictinfo->pseudoconstant,
/*
* Get the precise set of nullable relids appearing in the expression.
*/
- expr_relids = pull_varnos((Node *) expr);
+ expr_relids = pull_varnos(root, (Node *) expr);
nullable_relids = bms_intersect(nullable_relids, expr_relids);
newem = add_eq_member(newec, copyObject(expr), expr_relids,
*/
oldcontext = MemoryContextSwitchTo(root->planner_cxt);
- rinfo = build_implied_join_equality(opno,
+ rinfo = build_implied_join_equality(root,
+ opno,
ec->ec_collation,
leftem->em_expr,
rightem->em_expr,
cur_em->em_datatype);
if (!OidIsValid(eq_op))
continue; /* can't generate equality */
- newrinfo = build_implied_join_equality(eq_op,
+ newrinfo = build_implied_join_equality(root,
+ eq_op,
cur_ec->ec_collation,
innervar,
cur_em->em_expr,
cur_em->em_datatype);
if (OidIsValid(eq_op))
{
- newrinfo = build_implied_join_equality(eq_op,
+ newrinfo = build_implied_join_equality(root,
+ eq_op,
cur_ec->ec_collation,
leftvar,
cur_em->em_expr,
cur_em->em_datatype);
if (OidIsValid(eq_op))
{
- newrinfo = build_implied_join_equality(eq_op,
+ newrinfo = build_implied_join_equality(root,
+ eq_op,
cur_ec->ec_collation,
rightvar,
cur_em->em_expr,
RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index);
-static IndexClause *match_boolean_index_clause(RestrictInfo *rinfo,
+static IndexClause *match_boolean_index_clause(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol, IndexOptInfo *index);
static IndexClause *match_opclause_to_indexcol(PlannerInfo *root,
RestrictInfo *rinfo,
int indexarg,
int indexcol,
IndexOptInfo *index);
-static IndexClause *match_saopclause_to_indexcol(RestrictInfo *rinfo,
+static IndexClause *match_saopclause_to_indexcol(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index);
-static IndexClause *match_rowcompare_to_indexcol(RestrictInfo *rinfo,
+static IndexClause *match_rowcompare_to_indexcol(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index);
-static IndexClause *expand_indexqual_rowcompare(RestrictInfo *rinfo,
+static IndexClause *expand_indexqual_rowcompare(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index,
Oid expr_op,
opfamily = index->opfamily[indexcol];
if (IsBooleanOpfamily(opfamily))
{
- iclause = match_boolean_index_clause(rinfo, indexcol, index);
+ iclause = match_boolean_index_clause(root, rinfo, indexcol, index);
if (iclause)
return iclause;
}
}
else if (IsA(clause, ScalarArrayOpExpr))
{
- return match_saopclause_to_indexcol(rinfo, indexcol, index);
+ return match_saopclause_to_indexcol(root, rinfo, indexcol, index);
}
else if (IsA(clause, RowCompareExpr))
{
- return match_rowcompare_to_indexcol(rinfo, indexcol, index);
+ return match_rowcompare_to_indexcol(root, rinfo, indexcol, index);
}
else if (index->amsearchnulls && IsA(clause, NullTest))
{
* index's key, and if so, build a suitable IndexClause.
*/
static IndexClause *
-match_boolean_index_clause(RestrictInfo *rinfo,
+match_boolean_index_clause(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index)
{
IndexClause *iclause = makeNode(IndexClause);
iclause->rinfo = rinfo;
- iclause->indexquals = list_make1(make_simple_restrictinfo(op));
+ iclause->indexquals = list_make1(make_simple_restrictinfo(root, op));
iclause->lossy = false;
iclause->indexcol = indexcol;
iclause->indexcols = NIL;
{
Expr *clause = (Expr *) lfirst(lc);
- indexquals = lappend(indexquals, make_simple_restrictinfo(clause));
+ indexquals = lappend(indexquals,
+ make_simple_restrictinfo(root, clause));
}
iclause->rinfo = rinfo;
* which see for comments.
*/
static IndexClause *
-match_saopclause_to_indexcol(RestrictInfo *rinfo,
+match_saopclause_to_indexcol(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index)
{
return NULL;
leftop = (Node *) linitial(saop->args);
rightop = (Node *) lsecond(saop->args);
- right_relids = pull_varnos(rightop);
+ right_relids = pull_varnos(root, rightop);
expr_op = saop->opno;
expr_coll = saop->inputcollid;
* is handled by expand_indexqual_rowcompare().
*/
static IndexClause *
-match_rowcompare_to_indexcol(RestrictInfo *rinfo,
+match_rowcompare_to_indexcol(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index)
{
* These syntactic tests are the same as in match_opclause_to_indexcol()
*/
if (match_index_to_operand(leftop, indexcol, index) &&
- !bms_is_member(index_relid, pull_varnos(rightop)) &&
+ !bms_is_member(index_relid, pull_varnos(root, rightop)) &&
!contain_volatile_functions(rightop))
{
/* OK, indexkey is on left */
var_on_left = true;
}
else if (match_index_to_operand(rightop, indexcol, index) &&
- !bms_is_member(index_relid, pull_varnos(leftop)) &&
+ !bms_is_member(index_relid, pull_varnos(root, leftop)) &&
!contain_volatile_functions(leftop))
{
/* indexkey is on right, so commute the operator */
case BTLessEqualStrategyNumber:
case BTGreaterEqualStrategyNumber:
case BTGreaterStrategyNumber:
- return expand_indexqual_rowcompare(rinfo,
+ return expand_indexqual_rowcompare(root,
+ rinfo,
indexcol,
index,
expr_op,
* but we split it out for comprehensibility.
*/
static IndexClause *
-expand_indexqual_rowcompare(RestrictInfo *rinfo,
+expand_indexqual_rowcompare(PlannerInfo *root,
+ RestrictInfo *rinfo,
int indexcol,
IndexOptInfo *index,
Oid expr_op,
if (expr_op == InvalidOid)
break; /* operator is not usable */
}
- if (bms_is_member(index->rel->relid, pull_varnos(constop)))
+ if (bms_is_member(index->rel->relid, pull_varnos(root, constop)))
break; /* no good, Var on wrong side */
if (contain_volatile_functions(constop))
break; /* no good, volatile comparison value */
matching_cols);
rc->rargs = list_truncate(copyObject(non_var_args),
matching_cols);
- iclause->indexquals = list_make1(make_simple_restrictinfo((Expr *) rc));
+ iclause->indexquals = list_make1(make_simple_restrictinfo(root,
+ (Expr *) rc));
}
else
{
copyObject(linitial(non_var_args)),
InvalidOid,
linitial_oid(clause->inputcollids));
- iclause->indexquals = list_make1(make_simple_restrictinfo(op));
+ iclause->indexquals = list_make1(make_simple_restrictinfo(root, op));
}
}
* specified index column matches a boolean restriction clause.
*/
bool
-indexcol_is_bool_constant_for_query(IndexOptInfo *index, int indexcol)
+indexcol_is_bool_constant_for_query(PlannerInfo *root,
+ IndexOptInfo *index,
+ int indexcol)
{
ListCell *lc;
continue;
/* See if we can match the clause's expression to the index column */
- if (match_boolean_index_clause(rinfo, indexcol, index))
+ if (match_boolean_index_clause(root, rinfo, indexcol, index))
return true;
}
* index: the index of interest
*/
bool
-is_pseudo_constant_for_index(Node *expr, IndexOptInfo *index)
+is_pseudo_constant_for_index(PlannerInfo *root, Node *expr, IndexOptInfo *index)
{
/* pull_varnos is cheaper than volatility check, so do that first */
- if (bms_is_member(index->rel->relid, pull_varnos(expr)))
+ if (bms_is_member(index->rel->relid, pull_varnos(root, expr)))
return false; /* no good, contains Var of table */
if (contain_volatile_functions(expr))
return false; /* no good, volatile comparison value */
* should stop considering index columns; any lower-order sort
* keys won't be useful either.
*/
- if (!indexcol_is_bool_constant_for_query(index, i))
+ if (!indexcol_is_bool_constant_for_query(root, index, i))
break;
}
* other side of the clause does.
*/
static bool
-IsTidEqualAnyClause(RestrictInfo *rinfo, RelOptInfo *rel)
+IsTidEqualAnyClause(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
{
ScalarArrayOpExpr *node;
Node *arg1,
IsCTIDVar((Var *) arg1, rel))
{
/* The other argument must be a pseudoconstant */
- if (bms_is_member(rel->relid, pull_varnos(arg2)) ||
+ if (bms_is_member(rel->relid, pull_varnos(root, arg2)) ||
contain_volatile_functions(arg2))
return false;
* (Using a List may seem a bit weird, but it simplifies the caller.)
*/
static List *
-TidQualFromRestrictInfo(RestrictInfo *rinfo, RelOptInfo *rel)
+TidQualFromRestrictInfo(PlannerInfo *root, RestrictInfo *rinfo, RelOptInfo *rel)
{
/*
* We may ignore pseudoconstant clauses (they can't contain Vars, so could
* Check all base cases. If we get a match, return the clause.
*/
if (IsTidEqualClause(rinfo, rel) ||
- IsTidEqualAnyClause(rinfo, rel) ||
+ IsTidEqualAnyClause(root, rinfo, rel) ||
IsCurrentOfClause(rinfo, rel))
return list_make1(rinfo);
* This function is just concerned with handling AND/OR recursion.
*/
static List *
-TidQualFromRestrictInfoList(List *rlist, RelOptInfo *rel)
+TidQualFromRestrictInfoList(PlannerInfo *root, List *rlist, RelOptInfo *rel)
{
List *rlst = NIL;
ListCell *l;
List *andargs = ((BoolExpr *) orarg)->args;
/* Recurse in case there are sub-ORs */
- sublist = TidQualFromRestrictInfoList(andargs, rel);
+ sublist = TidQualFromRestrictInfoList(root, andargs, rel);
}
else
{
RestrictInfo *rinfo = castNode(RestrictInfo, orarg);
Assert(!restriction_is_or_clause(rinfo));
- sublist = TidQualFromRestrictInfo(rinfo, rel);
+ sublist = TidQualFromRestrictInfo(root, rinfo, rel);
}
/*
else
{
/* Not an OR clause, so handle base cases */
- rlst = TidQualFromRestrictInfo(rinfo, rel);
+ rlst = TidQualFromRestrictInfo(root, rinfo, rel);
}
/*
* If any suitable quals exist in the rel's baserestrict list, generate a
* plain (unparameterized) TidPath with them.
*/
- tidquals = TidQualFromRestrictInfoList(rel->baserestrictinfo, rel);
+ tidquals = TidQualFromRestrictInfoList(root, rel->baserestrictinfo, rel);
if (tidquals)
{
continue; /* it definitely doesn't reference innerrel */
if (bms_is_subset(phinfo->ph_eval_at, innerrel->relids))
return false; /* there isn't any other place to eval PHV */
- if (bms_overlap(pull_varnos((Node *) phinfo->ph_var->phexpr),
+ if (bms_overlap(pull_varnos(root, (Node *) phinfo->ph_var->phexpr),
innerrel->relids))
return false; /* it does reference innerrel */
}
Relids left_rels, Relids right_rels,
Relids inner_join_rels,
JoinType jointype, List *clause);
-static void compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause);
+static void compute_semijoin_info(PlannerInfo *root, SpecialJoinInfo *sjinfo,
+ List *clause);
static void distribute_qual_to_rels(PlannerInfo *root, Node *clause,
bool below_outer_join,
JoinType jointype,
/* this always starts out false */
sjinfo->delay_upper_joins = false;
- compute_semijoin_info(sjinfo, clause);
+ compute_semijoin_info(root, sjinfo, clause);
/* If it's a full join, no need to be very smart */
if (jointype == JOIN_FULL)
/*
* Retrieve all relids mentioned within the join clause.
*/
- clause_relids = pull_varnos((Node *) clause);
+ clause_relids = pull_varnos(root, (Node *) clause);
/*
* For which relids is the clause strict, ie, it cannot succeed if the
* SpecialJoinInfo; the rest may not be set yet.
*/
static void
-compute_semijoin_info(SpecialJoinInfo *sjinfo, List *clause)
+compute_semijoin_info(PlannerInfo *root, SpecialJoinInfo *sjinfo, List *clause)
{
List *semi_operators;
List *semi_rhs_exprs;
list_length(op->args) != 2)
{
/* No, but does it reference both sides? */
- all_varnos = pull_varnos((Node *) op);
+ all_varnos = pull_varnos(root, (Node *) op);
if (!bms_overlap(all_varnos, sjinfo->syn_righthand) ||
bms_is_subset(all_varnos, sjinfo->syn_righthand))
{
opno = op->opno;
left_expr = linitial(op->args);
right_expr = lsecond(op->args);
- left_varnos = pull_varnos(left_expr);
- right_varnos = pull_varnos(right_expr);
+ left_varnos = pull_varnos(root, left_expr);
+ right_varnos = pull_varnos(root, right_expr);
all_varnos = bms_union(left_varnos, right_varnos);
opinputtype = exprType(left_expr);
/*
* Retrieve all relids mentioned within the clause.
*/
- relids = pull_varnos(clause);
+ relids = pull_varnos(root, clause);
/*
* In ordinary SQL, a WHERE or JOIN/ON clause can't reference any rels
/*
* Build the RestrictInfo node itself.
*/
- restrictinfo = make_restrictinfo((Expr *) clause,
+ restrictinfo = make_restrictinfo(root,
+ (Expr *) clause,
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
*
* Retrieve all relids mentioned within the possibly-simplified clause.
*/
- relids = pull_varnos(clause);
+ relids = pull_varnos(root, clause);
Assert(bms_is_subset(relids, qualscope));
/*
/*
* Build the RestrictInfo node itself.
*/
- restrictinfo = make_restrictinfo((Expr *) clause,
+ restrictinfo = make_restrictinfo(root,
+ (Expr *) clause,
true, /* is_pushed_down */
false, /* outerjoin_delayed */
pseudoconstant,
* caller's responsibility that left_ec/right_ec be set as necessary.
*/
RestrictInfo *
-build_implied_join_equality(Oid opno,
+build_implied_join_equality(PlannerInfo *root,
+ Oid opno,
Oid collation,
Expr *item1,
Expr *item2,
/*
* Build the RestrictInfo node itself.
*/
- restrictinfo = make_restrictinfo(clause,
+ restrictinfo = make_restrictinfo(root,
+ clause,
true, /* is_pushed_down */
false, /* outerjoin_delayed */
false, /* pseudoconstant */
* it's not gonna be a join. (Note that it won't have Vars referring to
* the subquery, rather Params.)
*/
- upper_varnos = pull_varnos(sublink->testexpr);
+ upper_varnos = pull_varnos(root, sublink->testexpr);
if (bms_is_empty(upper_varnos))
return NULL;
* The ones <= rtoffset belong to the upper query; the ones > rtoffset do
* not.
*/
- clause_varnos = pull_varnos(whereClause);
+ clause_varnos = pull_varnos(root, whereClause);
upper_varnos = NULL;
while ((varno = bms_first_member(clause_varnos)) >= 0)
{
int childRToffset);
static void make_setop_translation_list(Query *query, Index newvarno,
AppendRelInfo *appinfo);
-static bool is_simple_subquery(Query *subquery, RangeTblEntry *rte,
+static bool is_simple_subquery(PlannerInfo *root, Query *subquery,
+ RangeTblEntry *rte,
JoinExpr *lowest_outer_join);
static Node *pull_up_simple_values(PlannerInfo *root, Node *jtnode,
RangeTblEntry *rte);
static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery,
List *colTypes);
static bool is_safe_append_member(Query *subquery);
-static bool jointree_contains_lateral_outer_refs(Node *jtnode, bool restricted,
+static bool jointree_contains_lateral_outer_refs(PlannerInfo *root,
+ Node *jtnode, bool restricted,
Relids safe_upper_varnos);
static void perform_pullup_replace_vars(PlannerInfo *root,
pullup_replace_vars_context *rvcontext,
* unless is_safe_append_member says so.
*/
if (rte->rtekind == RTE_SUBQUERY &&
- is_simple_subquery(rte->subquery, rte, lowest_outer_join) &&
+ is_simple_subquery(root, rte->subquery, rte, lowest_outer_join) &&
(containing_appendrel == NULL ||
is_safe_append_member(rte->subquery)))
return pull_up_simple_subquery(root, jtnode, rte,
* easier just to keep this "if" looking the same as the one in
* pull_up_subqueries_recurse.
*/
- if (is_simple_subquery(subquery, rte, lowest_outer_join) &&
+ if (is_simple_subquery(root, subquery, rte, lowest_outer_join) &&
(containing_appendrel == NULL || is_safe_append_member(subquery)))
{
/* good to go */
* lowest_outer_join is the lowest outer join above the subquery, or NULL.
*/
static bool
-is_simple_subquery(Query *subquery, RangeTblEntry *rte,
+is_simple_subquery(PlannerInfo *root, Query *subquery, RangeTblEntry *rte,
JoinExpr *lowest_outer_join)
{
/*
safe_upper_varnos = NULL; /* doesn't matter */
}
- if (jointree_contains_lateral_outer_refs((Node *) subquery->jointree,
+ if (jointree_contains_lateral_outer_refs(root,
+ (Node *) subquery->jointree,
restricted, safe_upper_varnos))
return false;
*/
if (lowest_outer_join != NULL)
{
- Relids lvarnos = pull_varnos_of_level((Node *) subquery->targetList, 1);
+ Relids lvarnos = pull_varnos_of_level(root,
+ (Node *) subquery->targetList,
+ 1);
if (!bms_is_subset(lvarnos, safe_upper_varnos))
return false;
* in safe_upper_varnos.
*/
static bool
-jointree_contains_lateral_outer_refs(Node *jtnode, bool restricted,
+jointree_contains_lateral_outer_refs(PlannerInfo *root, Node *jtnode,
+ bool restricted,
Relids safe_upper_varnos)
{
if (jtnode == NULL)
/* First, recurse to check child joins */
foreach(l, f->fromlist)
{
- if (jointree_contains_lateral_outer_refs(lfirst(l),
+ if (jointree_contains_lateral_outer_refs(root,
+ lfirst(l),
restricted,
safe_upper_varnos))
return true;
/* Then check the top-level quals */
if (restricted &&
- !bms_is_subset(pull_varnos_of_level(f->quals, 1),
+ !bms_is_subset(pull_varnos_of_level(root, f->quals, 1),
safe_upper_varnos))
return true;
}
}
/* Check the child joins */
- if (jointree_contains_lateral_outer_refs(j->larg,
+ if (jointree_contains_lateral_outer_refs(root,
+ j->larg,
restricted,
safe_upper_varnos))
return true;
- if (jointree_contains_lateral_outer_refs(j->rarg,
+ if (jointree_contains_lateral_outer_refs(root,
+ j->rarg,
restricted,
safe_upper_varnos))
return true;
/* Check the JOIN's qual clauses */
if (restricted &&
- !bms_is_subset(pull_varnos_of_level(j->quals, 1),
+ !bms_is_subset(pull_varnos_of_level(root, j->quals, 1),
safe_upper_varnos))
return true;
}
* level-zero var must belong to the subquery.
*/
if ((rcon->target_rte->lateral ?
- bms_overlap(pull_varnos((Node *) newnode), rcon->relids) :
+ bms_overlap(pull_varnos(rcon->root, (Node *) newnode),
+ rcon->relids) :
contain_vars_of_level((Node *) newnode, 0)) &&
!contain_nonstrict_functions((Node *) newnode))
{
overlap = list_intersection(local_nonnullable_vars,
forced_null_vars);
if (overlap != NIL &&
- bms_overlap(pull_varnos((Node *) overlap),
+ bms_overlap(pull_varnos(root, (Node *) overlap),
right_state->relids))
jointype = JOIN_ANTI;
}
* Returns the number of different relations referenced in 'clause'.
*/
int
-NumRelids(Node *clause)
+NumRelids(PlannerInfo *root, Node *clause)
{
- Relids varnos = pull_varnos(clause);
+ Relids varnos = pull_varnos(root, clause);
int result = bms_num_members(varnos);
bms_free(varnos);
}
/* reconstitute RestrictInfo with appropriate properties */
childquals = lappend(childquals,
- make_restrictinfo((Expr *) onecq,
+ make_restrictinfo(root,
+ (Expr *) onecq,
rinfo->is_pushed_down,
rinfo->outerjoin_delayed,
pseudoconstant,
/* not likely that we'd see constants here, so no check */
childquals = lappend(childquals,
- make_restrictinfo(qual,
+ make_restrictinfo(root, qual,
true, false, false,
security_level,
NULL, NULL, NULL));
* Build a RestrictInfo from the new OR clause. We can assume it's valid
* as a base restriction clause.
*/
- or_rinfo = make_restrictinfo(orclause,
+ or_rinfo = make_restrictinfo(root,
+ orclause,
true,
false,
false,
* ph_eval_at. If no referenced rels are within the syntactic scope,
* force evaluation at the syntactic location.
*/
- rels_used = pull_varnos((Node *) phv->phexpr);
+ rels_used = pull_varnos(root, (Node *) phv->phexpr);
phinfo->ph_lateral = bms_difference(rels_used, phv->phrels);
if (bms_is_empty(phinfo->ph_lateral))
phinfo->ph_lateral = NULL; /* make it exactly NULL if empty */
#include "optimizer/restrictinfo.h"
-static RestrictInfo *make_restrictinfo_internal(Expr *clause,
+static RestrictInfo *make_restrictinfo_internal(PlannerInfo *root,
+ Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool outerjoin_delayed,
Relids required_relids,
Relids outer_relids,
Relids nullable_relids);
-static Expr *make_sub_restrictinfos(Expr *clause,
+static Expr *make_sub_restrictinfos(PlannerInfo *root,
+ Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
* later.
*/
RestrictInfo *
-make_restrictinfo(Expr *clause,
+make_restrictinfo(PlannerInfo *root,
+ Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
* above each subclause of the top-level AND/OR structure.
*/
if (is_orclause(clause))
- return (RestrictInfo *) make_sub_restrictinfos(clause,
+ return (RestrictInfo *) make_sub_restrictinfos(root,
+ clause,
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
/* Shouldn't be an AND clause, else AND/OR flattening messed up */
Assert(!is_andclause(clause));
- return make_restrictinfo_internal(clause,
+ return make_restrictinfo_internal(root,
+ clause,
NULL,
is_pushed_down,
outerjoin_delayed,
* Common code for the main entry points and the recursive cases.
*/
static RestrictInfo *
-make_restrictinfo_internal(Expr *clause,
+make_restrictinfo_internal(PlannerInfo *root,
+ Expr *clause,
Expr *orclause,
bool is_pushed_down,
bool outerjoin_delayed,
*/
if (is_opclause(clause) && list_length(((OpExpr *) clause)->args) == 2)
{
- restrictinfo->left_relids = pull_varnos(get_leftop(clause));
- restrictinfo->right_relids = pull_varnos(get_rightop(clause));
+ restrictinfo->left_relids = pull_varnos(root, get_leftop(clause));
+ restrictinfo->right_relids = pull_varnos(root, get_rightop(clause));
restrictinfo->clause_relids = bms_union(restrictinfo->left_relids,
restrictinfo->right_relids);
restrictinfo->left_relids = NULL;
restrictinfo->right_relids = NULL;
/* and get the total relid set the hard way */
- restrictinfo->clause_relids = pull_varnos((Node *) clause);
+ restrictinfo->clause_relids = pull_varnos(root, (Node *) clause);
}
/* required_relids defaults to clause_relids */
* contained rels.
*/
static Expr *
-make_sub_restrictinfos(Expr *clause,
+make_sub_restrictinfos(PlannerInfo *root,
+ Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
foreach(temp, ((BoolExpr *) clause)->args)
orlist = lappend(orlist,
- make_sub_restrictinfos(lfirst(temp),
+ make_sub_restrictinfos(root,
+ lfirst(temp),
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
NULL,
outer_relids,
nullable_relids));
- return (Expr *) make_restrictinfo_internal(clause,
+ return (Expr *) make_restrictinfo_internal(root,
+ clause,
make_orclause(orlist),
is_pushed_down,
outerjoin_delayed,
foreach(temp, ((BoolExpr *) clause)->args)
andlist = lappend(andlist,
- make_sub_restrictinfos(lfirst(temp),
+ make_sub_restrictinfos(root,
+ lfirst(temp),
is_pushed_down,
outerjoin_delayed,
pseudoconstant,
return make_andclause(andlist);
}
else
- return (Expr *) make_restrictinfo_internal(clause,
+ return (Expr *) make_restrictinfo_internal(root,
+ clause,
NULL,
is_pushed_down,
outerjoin_delayed,
#include "access/sysattr.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
+#include "optimizer/placeholder.h"
#include "optimizer/prep.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteManip.h"
typedef struct
{
Relids varnos;
+ PlannerInfo *root;
int sublevels_up;
} pull_varnos_context;
* SubPlan, we only need to look at the parameters passed to the subplan.
*/
Relids
-pull_varnos(Node *node)
+pull_varnos(PlannerInfo *root, Node *node)
{
pull_varnos_context context;
context.varnos = NULL;
+ context.root = root;
context.sublevels_up = 0;
/*
* Only Vars of the specified level are considered.
*/
Relids
-pull_varnos_of_level(Node *node, int levelsup)
+pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup)
{
pull_varnos_context context;
context.varnos = NULL;
+ context.root = root;
context.sublevels_up = levelsup;
/*
}
if (IsA(node, PlaceHolderVar))
{
- /*
- * A PlaceHolderVar acts as a variable of its syntactic scope, or
- * lower than that if it references only a subset of the rels in its
- * syntactic scope. It might also contain lateral references, but we
- * should ignore such references when computing the set of varnos in
- * an expression tree. Also, if the PHV contains no variables within
- * its syntactic scope, it will be forced to be evaluated exactly at
- * the syntactic scope, so take that as the relid set.
- */
PlaceHolderVar *phv = (PlaceHolderVar *) node;
- pull_varnos_context subcontext;
- subcontext.varnos = NULL;
- subcontext.sublevels_up = context->sublevels_up;
- (void) pull_varnos_walker((Node *) phv->phexpr, &subcontext);
+ /*
+ * If a PlaceHolderVar is not of the target query level, ignore it,
+ * instead recursing into its expression to see if it contains any
+ * vars that are of the target level.
+ */
if (phv->phlevelsup == context->sublevels_up)
{
- subcontext.varnos = bms_int_members(subcontext.varnos,
- phv->phrels);
- if (bms_is_empty(subcontext.varnos))
+ /*
+ * Ideally, the PHV's contribution to context->varnos is its
+ * ph_eval_at set. However, this code can be invoked before
+ * that's been computed. If we cannot find a PlaceHolderInfo,
+ * fall back to the conservative assumption that the PHV will be
+ * evaluated at its syntactic level (phv->phrels).
+ *
+ * There is a second hazard: this code is also used to examine
+ * qual clauses during deconstruct_jointree, when we may have a
+ * PlaceHolderInfo but its ph_eval_at value is not yet final, so
+ * that theoretically we could obtain a relid set that's smaller
+ * than we'd see later on. That should never happen though,
+ * because we deconstruct the jointree working upwards. Any outer
+ * join that forces delay of evaluation of a given qual clause
+ * will be processed before we examine that clause here, so the
+ * ph_eval_at value should have been updated to include it.
+ */
+ PlaceHolderInfo *phinfo = NULL;
+
+ if (phv->phlevelsup == 0)
+ {
+ ListCell *lc;
+
+ foreach(lc, context->root->placeholder_list)
+ {
+ phinfo = (PlaceHolderInfo *) lfirst(lc);
+ if (phinfo->phid == phv->phid)
+ break;
+ phinfo = NULL;
+ }
+ }
+ if (phinfo != NULL)
+ context->varnos = bms_add_members(context->varnos,
+ phinfo->ph_eval_at);
+ else
context->varnos = bms_add_members(context->varnos,
phv->phrels);
+ return false; /* don't recurse into expression */
}
- context->varnos = bms_join(context->varnos, subcontext.varnos);
- return false;
}
- if (IsA(node, Query))
+ else if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
bool result;
/*
* Otherwise, it's a join if there's more than one relation used.
*/
- is_join_clause = (NumRelids((Node *) opargs) > 1);
+ is_join_clause = (NumRelids(root, (Node *) opargs) > 1);
}
if (is_join_clause)
* membership. Note that when varRelid isn't zero, only vars of that
* relation are considered "real" vars.
*/
- varnos = pull_varnos(basenode);
+ varnos = pull_varnos(root, basenode);
onerel = NULL;
extern bool is_pseudo_constant_clause(Node *clause);
extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids);
-extern int NumRelids(Node *clause);
+extern int NumRelids(PlannerInfo *root, Node *clause);
extern void CommuteOpExpr(OpExpr *clause);
/* in path/indxpath.c: */
-extern bool is_pseudo_constant_for_index(Node *expr, IndexOptInfo *index);
+extern bool is_pseudo_constant_for_index(PlannerInfo *root, Node *expr,
+ IndexOptInfo *index);
/* in plan/planner.c: */
#define PVC_RECURSE_PLACEHOLDERS 0x0020 /* recurse into PlaceHolderVar
* arguments */
-extern Bitmapset *pull_varnos(Node *node);
-extern Bitmapset *pull_varnos_of_level(Node *node, int levelsup);
+extern Bitmapset *pull_varnos(PlannerInfo *root, Node *node);
+extern Bitmapset *pull_varnos_of_level(PlannerInfo *root, Node *node, int levelsup);
extern void pull_varattnos(Node *node, Index varno, Bitmapset **varattnos);
extern List *pull_vars_of_level(Node *node, int levelsup);
extern bool contain_var_clause(Node *node);
extern bool relation_has_unique_index_for(PlannerInfo *root, RelOptInfo *rel,
List *restrictlist,
List *exprlist, List *oprlist);
-extern bool indexcol_is_bool_constant_for_query(IndexOptInfo *index,
+extern bool indexcol_is_bool_constant_for_query(PlannerInfo *root,
+ IndexOptInfo *index,
int indexcol);
extern bool match_index_to_operand(Node *operand, int indexcol,
IndexOptInfo *index);
Index security_level,
bool below_outer_join,
bool both_const);
-extern RestrictInfo *build_implied_join_equality(Oid opno,
+extern RestrictInfo *build_implied_join_equality(PlannerInfo *root,
+ Oid opno,
Oid collation,
Expr *item1,
Expr *item2,
/* Convenience macro for the common case of a valid-everywhere qual */
-#define make_simple_restrictinfo(clause) \
- make_restrictinfo(clause, true, false, false, 0, NULL, NULL, NULL)
+#define make_simple_restrictinfo(root, clause) \
+ make_restrictinfo(root, clause, true, false, false, 0, NULL, NULL, NULL)
-extern RestrictInfo *make_restrictinfo(Expr *clause,
+extern RestrictInfo *make_restrictinfo(PlannerInfo *root,
+ Expr *clause,
bool is_pushed_down,
bool outerjoin_delayed,
bool pseudoconstant,
(1 row)
rollback;
+-- test case to expose miscomputation of required relid set for a PHV
+explain (verbose, costs off)
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+ QUERY PLAN
+-------------------------------------------------------------
+ Nested Loop Left Join
+ Output: i8.q1, i8.q2, ((i4.f1 + 1)), t.unique2
+ -> Nested Loop Left Join
+ Output: i8.q1, i8.q2, (i4.f1 + 1)
+ -> Seq Scan on public.int8_tbl i8
+ Output: i8.q1, i8.q2
+ Filter: (i8.q2 = 456)
+ -> Seq Scan on public.int4_tbl i4
+ Output: i4.f1
+ Filter: (i4.f1 = 1)
+ -> Index Only Scan using tenk1_unique2 on public.tenk1 t
+ Output: t.unique2
+ Index Cond: (t.unique2 = ((i4.f1 + 1)))
+(13 rows)
+
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+ q1 | q2 | v | unique2
+-----+-----+---+---------
+ 123 | 456 | |
+(1 row)
+
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
select * from
int8_tbl x join (int4_tbl x cross join int4_tbl y) j on q1 = f1; -- error
rollback;
+-- test case to expose miscomputation of required relid set for a PHV
+explain (verbose, costs off)
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+
+select i8.*, ss.v, t.unique2
+ from int8_tbl i8
+ left join int4_tbl i4 on i4.f1 = 1
+ left join lateral (select i4.f1 + 1 as v) as ss on true
+ left join tenk1 t on t.unique2 = ss.v
+where q2 = 456;
+
-- bug #8444: we've historically allowed duplicate aliases within aliased JOINs
select * from