clauses.c
来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 2,294 行 · 第 1/5 页
C
2,294 行
* * Returns true if any nonstrict construct is found --- ie, anything that * could produce non-NULL output with a NULL input. * * The idea here is that the caller has verified that the expression contains * one or more Var or Param nodes (as appropriate for the caller's need), and * now wishes to prove that the expression result will be NULL if any of these * inputs is NULL. If we return false, then the proof succeeded. */boolcontain_nonstrict_functions(Node *clause){ return contain_nonstrict_functions_walker(clause, NULL);}static boolcontain_nonstrict_functions_walker(Node *node, void *context){ if (node == NULL) return false; if (IsA(node, Aggref)) { /* an aggregate could return non-null with null input */ return true; } if (IsA(node, ArrayRef)) { /* array assignment is nonstrict, but subscripting is strict */ if (((ArrayRef *) node)->refassgnexpr != NULL) return true; /* else fall through to check args */ } if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (!func_strict(expr->funcid)) return true; /* else fall through to check args */ } if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); if (!func_strict(expr->opfuncid)) return true; /* else fall through to check args */ } if (IsA(node, DistinctExpr)) { /* IS DISTINCT FROM is inherently non-strict */ return true; } if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; if (!is_strict_saop(expr, false)) return true; /* else fall through to check args */ } if (IsA(node, BoolExpr)) { BoolExpr *expr = (BoolExpr *) node; switch (expr->boolop) { case AND_EXPR: case OR_EXPR: /* AND, OR are inherently non-strict */ return true; default: break; } } if (IsA(node, SubLink)) { /* In some cases a sublink might be strict, but in general not */ return true; } if (IsA(node, SubPlan)) return true; /* ArrayCoerceExpr is strict at the array level, regardless of elemfunc */ if (IsA(node, FieldStore)) return true; if (IsA(node, CaseExpr)) return true; if (IsA(node, ArrayExpr)) return true; if (IsA(node, RowExpr)) return true; if (IsA(node, RowCompareExpr)) return true; if (IsA(node, CoalesceExpr)) return true; if (IsA(node, MinMaxExpr)) return true; if (IsA(node, XmlExpr)) return true; if (IsA(node, NullIfExpr)) return true; if (IsA(node, NullTest)) return true; if (IsA(node, BooleanTest)) return true; return expression_tree_walker(node, contain_nonstrict_functions_walker, context);}/* * find_nonnullable_rels * Determine which base rels are forced nonnullable by given clause. * * Returns the set of all Relids that are referenced in the clause in such * a way that the clause cannot possibly return TRUE if any of these Relids * is an all-NULL row. (It is OK to err on the side of conservatism; hence * the analysis here is simplistic.) * * The semantics here are subtly different from contain_nonstrict_functions: * that function is concerned with NULL results from arbitrary expressions, * but here we assume that the input is a Boolean expression, and wish to * see if NULL inputs will provably cause a FALSE-or-NULL result. We expect * the expression to have been AND/OR flattened and converted to implicit-AND * format. * * top_level is TRUE while scanning top-level AND/OR structure; here, showing * the result is either FALSE or NULL is good enough. top_level is FALSE when * we have descended below a NOT or a strict function: now we must be able to * prove that the subexpression goes to NULL. * * We don't use expression_tree_walker here because we don't want to descend * through very many kinds of nodes; only the ones we can be sure are strict. */Relidsfind_nonnullable_rels(Node *clause){ return find_nonnullable_rels_walker(clause, true);}static Relidsfind_nonnullable_rels_walker(Node *node, bool top_level){ Relids result = NULL; ListCell *l; if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; if (var->varlevelsup == 0) result = bms_make_singleton(var->varno); } else if (IsA(node, List)) { /* * At top level, we are examining an implicit-AND list: if any of the * arms produces FALSE-or-NULL then the result is FALSE-or-NULL. If * not at top level, we are examining the arguments of a strict * function: if any of them produce NULL then the result of the * function must be NULL. So in both cases, the set of nonnullable * rels is the union of those found in the arms, and we pass down the * top_level flag unmodified. */ foreach(l, (List *) node) { result = bms_join(result, find_nonnullable_rels_walker(lfirst(l), top_level)); } } else if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (func_strict(expr->funcid)) result = find_nonnullable_rels_walker((Node *) expr->args, false); } else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); if (func_strict(expr->opfuncid)) result = find_nonnullable_rels_walker((Node *) expr->args, false); } else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; if (is_strict_saop(expr, true)) result = find_nonnullable_rels_walker((Node *) expr->args, false); } else if (IsA(node, BoolExpr)) { BoolExpr *expr = (BoolExpr *) node; switch (expr->boolop) { case AND_EXPR: /* At top level we can just recurse (to the List case) */ if (top_level) { result = find_nonnullable_rels_walker((Node *) expr->args, top_level); break; } /* * Below top level, even if one arm produces NULL, the result * could be FALSE (hence not NULL). However, if *all* the * arms produce NULL then the result is NULL, so we can take * the intersection of the sets of nonnullable rels, just as * for OR. Fall through to share code. */ /* FALL THRU */ case OR_EXPR: /* * OR is strict if all of its arms are, so we can take the * intersection of the sets of nonnullable rels for each arm. * This works for both values of top_level. */ foreach(l, expr->args) { Relids subresult; subresult = find_nonnullable_rels_walker(lfirst(l), top_level); if (result == NULL) /* first subresult? */ result = subresult; else result = bms_int_members(result, subresult); /* * If the intersection is empty, we can stop looking. This * also justifies the test for first-subresult above. */ if (bms_is_empty(result)) break; } break; case NOT_EXPR: /* NOT will return null if its arg is null */ result = find_nonnullable_rels_walker((Node *) expr->args, false); break; default: elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); break; } } else if (IsA(node, RelabelType)) { RelabelType *expr = (RelabelType *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } else if (IsA(node, CoerceViaIO)) { /* not clear this is useful, but it can't hurt */ CoerceViaIO *expr = (CoerceViaIO *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } else if (IsA(node, ArrayCoerceExpr)) { /* ArrayCoerceExpr is strict at the array level */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } else if (IsA(node, ConvertRowtypeExpr)) { /* not clear this is useful, but it can't hurt */ ConvertRowtypeExpr *expr = (ConvertRowtypeExpr *) node; result = find_nonnullable_rels_walker((Node *) expr->arg, top_level); } else if (IsA(node, NullTest)) { /* IS NOT NULL can be considered strict, but only at top level */ NullTest *expr = (NullTest *) node; if (top_level && expr->nulltesttype == IS_NOT_NULL) result = find_nonnullable_rels_walker((Node *) expr->arg, false); } else if (IsA(node, BooleanTest)) { /* Boolean tests that reject NULL are strict at top level */ BooleanTest *expr = (BooleanTest *) node; if (top_level && (expr->booltesttype == IS_TRUE || expr->booltesttype == IS_FALSE || expr->booltesttype == IS_NOT_UNKNOWN)) result = find_nonnullable_rels_walker((Node *) expr->arg, false); } return result;}/* * Can we treat a ScalarArrayOpExpr as strict? * * If "falseOK" is true, then a "false" result can be considered strict, * else we need to guarantee an actual NULL result for NULL input. * * "foo op ALL array" is strict if the op is strict *and* we can prove * that the array input isn't an empty array. We can check that * for the cases of an array constant and an ARRAY[] construct. * * "foo op ANY array" is strict in the falseOK sense if the op is strict. * If not falseOK, the test is the same as for "foo op ALL array". */static boolis_strict_saop(ScalarArrayOpExpr *expr, bool falseOK){ Node *rightop; /* The contained operator must be strict. */ set_sa_opfuncid(expr); if (!func_strict(expr->opfuncid)) return false; /* If ANY and falseOK, that's all we need to check. */ if (expr->useOr && falseOK) return true; /* Else, we have to see if the array is provably non-empty. */ Assert(list_length(expr->args) == 2); rightop = (Node *) lsecond(expr->args); if (rightop && IsA(rightop, Const)) { Datum arraydatum = ((Const *) rightop)->constvalue; bool arrayisnull = ((Const *) rightop)->constisnull; ArrayType *arrayval; int nitems; if (arrayisnull) return false; arrayval = DatumGetArrayTypeP(arraydatum); nitems = ArrayGetNItems(ARR_NDIM(arrayval), ARR_DIMS(arrayval)); if (nitems > 0) return true; } else if (rightop && IsA(rightop, ArrayExpr)) { ArrayExpr *arrayexpr = (ArrayExpr *) rightop; if (arrayexpr->elements != NIL && !arrayexpr->multidims) return true; } return false;}/***************************************************************************** * Check for "pseudo-constant" clauses *****************************************************************************//* * is_pseudo_constant_clause * Detect whether an expression is "pseudo constant", ie, it contains no * variables of the current query level and no uses of volatile functions. * Such an expr is not necessarily a true constant: it can still contain * Params and outer-level Vars, not to mention functions whose results * may vary from one statement to the next. However, the expr's value * will be constant over any one scan of the current query, so it can be * used as, eg, an indexscan key. * * CAUTION: this function omits to test for one very important class of * not-constant expressions, namely aggregates (Aggrefs). In current usage * this is only applied to WHERE clauses and so a check for Aggrefs would be * a waste of cycles; but be sure to also check contain_agg_clause() if you * want to know about pseudo-constness in other contexts. */boolis_pseudo_constant_clause(Node *clause){ /* * We could implement this check in one recursive scan. But since the * check for volatile functions is both moderately expensive and unlikely * to fail, it seems better to look for Vars first and only check for * volatile functions if we find no Vars. */ if (!contain_var_clause(clause) && !contain_volatile_functions(clause)) return true; return false;}/* * is_pseudo_constant_clause_relids * Same as above, except caller already has available the var membership * of the expression; this lets us avoid the contain_var_clause() scan. */boolis_pseudo_constant_clause_relids(Node *clause, Relids relids){ if (bms_is_empty(relids) && !contain_volatile_functions(clause)) return true; return false;}/***************************************************************************** * Tests on clauses of queries * * Possibly this code should go someplace else, since this isn't quite the * same meaning of "clause" as is used elsewhere in this module. But I can't * think of a better place for it... *****************************************************************************//* * Test whether a query uses DISTINCT ON, ie, has a distinct-list that is * not the same as the set of output columns. */boolhas_distinct_on_clause(Query *query){ ListCell *l; /* Is there a DISTINCT clause at all? */ if (query->distinctClause == NIL) return false; /* * If the DISTINCT list contains all the nonjunk targetlist items, and * nothing else (ie, no junk tlist items), then it's a simple DISTINCT, * else it's DISTINCT ON. We do not require the lists to be in the same * order (since the parser may have adjusted the DISTINCT clause ordering * to agree with ORDER BY). Furthermore, a non-DISTINCT junk tlist item * that is in the sortClause is also evidence of DISTINCT ON, since we * don't allow ORDER BY on junk tlist items when plain DISTINCT is used. * * This code assumes that the DISTINCT list is valid, ie, all its entries * match some entry of the tlist. */ foreach(l, query->targetList) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (tle->ressortgroupref == 0) { if (tle->resjunk) continue; /* we can ignore unsorted junk cols */ return true; /* definitely not in DISTINCT list */ } if (targetIsInSortList(tle, InvalidOid, query->distinctClause)) { if (tle->resjunk) return true; /* junk TLE in DISTINCT means DISTINCT ON */ /* else this TLE is okay, keep looking */ } else { /* This TLE is not in DISTINCT list */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?