bool relation_excluded_by_constraints(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
predicate_refuted_by(safe_restrictions, safe_restrictions, true) // 表上谓词相互refute
predicate_refuted_by(safe_constraints, rel->baserestrictinfo, false) // CHECK约束与表上谓词refute
bool predicate_refuted_by(List *predicate_list, List *clause_list, bool weak){
Node *p, *c;
if (predicate_list == NIL) return false; /* no predicate: no refutation is possible */
if (clause_list == NIL) return false; /* no restriction: refutation must fail */
/* If either input is a single-element list, replace it with its lone member; this avoids one useless level of AND-recursion. We only need to worry about this at top level, since eval_const_expressions should have gotten rid of any trivial ANDs or ORs below that. */
if (list_length(predicate_list) == 1) p = (Node *) linitial(predicate_list);
else p = (Node *) predicate_list;
if (list_length(clause_list) == 1) c = (Node *) linitial(clause_list);
else c = (Node *) clause_list;
return predicate_refuted_by_recurse(c, p, weak); /* And away we go ... */
}
predicate_refuted_by函数用于递归判定clause_list中的clauses语句是否refute(驳斥;批驳;反驳;否认
)给定的predicate。支持两种refutation模式(上述代码也展示的这两种模式的使用状况):
1 “Strong” refutation: A refutes B means truth of A implies falsity of B. We use this to disprove a CHECK constraint given a WHERE clause, i.e., prove that any row satisfying the WHERE clause would violate the CHECK constraint. (Observe we must prove B yields false, not just not-true.) A反驳B意味着A的真意味着B的假。我们用它来反驳给定WHERE子句的CHECK约束,即证明任何满足WHERE子句的行都会违反CHECK约束。(注意,我们必须证明B是假的,而不仅仅是不正确。)
2 “Weak” refutation: A refutes B means truth of A implies non-truth of B (i.e., B must yield false or NULL). We use this to detect mutually contradictory WHERE clauses. A反驳B意味着A的真意味着B的非真(即,B必须产生false或NULL)。我们使用它来检测相互矛盾的WHERE子句。
弱反驳可以在某些情况下被证明,而强反驳不成立,所以在可能的情况下使用它是有用的。目前,我们不支持基于另一个CHECK约束来反驳一个约束,也不支持基于CHECK来反驳WHERE约束。(与言外之意一样,最后一种情况似乎不太实用。CHECK与CHECK可能很有用,但目前任何地方都不需要。)Weak refutation can be proven in some cases where strong refutation doesn’t hold, so it’s useful to use it when possible. We don’t currently have support for disproving one CHECK constraint based on another one, nor for disproving WHERE based on CHECK. (As with implication, the last case doesn’t seem very practical. CHECK-vs-CHECK might be useful, but isn’t currently needed anywhere.)
每个列表的顶级列表结构对应于一个AND列表。我们假设已经应用了eval_const_expression(),因此不存在未展开的and或or(例如,在and中没有立即的and,包括顶级List结构下方的and)。如果这不是真的,我们可能无法证明一个有效的暗示,但不会产生更糟糕的后果。The top-level List structure of each list corresponds to an AND list. We assume that eval_const_expressions() has been applied and so there are no un-flattened ANDs or ORs (e.g., no AND immediately within an AND, including AND just below the top-level List structure). If this is not true we might fail to prove an implication that is valid, but no worse consequences will ensue.
我们假设谓词已经被检查为只包含不可变的函数和运算符。我们不敢基于非不变函数进行推导,因为它们可能会在我们制定计划和执行计划之间改变答案。如果需要,这里会检查子句列表中函数的不可变性。 We assume the predicate has already been checked to contain only immutable functions and operators. We dare not make deductions based on non-immutable functions, because they might change answers between the time we make the plan and the time we execute the plan. Immutability of functions in the clause_list is checked here, if necessary. 注:在relation_excluded_by_constraints函数中已经对可变的函数的表达式列表进行了排除,由于列表中的元素之间的关系时AND,所以直接去除可变的函数的表达式元素后的列表依然可以用于推导,也就是如果被refute,那就可以确定进行refuteA和B之间不存在任何交集的可能。
predicate_refuted_by_recurse函数对non-NULL restriction和predicate clauses进行predicate refutation测试。首先先阐明两个先决条件:1 rel->baserestrictinfo
列表由RestrictInfo或OpExpr组成
2 传入该函数的列表中的原子表达式(ATOM:不可拆为AND/OR关系)之间只存在AND或OR关系,列表元素之间暗含AND关系。
predicate_refuted_by_recurse函数核心refute逻辑即是:两个表达式树根节点和中继节点是AND或OR关系,叶子节点是ATOM表达式。可以refute推导两个表达式树的叶子节点之间的是否排斥(atom A R=> atom B iff: predicate_refuted_by_simple_clause says so),然后叠加上父节点的AND或OR关系,确定两个表达式树在该中继节点上是否排斥。如下是确定中继节点是否排斥的规则:
# The logic followed here is ("R=>" means "refutes"):
# rules apply equally to strong or weak refutation
* atom A R=> atom B iff: predicate_refuted_by_simple_clause says so
* atom A R=> AND-expr B iff: A R=> any of B's components
* atom A R=> OR-expr B iff: A R=> each of B's components
* AND-expr A R=> atom B iff: any of A's components R=> B
* AND-expr A R=> AND-expr B iff: A R=> any of B's components,
* *or* any of A's components R=> B
* AND-expr A R=> OR-expr B iff: A R=> each of B's components
* OR-expr A R=> atom B iff: each of A's components R=> B
* OR-expr A R=> AND-expr B iff: each of A's components R=> any of B's
* OR-expr A R=> OR-expr B iff: A R=> each of B's components
# In addition, if the predicate is a NOT-clause then we can use
* A R=> NOT B if: A => B
* This works for several different SQL constructs that assert the non-truth of their argument, ie NOT, IS FALSE, IS NOT TRUE, IS UNKNOWN, although some of them require that we prove strong implication. Likewise, we can use
* NOT A R=> B if: B => A
* but here we must be careful about strong vs. weak refutation and make the appropriate type of implication proof (weak or strong respectively).
atom A表达式如果要refutes中继节点为AND关系的表达式,需要A表达式refutes中继节点下的所有子节点(atom A R=> AND-expr B iff: A R=> any of B’s components)。其他规则也是相似的道理。详细代码如下所示,从主体逻辑可以看出上述规则对应的代码:
static bool predicate_refuted_by_recurse(Node *clause, Node *predicate, bool weak){
PredIterInfoData clause_info; PredIterInfoData pred_info;
Node *not_arg; bool result;
if (IsA(clause, RestrictInfo)) clause = (Node *) ((RestrictInfo *) clause)->clause;
PredClass pclass = predicate_classify(predicate, &pred_info); // 确定该节点式atom还是AND/OR节点(叶子节点还是中继节点根节点)
switch (predicate_classify(clause, &clause_info)) {
case CLASS_AND:
switch (pclass){
case CLASS_AND:
/* AND-clause R=> AND-clause if A refutes any of B's items --> Needed to handle (x AND y) R=> ((!x OR !y) AND z) */
result = false;
iterate_begin(pitem, predicate, pred_info){ if (predicate_refuted_by_recurse(clause, pitem, weak)){ result = true; break; } }
iterate_end(pred_info);
if (result) return result;
/* Also check if any of A's items refutes B --> Needed to handle ((x OR y) AND z) R=> (!x AND !y) */
iterate_begin(citem, clause, clause_info){ if (predicate_refuted_by_recurse(citem, predicate, weak)) { result = true; break; } }
iterate_end(clause_info);
return result;
case CLASS_OR:
/* AND-clause R=> OR-clause if A refutes each of B's items */
result = true;
iterate_begin(pitem, predicate, pred_info){ if (!predicate_refuted_by_recurse(clause, pitem, weak)) { result = false; break; } }
iterate_end(pred_info);
return result;
case CLASS_ATOM:
/* If B is a NOT-type clause, A R=> B if A => B's arg
* Since, for either type of refutation, we are starting with the premise that A is true, we can use a strong implication test in all cases. That proves B's arg is true, which is more than we need for weak refutation if B is a simple NOT, but it allows not worrying about exactly which kind of negation clause we have. */
not_arg = extract_not_arg(predicate);
if (not_arg && predicate_implied_by_recurse(clause, not_arg, false))
return true;
/* AND-clause R=> atom if any of A's items refutes B */
result = false;
iterate_begin(citem, clause, clause_info){
if (predicate_refuted_by_recurse(citem, predicate, weak)){ result = true; break; }
}
iterate_end(clause_info);
return result;
}
break;
case CLASS_OR:
switch (pclass) {
case CLASS_OR:
/* OR-clause R=> OR-clause if A refutes each of B's items */
result = true;
iterate_begin(pitem, predicate, pred_info) { if (!predicate_refuted_by_recurse(clause, pitem, weak)) { result = false; break; } }
iterate_end(pred_info);
return result;
case CLASS_AND:
/* OR-clause R=> AND-clause if each of A's items refutes any of B's items. */
result = true;
iterate_begin(citem, clause, clause_info){
bool presult = false;
iterate_begin(pitem, predicate, pred_info){
if (predicate_refuted_by_recurse(citem, pitem, weak)){ presult = true; break; }
}
iterate_end(pred_info);
if (!presult) { result = false; /* citem refutes nothing */ break; }
}
iterate_end(clause_info);
return result;
case CLASS_ATOM:
/* If B is a NOT-type clause, A R=> B if A => B's arg Same logic as for the AND-clause case above. */
not_arg = extract_not_arg(predicate);
if (not_arg && predicate_implied_by_recurse(clause, not_arg, false))
return true;
/* OR-clause R=> atom if each of A's items refutes B */
result = true;
iterate_begin(citem, clause, clause_info){
if (!predicate_refuted_by_recurse(citem, predicate, weak)){ result = false; break; }
}
iterate_end(clause_info);
return result;
}
break;
case CLASS_ATOM:
/* If A is a strong NOT-clause, A R=> B if B => A's arg
* Since A is strong, we may assume A's arg is false (not just not-true). If B weakly implies A's arg, then B can be neither true nor null, so that strong refutation is proven. If B strongly implies A's arg, then B cannot be true, so that weak refutation is proven. */
not_arg = extract_strong_not_arg(clause);
if (not_arg && predicate_implied_by_recurse(predicate, not_arg, !weak))
return true;
switch (pclass){
case CLASS_AND:
/* atom R=> AND-clause if A refutes any of B's items */
result = false;
iterate_begin(pitem, predicate, pred_info){ if (predicate_refuted_by_recurse(clause, pitem, weak)){ result = true; break; } }
iterate_end(pred_info);
return result;
case CLASS_OR:
/* atom R=> OR-clause if A refutes each of B's items */
result = true;
iterate_begin(pitem, predicate, pred_info){
if (!predicate_refuted_by_recurse(clause, pitem, weak)) { result = false; break;}
}
iterate_end(pred_info);
return result;
case CLASS_ATOM:
/* If B is a NOT-type clause, A R=> B if A => B's arg Same logic as for the AND-clause case above. */
not_arg = extract_not_arg(predicate);
if (not_arg && predicate_implied_by_recurse(clause, not_arg, false))
return true;
/* atom R=> atom is the base case */
return predicate_refuted_by_simple_clause((Expr *) predicate, clause, weak);
}
break;
}
elog(ERROR, "predicate_classify returned a bogus value"); /* can't get here */
return false;
}
predicate_classify判定表达式类型:ATOM、OR、AND,并将*info形参填充为对应的回调函数。
static PredClass predicate_classify(Node *clause, PredIterInfo info) {
if (IsA(clause, List)) { /* If we see a List, assume it's an implicit-AND list; this is the correct semantics for lists of RestrictInfo nodes. */
info->startup_fn = list_startup_fn;
info->next_fn = list_next_fn;
info->cleanup_fn = list_cleanup_fn;
return CLASS_AND;
}
/* Handle normal AND and OR boolean clauses */
if (is_andclause(clause)){
info->startup_fn = boolexpr_startup_fn;
info->next_fn = list_next_fn;
info->cleanup_fn = list_cleanup_fn;
return CLASS_AND;
}
if (is_orclause(clause)){
info->startup_fn = boolexpr_startup_fn;
info->next_fn = list_next_fn;
info->cleanup_fn = list_cleanup_fn;
return CLASS_OR;
}
/* Handle ScalarArrayOpExpr */
if (IsA(clause, ScalarArrayOpExpr)){
ScalarArrayOpExpr *saop = (ScalarArrayOpExpr *) clause;
Node *arraynode = (Node *) lsecond(saop->args);
/* We can break this down into an AND or OR structure, but only if we know how to iterate through expressions for the array's elements. We can do that if the array operand is a non-null constant or a simple ArrayExpr.
*/
if (arraynode && IsA(arraynode, Const) && !((Const *) arraynode)->constisnull){
ArrayType *arrayval = DatumGetArrayTypeP(((Const *) arraynode)->constvalue);
int nelems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval));
if (nelems <= MAX_SAOP_ARRAY_SIZE){
info->startup_fn = arrayconst_startup_fn;
info->next_fn = arrayconst_next_fn;
info->cleanup_fn = arrayconst_cleanup_fn;
return saop->useOr ? CLASS_OR : CLASS_AND;
}
}else if (arraynode && IsA(arraynode, ArrayExpr) && !((ArrayExpr *) arraynode)->multidims && list_length(((ArrayExpr *) arraynode)->elements) <= MAX_SAOP_ARRAY_SIZE) {
info->startup_fn = arrayexpr_startup_fn;
info->next_fn = arrayexpr_next_fn;
info->cleanup_fn = arrayexpr_cleanup_fn;
return saop->useOr ? CLASS_OR : CLASS_AND;
}
}
return CLASS_ATOM; /* None of the above, so it's an atom */
}
predicate_refuted_by_simple_clause判定ATOM之间的排斥性。