#include "postgres.h"
#include "access/htup_details.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 "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/planmain.h"
#include "optimizer/prep.h"
#include "optimizer/var.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parse_func.h"
#include "rewrite/rewriteManip.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"
Go to the source code of this file.
Definition at line 3784 of file clauses.c.
References elog, ERROR, fetch_function_defaults(), GETSTRUCT, list_concat(), list_copy(), list_delete_first(), and list_length().
Referenced by expand_function_arguments().
{ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int nargsprovided = list_length(args); List *defaults; int ndelete; /* Get all the default expressions from the pg_proc tuple */ defaults = fetch_function_defaults(func_tuple); /* Delete any unused defaults from the list */ ndelete = nargsprovided + list_length(defaults) - funcform->pronargs; if (ndelete < 0) elog(ERROR, "not enough default arguments"); while (ndelete-- > 0) defaults = list_delete_first(defaults); /* And form the combined argument list, not modifying the input list */ return list_concat(list_copy(args), defaults); }
Definition at line 299 of file clauses.c.
References AND_EXPR, boolop(), IsA, and NULL.
Referenced by clause_selectivity(), ExecInitSubPlan(), find_duplicate_ors(), generate_bitmap_or_paths(), make_ands_implicit(), make_restrictinfo(), make_sub_restrictinfos(), predicate_classify(), process_duplicate_ors(), process_sublinks_mutator(), pull_ands(), pull_up_sublinks_qual_recurse(), simplify_and_arguments(), testexpr_is_hashable(), and TidQualFromExpr().
void CommuteOpExpr | ( | OpExpr * | clause | ) |
Definition at line 1969 of file clauses.c.
References OpExpr::args, elog, ERROR, get_commutator(), is_opclause, linitial, list_length(), lsecond, OidIsValid, OpExpr::opfuncid, and OpExpr::opno.
Referenced by fix_indexqual_references(), and get_switched_clauses().
{ Oid opoid; Node *temp; /* Sanity checks: caller is at fault if these fail */ if (!is_opclause(clause) || list_length(clause->args) != 2) elog(ERROR, "cannot commute non-binary-operator clause"); opoid = get_commutator(clause->opno); if (!OidIsValid(opoid)) elog(ERROR, "could not find commutator for operator %u", clause->opno); /* * modify the clause in-place! */ clause->opno = opoid; clause->opfuncid = InvalidOid; /* opresulttype, opretset, opcollid, inputcollid need not change */ temp = linitial(clause->args); linitial(clause->args) = lsecond(clause->args); lsecond(clause->args) = temp; }
void CommuteRowCompareExpr | ( | RowCompareExpr * | clause | ) |
Definition at line 2003 of file clauses.c.
References elog, ERROR, get_commutator(), IsA, lappend_oid(), RowCompareExpr::largs, lfirst_oid, OidIsValid, RowCompareExpr::opnos, RowCompareExpr::rargs, RowCompareExpr::rctype, ROWCOMPARE_GE, ROWCOMPARE_GT, ROWCOMPARE_LE, and ROWCOMPARE_LT.
Referenced by fix_indexqual_references().
{ List *newops; List *temp; ListCell *l; /* Sanity checks: caller is at fault if these fail */ if (!IsA(clause, RowCompareExpr)) elog(ERROR, "expected a RowCompareExpr"); /* Build list of commuted operators */ newops = NIL; foreach(l, clause->opnos) { Oid opoid = lfirst_oid(l); opoid = get_commutator(opoid); if (!OidIsValid(opoid)) elog(ERROR, "could not find commutator for operator %u", lfirst_oid(l)); newops = lappend_oid(newops, opoid); } /* * modify the clause in-place! */ switch (clause->rctype) { case ROWCOMPARE_LT: clause->rctype = ROWCOMPARE_GT; break; case ROWCOMPARE_LE: clause->rctype = ROWCOMPARE_GE; break; case ROWCOMPARE_GE: clause->rctype = ROWCOMPARE_LE; break; case ROWCOMPARE_GT: clause->rctype = ROWCOMPARE_LT; break; default: elog(ERROR, "unexpected RowCompare type: %d", (int) clause->rctype); break; } clause->opnos = newops; /* * Note: we need not change the opfamilies list; we assume any btree * opfamily containing an operator will also contain its commutator. * Collations don't change either. */ temp = clause->largs; clause->largs = clause->rargs; clause->rargs = temp; }
Definition at line 402 of file clauses.c.
References contain_agg_clause_walker(), and NULL.
Referenced by count_agg_clauses_walker(), get_eclass_for_sort_expr(), and subquery_planner().
{ return contain_agg_clause_walker(clause, NULL); }
Definition at line 408 of file clauses.c.
References Assert, expression_tree_walker(), IsA, and NULL.
Referenced by contain_agg_clause().
{ 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); }
Definition at line 1182 of file clauses.c.
References contain_leaky_functions_walker(), and NULL.
Referenced by set_subquery_pathlist().
{ return contain_leaky_functions_walker(clause, NULL); }
Definition at line 1188 of file clauses.c.
References ArrayCoerceExpr::arg, CoerceViaIO::arg, expression_tree_walker(), exprType(), FuncExpr::funcid, get_func_leakproof(), get_opcode(), getTypeInputInfo(), getTypeOutputInfo(), lfirst_oid, nodeTag, NULL, ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, RowCompareExpr::opnos, ArrayCoerceExpr::resulttype, CoerceViaIO::resulttype, set_opfuncid(), set_sa_opfuncid(), T_ArrayCoerceExpr, T_ArrayExpr, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CaseTestExpr, T_CoerceViaIO, T_Const, T_DistinctExpr, T_FuncExpr, T_List, T_MinMaxExpr, T_NamedArgExpr, T_NullIfExpr, T_NullTest, T_OpExpr, T_Param, T_RelabelType, T_RowCompareExpr, T_RowExpr, T_ScalarArrayOpExpr, and T_Var.
Referenced by contain_leaky_functions().
{ if (node == NULL) return false; switch (nodeTag(node)) { case T_Var: case T_Const: case T_Param: case T_ArrayExpr: case T_NamedArgExpr: case T_BoolExpr: case T_RelabelType: case T_CaseExpr: case T_CaseTestExpr: case T_RowExpr: case T_MinMaxExpr: case T_NullTest: case T_BooleanTest: case T_List: /* * We know these node types don't contain function calls; but * something further down in the node tree might. */ break; case T_FuncExpr: { FuncExpr *expr = (FuncExpr *) node; if (!get_func_leakproof(expr->funcid)) return true; } break; case T_OpExpr: case T_DistinctExpr: /* struct-equivalent to OpExpr */ case T_NullIfExpr: /* struct-equivalent to OpExpr */ { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); if (!get_func_leakproof(expr->opfuncid)) return true; } break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; set_sa_opfuncid(expr); if (!get_func_leakproof(expr->opfuncid)) return true; } break; case T_CoerceViaIO: { CoerceViaIO *expr = (CoerceViaIO *) node; Oid funcid; Oid ioparam; bool varlena; getTypeInputInfo(exprType((Node *) expr->arg), &funcid, &ioparam); if (!get_func_leakproof(funcid)) return true; getTypeOutputInfo(expr->resulttype, &funcid, &varlena); if (!get_func_leakproof(funcid)) return true; } break; case T_ArrayCoerceExpr: { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; Oid funcid; Oid ioparam; bool varlena; getTypeInputInfo(exprType((Node *) expr->arg), &funcid, &ioparam); if (!get_func_leakproof(funcid)) return true; getTypeOutputInfo(expr->resulttype, &funcid, &varlena); if (!get_func_leakproof(funcid)) return true; } break; case T_RowCompareExpr: { RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *opid; foreach(opid, rcexpr->opnos) { Oid funcid = get_opcode(lfirst_oid(opid)); if (!get_func_leakproof(funcid)) return true; } } break; default: /* * If we don't recognize the node tag, assume it might be leaky. * This prevents an unexpected security hole if someone adds a new * node type that can call a function. */ return true; } return expression_tree_walker(node, contain_leaky_functions_walker, context); }
Definition at line 829 of file clauses.c.
References contain_mutable_functions_walker(), and NULL.
Referenced by CheckMutability(), create_bitmap_scan_plan(), create_indexscan_plan(), find_minmax_aggs_walker(), inline_function(), is_foreign_expr(), and relation_excluded_by_constraints().
{ return contain_mutable_functions_walker(clause, NULL); }
Definition at line 835 of file clauses.c.
References CoerceViaIO::arg, ArrayCoerceExpr::elemfuncid, expression_tree_walker(), exprType(), func_volatile(), FuncExpr::funcid, getTypeInputInfo(), getTypeOutputInfo(), IsA, lfirst_oid, NULL, OidIsValid, op_volatile(), ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, RowCompareExpr::opnos, PROVOLATILE_IMMUTABLE, CoerceViaIO::resulttype, set_opfuncid(), and set_sa_opfuncid().
Referenced by contain_mutable_functions().
{ if (node == NULL) return false; if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (func_volatile(expr->funcid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, DistinctExpr)) { DistinctExpr *expr = (DistinctExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, NullIfExpr)) { NullIfExpr *expr = (NullIfExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; set_sa_opfuncid(expr); if (func_volatile(expr->opfuncid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *expr = (CoerceViaIO *) node; Oid iofunc; Oid typioparam; bool typisvarlena; /* check the result type's input function */ getTypeInputInfo(expr->resulttype, &iofunc, &typioparam); if (func_volatile(iofunc) != PROVOLATILE_IMMUTABLE) return true; /* check the input type's output function */ getTypeOutputInfo(exprType((Node *) expr->arg), &iofunc, &typisvarlena); if (func_volatile(iofunc) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; if (OidIsValid(expr->elemfuncid) && func_volatile(expr->elemfuncid) != PROVOLATILE_IMMUTABLE) return true; /* else fall through to check args */ } else if (IsA(node, RowCompareExpr)) { RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *opid; foreach(opid, rcexpr->opnos) { if (op_volatile(lfirst_oid(opid)) != PROVOLATILE_IMMUTABLE) return true; } /* else fall through to check args */ } return expression_tree_walker(node, contain_mutable_functions_walker, context); }
Definition at line 1061 of file clauses.c.
References contain_nonstrict_functions_walker(), and NULL.
Referenced by inline_function(), process_equivalence(), and pullup_replace_vars_callback().
{ return contain_nonstrict_functions_walker(clause, NULL); }
Definition at line 1067 of file clauses.c.
References AND_EXPR, BoolExpr::boolop, expression_tree_walker(), func_strict(), FuncExpr::funcid, is_strict_saop(), IsA, NULL, OpExpr::opfuncid, OR_EXPR, and set_opfuncid().
Referenced by contain_nonstrict_functions().
{ if (node == NULL) return false; if (IsA(node, Aggref)) { /* an aggregate could return non-null with null input */ return true; } if (IsA(node, WindowFunc)) { /* a window function 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, NullIfExpr)) 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; if (IsA(node, AlternativeSubPlan)) 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, NullTest)) return true; if (IsA(node, BooleanTest)) return true; return expression_tree_walker(node, contain_nonstrict_functions_walker, context); }
Definition at line 794 of file clauses.c.
References contain_subplans_walker(), and NULL.
Referenced by convert_EXISTS_to_ANY(), inline_function(), inline_set_returning_function(), qual_is_pushdown_safe(), and subquery_planner().
{ return contain_subplans_walker(clause, NULL); }
Definition at line 800 of file clauses.c.
References expression_tree_walker(), IsA, and NULL.
Referenced by contain_subplans().
{ if (node == NULL) return false; if (IsA(node, SubPlan) || IsA(node, AlternativeSubPlan) || IsA(node, SubLink)) return true; /* abort the tree traversal and return true */ return expression_tree_walker(node, contain_subplans_walker, context); }
Definition at line 944 of file clauses.c.
References contain_volatile_functions_walker(), and NULL.
Referenced by adjust_rowcompare_for_index(), BeginCopyFrom(), check_hashjoinable(), check_mergejoinable(), convert_ANY_sublink_to_join(), convert_EXISTS_sublink_to_join(), convert_EXISTS_to_ANY(), create_unique_path(), distribute_qual_to_rels(), estimate_num_groups(), ExecInitAgg(), ExecInitWindowAgg(), get_eclass_for_sort_expr(), inline_function(), inline_set_returning_function(), is_pseudo_constant_clause(), is_pseudo_constant_clause_relids(), is_simple_subquery(), make_restrictinfos_from_actual_clauses(), match_clause_to_indexcol(), match_clause_to_ordering_op(), match_rowcompare_to_indexcol(), qual_is_pushdown_safe(), and subquery_planner().
{ return contain_volatile_functions_walker(clause, NULL); }
Definition at line 950 of file clauses.c.
References CoerceViaIO::arg, ArrayCoerceExpr::elemfuncid, expression_tree_walker(), exprType(), func_volatile(), FuncExpr::funcid, getTypeInputInfo(), getTypeOutputInfo(), IsA, lfirst_oid, NULL, OidIsValid, op_volatile(), ScalarArrayOpExpr::opfuncid, OpExpr::opfuncid, RowCompareExpr::opnos, PROVOLATILE_VOLATILE, CoerceViaIO::resulttype, set_opfuncid(), and set_sa_opfuncid().
Referenced by contain_volatile_functions().
{ if (node == NULL) return false; if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (func_volatile(expr->funcid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; set_opfuncid(expr); if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, DistinctExpr)) { DistinctExpr *expr = (DistinctExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, NullIfExpr)) { NullIfExpr *expr = (NullIfExpr *) node; set_opfuncid((OpExpr *) expr); /* rely on struct equivalence */ if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; set_sa_opfuncid(expr); if (func_volatile(expr->opfuncid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *expr = (CoerceViaIO *) node; Oid iofunc; Oid typioparam; bool typisvarlena; /* check the result type's input function */ getTypeInputInfo(expr->resulttype, &iofunc, &typioparam); if (func_volatile(iofunc) == PROVOLATILE_VOLATILE) return true; /* check the input type's output function */ getTypeOutputInfo(exprType((Node *) expr->arg), &iofunc, &typisvarlena); if (func_volatile(iofunc) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; if (OidIsValid(expr->elemfuncid) && func_volatile(expr->elemfuncid) == PROVOLATILE_VOLATILE) return true; /* else fall through to check args */ } else if (IsA(node, RowCompareExpr)) { /* RowCompare probably can't have volatile ops, but check anyway */ RowCompareExpr *rcexpr = (RowCompareExpr *) node; ListCell *opid; foreach(opid, rcexpr->opnos) { if (op_volatile(lfirst_oid(opid)) == PROVOLATILE_VOLATILE) return true; } /* else fall through to check args */ } return expression_tree_walker(node, contain_volatile_functions_walker, context); }
Definition at line 599 of file clauses.c.
References contain_windowfuncs().
Referenced by find_window_functions_walker(), get_eclass_for_sort_expr(), and qual_is_pushdown_safe().
{ return contain_windowfuncs(clause); }
void count_agg_clauses | ( | PlannerInfo * | root, | |
Node * | clause, | |||
AggClauseCosts * | costs | |||
) |
Definition at line 441 of file clauses.c.
References count_agg_clauses_context::costs, count_agg_clauses_walker(), and count_agg_clauses_context::root.
Referenced by grouping_planner().
{ count_agg_clauses_context context; context.root = root; context.costs = costs; (void) count_agg_clauses_walker(clause, &context); }
static bool count_agg_clauses_walker | ( | Node * | node, | |
count_agg_clauses_context * | context | |||
) | [static] |
Definition at line 451 of file clauses.c.
References Aggref::aggdistinct, Aggref::aggfnoid, AGGFNOID, Aggref::agglevelsup, Aggref::aggorder, Aggref::args, Assert, contain_agg_clause(), cost_qual_eval_node(), count_agg_clauses_context::costs, cpu_operator_cost, elog, enforce_generic_type_consistency(), ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, expression_tree_walker(), exprType(), exprTypmod(), AggClauseCosts::finalCost, get_func_cost(), get_func_signature(), get_typavgwidth(), get_typbyval(), GETSTRUCT, HeapTupleIsValid, INTERNALOID, IsA, IsPolymorphicType, lfirst, linitial, list_length(), MAXALIGN, NIL, NULL, AggClauseCosts::numAggs, AggClauseCosts::numOrderedAggs, ObjectIdGetDatum, OidIsValid, palloc(), QualCost::per_tuple, pfree(), ReleaseSysCache(), TargetEntry::resjunk, count_agg_clauses_context::root, SearchSysCache1, QualCost::startup, AggClauseCosts::transCost, and AggClauseCosts::transitionSpace.
Referenced by count_agg_clauses().
{ if (node == NULL) return false; if (IsA(node, Aggref)) { Aggref *aggref = (Aggref *) node; AggClauseCosts *costs = context->costs; HeapTuple aggTuple; Form_pg_aggregate aggform; Oid aggtransfn; Oid aggfinalfn; Oid aggtranstype; QualCost argcosts; Oid *inputTypes; int numArguments; ListCell *l; Assert(aggref->agglevelsup == 0); /* fetch info about aggregate from pg_aggregate */ aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggref->aggfnoid)); if (!HeapTupleIsValid(aggTuple)) elog(ERROR, "cache lookup failed for aggregate %u", aggref->aggfnoid); aggform = (Form_pg_aggregate) GETSTRUCT(aggTuple); aggtransfn = aggform->aggtransfn; aggfinalfn = aggform->aggfinalfn; aggtranstype = aggform->aggtranstype; ReleaseSysCache(aggTuple); /* count it */ costs->numAggs++; if (aggref->aggorder != NIL || aggref->aggdistinct != NIL) costs->numOrderedAggs++; /* add component function execution costs to appropriate totals */ costs->transCost.per_tuple += get_func_cost(aggtransfn) * cpu_operator_cost; if (OidIsValid(aggfinalfn)) costs->finalCost += get_func_cost(aggfinalfn) * cpu_operator_cost; /* also add the input expressions' cost to per-input-row costs */ cost_qual_eval_node(&argcosts, (Node *) aggref->args, context->root); costs->transCost.startup += argcosts.startup; costs->transCost.per_tuple += argcosts.per_tuple; /* extract argument types (ignoring any ORDER BY expressions) */ inputTypes = (Oid *) palloc(sizeof(Oid) * list_length(aggref->args)); numArguments = 0; foreach(l, aggref->args) { TargetEntry *tle = (TargetEntry *) lfirst(l); if (!tle->resjunk) inputTypes[numArguments++] = exprType((Node *) tle->expr); } /* resolve actual type of transition state, if polymorphic */ if (IsPolymorphicType(aggtranstype)) { /* have to fetch the agg's declared input types... */ Oid *declaredArgTypes; int agg_nargs; (void) get_func_signature(aggref->aggfnoid, &declaredArgTypes, &agg_nargs); Assert(agg_nargs == numArguments); aggtranstype = enforce_generic_type_consistency(inputTypes, declaredArgTypes, agg_nargs, aggtranstype, false); pfree(declaredArgTypes); } /* * 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 first 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 (numArguments > 0 && aggtranstype == inputTypes[0]) aggtranstypmod = exprTypmod((Node *) linitial(aggref->args)); else aggtranstypmod = -1; avgwidth = get_typavgwidth(aggtranstype, aggtranstypmod); avgwidth = MAXALIGN(avgwidth); costs->transitionSpace += avgwidth + 2 * sizeof(void *); } else if (aggtranstype == INTERNALOID) { /* * INTERNAL transition type is a special case: although INTERNAL * is pass-by-value, it's almost certainly being used as a pointer * to some large data structure. We assume usage of * ALLOCSET_DEFAULT_INITSIZE, which is a good guess if the data is * being kept in a private memory context, as is done by * array_agg() for instance. */ costs->transitionSpace += ALLOCSET_DEFAULT_INITSIZE; } /* * Complain if the aggregate's arguments contain any aggregates; * nested agg functions are semantically nonsensical. */ if (contain_agg_clause((Node *) aggref->args)) ereport(ERROR, (errcode(ERRCODE_GROUPING_ERROR), errmsg("aggregate function calls cannot be nested"))); /* * Having checked that, we need not recurse into the argument. */ return false; } Assert(!IsA(node, SubLink)); return expression_tree_walker(node, count_agg_clauses_walker, (void *) context); }
Node* estimate_expression_value | ( | PlannerInfo * | root, | |
Node * | node | |||
) |
Definition at line 2226 of file clauses.c.
References eval_const_expressions_context::active_fns, PlannerGlobal::boundParams, eval_const_expressions_context::boundParams, eval_const_expressions_context::case_val, eval_const_expressions_context::estimate, eval_const_expressions_mutator(), PlannerInfo::glob, and eval_const_expressions_context::root.
Referenced by clause_selectivity(), get_restriction_variable(), preprocess_limit(), and scalararraysel_containment().
{ eval_const_expressions_context context; context.boundParams = root->glob->boundParams; /* bound Params */ /* we do not need to mark the plan as depending on inlined functions */ context.root = NULL; context.active_fns = NIL; /* nothing being recursively simplified */ context.case_val = NULL; /* no CASE being examined */ context.estimate = true; /* unsafe transformations OK */ return eval_const_expressions_mutator(node, &context); }
Node* eval_const_expressions | ( | PlannerInfo * | root, | |
Node * | node | |||
) |
Definition at line 2193 of file clauses.c.
References eval_const_expressions_context::active_fns, PlannerGlobal::boundParams, eval_const_expressions_context::boundParams, eval_const_expressions_context::case_val, eval_const_expressions_context::estimate, eval_const_expressions_mutator(), PlannerInfo::glob, and eval_const_expressions_context::root.
Referenced by convert_EXISTS_to_ANY(), expression_planner(), get_relation_constraints(), inline_set_returning_function(), preprocess_expression(), process_implied_equality(), RelationGetIndexExpressions(), RelationGetIndexPredicate(), and set_append_rel_size().
{ eval_const_expressions_context context; if (root) context.boundParams = root->glob->boundParams; /* bound Params */ else context.boundParams = NULL; context.root = root; /* for inlined-function dependencies */ context.active_fns = NIL; /* nothing being recursively simplified */ context.case_val = NULL; /* no CASE being examined */ context.estimate = false; /* safe transformations only */ return eval_const_expressions_mutator(node, &context); }
static Node * eval_const_expressions_mutator | ( | Node * | node, | |
eval_const_expressions_context * | context | |||
) | [static] |
Definition at line 2240 of file clauses.c.
References AND_EXPR, BooleanTest::arg, NullTest::arg, FieldSelect::arg, CaseExpr::arg, CollateExpr::arg, ArrayCoerceExpr::arg, CoerceViaIO::arg, RelabelType::arg, arg, NullTest::argisrow, RowExpr::args, CoalesceExpr::args, CaseExpr::args, BoolExpr::args, OpExpr::args, FuncExpr::args, ArrayExpr::array_collid, ArrayExpr::array_typeid, Assert, BooleanEqualOperator, BooleanNotEqualOperator, BoolGetDatum, BoolExpr::boolop, BooleanTest::booltesttype, eval_const_expressions_context::boundParams, eval_const_expressions_context::case_val, CaseExpr::casecollid, CaseExpr::casetype, CoalesceExpr::coalescecollid, CoalesceExpr::coalescetype, ArrayCoerceExpr::coerceformat, CoerceViaIO::coerceformat, CollateExpr::collOid, Const::constcollid, Const::constisnull, Const::consttype, Const::consttypmod, Const::constvalue, copyObject(), CSTRINGOID, datumCopy(), DatumGetBool, CaseExpr::defresult, ArrayExpr::element_typeid, ArrayExpr::elements, ArrayCoerceExpr::elemfuncid, elog, ERROR, eval_const_expressions_context::estimate, evaluate_expr(), CaseWhen::expr, exprCollation(), expression_tree_mutator(), exprType(), exprTypmod(), FieldSelect::fieldnum, func_volatile(), FuncExpr::funccollid, FuncExpr::funcformat, FuncExpr::funcid, FuncExpr::funcresulttype, FuncExpr::funcretset, FuncExpr::funcvariadic, get_typlenbyval(), getTypeInputInfo(), getTypeOutputInfo(), OpExpr::inputcollid, FuncExpr::inputcollid, Int32GetDatum, INT4OID, InvalidAttrNumber, InvalidOid, IS_FALSE, IS_NOT_FALSE, IS_NOT_NULL, IS_NOT_TRUE, IS_NOT_UNKNOWN, IS_NULL, IS_TRUE, IS_UNKNOWN, IsA, ArrayCoerceExpr::isExplicit, ParamExternData::isnull, lappend(), lfirst, linitial, list_length(), list_make1, list_make3, list_nth(), CoalesceExpr::location, ArrayExpr::location, CaseExpr::location, CaseWhen::location, CollateExpr::location, ArrayCoerceExpr::location, CoerceViaIO::location, RelabelType::location, OpExpr::location, FuncExpr::location, make_andclause(), make_orclause(), makeBoolConst(), makeConst(), makeNode, makeNullConst(), makeVar(), ArrayExpr::multidims, negate_clause(), NIL, nodeTag, NOT_EXPR, NULL, NullTest::nulltesttype, ParamListInfoData::numParams, ObjectIdGetDatum, OidIsValid, OIDOID, OpExpr::opcollid, OpExpr::opfuncid, OpExpr::opno, OpExpr::opresulttype, OpExpr::opretset, OR_EXPR, PARAM_EXTERN, PARAM_FLAG_CONST, Param::paramcollid, Param::paramid, Param::paramkind, ParamListInfoData::params, Param::paramtype, Param::paramtypmod, ParamExternData::pflags, PlaceHolderVar::phexpr, PROVOLATILE_IMMUTABLE, ParamExternData::ptype, RelabelType::relabelformat, CaseWhen::result, FieldSelect::resultcollid, ArrayCoerceExpr::resultcollid, CoerceViaIO::resultcollid, RelabelType::resultcollid, FieldSelect::resulttype, ArrayCoerceExpr::resulttype, CoerceViaIO::resulttype, RelabelType::resulttype, FieldSelect::resulttypmod, ArrayCoerceExpr::resulttypmod, RelabelType::resulttypmod, RowExpr::row_typeid, rowtype_field_matches(), set_opfuncid(), simplify_and_arguments(), simplify_boolean_equality(), simplify_function(), simplify_or_arguments(), T_AlternativeSubPlan, T_ArrayCoerceExpr, T_ArrayExpr, T_BooleanTest, T_BoolExpr, T_CaseExpr, T_CaseTestExpr, T_CoalesceExpr, T_CoerceViaIO, T_CollateExpr, T_DistinctExpr, T_FieldSelect, T_FuncExpr, T_NullTest, T_OpExpr, T_Param, T_PlaceHolderVar, T_RelabelType, T_SubPlan, type_is_rowtype(), and ParamExternData::value.
Referenced by estimate_expression_value(), eval_const_expressions(), inline_function(), simplify_and_arguments(), and simplify_or_arguments().
{ if (node == NULL) return NULL; switch (nodeTag(node)) { case T_Param: { Param *param = (Param *) node; /* Look to see if we've been given a value for this Param */ if (param->paramkind == PARAM_EXTERN && context->boundParams != NULL && param->paramid > 0 && param->paramid <= context->boundParams->numParams) { ParamExternData *prm = &context->boundParams->params[param->paramid - 1]; if (OidIsValid(prm->ptype)) { /* OK to substitute parameter value? */ if (context->estimate || (prm->pflags & PARAM_FLAG_CONST)) { /* * Return a Const representing the param value. * Must copy pass-by-ref datatypes, since the * Param might be in a memory context * shorter-lived than our output plan should be. */ int16 typLen; bool typByVal; Datum pval; Assert(prm->ptype == param->paramtype); get_typlenbyval(param->paramtype, &typLen, &typByVal); if (prm->isnull || typByVal) pval = prm->value; else pval = datumCopy(prm->value, typByVal, typLen); return (Node *) makeConst(param->paramtype, param->paramtypmod, param->paramcollid, (int) typLen, pval, prm->isnull, typByVal); } } } /* * Not replaceable, so just copy the Param (no need to * recurse) */ return (Node *) copyObject(param); } case T_FuncExpr: { FuncExpr *expr = (FuncExpr *) node; List *args = expr->args; Expr *simple; FuncExpr *newexpr; /* * Code for op/func reduction is pretty bulky, so split it out * as a separate function. Note: exprTypmod normally returns * -1 for a FuncExpr, but not when the node is recognizably a * length coercion; we want to preserve the typmod in the * eventual Const if so. */ simple = simplify_function(expr->funcid, expr->funcresulttype, exprTypmod(node), expr->funccollid, expr->inputcollid, &args, expr->funcvariadic, true, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; /* * The expression cannot be simplified any further, so build * and return a replacement FuncExpr node using the * possibly-simplified arguments. Note that we have also * converted the argument list to positional notation. */ newexpr = makeNode(FuncExpr); newexpr->funcid = expr->funcid; newexpr->funcresulttype = expr->funcresulttype; newexpr->funcretset = expr->funcretset; newexpr->funcvariadic = expr->funcvariadic; newexpr->funcformat = expr->funcformat; newexpr->funccollid = expr->funccollid; newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; return (Node *) newexpr; } case T_OpExpr: { OpExpr *expr = (OpExpr *) node; List *args = expr->args; Expr *simple; OpExpr *newexpr; /* * Need to get OID of underlying function. Okay to scribble * on input to this extent. */ set_opfuncid(expr); /* * Code for op/func reduction is pretty bulky, so split it out * as a separate function. */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, expr->opcollid, expr->inputcollid, &args, false, true, true, context); if (simple) /* successfully simplified it */ return (Node *) simple; /* * If the operator is boolean equality or inequality, we know * how to simplify cases involving one constant and one * non-constant argument. */ if (expr->opno == BooleanEqualOperator || expr->opno == BooleanNotEqualOperator) { simple = (Expr *) simplify_boolean_equality(expr->opno, args); if (simple) /* successfully simplified it */ return (Node *) simple; } /* * The expression cannot be simplified any further, so build * and return a replacement OpExpr node using the * possibly-simplified arguments. */ newexpr = makeNode(OpExpr); newexpr->opno = expr->opno; newexpr->opfuncid = expr->opfuncid; newexpr->opresulttype = expr->opresulttype; newexpr->opretset = expr->opretset; newexpr->opcollid = expr->opcollid; newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; return (Node *) newexpr; } case T_DistinctExpr: { DistinctExpr *expr = (DistinctExpr *) node; List *args; ListCell *arg; bool has_null_input = false; bool all_null_input = true; bool has_nonconst_input = false; Expr *simple; DistinctExpr *newexpr; /* * Reduce constants in the DistinctExpr's arguments. We know * args is either NIL or a List node, so we can call * expression_tree_mutator directly rather than recursing to * self. */ args = (List *) expression_tree_mutator((Node *) expr->args, eval_const_expressions_mutator, (void *) context); /* * We must do our own check for NULLs because DistinctExpr has * different results for NULL input than the underlying * operator does. */ foreach(arg, args) { if (IsA(lfirst(arg), Const)) { has_null_input |= ((Const *) lfirst(arg))->constisnull; all_null_input &= ((Const *) lfirst(arg))->constisnull; } else has_nonconst_input = true; } /* all constants? then can optimize this out */ if (!has_nonconst_input) { /* all nulls? then not distinct */ if (all_null_input) return makeBoolConst(false, false); /* one null? then distinct */ if (has_null_input) return makeBoolConst(true, false); /* otherwise try to evaluate the '=' operator */ /* (NOT okay to try to inline it, though!) */ /* * Need to get OID of underlying function. Okay to * scribble on input to this extent. */ set_opfuncid((OpExpr *) expr); /* rely on struct * equivalence */ /* * Code for op/func reduction is pretty bulky, so split it * out as a separate function. */ simple = simplify_function(expr->opfuncid, expr->opresulttype, -1, expr->opcollid, expr->inputcollid, &args, false, false, false, context); if (simple) /* successfully simplified it */ { /* * Since the underlying operator is "=", must negate * its result */ Const *csimple = (Const *) simple; Assert(IsA(csimple, Const)); csimple->constvalue = BoolGetDatum(!DatumGetBool(csimple->constvalue)); return (Node *) csimple; } } /* * The expression cannot be simplified any further, so build * and return a replacement DistinctExpr node using the * possibly-simplified arguments. */ newexpr = makeNode(DistinctExpr); newexpr->opno = expr->opno; newexpr->opfuncid = expr->opfuncid; newexpr->opresulttype = expr->opresulttype; newexpr->opretset = expr->opretset; newexpr->opcollid = expr->opcollid; newexpr->inputcollid = expr->inputcollid; newexpr->args = args; newexpr->location = expr->location; return (Node *) newexpr; } case T_BoolExpr: { BoolExpr *expr = (BoolExpr *) node; switch (expr->boolop) { case OR_EXPR: { List *newargs; bool haveNull = false; bool forceTrue = false; newargs = simplify_or_arguments(expr->args, context, &haveNull, &forceTrue); if (forceTrue) return makeBoolConst(true, false); if (haveNull) newargs = lappend(newargs, makeBoolConst(false, true)); /* If all the inputs are FALSE, result is FALSE */ if (newargs == NIL) return makeBoolConst(false, false); /* * If only one nonconst-or-NULL input, it's the * result */ if (list_length(newargs) == 1) return (Node *) linitial(newargs); /* Else we still need an OR node */ return (Node *) make_orclause(newargs); } case AND_EXPR: { List *newargs; bool haveNull = false; bool forceFalse = false; newargs = simplify_and_arguments(expr->args, context, &haveNull, &forceFalse); if (forceFalse) return makeBoolConst(false, false); if (haveNull) newargs = lappend(newargs, makeBoolConst(false, true)); /* If all the inputs are TRUE, result is TRUE */ if (newargs == NIL) return makeBoolConst(true, false); /* * If only one nonconst-or-NULL input, it's the * result */ if (list_length(newargs) == 1) return (Node *) linitial(newargs); /* Else we still need an AND node */ return (Node *) make_andclause(newargs); } case NOT_EXPR: { Node *arg; Assert(list_length(expr->args) == 1); arg = eval_const_expressions_mutator(linitial(expr->args), context); /* * Use negate_clause() to see if we can simplify * away the NOT. */ return negate_clause(arg); } default: elog(ERROR, "unrecognized boolop: %d", (int) expr->boolop); break; } break; } case T_SubPlan: case T_AlternativeSubPlan: /* * Return a SubPlan unchanged --- too late to do anything with it. * * XXX should we ereport() here instead? Probably this routine * should never be invoked after SubPlan creation. */ return node; case T_RelabelType: { /* * If we can simplify the input to a constant, then we don't * need the RelabelType node anymore: just change the type * field of the Const node. Otherwise, must copy the * RelabelType node. */ RelabelType *relabel = (RelabelType *) node; Node *arg; arg = eval_const_expressions_mutator((Node *) relabel->arg, context); /* * If we find stacked RelabelTypes (eg, from foo :: int :: * oid) we can discard all but the top one. */ while (arg && IsA(arg, RelabelType)) arg = (Node *) ((RelabelType *) arg)->arg; if (arg && IsA(arg, Const)) { Const *con = (Const *) arg; con->consttype = relabel->resulttype; con->consttypmod = relabel->resulttypmod; con->constcollid = relabel->resultcollid; return (Node *) con; } else { RelabelType *newrelabel = makeNode(RelabelType); newrelabel->arg = (Expr *) arg; newrelabel->resulttype = relabel->resulttype; newrelabel->resulttypmod = relabel->resulttypmod; newrelabel->resultcollid = relabel->resultcollid; newrelabel->relabelformat = relabel->relabelformat; newrelabel->location = relabel->location; return (Node *) newrelabel; } } case T_CoerceViaIO: { CoerceViaIO *expr = (CoerceViaIO *) node; List *args; Oid outfunc; bool outtypisvarlena; Oid infunc; Oid intypioparam; Expr *simple; CoerceViaIO *newexpr; /* Make a List so we can use simplify_function */ args = list_make1(expr->arg); /* * CoerceViaIO represents calling the source type's output * function then the result type's input function. So, try to * simplify it as though it were a stack of two such function * calls. First we need to know what the functions are. * * Note that the coercion functions are assumed not to care * about input collation, so we just pass InvalidOid for that. */ getTypeOutputInfo(exprType((Node *) expr->arg), &outfunc, &outtypisvarlena); getTypeInputInfo(expr->resulttype, &infunc, &intypioparam); simple = simplify_function(outfunc, CSTRINGOID, -1, InvalidOid, InvalidOid, &args, false, true, true, context); if (simple) /* successfully simplified output fn */ { /* * Input functions may want 1 to 3 arguments. We always * supply all three, trusting that nothing downstream will * complain. */ args = list_make3(simple, makeConst(OIDOID, -1, InvalidOid, sizeof(Oid), ObjectIdGetDatum(intypioparam), false, true), makeConst(INT4OID, -1, InvalidOid, sizeof(int32), Int32GetDatum(-1), false, true)); simple = simplify_function(infunc, expr->resulttype, -1, expr->resultcollid, InvalidOid, &args, false, false, true, context); if (simple) /* successfully simplified input fn */ return (Node *) simple; } /* * The expression cannot be simplified any further, so build * and return a replacement CoerceViaIO node using the * possibly-simplified argument. */ newexpr = makeNode(CoerceViaIO); newexpr->arg = (Expr *) linitial(args); newexpr->resulttype = expr->resulttype; newexpr->resultcollid = expr->resultcollid; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; return (Node *) newexpr; } case T_ArrayCoerceExpr: { ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; Expr *arg; ArrayCoerceExpr *newexpr; /* * Reduce constants in the ArrayCoerceExpr's argument, then * build a new ArrayCoerceExpr. */ arg = (Expr *) eval_const_expressions_mutator((Node *) expr->arg, context); newexpr = makeNode(ArrayCoerceExpr); newexpr->arg = arg; newexpr->elemfuncid = expr->elemfuncid; newexpr->resulttype = expr->resulttype; newexpr->resulttypmod = expr->resulttypmod; newexpr->resultcollid = expr->resultcollid; newexpr->isExplicit = expr->isExplicit; newexpr->coerceformat = expr->coerceformat; newexpr->location = expr->location; /* * If constant argument and it's a binary-coercible or * immutable conversion, we can simplify it to a constant. */ if (arg && IsA(arg, Const) && (!OidIsValid(newexpr->elemfuncid) || func_volatile(newexpr->elemfuncid) == PROVOLATILE_IMMUTABLE)) return (Node *) evaluate_expr((Expr *) newexpr, newexpr->resulttype, newexpr->resulttypmod, newexpr->resultcollid); /* Else we must return the partially-simplified node */ return (Node *) newexpr; } case T_CollateExpr: { /* * If we can simplify the input to a constant, then we don't * need the CollateExpr node at all: just change the * constcollid field of the Const node. Otherwise, replace * the CollateExpr with a RelabelType. (We do that so as to * improve uniformity of expression representation and thus * simplify comparison of expressions.) */ CollateExpr *collate = (CollateExpr *) node; Node *arg; arg = eval_const_expressions_mutator((Node *) collate->arg, context); if (arg && IsA(arg, Const)) { Const *con = (Const *) arg; con->constcollid = collate->collOid; return (Node *) con; } else if (collate->collOid == exprCollation(arg)) { /* Don't need a RelabelType either... */ return arg; } else { RelabelType *relabel = makeNode(RelabelType); relabel->resulttype = exprType(arg); relabel->resulttypmod = exprTypmod(arg); relabel->resultcollid = collate->collOid; relabel->relabelformat = COERCE_IMPLICIT_CAST; relabel->location = collate->location; /* Don't create stacked RelabelTypes */ while (arg && IsA(arg, RelabelType)) arg = (Node *) ((RelabelType *) arg)->arg; relabel->arg = (Expr *) arg; return (Node *) relabel; } } case T_CaseExpr: { /*---------- * CASE expressions can be simplified if there are constant * condition clauses: * FALSE (or NULL): drop the alternative * TRUE: drop all remaining alternatives * If the first non-FALSE alternative is a constant TRUE, * we can simplify the entire CASE to that alternative's * expression. If there are no non-FALSE alternatives, * we simplify the entire CASE to the default result (ELSE). * * If we have a simple-form CASE with constant test * expression, we substitute the constant value for contained * CaseTestExpr placeholder nodes, so that we have the * opportunity to reduce constant test conditions. For * example this allows * CASE 0 WHEN 0 THEN 1 ELSE 1/0 END * to reduce to 1 rather than drawing a divide-by-0 error. * Note that when the test expression is constant, we don't * have to include it in the resulting CASE; for example * CASE 0 WHEN x THEN y ELSE z END * is transformed by the parser to * CASE 0 WHEN CaseTestExpr = x THEN y ELSE z END * which we can simplify to * CASE WHEN 0 = x THEN y ELSE z END * It is not necessary for the executor to evaluate the "arg" * expression when executing the CASE, since any contained * CaseTestExprs that might have referred to it will have been * replaced by the constant. *---------- */ CaseExpr *caseexpr = (CaseExpr *) node; CaseExpr *newcase; Node *save_case_val; Node *newarg; List *newargs; bool const_true_cond; Node *defresult = NULL; ListCell *arg; /* Simplify the test expression, if any */ newarg = eval_const_expressions_mutator((Node *) caseexpr->arg, context); /* Set up for contained CaseTestExpr nodes */ save_case_val = context->case_val; if (newarg && IsA(newarg, Const)) { context->case_val = newarg; newarg = NULL; /* not needed anymore, see above */ } else context->case_val = NULL; /* Simplify the WHEN clauses */ newargs = NIL; const_true_cond = false; foreach(arg, caseexpr->args) { CaseWhen *oldcasewhen = (CaseWhen *) lfirst(arg); Node *casecond; Node *caseresult; Assert(IsA(oldcasewhen, CaseWhen)); /* Simplify this alternative's test condition */ casecond = eval_const_expressions_mutator((Node *) oldcasewhen->expr, context); /* * If the test condition is constant FALSE (or NULL), then * drop this WHEN clause completely, without processing * the result. */ if (casecond && IsA(casecond, Const)) { Const *const_input = (Const *) casecond; if (const_input->constisnull || !DatumGetBool(const_input->constvalue)) continue; /* drop alternative with FALSE cond */ /* Else it's constant TRUE */ const_true_cond = true; } /* Simplify this alternative's result value */ caseresult = eval_const_expressions_mutator((Node *) oldcasewhen->result, context); /* If non-constant test condition, emit a new WHEN node */ if (!const_true_cond) { CaseWhen *newcasewhen = makeNode(CaseWhen); newcasewhen->expr = (Expr *) casecond; newcasewhen->result = (Expr *) caseresult; newcasewhen->location = oldcasewhen->location; newargs = lappend(newargs, newcasewhen); continue; } /* * Found a TRUE condition, so none of the remaining * alternatives can be reached. We treat the result as * the default result. */ defresult = caseresult; break; } /* Simplify the default result, unless we replaced it above */ if (!const_true_cond) defresult = eval_const_expressions_mutator((Node *) caseexpr->defresult, context); context->case_val = save_case_val; /* * If no non-FALSE alternatives, CASE reduces to the default * result */ if (newargs == NIL) return defresult; /* Otherwise we need a new CASE node */ newcase = makeNode(CaseExpr); newcase->casetype = caseexpr->casetype; newcase->casecollid = caseexpr->casecollid; newcase->arg = (Expr *) newarg; newcase->args = newargs; newcase->defresult = (Expr *) defresult; newcase->location = caseexpr->location; return (Node *) newcase; } case T_CaseTestExpr: { /* * If we know a constant test value for the current CASE * construct, substitute it for the placeholder. Else just * return the placeholder as-is. */ if (context->case_val) return copyObject(context->case_val); else return copyObject(node); } case T_ArrayExpr: { ArrayExpr *arrayexpr = (ArrayExpr *) node; ArrayExpr *newarray; bool all_const = true; List *newelems; ListCell *element; newelems = NIL; foreach(element, arrayexpr->elements) { Node *e; e = eval_const_expressions_mutator((Node *) lfirst(element), context); if (!IsA(e, Const)) all_const = false; newelems = lappend(newelems, e); } newarray = makeNode(ArrayExpr); newarray->array_typeid = arrayexpr->array_typeid; newarray->array_collid = arrayexpr->array_collid; newarray->element_typeid = arrayexpr->element_typeid; newarray->elements = newelems; newarray->multidims = arrayexpr->multidims; newarray->location = arrayexpr->location; if (all_const) return (Node *) evaluate_expr((Expr *) newarray, newarray->array_typeid, exprTypmod(node), newarray->array_collid); return (Node *) newarray; } case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; CoalesceExpr *newcoalesce; List *newargs; ListCell *arg; newargs = NIL; foreach(arg, coalesceexpr->args) { Node *e; e = eval_const_expressions_mutator((Node *) lfirst(arg), context); /* * We can remove null constants from the list. For a * non-null constant, if it has not been preceded by any * other non-null-constant expressions then it is the * result. Otherwise, it's the next argument, but we can * drop following arguments since they will never be * reached. */ if (IsA(e, Const)) { if (((Const *) e)->constisnull) continue; /* drop null constant */ if (newargs == NIL) return e; /* first expr */ newargs = lappend(newargs, e); break; } newargs = lappend(newargs, e); } /* * If all the arguments were constant null, the result is just * null */ if (newargs == NIL) return (Node *) makeNullConst(coalesceexpr->coalescetype, -1, coalesceexpr->coalescecollid); newcoalesce = makeNode(CoalesceExpr); newcoalesce->coalescetype = coalesceexpr->coalescetype; newcoalesce->coalescecollid = coalesceexpr->coalescecollid; newcoalesce->args = newargs; newcoalesce->location = coalesceexpr->location; return (Node *) newcoalesce; } case T_FieldSelect: { /* * We can optimize field selection from a whole-row Var into a * simple Var. (This case won't be generated directly by the * parser, because ParseComplexProjection short-circuits it. * But it can arise while simplifying functions.) Also, we * can optimize field selection from a RowExpr construct. * * We must however check that the declared type of the field * is still the same as when the FieldSelect was created --- * this can change if someone did ALTER COLUMN TYPE on the * rowtype. */ FieldSelect *fselect = (FieldSelect *) node; FieldSelect *newfselect; Node *arg; arg = eval_const_expressions_mutator((Node *) fselect->arg, context); if (arg && IsA(arg, Var) && ((Var *) arg)->varattno == InvalidAttrNumber) { if (rowtype_field_matches(((Var *) arg)->vartype, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod, fselect->resultcollid)) return (Node *) makeVar(((Var *) arg)->varno, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod, fselect->resultcollid, ((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->resultcollid) && fselect->resulttype == exprType(fld) && fselect->resulttypmod == exprTypmod(fld) && fselect->resultcollid == exprCollation(fld)) return fld; } } newfselect = makeNode(FieldSelect); newfselect->arg = (Expr *) arg; newfselect->fieldnum = fselect->fieldnum; newfselect->resulttype = fselect->resulttype; newfselect->resulttypmod = fselect->resulttypmod; newfselect->resultcollid = fselect->resultcollid; return (Node *) newfselect; } case T_NullTest: { NullTest *ntest = (NullTest *) node; NullTest *newntest; Node *arg; arg = eval_const_expressions_mutator((Node *) ntest->arg, context); if (arg && IsA(arg, RowExpr)) { /* * We break ROW(...) IS [NOT] NULL into separate tests on * its component fields. This form is usually more * efficient to evaluate, as well as being more amenable * to optimization. */ RowExpr *rarg = (RowExpr *) arg; List *newargs = NIL; ListCell *l; Assert(ntest->argisrow); foreach(l, rarg->args) { Node *relem = (Node *) lfirst(l); /* * A constant field refutes the whole NullTest if it's * of the wrong nullness; else we can discard it. */ if (relem && IsA(relem, Const)) { Const *carg = (Const *) relem; if (carg->constisnull ? (ntest->nulltesttype == IS_NOT_NULL) : (ntest->nulltesttype == IS_NULL)) return makeBoolConst(false, false); continue; } newntest = makeNode(NullTest); newntest->arg = (Expr *) relem; newntest->nulltesttype = ntest->nulltesttype; newntest->argisrow = type_is_rowtype(exprType(relem)); newargs = lappend(newargs, newntest); } /* If all the inputs were constants, result is TRUE */ if (newargs == NIL) return makeBoolConst(true, false); /* If only one nonconst input, it's the result */ if (list_length(newargs) == 1) return (Node *) linitial(newargs); /* Else we need an AND node */ return (Node *) make_andclause(newargs); } if (!ntest->argisrow && arg && IsA(arg, Const)) { Const *carg = (Const *) arg; bool result; switch (ntest->nulltesttype) { case IS_NULL: result = carg->constisnull; break; case IS_NOT_NULL: result = !carg->constisnull; break; default: elog(ERROR, "unrecognized nulltesttype: %d", (int) ntest->nulltesttype); result = false; /* keep compiler quiet */ break; } return makeBoolConst(result, false); } newntest = makeNode(NullTest); newntest->arg = (Expr *) arg; newntest->nulltesttype = ntest->nulltesttype; newntest->argisrow = ntest->argisrow; return (Node *) newntest; } case T_BooleanTest: { BooleanTest *btest = (BooleanTest *) node; BooleanTest *newbtest; Node *arg; arg = eval_const_expressions_mutator((Node *) btest->arg, context); if (arg && IsA(arg, Const)) { Const *carg = (Const *) arg; bool result; switch (btest->booltesttype) { case IS_TRUE: result = (!carg->constisnull && DatumGetBool(carg->constvalue)); break; case IS_NOT_TRUE: result = (carg->constisnull || !DatumGetBool(carg->constvalue)); break; case IS_FALSE: result = (!carg->constisnull && !DatumGetBool(carg->constvalue)); break; case IS_NOT_FALSE: result = (carg->constisnull || DatumGetBool(carg->constvalue)); break; case IS_UNKNOWN: result = carg->constisnull; break; case IS_NOT_UNKNOWN: result = !carg->constisnull; break; default: elog(ERROR, "unrecognized booltesttype: %d", (int) btest->booltesttype); result = false; /* keep compiler quiet */ break; } return makeBoolConst(result, false); } newbtest = makeNode(BooleanTest); newbtest->arg = (Expr *) arg; newbtest->booltesttype = btest->booltesttype; return (Node *) newbtest; } case T_PlaceHolderVar: /* * In estimation mode, just strip the PlaceHolderVar node * altogether; this amounts to estimating that the contained value * won't be forced to null by an outer join. In regular mode we * just use the default behavior (ie, simplify the expression but * leave the PlaceHolderVar node intact). */ if (context->estimate) { PlaceHolderVar *phv = (PlaceHolderVar *) node; return eval_const_expressions_mutator((Node *) phv->phexpr, context); } break; default: break; } /* * 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); }
static Expr * evaluate_expr | ( | Expr * | expr, | |
Oid | result_type, | |||
int32 | result_typmod, | |||
Oid | result_collation | |||
) | [static] |
Definition at line 4383 of file clauses.c.
References CreateExecutorState(), datumCopy(), EState::es_query_cxt, ExecEvalExprSwitchContext(), ExecInitExpr(), fix_opfuncids(), FreeExecutorState(), get_typlenbyval(), GetPerTupleExprContext, makeConst(), MemoryContextSwitchTo(), NULL, PG_DETOAST_DATUM_COPY, and PointerGetDatum.
Referenced by eval_const_expressions_mutator(), and evaluate_function().
{ EState *estate; ExprState *exprstate; MemoryContext oldcontext; Datum const_val; bool const_is_null; int16 resultTypLen; bool resultTypByVal; /* * To use the executor, we need an EState. */ estate = CreateExecutorState(); /* We can use the estate's working context to avoid memory leaks. */ oldcontext = MemoryContextSwitchTo(estate->es_query_cxt); /* Make sure any opfuncids are filled in. */ fix_opfuncids((Node *) expr); /* * Prepare expr for execution. (Note: we can't use ExecPrepareExpr * because it'd result in recursively invoking eval_const_expressions.) */ exprstate = ExecInitExpr(expr, NULL); /* * And evaluate it. * * It is OK to use a default econtext because none of the ExecEvalExpr() * code used in this situation will use econtext. That might seem * fortuitous, but it's not so unreasonable --- a constant expression does * not depend on context, by definition, n'est ce pas? */ const_val = ExecEvalExprSwitchContext(exprstate, GetPerTupleExprContext(estate), &const_is_null, NULL); /* Get info needed about result datatype */ get_typlenbyval(result_type, &resultTypLen, &resultTypByVal); /* Get back to outer memory context */ MemoryContextSwitchTo(oldcontext); /* * Must copy result out of sub-context used by expression eval. * * Also, if it's varlena, forcibly detoast it. This protects us against * storing TOAST pointers into plans that might outlive the referenced * data. */ if (!const_is_null) { if (resultTypLen == -1) const_val = PointerGetDatum(PG_DETOAST_DATUM_COPY(const_val)); else const_val = datumCopy(const_val, resultTypByVal, resultTypLen); } /* Release all the junk we just created */ FreeExecutorState(estate); /* * Make the constant result node. */ return (Expr *) makeConst(result_type, result_typmod, result_collation, resultTypLen, const_val, const_is_null, resultTypByVal); }
static Expr * evaluate_function | ( | Oid | funcid, | |
Oid | result_type, | |||
int32 | result_typmod, | |||
Oid | result_collid, | |||
Oid | input_collid, | |||
List * | args, | |||
bool | funcvariadic, | |||
HeapTuple | func_tuple, | |||
eval_const_expressions_context * | context | |||
) | [static] |
Definition at line 3889 of file clauses.c.
References arg, FuncExpr::args, eval_const_expressions_context::estimate, evaluate_expr(), FuncExpr::funccollid, FuncExpr::funcformat, FuncExpr::funcid, FuncExpr::funcresulttype, FuncExpr::funcretset, FuncExpr::funcvariadic, GETSTRUCT, FuncExpr::inputcollid, IsA, lfirst, FuncExpr::location, makeNode, makeNullConst(), PROVOLATILE_IMMUTABLE, PROVOLATILE_STABLE, and RECORDOID.
Referenced by simplify_function().
{ 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, result_typmod, result_collid); /* * 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->funcvariadic = funcvariadic; newexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ newexpr->funccollid = result_collid; /* doesn't matter */ newexpr->inputcollid = input_collid; newexpr->args = args; newexpr->location = -1; return evaluate_expr((Expr *) newexpr, result_type, result_typmod, result_collid); }
static List * expand_function_arguments | ( | List * | args, | |
Oid | result_type, | |||
HeapTuple | func_tuple | |||
) | [static] |
Definition at line 3671 of file clauses.c.
References add_function_defaults(), arg, GETSTRUCT, IsA, lfirst, list_length(), recheck_cast_function_args(), and reorder_function_arguments().
Referenced by simplify_function().
{ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); bool has_named_args = false; ListCell *lc; /* Do we have any named arguments? */ foreach(lc, args) { Node *arg = (Node *) lfirst(lc); if (IsA(arg, NamedArgExpr)) { has_named_args = true; break; } } /* If so, we must apply reorder_function_arguments */ if (has_named_args) { args = reorder_function_arguments(args, func_tuple); /* Recheck argument types and add casts if needed */ recheck_cast_function_args(args, result_type, func_tuple); } else if (list_length(args) < funcform->pronargs) { /* No named args, but we seem to be short some defaults */ args = add_function_defaults(args, func_tuple); /* Recheck argument types and add casts if needed */ recheck_cast_function_args(args, result_type, func_tuple); } return args; }
double expression_returns_set_rows | ( | Node * | clause | ) |
Definition at line 676 of file clauses.c.
References clamp_row_est(), and expression_returns_set_rows_walker().
Referenced by set_function_size_estimates(), and tlist_returns_set_rows().
{ double result = 1; (void) expression_returns_set_rows_walker(clause, &result); return clamp_row_est(result); }
Definition at line 685 of file clauses.c.
References expression_tree_walker(), FuncExpr::funcid, FuncExpr::funcretset, get_func_rows(), IsA, NULL, OpExpr::opfuncid, OpExpr::opretset, and set_opfuncid().
Referenced by expression_returns_set_rows().
{ if (node == NULL) return false; if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (expr->funcretset) *count *= get_func_rows(expr->funcid); } if (IsA(node, OpExpr)) { OpExpr *expr = (OpExpr *) node; if (expr->opretset) { set_opfuncid(expr); *count *= get_func_rows(expr->opfuncid); } } /* Avoid recursion for some cases that can't return a set */ if (IsA(node, Aggref)) return false; if (IsA(node, WindowFunc)) return false; if (IsA(node, DistinctExpr)) return false; if (IsA(node, NullIfExpr)) return false; if (IsA(node, ScalarArrayOpExpr)) return false; if (IsA(node, BoolExpr)) return false; if (IsA(node, SubLink)) return false; if (IsA(node, SubPlan)) return false; if (IsA(node, AlternativeSubPlan)) return false; if (IsA(node, ArrayExpr)) return false; if (IsA(node, RowExpr)) return false; if (IsA(node, RowCompareExpr)) return false; if (IsA(node, CoalesceExpr)) return false; if (IsA(node, MinMaxExpr)) return false; if (IsA(node, XmlExpr)) return false; return expression_tree_walker(node, expression_returns_set_rows_walker, (void *) count); }
Definition at line 3809 of file clauses.c.
References Anum_pg_proc_proargdefaults, Assert, elog, ERROR, IsA, pfree(), PROCOID, stringToNode(), SysCacheGetAttr(), and TextDatumGetCString.
Referenced by add_function_defaults(), and reorder_function_arguments().
{ List *defaults; Datum proargdefaults; bool isnull; char *str; /* The error cases here shouldn't happen, but check anyway */ proargdefaults = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_proargdefaults, &isnull); if (isnull) elog(ERROR, "not enough default arguments"); str = TextDatumGetCString(proargdefaults); defaults = (List *) stringToNode(str); Assert(IsA(defaults, List)); pfree(str); return defaults; }
Definition at line 1802 of file clauses.c.
References BooleanTest::arg, NullTest::arg, NullTest::argisrow, BooleanTest::booltesttype, IS_NULL, IS_UNKNOWN, IsA, NULL, NullTest::nulltesttype, and Var::varlevelsup.
Referenced by check_redundant_nullability_qual(), and find_forced_null_vars().
{ if (node == NULL) return NULL; if (IsA(node, NullTest)) { /* check for var IS NULL */ NullTest *expr = (NullTest *) node; if (expr->nulltesttype == IS_NULL && !expr->argisrow) { Var *var = (Var *) expr->arg; if (var && IsA(var, Var) && var->varlevelsup == 0) return var; } } else if (IsA(node, BooleanTest)) { /* var IS UNKNOWN is equivalent to var IS NULL */ BooleanTest *expr = (BooleanTest *) node; if (expr->booltesttype == IS_UNKNOWN) { Var *var = (Var *) expr->arg; if (var && IsA(var, Var) && var->varlevelsup == 0) return var; } } return NULL; }
Definition at line 1743 of file clauses.c.
References AND_EXPR, BoolExpr::args, BoolExpr::boolop, find_forced_null_var(), find_forced_null_vars(), IsA, lfirst, list_concat(), list_make1, and NULL.
Referenced by find_forced_null_vars(), and reduce_outer_joins_pass2().
{ List *result = NIL; Var *var; ListCell *l; if (node == NULL) return NIL; /* Check single-clause cases using subroutine */ var = find_forced_null_var(node); if (var) { result = list_make1(var); } /* Otherwise, handle AND-conditions */ 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. */ foreach(l, (List *) node) { result = list_concat(result, find_forced_null_vars(lfirst(l))); } } else if (IsA(node, BoolExpr)) { BoolExpr *expr = (BoolExpr *) node; /* * We don't bother considering the OR case, because it's fairly * unlikely anyone would write "v1 IS NULL OR v1 IS NULL". Likewise, * the NOT case isn't worth expending code on. */ if (expr->boolop == AND_EXPR) { /* At top level we can just recurse (to the List case) */ result = find_forced_null_vars((Node *) expr->args); } } return result; }
Definition at line 1342 of file clauses.c.
References find_nonnullable_rels_walker().
Referenced by make_outerjoininfo(), and reduce_outer_joins_pass2().
{ return find_nonnullable_rels_walker(clause, true); }
Definition at line 1348 of file clauses.c.
References AND_EXPR, BooleanTest::arg, NullTest::arg, CollateExpr::arg, ConvertRowtypeExpr::arg, ArrayCoerceExpr::arg, CoerceViaIO::arg, RelabelType::arg, NullTest::argisrow, BoolExpr::args, ScalarArrayOpExpr::args, OpExpr::args, FuncExpr::args, bms_int_members(), bms_is_empty(), bms_join(), bms_make_singleton(), BoolExpr::boolop, BooleanTest::booltesttype, elog, ERROR, func_strict(), FuncExpr::funcid, IS_FALSE, IS_NOT_NULL, IS_NOT_UNKNOWN, is_strict_saop(), IS_TRUE, IsA, lfirst, NOT_EXPR, NULL, NullTest::nulltesttype, OpExpr::opfuncid, OR_EXPR, PlaceHolderVar::phexpr, set_opfuncid(), Var::varlevelsup, and Var::varno.
Referenced by find_nonnullable_rels().
{ 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, CollateExpr)) { CollateExpr *expr = (CollateExpr *) 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 && !expr->argisrow) 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); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; result = find_nonnullable_rels_walker((Node *) phv->phexpr, top_level); } return result; }
Definition at line 1550 of file clauses.c.
References find_nonnullable_vars_walker().
Referenced by reduce_outer_joins_pass2().
{ return find_nonnullable_vars_walker(clause, true); }
Definition at line 1556 of file clauses.c.
References AND_EXPR, BooleanTest::arg, NullTest::arg, CollateExpr::arg, ConvertRowtypeExpr::arg, ArrayCoerceExpr::arg, CoerceViaIO::arg, RelabelType::arg, NullTest::argisrow, BoolExpr::args, ScalarArrayOpExpr::args, OpExpr::args, FuncExpr::args, BoolExpr::boolop, BooleanTest::booltesttype, elog, ERROR, func_strict(), FuncExpr::funcid, IS_FALSE, IS_NOT_NULL, IS_NOT_UNKNOWN, is_strict_saop(), IS_TRUE, IsA, lfirst, list_concat(), list_intersection(), list_make1, NIL, NOT_EXPR, NULL, NullTest::nulltesttype, OpExpr::opfuncid, OR_EXPR, PlaceHolderVar::phexpr, set_opfuncid(), and Var::varlevelsup.
Referenced by find_nonnullable_vars().
{ List *result = NIL; ListCell *l; if (node == NULL) return NIL; if (IsA(node, Var)) { Var *var = (Var *) node; if (var->varlevelsup == 0) result = list_make1(var); } 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 * vars is the union of those found in the arms, and we pass down the * top_level flag unmodified. */ foreach(l, (List *) node) { result = list_concat(result, find_nonnullable_vars_walker(lfirst(l), top_level)); } } else if (IsA(node, FuncExpr)) { FuncExpr *expr = (FuncExpr *) node; if (func_strict(expr->funcid)) result = find_nonnullable_vars_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_vars_walker((Node *) expr->args, false); } else if (IsA(node, ScalarArrayOpExpr)) { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; if (is_strict_saop(expr, true)) result = find_nonnullable_vars_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_vars_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 vars, 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 vars for each arm. * This works for both values of top_level. */ foreach(l, expr->args) { List *subresult; subresult = find_nonnullable_vars_walker(lfirst(l), top_level); if (result == NIL) /* first subresult? */ result = subresult; else result = list_intersection(result, subresult); /* * If the intersection is empty, we can stop looking. This * also justifies the test for first-subresult above. */ if (result == NIL) break; } break; case NOT_EXPR: /* NOT will return null if its arg is null */ result = find_nonnullable_vars_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_vars_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_vars_walker((Node *) expr->arg, false); } else if (IsA(node, ArrayCoerceExpr)) { /* ArrayCoerceExpr is strict at the array level */ ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node; result = find_nonnullable_vars_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_vars_walker((Node *) expr->arg, top_level); } else if (IsA(node, CollateExpr)) { CollateExpr *expr = (CollateExpr *) node; result = find_nonnullable_vars_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 && !expr->argisrow) result = find_nonnullable_vars_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_vars_walker((Node *) expr->arg, false); } else if (IsA(node, PlaceHolderVar)) { PlaceHolderVar *phv = (PlaceHolderVar *) node; result = find_nonnullable_vars_walker((Node *) phv->phexpr, top_level); } return result; }
WindowFuncLists* find_window_functions | ( | Node * | clause, | |
Index | maxWinRef | |||
) |
Definition at line 612 of file clauses.c.
References find_window_functions_walker(), WindowFuncLists::maxWinRef, WindowFuncLists::numWindowFuncs, palloc(), palloc0(), and WindowFuncLists::windowFuncs.
Referenced by grouping_planner().
{ WindowFuncLists *lists = palloc(sizeof(WindowFuncLists)); lists->numWindowFuncs = 0; lists->maxWinRef = maxWinRef; lists->windowFuncs = (List **) palloc0((maxWinRef + 1) * sizeof(List *)); (void) find_window_functions_walker(clause, lists); return lists; }
static bool find_window_functions_walker | ( | Node * | node, | |
WindowFuncLists * | lists | |||
) | [static] |
Definition at line 624 of file clauses.c.
References WindowFunc::args, Assert, contain_window_function(), elog, ereport, errcode(), errmsg(), ERROR, expression_tree_walker(), IsA, lappend(), WindowFuncLists::maxWinRef, NULL, WindowFuncLists::numWindowFuncs, WindowFuncLists::windowFuncs, and WindowFunc::winref.
Referenced by find_window_functions().
{ if (node == NULL) return false; if (IsA(node, WindowFunc)) { WindowFunc *wfunc = (WindowFunc *) node; /* winref is unsigned, so one-sided test is OK */ if (wfunc->winref > lists->maxWinRef) elog(ERROR, "WindowFunc contains out-of-range winref %u", wfunc->winref); lists->windowFuncs[wfunc->winref] = lappend(lists->windowFuncs[wfunc->winref], wfunc); lists->numWindowFuncs++; /* * Complain if the window function's arguments contain window * functions */ if (contain_window_function((Node *) wfunc->args)) ereport(ERROR, (errcode(ERRCODE_WINDOWING_ERROR), errmsg("window function calls cannot be nested"))); /* * Having checked that, we need not recurse into the argument. */ return false; } Assert(!IsA(node, SubLink)); return expression_tree_walker(node, find_window_functions_walker, (void *) lists); }
Definition at line 184 of file clauses.c.
References OpExpr::args, linitial, and NIL.
Referenced by addRangeClause(), btcostestimate(), btree_predicate_proof(), ExecIndexBuildScanKeys(), expand_indexqual_opclause(), final_cost_hashjoin(), gincost_opexpr(), initialize_mergeclause_eclasses(), make_restrictinfo_internal(), match_clause_to_indexcol(), match_clause_to_ordering_op(), mergejoinscansel(), print_expr(), process_equivalence(), reconsider_full_join_clause(), reconsider_outer_join_clause(), relation_has_unique_index_for(), and TidListCreate().
Definition at line 250 of file clauses.c.
References linitial.
Referenced by clause_selectivity(), expand_boolean_index_clause(), match_boolean_index_clause(), and pull_up_sublinks_qual_recurse().
Definition at line 201 of file clauses.c.
References OpExpr::args, list_length(), and lsecond.
Referenced by addRangeClause(), btcostestimate(), btree_predicate_proof(), ExecIndexBuildScanKeys(), expand_indexqual_opclause(), final_cost_hashjoin(), gincost_opexpr(), initialize_mergeclause_eclasses(), make_restrictinfo_internal(), match_clause_to_indexcol(), match_clause_to_ordering_op(), match_special_index_operator(), mergejoinscansel(), print_expr(), process_equivalence(), reconsider_full_join_clause(), reconsider_outer_join_clause(), relation_has_unique_index_for(), and TidListCreate().
static Expr * inline_function | ( | Oid | funcid, | |
Oid | result_type, | |||
Oid | result_collid, | |||
Oid | input_collid, | |||
List * | args, | |||
bool | funcvariadic, | |||
HeapTuple | func_tuple, | |||
eval_const_expressions_context * | context | |||
) | [static] |
Definition at line 4014 of file clauses.c.
References ACL_EXECUTE, eval_const_expressions_context::active_fns, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_proc_proconfig, Anum_pg_proc_prosrc, CollateExpr::arg, ErrorContextCallback::arg, arg, FuncExpr::args, Assert, ErrorContextCallback::callback, check_sql_fn_retval(), CMD_SELECT, CollateExpr::collOid, Query::commandType, contain_mutable_functions(), contain_nonstrict_functions(), contain_subplans(), contain_volatile_functions(), copyObject(), cost_qual_eval(), cpu_operator_cost, Query::cteList, CurrentMemoryContext, Query::distinctClause, elog, ERROR, error_context_stack, eval_const_expressions_mutator(), exprCollation(), expression_returns_set(), exprType(), FmgrHookIsNeeded, free_parsestate(), FromExpr::fromlist, FuncExpr::funccollid, FuncExpr::funcformat, FuncExpr::funcid, FuncExpr::funcresulttype, FuncExpr::funcretset, FuncExpr::funcvariadic, GETSTRUCT, GetUserId(), Query::groupClause, Query::hasAggs, Query::hasSubLinks, Query::hasWindowFuncs, Query::havingQual, heap_attisnull(), i, FuncExpr::inputcollid, IsA, Query::jointree, lcons_oid(), lfirst, Query::limitCount, Query::limitOffset, linitial, list_delete_first(), list_length(), list_make1, list_member_oid(), CollateExpr::location, FuncExpr::location, make_parsestate(), makeNode, MemoryContextDelete(), MemoryContextSwitchTo(), NameStr, NULL, OidIsValid, ParseState::p_sourcetext, palloc0(), QualCost::per_tuple, pg_parse_query(), pg_proc_aclcheck(), prepare_sql_fn_parse_info(), ErrorContextCallback::previous, PROCOID, inline_error_callback_arg::proname, inline_error_callback_arg::prosrc, PROVOLATILE_IMMUTABLE, PROVOLATILE_STABLE, FromExpr::quals, record_plan_function_dependency(), RECORDOID, eval_const_expressions_context::root, Query::rtable, Query::setOperations, Query::sortClause, sql_fn_parser_setup(), SQLlanguageId, QualCost::startup, substitute_actual_parameters(), SysCacheGetAttr(), Query::targetList, TextDatumGetCString, transformTopLevelStmt(), Query::utilityStmt, and Query::windowClause.
Referenced by simplify_function().
{ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); char *src; Datum tmp; bool isNull; bool modifyTargetList; MemoryContext oldcxt; MemoryContext mycxt; inline_error_callback_arg callback_arg; ErrorContextCallback sqlerrcontext; FuncExpr *fexpr; SQLFunctionParseInfoPtr pinfo; ParseState *pstate; List *raw_parsetree_list; Query *querytree; Node *newexpr; int *usecounts; ListCell *arg; int i; /* * Forget it if the function is not SQL-language or has other showstopper * properties. (The nargs check is just paranoia.) */ if (funcform->prolang != SQLlanguageId || funcform->prosecdef || funcform->proretset || funcform->prorettype == RECORDOID || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) || funcform->pronargs != list_length(args)) return NULL; /* Check for recursive function, and give up trying to expand if so */ if (list_member_oid(context->active_fns, funcid)) return NULL; /* Check permission to call function (fail later, if not) */ if (pg_proc_aclcheck(funcid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; /* Check whether a plugin wants to hook function entry/exit */ if (FmgrHookIsNeeded(funcid)) return NULL; /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. */ mycxt = AllocSetContextCreate(CurrentMemoryContext, "inline_function", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(mycxt); /* Fetch the function body */ tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc, &isNull); if (isNull) elog(ERROR, "null prosrc for function %u", funcid); src = TextDatumGetCString(tmp); /* * Setup error traceback support for ereport(). This is so that we can * finger the function that bad information came from. */ callback_arg.proname = NameStr(funcform->proname); callback_arg.prosrc = src; sqlerrcontext.callback = sql_inline_error_callback; sqlerrcontext.arg = (void *) &callback_arg; sqlerrcontext.previous = error_context_stack; error_context_stack = &sqlerrcontext; /* * Set up to handle parameters while parsing the function body. We need a * dummy FuncExpr node containing the already-simplified arguments to pass * to prepare_sql_fn_parse_info. (It is really only needed if there are * some polymorphic arguments, but for simplicity we always build it.) */ fexpr = makeNode(FuncExpr); fexpr->funcid = funcid; fexpr->funcresulttype = result_type; fexpr->funcretset = false; fexpr->funcvariadic = funcvariadic; fexpr->funcformat = COERCE_EXPLICIT_CALL; /* doesn't matter */ fexpr->funccollid = result_collid; /* doesn't matter */ fexpr->inputcollid = input_collid; fexpr->args = args; fexpr->location = -1; pinfo = prepare_sql_fn_parse_info(func_tuple, (Node *) fexpr, input_collid); /* * We just do parsing and parse analysis, not rewriting, because rewriting * will not affect table-free-SELECT-only queries, which is all that we * care about. Also, we can punt as soon as we detect more than one * command in the function body. */ raw_parsetree_list = pg_parse_query(src); if (list_length(raw_parsetree_list) != 1) goto fail; pstate = make_parsestate(NULL); pstate->p_sourcetext = src; sql_fn_parser_setup(pstate, pinfo); querytree = transformTopLevelStmt(pstate, linitial(raw_parsetree_list)); free_parsestate(pstate); /* * The single command must be a simple "SELECT expression". */ if (!IsA(querytree, Query) || querytree->commandType != CMD_SELECT || querytree->utilityStmt || querytree->hasAggs || querytree->hasWindowFuncs || querytree->hasSubLinks || querytree->cteList || querytree->rtable || querytree->jointree->fromlist || querytree->jointree->quals || querytree->groupClause || querytree->havingQual || querytree->windowClause || querytree->distinctClause || querytree->sortClause || querytree->limitOffset || querytree->limitCount || querytree->setOperations || list_length(querytree->targetList) != 1) goto fail; /* * Make sure the function (still) returns what it's declared to. This * will raise an error if wrong, but that's okay since the function would * fail at runtime anyway. Note that check_sql_fn_retval will also insert * a RelabelType if needed to make the tlist expression match the declared * type of the function. * * Note: we do not try this until we have verified that no rewriting was * needed; that's probably not important, but let's be careful. */ if (check_sql_fn_retval(funcid, result_type, list_make1(querytree), &modifyTargetList, NULL)) goto fail; /* reject whole-tuple-result cases */ /* Now we can grab the tlist expression */ newexpr = (Node *) ((TargetEntry *) linitial(querytree->targetList))->expr; /* Assert that check_sql_fn_retval did the right thing */ Assert(exprType(newexpr) == result_type); /* It couldn't have made any dangerous tlist changes, either */ Assert(!modifyTargetList); /* * Additional validity checks on the expression. It mustn't return a set, * and it mustn't be more volatile than the surrounding function (this is * to avoid breaking hacks that involve pretending a function is immutable * when it really ain't). If the surrounding function is declared strict, * then the expression must contain only strict constructs and must use * all of the function parameters (this is overkill, but an exact analysis * is hard). */ if (expression_returns_set(newexpr)) goto fail; if (funcform->provolatile == PROVOLATILE_IMMUTABLE && contain_mutable_functions(newexpr)) goto fail; else if (funcform->provolatile == PROVOLATILE_STABLE && contain_volatile_functions(newexpr)) goto fail; if (funcform->proisstrict && contain_nonstrict_functions(newexpr)) goto fail; /* * We may be able to do it; there are still checks on parameter usage to * make, but those are most easily done in combination with the actual * substitution of the inputs. So start building expression with inputs * substituted. */ usecounts = (int *) palloc0(funcform->pronargs * sizeof(int)); newexpr = substitute_actual_parameters(newexpr, funcform->pronargs, args, usecounts); /* Now check for parameter usage */ i = 0; foreach(arg, args) { Node *param = lfirst(arg); if (usecounts[i] == 0) { /* Param not used at all: uncool if func is strict */ if (funcform->proisstrict) goto fail; } else if (usecounts[i] != 1) { /* Param used multiple times: uncool if expensive or volatile */ QualCost eval_cost; /* * We define "expensive" as "contains any subplan or more than 10 * operators". Note that the subplan search has to be done * explicitly, since cost_qual_eval() will barf on unplanned * subselects. */ if (contain_subplans(param)) goto fail; cost_qual_eval(&eval_cost, list_make1(param), NULL); if (eval_cost.startup + eval_cost.per_tuple > 10 * cpu_operator_cost) goto fail; /* * Check volatility last since this is more expensive than the * above tests */ if (contain_volatile_functions(param)) goto fail; } i++; } /* * Whew --- we can make the substitution. Copy the modified expression * out of the temporary memory context, and clean up. */ MemoryContextSwitchTo(oldcxt); newexpr = copyObject(newexpr); MemoryContextDelete(mycxt); /* * If the result is of a collatable type, force the result to expose the * correct collation. In most cases this does not matter, but it's * possible that the function result is used directly as a sort key or in * other places where we expect exprCollation() to tell the truth. */ if (OidIsValid(result_collid)) { Oid exprcoll = exprCollation(newexpr); if (OidIsValid(exprcoll) && exprcoll != result_collid) { CollateExpr *newnode = makeNode(CollateExpr); newnode->arg = (Expr *) newexpr; newnode->collOid = result_collid; newnode->location = -1; newexpr = (Node *) newnode; } } /* * Since there is now no trace of the function in the plan tree, we must * explicitly record the plan's dependency on the function. */ if (context->root) record_plan_function_dependency(context->root, funcid); /* * Recursively try to simplify the modified expression. Here we must add * the current function to the context list of active functions. */ context->active_fns = lcons_oid(funcid, context->active_fns); newexpr = eval_const_expressions_mutator(newexpr, context); context->active_fns = list_delete_first(context->active_fns); error_context_stack = sqlerrcontext.previous; return (Expr *) newexpr; /* Here if func is not inlinable: release temp memory and return NULL */ fail: MemoryContextSwitchTo(oldcxt); MemoryContextDelete(mycxt); error_context_stack = sqlerrcontext.previous; return NULL; }
Query* inline_set_returning_function | ( | PlannerInfo * | root, | |
RangeTblEntry * | rte | |||
) |
Definition at line 4470 of file clauses.c.
References ACL_EXECUTE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE, ALLOCSET_DEFAULT_MINSIZE, AllocSetContextCreate(), Anum_pg_proc_proconfig, Anum_pg_proc_prosrc, ErrorContextCallback::arg, FuncExpr::args, Assert, ErrorContextCallback::callback, check_sql_fn_retval(), check_stack_depth(), CMD_SELECT, Query::commandType, contain_subplans(), contain_volatile_functions(), copyObject(), CurrentMemoryContext, elog, ERROR, error_context_stack, eval_const_expressions(), FmgrHookIsNeeded, RangeTblEntry::funccoltypes, RangeTblEntry::funcexpr, FuncExpr::funcid, FuncExpr::funcretset, get_func_result_type(), get_typtype(), GETSTRUCT, GetUserId(), PlannerInfo::glob, heap_attisnull(), HeapTupleIsValid, PlannerGlobal::invalItems, IsA, linitial, list_concat(), list_length(), MemoryContextDelete(), MemoryContextSwitchTo(), NameStr, NULL, ObjectIdGetDatum, pg_analyze_and_rewrite_params(), pg_parse_query(), pg_proc_aclcheck(), prepare_sql_fn_parse_info(), ErrorContextCallback::previous, PROCOID, inline_error_callback_arg::proname, inline_error_callback_arg::prosrc, PROVOLATILE_VOLATILE, record_plan_function_dependency(), RECORDOID, ReleaseSysCache(), RTE_FUNCTION, RangeTblEntry::rtekind, SearchSysCache1, sql_fn_parser_setup(), SQLlanguageId, substitute_actual_srf_parameters(), SysCacheGetAttr(), Query::targetList, TextDatumGetCString, tlist_matches_coltypelist(), TYPEFUNC_RECORD, TYPTYPE_COMPOSITE, and Query::utilityStmt.
Referenced by inline_set_returning_functions().
{ FuncExpr *fexpr; Oid func_oid; HeapTuple func_tuple; Form_pg_proc funcform; char *src; Datum tmp; bool isNull; bool modifyTargetList; MemoryContext oldcxt; MemoryContext mycxt; List *saveInvalItems; inline_error_callback_arg callback_arg; ErrorContextCallback sqlerrcontext; SQLFunctionParseInfoPtr pinfo; List *raw_parsetree_list; List *querytree_list; Query *querytree; Assert(rte->rtekind == RTE_FUNCTION); /* * It doesn't make a lot of sense for a SQL SRF to refer to itself in its * own FROM clause, since that must cause infinite recursion at runtime. * It will cause this code to recurse too, so check for stack overflow. * (There's no need to do more.) */ check_stack_depth(); /* Fail if FROM item isn't a simple FuncExpr */ fexpr = (FuncExpr *) rte->funcexpr; if (fexpr == NULL || !IsA(fexpr, FuncExpr)) return NULL; func_oid = fexpr->funcid; /* * The function must be declared to return a set, else inlining would * change the results if the contained SELECT didn't return exactly one * row. */ if (!fexpr->funcretset) return NULL; /* * Refuse to inline if the arguments contain any volatile functions or * sub-selects. Volatile functions are rejected because inlining may * result in the arguments being evaluated multiple times, risking a * change in behavior. Sub-selects are rejected partly for implementation * reasons (pushing them down another level might change their behavior) * and partly because they're likely to be expensive and so multiple * evaluation would be bad. */ if (contain_volatile_functions((Node *) fexpr->args) || contain_subplans((Node *) fexpr->args)) return NULL; /* Check permission to call function (fail later, if not) */ if (pg_proc_aclcheck(func_oid, GetUserId(), ACL_EXECUTE) != ACLCHECK_OK) return NULL; /* Check whether a plugin wants to hook function entry/exit */ if (FmgrHookIsNeeded(func_oid)) return NULL; /* * OK, let's take a look at the function's pg_proc entry. */ func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_oid)); if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", func_oid); funcform = (Form_pg_proc) GETSTRUCT(func_tuple); /* * Forget it if the function is not SQL-language or has other showstopper * properties. In particular it mustn't be declared STRICT, since we * couldn't enforce that. It also mustn't be VOLATILE, because that is * supposed to cause it to be executed with its own snapshot, rather than * sharing the snapshot of the calling query. (Rechecking proretset is * just paranoia.) */ if (funcform->prolang != SQLlanguageId || funcform->proisstrict || funcform->provolatile == PROVOLATILE_VOLATILE || funcform->prosecdef || !funcform->proretset || !heap_attisnull(func_tuple, Anum_pg_proc_proconfig)) { ReleaseSysCache(func_tuple); return NULL; } /* * Make a temporary memory context, so that we don't leak all the stuff * that parsing might create. */ mycxt = AllocSetContextCreate(CurrentMemoryContext, "inline_set_returning_function", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcxt = MemoryContextSwitchTo(mycxt); /* * When we call eval_const_expressions below, it might try to add items to * root->glob->invalItems. Since it is running in the temp context, those * items will be in that context, and will need to be copied out if we're * successful. Temporarily reset the list so that we can keep those items * separate from the pre-existing list contents. */ saveInvalItems = root->glob->invalItems; root->glob->invalItems = NIL; /* Fetch the function body */ tmp = SysCacheGetAttr(PROCOID, func_tuple, Anum_pg_proc_prosrc, &isNull); if (isNull) elog(ERROR, "null prosrc for function %u", func_oid); src = TextDatumGetCString(tmp); /* * Setup error traceback support for ereport(). This is so that we can * finger the function that bad information came from. */ callback_arg.proname = NameStr(funcform->proname); callback_arg.prosrc = src; sqlerrcontext.callback = sql_inline_error_callback; sqlerrcontext.arg = (void *) &callback_arg; sqlerrcontext.previous = error_context_stack; error_context_stack = &sqlerrcontext; /* * Run eval_const_expressions on the function call. This is necessary to * ensure that named-argument notation is converted to positional notation * and any default arguments are inserted. It's a bit of overkill for the * arguments, since they'll get processed again later, but no harm will be * done. */ fexpr = (FuncExpr *) eval_const_expressions(root, (Node *) fexpr); /* It should still be a call of the same function, but let's check */ if (!IsA(fexpr, FuncExpr) || fexpr->funcid != func_oid) goto fail; /* Arg list length should now match the function */ if (list_length(fexpr->args) != funcform->pronargs) goto fail; /* * Set up to handle parameters while parsing the function body. We can * use the FuncExpr just created as the input for * prepare_sql_fn_parse_info. */ pinfo = prepare_sql_fn_parse_info(func_tuple, (Node *) fexpr, fexpr->inputcollid); /* * Parse, analyze, and rewrite (unlike inline_function(), we can't skip * rewriting here). We can fail as soon as we find more than one query, * though. */ raw_parsetree_list = pg_parse_query(src); if (list_length(raw_parsetree_list) != 1) goto fail; querytree_list = pg_analyze_and_rewrite_params(linitial(raw_parsetree_list), src, (ParserSetupHook) sql_fn_parser_setup, pinfo); if (list_length(querytree_list) != 1) goto fail; querytree = linitial(querytree_list); /* * The single command must be a plain SELECT. */ if (!IsA(querytree, Query) || querytree->commandType != CMD_SELECT || querytree->utilityStmt) goto fail; /* * Make sure the function (still) returns what it's declared to. This * will raise an error if wrong, but that's okay since the function would * fail at runtime anyway. Note that check_sql_fn_retval will also insert * RelabelType(s) and/or NULL columns if needed to make the tlist * expression(s) match the declared type of the function. * * If the function returns a composite type, don't inline unless the check * shows it's returning a whole tuple result; otherwise what it's * returning is a single composite column which is not what we need. */ if (!check_sql_fn_retval(func_oid, fexpr->funcresulttype, querytree_list, &modifyTargetList, NULL) && (get_typtype(fexpr->funcresulttype) == TYPTYPE_COMPOSITE || fexpr->funcresulttype == RECORDOID)) goto fail; /* reject not-whole-tuple-result cases */ /* * If we had to modify the tlist to make it match, and the statement is * one in which changing the tlist contents could change semantics, we * have to punt and not inline. */ if (modifyTargetList) goto fail; /* * If it returns RECORD, we have to check against the column type list * provided in the RTE; check_sql_fn_retval can't do that. (If no match, * we just fail to inline, rather than complaining; see notes for * tlist_matches_coltypelist.) We don't have to do this for functions * with declared OUT parameters, even though their funcresulttype is * RECORDOID, so check get_func_result_type too. */ if (fexpr->funcresulttype == RECORDOID && get_func_result_type(func_oid, NULL, NULL) == TYPEFUNC_RECORD && !tlist_matches_coltypelist(querytree->targetList, rte->funccoltypes)) goto fail; /* * Looks good --- substitute parameters into the query. */ querytree = substitute_actual_srf_parameters(querytree, funcform->pronargs, fexpr->args); /* * Copy the modified query out of the temporary memory context, and clean * up. */ MemoryContextSwitchTo(oldcxt); querytree = copyObject(querytree); /* copy up any new invalItems, too */ root->glob->invalItems = list_concat(saveInvalItems, copyObject(root->glob->invalItems)); MemoryContextDelete(mycxt); error_context_stack = sqlerrcontext.previous; ReleaseSysCache(func_tuple); /* * We don't have to fix collations here because the upper query is already * parsed, ie, the collations in the RTE are what count. */ /* * Since there is now no trace of the function in the plan tree, we must * explicitly record the plan's dependency on the function. */ record_plan_function_dependency(root, func_oid); return querytree; /* Here if func is not inlinable: release temp memory and return NULL */ fail: MemoryContextSwitchTo(oldcxt); root->glob->invalItems = saveInvalItems; MemoryContextDelete(mycxt); error_context_stack = sqlerrcontext.previous; ReleaseSysCache(func_tuple); return NULL; }
Definition at line 1912 of file clauses.c.
References contain_var_clause(), and contain_volatile_functions().
Referenced by clauselist_selectivity(), IsTidEqualAnyClause(), and IsTidEqualClause().
{ /* * 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; }
Definition at line 1932 of file clauses.c.
References bms_is_empty(), and contain_volatile_functions().
Referenced by clauselist_selectivity().
{ if (bms_is_empty(relids) && !contain_volatile_functions(clause)) return true; return false; }
static bool is_strict_saop | ( | ScalarArrayOpExpr * | expr, | |
bool | falseOK | |||
) | [static] |
Definition at line 1851 of file clauses.c.
References ScalarArrayOpExpr::args, ARR_DIMS, ARR_NDIM, ArrayGetNItems(), Assert, DatumGetArrayTypeP, ArrayExpr::elements, func_strict(), IsA, list_length(), lsecond, ArrayExpr::multidims, NIL, ScalarArrayOpExpr::opfuncid, set_sa_opfuncid(), and ScalarArrayOpExpr::useOr.
Referenced by contain_nonstrict_functions_walker(), find_nonnullable_rels_walker(), and find_nonnullable_vars_walker().
{ 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; }
Definition at line 333 of file clauses.c.
References list_make2, make_andclause(), and NULL.
Referenced by AddQual(), and subquery_push_qual().
{ if (qual1 == NULL) return qual2; if (qual2 == NULL) return qual1; return (Node *) make_andclause(list_make2(qual1, qual2)); }
Definition at line 312 of file clauses.c.
References BoolExpr::args, BoolExpr::boolop, BoolExpr::location, and makeNode.
Referenced by eval_const_expressions_mutator(), find_duplicate_ors(), make_and_qual(), make_ands_explicit(), make_restrictinfo_from_bitmapqual(), make_sub_restrictinfos(), negate_clause(), process_duplicate_ors(), process_sublinks_mutator(), and pull_up_sublinks_qual_recurse().
Definition at line 352 of file clauses.c.
References linitial, list_length(), make_andclause(), makeBoolConst(), and NIL.
Referenced by convert_EXISTS_to_ANY(), create_bitmap_subplan(), set_append_rel_size(), show_qual(), and UpdateIndexRelation().
{ if (andclauses == NIL) return (Expr *) makeBoolConst(true, false); else if (list_length(andclauses) == 1) return (Expr *) linitial(andclauses); else return make_andclause(andclauses); }
Definition at line 363 of file clauses.c.
References and_clause(), DatumGetBool, IsA, list_make1, and NULL.
Referenced by ATAddCheckConstraint(), convert_EXISTS_to_ANY(), cost_subplan(), DefineIndex(), ExecRelCheck(), get_relation_constraints(), preprocess_expression(), RelationGetIndexPredicate(), set_append_rel_size(), TriggerEnabled(), and validateCheckConstraint().
{ /* * 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); }
Definition at line 234 of file clauses.c.
References BoolExpr::args, BoolExpr::boolop, list_make1, BoolExpr::location, and makeNode.
Referenced by negate_clause().
Expr* make_opclause | ( | Oid | opno, | |
Oid | opresulttype, | |||
bool | opretset, | |||
Expr * | leftop, | |||
Expr * | rightop, | |||
Oid | opcollid, | |||
Oid | inputcollid | |||
) |
Definition at line 157 of file clauses.c.
References OpExpr::args, OpExpr::inputcollid, list_make1, list_make2, OpExpr::location, makeNode, OpExpr::opcollid, OpExpr::opfuncid, OpExpr::opno, OpExpr::opresulttype, and OpExpr::opretset.
Referenced by adjust_rowcompare_for_index(), btree_predicate_proof(), build_implied_join_equality(), convert_EXISTS_to_ANY(), expand_boolean_index_clause(), network_prefix_quals(), prefix_quals(), and process_implied_equality().
{ OpExpr *expr = makeNode(OpExpr); expr->opno = opno; expr->opfuncid = InvalidOid; expr->opresulttype = opresulttype; expr->opretset = opretset; expr->opcollid = opcollid; expr->inputcollid = inputcollid; if (rightop) expr->args = list_make2(leftop, rightop); else expr->args = list_make1(leftop); expr->location = -1; return (Expr *) expr; }
Definition at line 278 of file clauses.c.
References BoolExpr::args, BoolExpr::boolop, BoolExpr::location, and makeNode.
Referenced by create_bitmap_subplan(), create_tidscan_plan(), eval_const_expressions_mutator(), ExplainNode(), make_restrictinfo_from_bitmapqual(), make_sub_restrictinfos(), negate_clause(), process_duplicate_ors(), and process_sublinks_mutator().
Definition at line 221 of file clauses.c.
References boolop(), IsA, NOT_EXPR, and NULL.
Referenced by clause_selectivity(), expand_boolean_index_clause(), match_boolean_index_clause(), and pull_up_sublinks_qual_recurse().
int NumRelids | ( | Node * | clause | ) |
Definition at line 1954 of file clauses.c.
References bms_free(), bms_num_members(), and pull_varnos().
Referenced by clauselist_selectivity(), rowcomparesel(), and treat_as_join_clause().
{ Relids varnos = pull_varnos(clause); int result = bms_num_members(varnos); bms_free(varnos); return result; }
Definition at line 265 of file clauses.c.
References boolop(), IsA, NULL, and OR_EXPR.
Referenced by clause_selectivity(), find_duplicate_ors(), make_restrictinfo(), make_restrictinfo_from_bitmapqual(), make_sub_restrictinfos(), predicate_classify(), process_sublinks_mutator(), pull_ors(), simplify_or_arguments(), and TidQualFromExpr().
static void recheck_cast_function_args | ( | List * | args, | |
Oid | result_type, | |||
HeapTuple | func_tuple | |||
) | [static] |
Definition at line 3845 of file clauses.c.
References Assert, elog, enforce_generic_type_consistency(), ERROR, exprType(), GETSTRUCT, lfirst, list_length(), make_fn_arguments(), and NULL.
Referenced by expand_function_arguments().
{ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int nargs; Oid actual_arg_types[FUNC_MAX_ARGS]; Oid declared_arg_types[FUNC_MAX_ARGS]; Oid rettype; ListCell *lc; if (list_length(args) > FUNC_MAX_ARGS) elog(ERROR, "too many function arguments"); nargs = 0; foreach(lc, args) { actual_arg_types[nargs++] = exprType((Node *) lfirst(lc)); } Assert(nargs == funcform->pronargs); memcpy(declared_arg_types, funcform->proargtypes.values, funcform->pronargs * sizeof(Oid)); rettype = enforce_generic_type_consistency(actual_arg_types, declared_arg_types, nargs, funcform->prorettype, false); /* let's just check we got the same answer as the parser did ... */ if (rettype != result_type) elog(ERROR, "function's resolved result type changed during planning"); /* perform any necessary typecasting of arguments */ make_fn_arguments(NULL, args, actual_arg_types, declared_arg_types); }
Definition at line 3714 of file clauses.c.
References NamedArgExpr::arg, arg, NamedArgExpr::argnumber, Assert, elog, ERROR, fetch_function_defaults(), FUNC_MAX_ARGS, GETSTRUCT, i, IsA, lappend(), lfirst, list_length(), MemSet, and NULL.
Referenced by expand_function_arguments().
{ Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); int pronargs = funcform->pronargs; int nargsprovided = list_length(args); Node *argarray[FUNC_MAX_ARGS]; ListCell *lc; int i; Assert(nargsprovided <= pronargs); if (pronargs > FUNC_MAX_ARGS) elog(ERROR, "too many function arguments"); MemSet(argarray, 0, pronargs * sizeof(Node *)); /* Deconstruct the argument list into an array indexed by argnumber */ i = 0; foreach(lc, args) { Node *arg = (Node *) lfirst(lc); if (!IsA(arg, NamedArgExpr)) { /* positional argument, assumed to precede all named args */ Assert(argarray[i] == NULL); argarray[i++] = arg; } else { NamedArgExpr *na = (NamedArgExpr *) arg; Assert(argarray[na->argnumber] == NULL); argarray[na->argnumber] = (Node *) na->arg; } } /* * Fetch default expressions, if needed, and insert into array at proper * locations (they aren't necessarily consecutive or all used) */ if (nargsprovided < pronargs) { List *defaults = fetch_function_defaults(func_tuple); i = pronargs - funcform->pronargdefaults; foreach(lc, defaults) { if (argarray[i] == NULL) argarray[i] = (Node *) lfirst(lc); i++; } } /* Now reconstruct the args list in proper order */ args = NIL; for (i = 0; i < pronargs; i++) { Assert(argarray[i] != NULL); args = lappend(args, argarray[i]); } return args; }
static bool rowtype_field_matches | ( | Oid | rowtypeid, | |
int | fieldnum, | |||
Oid | expectedtype, | |||
int32 | expectedtypmod, | |||
Oid | expectedcollation | |||
) | [static] |
Definition at line 2125 of file clauses.c.
References tupleDesc::attrs, lookup_rowtype_tupdesc(), tupleDesc::natts, RECORDOID, and ReleaseTupleDesc.
Referenced by eval_const_expressions_mutator().
{ TupleDesc tupdesc; Form_pg_attribute attr; /* No issue for RECORD, since there is no way to ALTER such a type */ if (rowtypeid == RECORDOID) return true; tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1); if (fieldnum <= 0 || fieldnum > tupdesc->natts) { ReleaseTupleDesc(tupdesc); return false; } attr = tupdesc->attrs[fieldnum - 1]; if (attr->attisdropped || attr->atttypid != expectedtype || attr->atttypmod != expectedtypmod || attr->attcollation != expectedcollation) { ReleaseTupleDesc(tupdesc); return false; } ReleaseTupleDesc(tupdesc); return true; }
static List * simplify_and_arguments | ( | List * | args, | |
eval_const_expressions_context * | context, | |||
bool * | haveNull, | |||
bool * | forceFalse | |||
) | [static] |
Definition at line 3405 of file clauses.c.
References and_clause(), arg, Const::constisnull, Const::constvalue, DatumGetBool, eval_const_expressions_mutator(), IsA, lappend(), linitial, list_concat(), list_copy(), list_delete_first(), and pfree().
Referenced by eval_const_expressions_mutator().
{ 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; }
Definition at line 3505 of file clauses.c.
References Assert, BooleanEqualOperator, DatumGetBool, IsA, linitial, list_length(), lsecond, and negate_clause().
Referenced by eval_const_expressions_mutator().
{ Node *leftop; Node *rightop; Assert(list_length(args) == 2); leftop = linitial(args); rightop = lsecond(args); if (leftop && IsA(leftop, Const)) { Assert(!((Const *) leftop)->constisnull); if (opno == BooleanEqualOperator) { if (DatumGetBool(((Const *) leftop)->constvalue)) return rightop; /* true = foo */ else return negate_clause(rightop); /* false = foo */ } else { if (DatumGetBool(((Const *) leftop)->constvalue)) return negate_clause(rightop); /* true <> foo */ else return rightop; /* false <> foo */ } } if (rightop && IsA(rightop, Const)) { Assert(!((Const *) rightop)->constisnull); if (opno == BooleanEqualOperator) { if (DatumGetBool(((Const *) rightop)->constvalue)) return leftop; /* foo = true */ else return negate_clause(leftop); /* foo = false */ } else { if (DatumGetBool(((Const *) rightop)->constvalue)) return negate_clause(leftop); /* foo <> true */ else return leftop; /* foo <> false */ } } return NULL; }
static Expr * simplify_function | ( | Oid | funcid, | |
Oid | result_type, | |||
int32 | result_typmod, | |||
Oid | result_collid, | |||
Oid | input_collid, | |||
List ** | args_p, | |||
bool | funcvariadic, | |||
bool | process_args, | |||
bool | allow_non_const, | |||
eval_const_expressions_context * | context | |||
) | [static] |
Definition at line 3574 of file clauses.c.
References FuncExpr::args, DatumGetPointer, elog, ERROR, evaluate_function(), expand_function_arguments(), expression_tree_mutator(), FuncExpr::funccollid, FuncExpr::funcformat, FuncExpr::funcid, FuncExpr::funcresulttype, FuncExpr::funcretset, FuncExpr::funcvariadic, GETSTRUCT, HeapTupleIsValid, inline_function(), FuncExpr::inputcollid, FuncExpr::location, ObjectIdGetDatum, OidFunctionCall1, OidIsValid, PointerGetDatum, PROCOID, ReleaseSysCache(), SearchSysCache1, Expr::type, and FuncExpr::xpr.
Referenced by eval_const_expressions_mutator().
{ List *args = *args_p; HeapTuple func_tuple; Form_pg_proc func_form; Expr *newexpr; /* * We have three strategies for simplification: execute the function to * deliver a constant result, use a transform function to generate a * substitute node tree, or expand in-line the body of the function * definition (which only works for simple SQL-language functions, but * that is a common case). Each case needs access to the function's * pg_proc tuple, so fetch it just once. * * Note: the allow_non_const flag suppresses both the second and third * strategies; so if !allow_non_const, simplify_function can only return a * Const or NULL. Argument-list rewriting happens anyway, though. */ func_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(func_tuple)) elog(ERROR, "cache lookup failed for function %u", funcid); func_form = (Form_pg_proc) GETSTRUCT(func_tuple); /* * Process the function arguments, unless the caller did it already. * * Here we must deal with named or defaulted arguments, and then * recursively apply eval_const_expressions to the whole argument list. */ if (process_args) { args = expand_function_arguments(args, result_type, func_tuple); args = (List *) expression_tree_mutator((Node *) args, eval_const_expressions_mutator, (void *) context); /* Argument processing done, give it back to the caller */ *args_p = args; } /* Now attempt simplification of the function call proper. */ newexpr = evaluate_function(funcid, result_type, result_typmod, result_collid, input_collid, args, funcvariadic, func_tuple, context); if (!newexpr && allow_non_const && OidIsValid(func_form->protransform)) { /* * Build a dummy FuncExpr node containing the simplified arg list. We * use this approach to present a uniform interface to the transform * function regardless of how the function is actually being invoked. */ FuncExpr fexpr; fexpr.xpr.type = T_FuncExpr; fexpr.funcid = funcid; fexpr.funcresulttype = result_type; fexpr.funcretset = func_form->proretset; fexpr.funcvariadic = funcvariadic; fexpr.funcformat = COERCE_EXPLICIT_CALL; fexpr.funccollid = result_collid; fexpr.inputcollid = input_collid; fexpr.args = args; fexpr.location = -1; newexpr = (Expr *) DatumGetPointer(OidFunctionCall1(func_form->protransform, PointerGetDatum(&fexpr))); } if (!newexpr && allow_non_const) newexpr = inline_function(funcid, result_type, result_collid, input_collid, args, funcvariadic, func_tuple, context); ReleaseSysCache(func_tuple); return newexpr; }
static List * simplify_or_arguments | ( | List * | args, | |
eval_const_expressions_context * | context, | |||
bool * | haveNull, | |||
bool * | forceTrue | |||
) | [static] |
Definition at line 3296 of file clauses.c.
References arg, Const::constisnull, Const::constvalue, DatumGetBool, eval_const_expressions_mutator(), IsA, lappend(), linitial, list_concat(), list_copy(), list_delete_first(), or_clause(), and pfree().
Referenced by eval_const_expressions_mutator().
{ 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; }
static void sql_inline_error_callback | ( | void * | arg | ) | [static] |
Definition at line 4359 of file clauses.c.
References errcontext, errposition(), geterrposition(), internalerrposition(), internalerrquery(), inline_error_callback_arg::proname, and inline_error_callback_arg::prosrc.
{ inline_error_callback_arg *callback_arg = (inline_error_callback_arg *) arg; int syntaxerrposition; /* If it's a syntax error, convert to internal syntax error report */ syntaxerrposition = geterrposition(); if (syntaxerrposition > 0) { errposition(0); internalerrposition(syntaxerrposition); internalerrquery(callback_arg->prosrc); } errcontext("SQL function \"%s\" during inlining", callback_arg->proname); }
Definition at line 2069 of file clauses.c.
References CoerceToDomain::arg, ConvertRowtypeExpr::arg, ArrayCoerceExpr::arg, CoerceViaIO::arg, RelabelType::arg, FuncExpr::args, COERCE_IMPLICIT_CAST, ArrayCoerceExpr::coerceformat, CoerceViaIO::coerceformat, CoerceToDomain::coercionformat, ConvertRowtypeExpr::convertformat, FuncExpr::funcformat, IsA, linitial, NULL, RelabelType::relabelformat, and strip_implicit_coercions().
Referenced by ATExecAlterColumnType(), findTargetlistEntrySQL99(), flatten_join_using_qual(), get_rule_expr(), and strip_implicit_coercions().
{ if (node == NULL) return NULL; if (IsA(node, FuncExpr)) { FuncExpr *f = (FuncExpr *) node; if (f->funcformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions(linitial(f->args)); } else if (IsA(node, RelabelType)) { RelabelType *r = (RelabelType *) node; if (r->relabelformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) r->arg); } else if (IsA(node, CoerceViaIO)) { CoerceViaIO *c = (CoerceViaIO *) node; if (c->coerceformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } else if (IsA(node, ArrayCoerceExpr)) { ArrayCoerceExpr *c = (ArrayCoerceExpr *) node; if (c->coerceformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } else if (IsA(node, ConvertRowtypeExpr)) { ConvertRowtypeExpr *c = (ConvertRowtypeExpr *) node; if (c->convertformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } else if (IsA(node, CoerceToDomain)) { CoerceToDomain *c = (CoerceToDomain *) node; if (c->coercionformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } return node; }
static Node * substitute_actual_parameters | ( | Node * | expr, | |
int | nargs, | |||
List * | args, | |||
int * | usecounts | |||
) | [static] |
Definition at line 4317 of file clauses.c.
References substitute_actual_parameters_context::args, substitute_actual_parameters_context::nargs, substitute_actual_parameters_mutator(), and substitute_actual_parameters_context::usecounts.
Referenced by inline_function().
{ substitute_actual_parameters_context context; context.nargs = nargs; context.args = args; context.usecounts = usecounts; return substitute_actual_parameters_mutator(expr, &context); }
static Node * substitute_actual_parameters_mutator | ( | Node * | node, | |
substitute_actual_parameters_context * | context | |||
) | [static] |
Definition at line 4330 of file clauses.c.
References substitute_actual_parameters_context::args, elog, ERROR, expression_tree_mutator(), IsA, list_nth(), substitute_actual_parameters_context::nargs, NULL, PARAM_EXTERN, Param::paramid, Param::paramkind, and substitute_actual_parameters_context::usecounts.
Referenced by substitute_actual_parameters().
{ if (node == NULL) return NULL; if (IsA(node, Param)) { Param *param = (Param *) node; if (param->paramkind != PARAM_EXTERN) elog(ERROR, "unexpected paramkind: %d", (int) param->paramkind); if (param->paramid <= 0 || param->paramid > context->nargs) elog(ERROR, "invalid paramid: %d", param->paramid); /* Count usage of parameter */ context->usecounts[param->paramid - 1]++; /* Select the appropriate actual arg and replace the Param with it */ /* We don't need to copy at this time (it'll get done later) */ return list_nth(context->args, param->paramid - 1); } return expression_tree_mutator(node, substitute_actual_parameters_mutator, (void *) context); }
Definition at line 4749 of file clauses.c.
References substitute_actual_srf_parameters_context::args, substitute_actual_srf_parameters_context::nargs, query_tree_mutator(), substitute_actual_srf_parameters_context::sublevels_up, and substitute_actual_srf_parameters_mutator().
Referenced by inline_set_returning_function().
{ substitute_actual_srf_parameters_context context; context.nargs = nargs; context.args = args; context.sublevels_up = 1; return query_tree_mutator(expr, substitute_actual_srf_parameters_mutator, &context, 0); }
static Node * substitute_actual_srf_parameters_mutator | ( | Node * | node, | |
substitute_actual_srf_parameters_context * | context | |||
) | [static] |
Definition at line 4764 of file clauses.c.
References substitute_actual_srf_parameters_context::args, copyObject(), elog, ERROR, expression_tree_mutator(), IncrementVarSublevelsUp(), IsA, list_nth(), substitute_actual_srf_parameters_context::nargs, NULL, PARAM_EXTERN, Param::paramid, Param::paramkind, query_tree_mutator(), and substitute_actual_srf_parameters_context::sublevels_up.
Referenced by substitute_actual_srf_parameters().
{ Node *result; if (node == NULL) return NULL; if (IsA(node, Query)) { context->sublevels_up++; result = (Node *) query_tree_mutator((Query *) node, substitute_actual_srf_parameters_mutator, (void *) context, 0); context->sublevels_up--; return result; } if (IsA(node, Param)) { Param *param = (Param *) node; if (param->paramkind == PARAM_EXTERN) { if (param->paramid <= 0 || param->paramid > context->nargs) elog(ERROR, "invalid paramid: %d", param->paramid); /* * Since the parameter is being inserted into a subquery, we must * adjust levels. */ result = copyObject(list_nth(context->args, param->paramid - 1)); IncrementVarSublevelsUp(result, context->sublevels_up, 0); return result; } } return expression_tree_mutator(node, substitute_actual_srf_parameters_mutator, (void *) context); }
Definition at line 4817 of file clauses.c.
References TargetEntry::expr, exprType(), lfirst, lfirst_oid, list_head(), lnext, NULL, and TargetEntry::resjunk.
Referenced by inline_set_returning_function().
{ ListCell *tlistitem; ListCell *clistitem; clistitem = list_head(coltypelist); foreach(tlistitem, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(tlistitem); Oid coltype; if (tle->resjunk) continue; /* ignore junk columns */ if (clistitem == NULL) return false; /* too many tlist items */ coltype = lfirst_oid(clistitem); clistitem = lnext(clistitem); if (exprType((Node *) tle->expr) != coltype) return false; /* column type mismatch */ } if (clistitem != NULL) return false; /* too few tlist items */ return true; }
double tlist_returns_set_rows | ( | List * | tlist | ) |
Definition at line 760 of file clauses.c.
References TargetEntry::expr, expression_returns_set_rows(), and lfirst.
Referenced by add_tlist_costs_to_plan().
{ double result = 1; ListCell *lc; foreach(lc, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(lc); double colresult; colresult = expression_returns_set_rows((Node *) tle->expr); if (result < colresult) result = colresult; } return result; }