mirror of
https://github.com/postgres/postgres.git
synced 2025-07-03 20:02:46 +03:00
Refactor to reduce code duplication for function property checking.
As noted by Andres Freund, we'd accumulated quite a few similar functions in clauses.c that examine all functions in an expression tree to see if they satisfy some boolean test. Reduce the duplication by inventing a function check_functions_in_node() that applies a simple callback function to each SQL function OID appearing in a given expression node. This also fixes some arguable oversights; for example, contain_mutable_functions() did not check aggregate or window functions for mutability. I doubt that that represents a live bug at the moment, because we don't really consider mutability for aggregates; but it might someday be one. I chose to put check_functions_in_node() in nodeFuncs.c because it seemed like other modules might wish to use it in future. That in turn forced moving set_opfuncid() et al into nodeFuncs.c, as the alternative was for nodeFuncs.c to depend on optimizer/setrefs.c which didn't seem very clean. In passing, teach contain_leaked_vars_walker() about a few more expression node types it can safely look through, and improve the rather messy and undercommented code in has_parallel_hazard_walker(). Discussion: <20160527185853.ziol2os2zskahl7v@alap3.anarazel.de>
This commit is contained in:
@ -27,6 +27,7 @@
|
||||
|
||||
static bool expression_returns_set_walker(Node *node, void *context);
|
||||
static int leftmostLoc(int loc1, int loc2);
|
||||
static bool fix_opfuncids_walker(Node *node, void *context);
|
||||
static bool planstate_walk_subplans(List *plans, bool (*walker) (),
|
||||
void *context);
|
||||
static bool planstate_walk_members(List *plans, PlanState **planstates,
|
||||
@ -1559,6 +1560,183 @@ leftmostLoc(int loc1, int loc2)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* fix_opfuncids
|
||||
* Calculate opfuncid field from opno for each OpExpr node in given tree.
|
||||
* The given tree can be anything expression_tree_walker handles.
|
||||
*
|
||||
* The argument is modified in-place. (This is OK since we'd want the
|
||||
* same change for any node, even if it gets visited more than once due to
|
||||
* shared structure.)
|
||||
*/
|
||||
void
|
||||
fix_opfuncids(Node *node)
|
||||
{
|
||||
/* This tree walk requires no special setup, so away we go... */
|
||||
fix_opfuncids_walker(node, NULL);
|
||||
}
|
||||
|
||||
static bool
|
||||
fix_opfuncids_walker(Node *node, void *context)
|
||||
{
|
||||
if (node == NULL)
|
||||
return false;
|
||||
if (IsA(node, OpExpr))
|
||||
set_opfuncid((OpExpr *) node);
|
||||
else if (IsA(node, DistinctExpr))
|
||||
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
||||
else if (IsA(node, NullIfExpr))
|
||||
set_opfuncid((OpExpr *) node); /* rely on struct equivalence */
|
||||
else if (IsA(node, ScalarArrayOpExpr))
|
||||
set_sa_opfuncid((ScalarArrayOpExpr *) node);
|
||||
return expression_tree_walker(node, fix_opfuncids_walker, context);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_opfuncid
|
||||
* Set the opfuncid (procedure OID) in an OpExpr node,
|
||||
* if it hasn't been set already.
|
||||
*
|
||||
* Because of struct equivalence, this can also be used for
|
||||
* DistinctExpr and NullIfExpr nodes.
|
||||
*/
|
||||
void
|
||||
set_opfuncid(OpExpr *opexpr)
|
||||
{
|
||||
if (opexpr->opfuncid == InvalidOid)
|
||||
opexpr->opfuncid = get_opcode(opexpr->opno);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_sa_opfuncid
|
||||
* As above, for ScalarArrayOpExpr nodes.
|
||||
*/
|
||||
void
|
||||
set_sa_opfuncid(ScalarArrayOpExpr *opexpr)
|
||||
{
|
||||
if (opexpr->opfuncid == InvalidOid)
|
||||
opexpr->opfuncid = get_opcode(opexpr->opno);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* check_functions_in_node -
|
||||
* apply checker() to each function OID contained in given expression node
|
||||
*
|
||||
* Returns TRUE if the checker() function does; for nodes representing more
|
||||
* than one function call, returns TRUE if the checker() function does so
|
||||
* for any of those functions. Returns FALSE if node does not invoke any
|
||||
* SQL-visible function. Caller must not pass node == NULL.
|
||||
*
|
||||
* This function examines only the given node; it does not recurse into any
|
||||
* sub-expressions. Callers typically prefer to keep control of the recursion
|
||||
* for themselves, in case additional checks should be made, or because they
|
||||
* have special rules about which parts of the tree need to be visited.
|
||||
*
|
||||
* Note: we ignore MinMaxExpr, XmlExpr, and CoerceToDomain nodes, because they
|
||||
* do not contain SQL function OIDs. However, they can invoke SQL-visible
|
||||
* functions, so callers should take thought about how to treat them.
|
||||
*/
|
||||
bool
|
||||
check_functions_in_node(Node *node, check_function_callback checker,
|
||||
void *context)
|
||||
{
|
||||
switch (nodeTag(node))
|
||||
{
|
||||
case T_Aggref:
|
||||
{
|
||||
Aggref *expr = (Aggref *) node;
|
||||
|
||||
if (checker(expr->aggfnoid, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_WindowFunc:
|
||||
{
|
||||
WindowFunc *expr = (WindowFunc *) node;
|
||||
|
||||
if (checker(expr->winfnoid, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_FuncExpr:
|
||||
{
|
||||
FuncExpr *expr = (FuncExpr *) node;
|
||||
|
||||
if (checker(expr->funcid, context))
|
||||
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 if it wasn't set already */
|
||||
set_opfuncid(expr);
|
||||
if (checker(expr->opfuncid, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_ScalarArrayOpExpr:
|
||||
{
|
||||
ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
|
||||
|
||||
set_sa_opfuncid(expr);
|
||||
if (checker(expr->opfuncid, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_CoerceViaIO:
|
||||
{
|
||||
CoerceViaIO *expr = (CoerceViaIO *) node;
|
||||
Oid iofunc;
|
||||
Oid typioparam;
|
||||
bool typisvarlena;
|
||||
|
||||
/* check the result type's input function */
|
||||
getTypeInputInfo(expr->resulttype,
|
||||
&iofunc, &typioparam);
|
||||
if (checker(iofunc, context))
|
||||
return true;
|
||||
/* check the input type's output function */
|
||||
getTypeOutputInfo(exprType((Node *) expr->arg),
|
||||
&iofunc, &typisvarlena);
|
||||
if (checker(iofunc, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_ArrayCoerceExpr:
|
||||
{
|
||||
ArrayCoerceExpr *expr = (ArrayCoerceExpr *) node;
|
||||
|
||||
if (OidIsValid(expr->elemfuncid) &&
|
||||
checker(expr->elemfuncid, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_RowCompareExpr:
|
||||
{
|
||||
RowCompareExpr *rcexpr = (RowCompareExpr *) node;
|
||||
ListCell *opid;
|
||||
|
||||
foreach(opid, rcexpr->opnos)
|
||||
{
|
||||
Oid opfuncid = get_opcode(lfirst_oid(opid));
|
||||
|
||||
if (checker(opfuncid, context))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Standard expression-tree walking support
|
||||
*
|
||||
|
Reference in New Issue
Block a user