📄 clauses.c
字号:
fselect->resulttype, fselect->resulttypmod)) return (Node *) makeVar(((Var *) arg)->varno, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod, ((Var *) arg)->varlevelsup); } if (arg && IsA(arg, RowExpr)) { RowExpr *rowexpr = (RowExpr *) arg; if (fselect->fieldnum > 0 && fselect->fieldnum <= list_length(rowexpr->args)) { Node *fld = (Node *) list_nth(rowexpr->args, fselect->fieldnum - 1); if (rowtype_field_matches(rowexpr->row_typeid, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod) && fselect->resulttype == exprType(fld) && fselect->resulttypmod == exprTypmod(fld)) return fld; } } newfselect = makeNode(FieldSelect); newfselect->arg = (Expr *) arg; newfselect->fieldnum = fselect->fieldnum; newfselect->resulttype = fselect->resulttype; newfselect->resulttypmod = fselect->resulttypmod; return (Node *) newfselect; } /* * For any node type not handled above, we recurse using * expression_tree_mutator, which will copy the node unchanged but try to * simplify its arguments (if any) using this routine. For example: we * cannot eliminate an ArrayRef node, but we might be able to simplify * constant expressions in its subscripts. */ return expression_tree_mutator(node, eval_const_expressions_mutator, (void *) context);}/* * Subroutine for eval_const_expressions: process arguments of an OR clause * * This includes flattening of nested ORs as well as recursion to * eval_const_expressions to simplify the OR arguments. * * After simplification, OR arguments are handled as follows: * non constant: keep * FALSE: drop (does not affect result) * TRUE: force result to TRUE * NULL: keep only one * We must keep one NULL input because ExecEvalOr returns NULL when no input * is TRUE and at least one is NULL. We don't actually include the NULL * here, that's supposed to be done by the caller. * * The output arguments *haveNull and *forceTrue must be initialized FALSE * by the caller. They will be set TRUE if a null constant or true constant, * respectively, is detected anywhere in the argument list. */static List *simplify_or_arguments(List *args, eval_const_expressions_context *context, bool *haveNull, bool *forceTrue){ List *newargs = NIL; List *unprocessed_args; /* * Since the parser considers OR to be a binary operator, long OR lists * become deeply nested expressions. We must flatten these into long * argument lists of a single OR operator. To avoid blowing out the stack * with recursion of eval_const_expressions, we resort to some tenseness * here: we keep a list of not-yet-processed inputs, and handle flattening * of nested ORs by prepending to the to-do list instead of recursing. */ unprocessed_args = list_copy(args); while (unprocessed_args) { Node *arg = (Node *) linitial(unprocessed_args); unprocessed_args = list_delete_first(unprocessed_args); /* flatten nested ORs as per above comment */ if (or_clause(arg)) { List *subargs = list_copy(((BoolExpr *) arg)->args); /* overly tense code to avoid leaking unused list header */ if (!unprocessed_args) unprocessed_args = subargs; else { List *oldhdr = unprocessed_args; unprocessed_args = list_concat(subargs, unprocessed_args); pfree(oldhdr); } continue; } /* If it's not an OR, simplify it */ arg = eval_const_expressions_mutator(arg, context); /* * It is unlikely but not impossible for simplification of a non-OR * clause to produce an OR. Recheck, but don't be too tense about it * since it's not a mainstream case. In particular we don't worry * about const-simplifying the input twice. */ if (or_clause(arg)) { List *subargs = list_copy(((BoolExpr *) arg)->args); unprocessed_args = list_concat(subargs, unprocessed_args); continue; } /* * OK, we have a const-simplified non-OR argument. Process it per * comments above. */ if (IsA(arg, Const)) { Const *const_input = (Const *) arg; if (const_input->constisnull) *haveNull = true; else if (DatumGetBool(const_input->constvalue)) { *forceTrue = true; /* * Once we detect a TRUE result we can just exit the loop * immediately. However, if we ever add a notion of * non-removable functions, we'd need to keep scanning. */ return NIL; } /* otherwise, we can drop the constant-false input */ continue; } /* else emit the simplified arg into the result list */ newargs = lappend(newargs, arg); } return newargs;}/* * Subroutine for eval_const_expressions: process arguments of an AND clause * * This includes flattening of nested ANDs as well as recursion to * eval_const_expressions to simplify the AND arguments. * * After simplification, AND arguments are handled as follows: * non constant: keep * TRUE: drop (does not affect result) * FALSE: force result to FALSE * NULL: keep only one * We must keep one NULL input because ExecEvalAnd returns NULL when no input * is FALSE and at least one is NULL. We don't actually include the NULL * here, that's supposed to be done by the caller. * * The output arguments *haveNull and *forceFalse must be initialized FALSE * by the caller. They will be set TRUE if a null constant or false constant, * respectively, is detected anywhere in the argument list. */static List *simplify_and_arguments(List *args, eval_const_expressions_context *context, bool *haveNull, bool *forceFalse){ List *newargs = NIL; List *unprocessed_args; /* See comments in simplify_or_arguments */ unprocessed_args = list_copy(args); while (unprocessed_args) { Node *arg = (Node *) linitial(unprocessed_args); unprocessed_args = list_delete_first(unprocessed_args); /* flatten nested ANDs as per above comment */ if (and_clause(arg)) { List *subargs = list_copy(((BoolExpr *) arg)->args); /* overly tense code to avoid leaking unused list header */ if (!unprocessed_args) unprocessed_args = subargs; else { List *oldhdr = unprocessed_args; unprocessed_args = list_concat(subargs, unprocessed_args); pfree(oldhdr); } continue; } /* If it's not an AND, simplify it */ arg = eval_const_expressions_mutator(arg, context); /* * It is unlikely but not impossible for simplification of a non-AND * clause to produce an AND. Recheck, but don't be too tense about it * since it's not a mainstream case. In particular we don't worry * about const-simplifying the input twice. */ if (and_clause(arg)) { List *subargs = list_copy(((BoolExpr *) arg)->args); unprocessed_args = list_concat(subargs, unprocessed_args); continue; } /* * OK, we have a const-simplified non-AND argument. Process it per * comments above. */ if (IsA(arg, Const)) { Const *const_input = (Const *) arg; if (const_input->constisnull) *haveNull = true; else if (!DatumGetBool(const_input->constvalue)) { *forceFalse = true; /* * Once we detect a FALSE result we can just exit the loop * immediately. However, if we ever add a notion of * non-removable functions, we'd need to keep scanning. */ return NIL; } /* otherwise, we can drop the constant-true input */ continue; } /* else emit the simplified arg into the result list */ newargs = lappend(newargs, arg); } return newargs;}/* * Subroutine for eval_const_expressions: try to simplify boolean equality * * Input is the list of simplified arguments to the operator. * Returns a simplified expression if successful, or NULL if cannot * simplify the expression. * * The idea here is to reduce "x = true" to "x" and "x = false" to "NOT x". * This is only marginally useful in itself, but doing it in constant folding * ensures that we will recognize the two forms as being equivalent in, for * example, partial index matching. * * We come here only if simplify_function has failed; therefore we cannot * see two constant inputs, nor a constant-NULL input. */static Expr *simplify_boolean_equality(List *args){ Expr *leftop; Expr *rightop; Assert(list_length(args) == 2); leftop = linitial(args); rightop = lsecond(args); if (leftop && IsA(leftop, Const)) { Assert(!((Const *) leftop)->constisnull); if (DatumGetBool(((Const *) leftop)->constvalue)) return rightop; /* true = foo */ else return make_notclause(rightop); /* false = foo */ } if (rightop && IsA(rightop, Const)) { Assert(!((Const *) rightop)->constisnull); if (DatumGetBool(((Const *) rightop)->constvalue)) return leftop; /* foo = true */ else return make_notclause(leftop); /* foo = false */ } return NULL;}/* * Subroutine for eval_const_expressions: try to simplify a function call * (which might originally have been an operator; we don't care) * * Inputs are the function OID, actual result type OID (which is needed for * polymorphic functions), and the pre-simplified argument list; * also the context data for eval_const_expressions. * * Returns a simplified expression if successful, or NULL if cannot * simplify the function call. */static Expr *simplify_function(Oid funcid, Oid result_type, List *args, bool allow_inline, eval_const_expressions_context *context){ HeapTuple func_tuple; Expr *newexpr; /* * We have two strategies for simplification: either execute the function * to deliver a constant result, or expand in-line the body of the * function definition (which only works for simple SQL-language * functions, but that is a common case). In either case we need access * to the function's pg_proc tuple, so fetch it just once to use in both * attempts. */ func_tuple = SearchSysCache(PROCOID, ObjectIdGetDatum(funcid), 0, 0, 0); if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", funcid); newexpr = evaluate_function(funcid, result_type, args, func_tuple, context); if (!newexpr && allow_inline) newexpr = inline_function(funcid, result_type, args, func_tuple, context); ReleaseSysCache(func_tuple); return newexpr;}/* * evaluate_function: try to pre-evaluate a function call * * We can do this if the function is strict and has any constant-null inputs * (just return a null constant), or if the function is immutable and has all * constant inputs (call it and return the result as a Const node). In * estimation mode we are willing to pre-evaluate stable functions too. * * Returns a simplified expression if successful, or NULL if cannot * simplify the function. */static Expr *evaluate_function(Oid funcid, Oid result_type, List *args, HeapTuple func_tuple, eval_const_expressions_context *context){ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); bool has_nonconst_input = false; bool has_null_input = false; ListCell *arg; FuncExpr *newexpr; /* * Can't simplify if it returns a set. */ if (funcform->proretset) return NULL; /* * Can't simplify if it returns RECORD. The immediate problem is that it * will be needing an expected tupdesc which we can't supply here. * * In the case where it has OUT parameters, it could get by without an * expected tupdesc, but we still have issues: get_expr_result_type() * doesn't know how to extract type info from a RECORD constant, and in * the case of a NULL function result there doesn't seem to be any clean * way to fix that. In view of the likelihood of there being still other * gotchas, seems best to leave the function call unreduced. */ if (funcform->prorettype == RECORDOID) return NULL; /* * Check for constant inputs and especially constant-NULL inputs. */ foreach(arg, args) { if (IsA(lfirst(arg), Const)) has_null_input |= ((Const *) lfirst(arg))->constisnull; else has_nonconst_input = true; } /* * If the function is strict and has a constant-NULL input, it will never * be called at all, so we can replace the call by a NULL constant, even * if there are other inputs that aren't constant, and even if the * function is not otherwise immutable. */ if (funcform->proisstrict && has_null_input) return (Expr *) makeNullConst(result_type); /* * Otherwise, can simplify only if all inputs are constants. (For a * non-strict function, constant NULL inputs are treated the same as * constant non-NULL inputs.) */ if (has_nonconst_input) return NULL; /* * Ordinarily we are only allowed to simplify immutable functions. But for * purposes of estimation, we consider it okay to simplify functions that * are merely stable; the risk that the result might change from planning * time to execution time is worth taking in preference to not being able * to estimate the value at all. */ if (funcform->provolatile == PROVOLATILE_IMMUTABLE) /* okay */ ; else if (context->estimate && funcform->provolatile == PROVOLATILE_STABLE) /* okay */ ; else return NULL; /* * OK, looks like we can simplify this operator/function. * * Build a new FuncExpr node containing the already-simplified arguments. */ newexpr = makeNode(FuncExpr); newexpr->funcid = funcid; newexpr->funcresulttype = result_type; newexpr->funcretset = false; newexpr->funcformat = COERCE_DONTCARE; /* doesn't matter */ newexpr->args = args; return evaluate_expr((Expr *) newexpr, result_type);}/* * inline_function: try to expand a function call inline * * If the function is a sufficiently simple SQL-language function * (just "SELECT expression"), then we can inline it and avoid the rather * high per-call overhead of SQL functions. Furthermore, this can expose * opportunities for constant-folding within the function expression. * * We have to beware of some speci
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -