📄 clauses.c
字号:
/*------------------------------------------------------------------------- * * clauses.c * routines to manipulate qualification clauses * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.201.2.1 2005/11/22 18:23:12 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT * Andrew Yu Nov 3, 1994 clause.c and clauses.c combined * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "catalog/pg_aggregate.h"#include "catalog/pg_language.h"#include "catalog/pg_operator.h"#include "catalog/pg_proc.h"#include "catalog/pg_type.h"#include "executor/executor.h"#include "executor/functions.h"#include "miscadmin.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/cost.h"#include "optimizer/planmain.h"#include "optimizer/planner.h"#include "optimizer/var.h"#include "parser/analyze.h"#include "parser/parse_clause.h"#include "parser/parse_coerce.h"#include "parser/parse_expr.h"#include "tcop/tcopprot.h"#include "utils/acl.h"#include "utils/builtins.h"#include "utils/datum.h"#include "utils/lsyscache.h"#include "utils/memutils.h"#include "utils/syscache.h"#include "utils/typcache.h"typedef struct{ List *active_fns; Node *case_val; bool estimate;} eval_const_expressions_context;typedef struct{ int nargs; List *args; int *usecounts;} substitute_actual_parameters_context;static bool contain_agg_clause_walker(Node *node, void *context);static bool count_agg_clauses_walker(Node *node, AggClauseCounts *counts);static bool expression_returns_set_walker(Node *node, void *context);static bool contain_subplans_walker(Node *node, void *context);static bool contain_mutable_functions_walker(Node *node, void *context);static bool contain_volatile_functions_walker(Node *node, void *context);static bool contain_nonstrict_functions_walker(Node *node, void *context);static bool set_coercionform_dontcare_walker(Node *node, void *context);static Node *eval_const_expressions_mutator(Node *node, eval_const_expressions_context *context);static List *simplify_or_arguments(List *args, eval_const_expressions_context *context, bool *haveNull, bool *forceTrue);static List *simplify_and_arguments(List *args, eval_const_expressions_context *context, bool *haveNull, bool *forceFalse);static Expr *simplify_boolean_equality(List *args);static Expr *simplify_function(Oid funcid, Oid result_type, List *args, bool allow_inline, eval_const_expressions_context *context);static Expr *evaluate_function(Oid funcid, Oid result_type, List *args, HeapTuple func_tuple, eval_const_expressions_context *context);static Expr *inline_function(Oid funcid, Oid result_type, List *args, HeapTuple func_tuple, eval_const_expressions_context *context);static Node *substitute_actual_parameters(Node *expr, int nargs, List *args, int *usecounts);static Node *substitute_actual_parameters_mutator(Node *node, substitute_actual_parameters_context *context);static void sql_inline_error_callback(void *arg);static Expr *evaluate_expr(Expr *expr, Oid result_type);/***************************************************************************** * OPERATOR clause functions *****************************************************************************//* * make_opclause * Creates an operator clause given its operator info, left operand, * and right operand (pass NULL to create single-operand clause). */Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset, Expr *leftop, Expr *rightop){ OpExpr *expr = makeNode(OpExpr); expr->opno = opno; expr->opfuncid = InvalidOid; expr->opresulttype = opresulttype; expr->opretset = opretset; if (rightop) expr->args = list_make2(leftop, rightop); else expr->args = list_make1(leftop); return (Expr *) expr;}/* * get_leftop * * Returns the left operand of a clause of the form (op expr expr) * or (op expr) */Node *get_leftop(Expr *clause){ OpExpr *expr = (OpExpr *) clause; if (expr->args != NIL) return linitial(expr->args); else return NULL;}/* * get_rightop * * Returns the right operand in a clause of the form (op expr expr). * NB: result will be NULL if applied to a unary op clause. */Node *get_rightop(Expr *clause){ OpExpr *expr = (OpExpr *) clause; if (list_length(expr->args) >= 2) return lsecond(expr->args); else return NULL;}/***************************************************************************** * NOT clause functions *****************************************************************************//* * not_clause * * Returns t iff this is a 'not' clause: (NOT expr). */boolnot_clause(Node *clause){ return (clause != NULL && IsA(clause, BoolExpr) && ((BoolExpr *) clause)->boolop == NOT_EXPR);}/* * make_notclause * * Create a 'not' clause given the expression to be negated. */Expr *make_notclause(Expr *notclause){ BoolExpr *expr = makeNode(BoolExpr); expr->boolop = NOT_EXPR; expr->args = list_make1(notclause); return (Expr *) expr;}/* * get_notclausearg * * Retrieve the clause within a 'not' clause */Expr *get_notclausearg(Expr *notclause){ return linitial(((BoolExpr *) notclause)->args);}/***************************************************************************** * OR clause functions *****************************************************************************//* * or_clause * * Returns t iff the clause is an 'or' clause: (OR { expr }). */boolor_clause(Node *clause){ return (clause != NULL && IsA(clause, BoolExpr) && ((BoolExpr *) clause)->boolop == OR_EXPR);}/* * make_orclause * * Creates an 'or' clause given a list of its subclauses. */Expr *make_orclause(List *orclauses){ BoolExpr *expr = makeNode(BoolExpr); expr->boolop = OR_EXPR; expr->args = orclauses; return (Expr *) expr;}/***************************************************************************** * AND clause functions *****************************************************************************//* * and_clause * * Returns t iff its argument is an 'and' clause: (AND { expr }). */booland_clause(Node *clause){ return (clause != NULL && IsA(clause, BoolExpr) && ((BoolExpr *) clause)->boolop == AND_EXPR);}/* * make_andclause * * Creates an 'and' clause given a list of its subclauses. */Expr *make_andclause(List *andclauses){ BoolExpr *expr = makeNode(BoolExpr); expr->boolop = AND_EXPR; expr->args = andclauses; return (Expr *) expr;}/* * make_and_qual * * Variant of make_andclause for ANDing two qual conditions together. * Qual conditions have the property that a NULL nodetree is interpreted * as 'true'. * * NB: this makes no attempt to preserve AND/OR flatness; so it should not * be used on a qual that has already been run through prepqual.c. */Node *make_and_qual(Node *qual1, Node *qual2){ if (qual1 == NULL) return qual2; if (qual2 == NULL) return qual1; return (Node *) make_andclause(list_make2(qual1, qual2));}/* * Sometimes (such as in the input of ExecQual), we use lists of expression * nodes with implicit AND semantics. * * These functions convert between an AND-semantics expression list and the * ordinary representation of a boolean expression. * * Note that an empty list is considered equivalent to TRUE. */Expr *make_ands_explicit(List *andclauses){ if (andclauses == NIL) return (Expr *) makeBoolConst(true, false); else if (list_length(andclauses) == 1) return (Expr *) linitial(andclauses); else return make_andclause(andclauses);}List *make_ands_implicit(Expr *clause){ /* * NB: because the parser sets the qual field to NULL in a query that has * no WHERE clause, we must consider a NULL input clause as TRUE, even * though one might more reasonably think it FALSE. Grumble. If this * causes trouble, consider changing the parser's behavior. */ if (clause == NULL) return NIL; /* NULL -> NIL list == TRUE */ else if (and_clause((Node *) clause)) return ((BoolExpr *) clause)->args; else if (IsA(clause, Const) && !((Const *) clause)->constisnull && DatumGetBool(((Const *) clause)->constvalue)) return NIL; /* constant TRUE input -> NIL list */ else return list_make1(clause);}/***************************************************************************** * Aggregate-function clause manipulation *****************************************************************************//* * contain_agg_clause * Recursively search for Aggref nodes within a clause. * * Returns true if any aggregate found. * * This does not descend into subqueries, and so should be used only after * reduction of sublinks to subplans, or in contexts where it's known there * are no subqueries. There mustn't be outer-aggregate references either. * * (If you want something like this but able to deal with subqueries, * see rewriteManip.c's checkExprHasAggs().) */boolcontain_agg_clause(Node *clause){ return contain_agg_clause_walker(clause, NULL);}static boolcontain_agg_clause_walker(Node *node, void *context){ if (node == NULL) return false; if (IsA(node, Aggref)) { Assert(((Aggref *) node)->agglevelsup == 0); return true; /* abort the tree traversal and return true */ } Assert(!IsA(node, SubLink)); return expression_tree_walker(node, contain_agg_clause_walker, context);}/* * count_agg_clauses * Recursively count the Aggref nodes in an expression tree. * * Note: this also checks for nested aggregates, which are an error. * * We not only count the nodes, but attempt to estimate the total space * needed for their transition state values if all are evaluated in parallel * (as would be done in a HashAgg plan). See AggClauseCounts for the exact * set of statistics returned. * * NOTE that the counts are ADDED to those already in *counts ... so the * caller is responsible for zeroing the struct initially. * * This does not descend into subqueries, and so should be used only after * reduction of sublinks to subplans, or in contexts where it's known there * are no subqueries. There mustn't be outer-aggregate references either. */voidcount_agg_clauses(Node *clause, AggClauseCounts *counts){ /* no setup needed */ count_agg_clauses_walker(clause, counts);}static boolcount_agg_clauses_walker(Node *node, AggClauseCounts *counts){ if (node == NULL) return false; if (IsA(node, Aggref)) { Aggref *aggref = (Aggref *) node; Oid inputType; HeapTuple aggTuple; Form_pg_aggregate aggform; Oid aggtranstype; Assert(aggref->agglevelsup == 0); counts->numAggs++; if (aggref->aggdistinct) counts->numDistinctAggs++; inputType = exprType((Node *) aggref->target); /* fetch aggregate transition datatype from pg_aggregate */ aggTuple = SearchSysCache(AGGFNOID, ObjectIdGetDatum(aggref->aggfnoid), 0, 0, 0); if (!HeapTupleIsValid(aggTuple)) elog(ERROR, "cache lookup failed for aggregate %u", aggref->aggfnoid); aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); aggtranstype = aggform->aggtranstype; ReleaseSysCache(aggTuple); /* resolve actual type of transition state, if polymorphic */ if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID) { /* have to fetch the agg's declared input type... */ Oid *agg_arg_types; int agg_nargs; (void) get_func_signature(aggref->aggfnoid, &agg_arg_types, &agg_nargs); Assert(agg_nargs == 1); aggtranstype = resolve_generic_type(aggtranstype, inputType, agg_arg_types[0]); pfree(agg_arg_types); } /* * If the transition type is pass-by-value then it doesn't add * anything to the required size of the hashtable. If it is * pass-by-reference then we have to add the estimated size of the * value itself, plus palloc overhead. */ if (!get_typbyval(aggtranstype)) { int32 aggtranstypmod; int32 avgwidth; /* * If transition state is of same type as input, assume it's the * same typmod (same width) as well. This works for cases like * MAX/MIN and is probably somewhat reasonable otherwise. */ if (aggtranstype == inputType) aggtranstypmod = exprTypmod((Node *) aggref->target);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -