From 92ee741fc8294186db689a4438009369b9c460b4 Mon Sep 17 00:00:00 2001 From: Marina Polyakova Date: Mon, 15 May 2017 14:24:36 +0300 Subject: [PATCH 1/3] Precalculate stable functions, infrastructure v2 Now in Postgresql only immutable functions are precalculated; stable functions are calculated for every row so in fact they don't differ from volatile functions. This patch includes: - creation of CachedExpr node - usual node functions for it - mutator to replace nonovolatile functions' and operators' expressions by appropriate cached expressions. --- src/backend/nodes/copyfuncs.c | 31 +++++ src/backend/nodes/equalfuncs.c | 31 +++++ src/backend/nodes/nodeFuncs.c | 151 ++++++++++++++++++++ src/backend/nodes/outfuncs.c | 56 ++++++++ src/backend/nodes/readfuncs.c | 48 +++++++ src/backend/optimizer/plan/planner.c | 259 +++++++++++++++++++++++++++++++++++ src/include/nodes/nodeFuncs.h | 1 + src/include/nodes/nodes.h | 1 + src/include/nodes/primnodes.h | 38 +++++ 9 files changed, 616 insertions(+) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 6ad3844..f9f69a1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1527,6 +1527,34 @@ _copyNullIfExpr(const NullIfExpr *from) return newnode; } +static CachedExpr * +_copyCachedExpr(const CachedExpr *from) +{ + CachedExpr *newnode = makeNode(CachedExpr); + + COPY_SCALAR_FIELD(subexprtype); + switch(from->subexprtype) + { + case CACHED_FUNCEXPR: + COPY_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + COPY_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + COPY_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + COPY_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + COPY_NODE_FIELD(subexpr.saopexpr); + break; + } + + return newnode; +} + /* * _copyScalarArrayOpExpr */ @@ -4867,6 +4895,9 @@ copyObjectImpl(const void *from) case T_NullIfExpr: retval = _copyNullIfExpr(from); break; + case T_CachedExpr: + retval = _copyCachedExpr(from); + break; case T_ScalarArrayOpExpr: retval = _copyScalarArrayOpExpr(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index c9a8c34..8863759 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -384,6 +384,34 @@ _equalNullIfExpr(const NullIfExpr *a, const NullIfExpr *b) } static bool +_equalCachedExpr(const CachedExpr *a, const CachedExpr *b) +{ + COMPARE_SCALAR_FIELD(subexprtype); + + /* the same subexprtype for b because we have already compared it */ + switch(a->subexprtype) + { + case CACHED_FUNCEXPR: + COMPARE_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + COMPARE_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + COMPARE_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + COMPARE_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + COMPARE_NODE_FIELD(subexpr.saopexpr); + break; + } + + return true; +} + +static bool _equalScalarArrayOpExpr(const ScalarArrayOpExpr *a, const ScalarArrayOpExpr *b) { COMPARE_SCALAR_FIELD(opno); @@ -3031,6 +3059,9 @@ equal(const void *a, const void *b) case T_NullIfExpr: retval = _equalNullIfExpr(a, b); break; + case T_CachedExpr: + retval = _equalCachedExpr(a, b); + break; case T_ScalarArrayOpExpr: retval = _equalScalarArrayOpExpr(a, b); break; diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 3e8189c..e3dd576 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -32,6 +32,7 @@ static bool planstate_walk_subplans(List *plans, bool (*walker) (), void *context); static bool planstate_walk_members(List *plans, PlanState **planstates, bool (*walker) (), void *context); +static const Node *get_const_subexpr(const CachedExpr *cachedexpr); /* @@ -92,6 +93,9 @@ exprType(const Node *expr) case T_NullIfExpr: type = ((const NullIfExpr *) expr)->opresulttype; break; + case T_CachedExpr: + type = exprType(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: type = BOOLOID; break; @@ -311,6 +315,8 @@ exprTypmod(const Node *expr) return exprTypmod((Node *) linitial(nexpr->args)); } break; + case T_CachedExpr: + return exprTypmod(get_const_subexpr((const CachedExpr *) expr)); case T_SubLink: { const SubLink *sublink = (const SubLink *) expr; @@ -573,6 +579,10 @@ exprIsLengthCoercion(const Node *expr, int32 *coercedTypmod) return true; } + if (expr && IsA(expr, CachedExpr)) + return exprIsLengthCoercion( + get_const_subexpr((const CachedExpr *) expr), coercedTypmod); + return false; } @@ -655,6 +665,10 @@ strip_implicit_coercions(Node *node) if (c->coercionformat == COERCE_IMPLICIT_CAST) return strip_implicit_coercions((Node *) c->arg); } + else if (IsA(node, CachedExpr)) + { + return strip_implicit_coercions(get_subexpr((CachedExpr *) node)); + } return node; } @@ -727,6 +741,8 @@ expression_returns_set_walker(Node *node, void *context) return false; if (IsA(node, XmlExpr)) return false; + if (IsA(node, CachedExpr)) + return false; return expression_tree_walker(node, expression_returns_set_walker, context); @@ -790,6 +806,9 @@ exprCollation(const Node *expr) case T_NullIfExpr: coll = ((const NullIfExpr *) expr)->opcollid; break; + case T_CachedExpr: + coll = exprCollation(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: coll = InvalidOid; /* result is always boolean */ break; @@ -973,6 +992,10 @@ exprInputCollation(const Node *expr) case T_NullIfExpr: coll = ((const NullIfExpr *) expr)->inputcollid; break; + case T_CachedExpr: + coll = exprInputCollation( + get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: coll = ((const ScalarArrayOpExpr *) expr)->inputcollid; break; @@ -1034,6 +1057,9 @@ exprSetCollation(Node *expr, Oid collation) case T_NullIfExpr: ((NullIfExpr *) expr)->opcollid = collation; break; + case T_CachedExpr: + exprSetCollation(get_subexpr((CachedExpr *) expr), collation); + break; case T_ScalarArrayOpExpr: Assert(!OidIsValid(collation)); /* result is always boolean */ break; @@ -1168,6 +1194,10 @@ exprSetInputCollation(Node *expr, Oid inputcollation) case T_NullIfExpr: ((NullIfExpr *) expr)->inputcollid = inputcollation; break; + case T_CachedExpr: + exprSetInputCollation(get_subexpr((CachedExpr *) expr), + inputcollation); + break; case T_ScalarArrayOpExpr: ((ScalarArrayOpExpr *) expr)->inputcollid = inputcollation; break; @@ -1277,6 +1307,9 @@ exprLocation(const Node *expr) exprLocation((Node *) opexpr->args)); } break; + case T_CachedExpr: + loc = exprLocation(get_const_subexpr((const CachedExpr *) expr)); + break; case T_ScalarArrayOpExpr: { const ScalarArrayOpExpr *saopexpr = (const ScalarArrayOpExpr *) expr; @@ -1611,6 +1644,8 @@ fix_opfuncids_walker(Node *node, void *context) { if (node == NULL) return false; + if (IsA(node, CachedExpr)) + return fix_opfuncids_walker(get_subexpr((CachedExpr *) node), context); if (IsA(node, OpExpr)) set_opfuncid((OpExpr *) node); else if (IsA(node, DistinctExpr)) @@ -1710,6 +1745,9 @@ check_functions_in_node(Node *node, check_function_callback checker, return true; } break; + case T_CachedExpr: + return check_functions_in_node(get_subexpr((CachedExpr *) node), + checker, context); case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -1980,6 +2018,17 @@ expression_tree_walker(Node *node, return true; } break; + case T_CachedExpr: + { + /* + * cachedexpr is processed by my_walker, so its subexpr is + * processed too and we need to process sub-nodes of subexpr. + */ + if (expression_tree_walker(get_subexpr((CachedExpr *) node), + walker, context)) + return true; + } + break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -2617,6 +2666,54 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_CachedExpr: + { + CachedExpr *expr = (CachedExpr *) node; + CachedExpr *newnode; + + FLATCOPY(newnode, expr, CachedExpr); + + /* + * expr is already mutated, so its subexpr is already mutated + * too and we need to mutate sub-nodes of subexpr. + */ + switch(newnode->subexprtype) + { + case CACHED_FUNCEXPR: + newnode->subexpr.funcexpr = (FuncExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.funcexpr, mutator, + context); + break; + case CACHED_OPEXPR: + newnode->subexpr.opexpr = (OpExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.opexpr, mutator, + context); + break; + case CACHED_DISTINCTEXPR: + newnode->subexpr.distinctexpr = (DistinctExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.distinctexpr, mutator, + context); + break; + case CACHED_NULLIFEXPR: + newnode->subexpr.nullifexpr = (NullIfExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.nullifexpr, mutator, + context); + break; + case CACHED_SCALARARRAYOPEXPR: + newnode->subexpr.saopexpr = (ScalarArrayOpExpr *) + expression_tree_mutator( + (Node *) expr->subexpr.saopexpr, mutator, + context); + break; + } + + return (Node *) newnode; + } + break; case T_ScalarArrayOpExpr: { ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node; @@ -3838,3 +3935,57 @@ planstate_walk_members(List *plans, PlanState **planstates, return false; } + +/* + * get_const_subexpr + * Get const subexpression of given const cached expression. + */ +static const Node * +get_const_subexpr(const CachedExpr *cachedexpr) +{ + if (cachedexpr == NULL) + return NULL; + + switch (cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + return (const Node *) cachedexpr->subexpr.funcexpr; + case CACHED_OPEXPR: + return (const Node *) cachedexpr->subexpr.opexpr; + case CACHED_DISTINCTEXPR: + return (const Node *) cachedexpr->subexpr.distinctexpr; + case CACHED_NULLIFEXPR: + return (const Node *) cachedexpr->subexpr.nullifexpr; + case CACHED_SCALARARRAYOPEXPR: + return (const Node *) cachedexpr->subexpr.saopexpr; + } + + return NULL; +} + +/* + * get_subexpr + * Get subexpression of given cached expression. + */ +Node * +get_subexpr(CachedExpr *cachedexpr) +{ + if (cachedexpr == NULL) + return NULL; + + switch (cachedexpr->subexprtype) + { + case CACHED_FUNCEXPR: + return (Node *) cachedexpr->subexpr.funcexpr; + case CACHED_OPEXPR: + return (Node *) cachedexpr->subexpr.opexpr; + case CACHED_DISTINCTEXPR: + return (Node *) cachedexpr->subexpr.distinctexpr; + case CACHED_NULLIFEXPR: + return (Node *) cachedexpr->subexpr.nullifexpr; + case CACHED_SCALARARRAYOPEXPR: + return (Node *) cachedexpr->subexpr.saopexpr; + } + + return NULL; +} diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 8d9ff63..c0c8363 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1237,6 +1237,59 @@ _outNullIfExpr(StringInfo str, const NullIfExpr *node) } static void +_outCachedExpr(StringInfo str, const CachedExpr *node) +{ + WRITE_NODE_TYPE("CACHEDEXPR"); + + /* do-it-yourself enum representation; out subexprtype begin... */ + appendStringInfoString(str, " :subexprtype "); + + switch(node->subexprtype) + { + case CACHED_FUNCEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_funcexpr"); + + WRITE_NODE_FIELD(subexpr.funcexpr); + } + break; + case CACHED_OPEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_opexpr"); + + WRITE_NODE_FIELD(subexpr.opexpr); + } + break; + case CACHED_DISTINCTEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_distinctexpr"); + + WRITE_NODE_FIELD(subexpr.distinctexpr); + } + break; + case CACHED_NULLIFEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_nullifexpr"); + + WRITE_NODE_FIELD(subexpr.nullifexpr); + } + break; + case CACHED_SCALARARRAYOPEXPR: + { + /* ... out subexprtype end */ + outToken(str, "cached_scalararrayopexpr"); + + WRITE_NODE_FIELD(subexpr.saopexpr); + } + break; + } +} + +static void _outScalarArrayOpExpr(StringInfo str, const ScalarArrayOpExpr *node) { WRITE_NODE_TYPE("SCALARARRAYOPEXPR"); @@ -3767,6 +3820,9 @@ outNode(StringInfo str, const void *obj) case T_NullIfExpr: _outNullIfExpr(str, obj); break; + case T_CachedExpr: + _outCachedExpr(str, obj); + break; case T_ScalarArrayOpExpr: _outScalarArrayOpExpr(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index e24f5d6..acb14f9 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -750,6 +750,52 @@ _readNullIfExpr(void) } /* + * _readCachedExpr + */ +static CachedExpr * +_readCachedExpr(void) +{ + READ_LOCALS(CachedExpr); + + /* do-it-yourself enum representation */ + token = pg_strtok(&length); /* skip :subexprtype */ + token = pg_strtok(&length); /* get field value */ + if (strncmp(token, "cached_funcexpr", 15) == 0) + local_node->subexprtype = CACHED_FUNCEXPR; + else if (strncmp(token, "cached_opexpr", 13) == 0) + local_node->subexprtype = CACHED_OPEXPR; + else if (strncmp(token, "cached_distinctexpr", 19) == 0) + local_node->subexprtype = CACHED_DISTINCTEXPR; + else if (strncmp(token, "cached_nullifexpr", 17) == 0) + local_node->subexprtype = CACHED_NULLIFEXPR; + else if (strncmp(token, "cached_scalararrayopexpr", 24) == 0) + local_node->subexprtype = CACHED_SCALARARRAYOPEXPR; + else + elog(ERROR, "unrecognized subexprtype \"%.*s\"", length, token); + + switch (local_node->subexprtype) + { + case CACHED_FUNCEXPR: + READ_NODE_FIELD(subexpr.funcexpr); + break; + case CACHED_OPEXPR: + READ_NODE_FIELD(subexpr.opexpr); + break; + case CACHED_DISTINCTEXPR: + READ_NODE_FIELD(subexpr.distinctexpr); + break; + case CACHED_NULLIFEXPR: + READ_NODE_FIELD(subexpr.nullifexpr); + break; + case CACHED_SCALARARRAYOPEXPR: + READ_NODE_FIELD(subexpr.saopexpr); + break; + } + + READ_DONE(); +} + +/* * _readScalarArrayOpExpr */ static ScalarArrayOpExpr * @@ -2462,6 +2508,8 @@ parseNodeString(void) return_value = _readDistinctExpr(); else if (MATCH("NULLIFEXPR", 10)) return_value = _readNullIfExpr(); + else if (MATCH("CACHEDEXPR", 10)) + return_value = _readCachedExpr(); else if (MATCH("SCALARARRAYOPEXPR", 17)) return_value = _readScalarArrayOpExpr(); else if (MATCH("BOOLEXPR", 8)) diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c4a5651..552b73d 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -184,6 +184,7 @@ static PathTarget *make_sort_input_target(PlannerInfo *root, bool *have_postponed_srfs); static void adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel, List *targets, List *targets_contain_srfs); +static Node *replace_cached_expressions_mutator(Node *node); /***************************************************************************** @@ -6086,3 +6087,261 @@ get_partitioned_child_rels(PlannerInfo *root, Index rti) return result; } + +static Node * +replace_cached_expressions_mutator(Node *node) +{ + if (node == NULL) + return NULL; + + /* mutate certain types of nodes */ + if (IsA(node, RestrictInfo)) + { + RestrictInfo *rinfo = (RestrictInfo *) node; + + /* + * For an OR clause, recurse into the marked-up tree so that we replace + * cached expressions for contained RestrictInfos too. + */ + if (rinfo->orclause) + rinfo->orclause = (Expr *) replace_cached_expressions_mutator( + (Node *) rinfo->orclause); + else + rinfo->clause = (Expr *) replace_cached_expressions_mutator( + (Node *) rinfo->clause); + + /* do NOT recurse into children */ + return node; + } + else if (IsA(node, FuncExpr)) + { + /* + * Function is cached if: + * 1) it doesn't return set, + * 2) it's not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + FuncExpr *funcexpr; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool func_returns_set; + + /* firstly recurse into children */ + funcexpr = (FuncExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + func_returns_set = funcexpr->funcretset || + expression_returns_set((Node *) funcexpr->args); + + foreach(arg, funcexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (func_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &funcexpr->xpr)) + { + /* return FuncExpr, which will not be cached */ + return (Node *) funcexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_FUNCEXPR; + new_node->subexpr.funcexpr = funcexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, OpExpr)) + { + /* + * Operator is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + OpExpr *opexpr = (OpExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid(opexpr); + + /* firstly recurse into children */ + opexpr = (OpExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = opexpr->opretset || + expression_returns_set((Node *) opexpr->args); + + foreach(arg, opexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &opexpr->xpr)) + { + /* return OpExpr, which will not be cached */ + return (Node *) opexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_OPEXPR; + new_node->subexpr.opexpr = opexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, DistinctExpr)) + { + /* + * Operator of DistinctExpr is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + DistinctExpr *distinctexpr = (DistinctExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid((OpExpr *) distinctexpr); + + /* firstly recurse into children */ + distinctexpr = (DistinctExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = distinctexpr->opretset || + expression_returns_set((Node *) distinctexpr->args); + + foreach(arg, distinctexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &distinctexpr->xpr)) + { + /* return DistinctExpr, which will not be cached */ + return (Node *) distinctexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_DISTINCTEXPR; + new_node->subexpr.distinctexpr = distinctexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, NullIfExpr)) + { + /* + * Operator of NullIfExpr is cached if: + * 1) its function doesn't return set, + * 1) its function is not volatile itself, + * 3) its arguments are constants or cached expressions too. + */ + NullIfExpr *nullifexpr = (NullIfExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + bool op_returns_set; + + /* rely on struct equivalence to treat these all alike */ + set_opfuncid((OpExpr *) nullifexpr); + + /* firstly recurse into children */ + nullifexpr = (NullIfExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + op_returns_set = nullifexpr->opretset || + expression_returns_set((Node *) nullifexpr->args); + + foreach(arg, nullifexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (op_returns_set || + has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &nullifexpr->xpr)) + { + /* return NullIfExpr, which will not be cached */ + return (Node *) nullifexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_NULLIFEXPR; + new_node->subexpr.nullifexpr = nullifexpr; + + return (Node *) new_node; + } + } + else if (IsA(node, ScalarArrayOpExpr)) + { + /* + * Operator of ScalarArrayOpExpr is cached if: + * 1) its function is not volatile itself, + * 2) its arguments are constants or cached expressions too. + * (it returns boolean so we don't need to check if it returns set) + */ + ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node; + ListCell *arg; + bool has_nonconst_or_noncached_input = false; + + set_sa_opfuncid(saopexpr); + + /* firstly recurse into children */ + saopexpr = (ScalarArrayOpExpr *) expression_tree_mutator(node, + replace_cached_expressions_mutator, + NULL); + + foreach(arg, saopexpr->args) + { + void *arg_lfirst = lfirst(arg); + if (!(IsA(arg_lfirst, Const) || IsA(arg_lfirst, CachedExpr))) + has_nonconst_or_noncached_input = true; + } + + if (has_nonconst_or_noncached_input || + contain_volatile_functions((Node *) &saopexpr->xpr)) + { + /* return ScalarArrayOpExpr, which will not be cached */ + return (Node *) saopexpr; + } + else + { + /* create and return CachedExpr */ + CachedExpr *new_node = makeNode(CachedExpr); + new_node->subexprtype = CACHED_SCALARARRAYOPEXPR; + new_node->subexpr.saopexpr = saopexpr; + + return (Node *) new_node; + } + } + + /* otherwise recurse into children */ + return expression_tree_mutator(node, replace_cached_expressions_mutator, + NULL); +} diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index b6c9b48..0dbfa12 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -76,5 +76,6 @@ extern bool raw_expression_tree_walker(Node *node, bool (*walker) (), struct PlanState; extern bool planstate_tree_walker(struct PlanState *planstate, bool (*walker) (), void *context); +extern Node * get_subexpr(CachedExpr *cachedexpr); #endif /* NODEFUNCS_H */ diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f59d719..054bc61 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -155,6 +155,7 @@ typedef enum NodeTag T_OpExpr, T_DistinctExpr, T_NullIfExpr, + T_CachedExpr, T_ScalarArrayOpExpr, T_BoolExpr, T_SubLink, diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h index 86ec82e..3f89653 100644 --- a/src/include/nodes/primnodes.h +++ b/src/include/nodes/primnodes.h @@ -1498,4 +1498,42 @@ typedef struct OnConflictExpr List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */ } OnConflictExpr; +/* + * Discriminator for CachedExpr. + * + * Identifies the subexpression to be cached in execution (= executed only once + * and then used cached value) and which member in the CachedExpr->subexpr union + * is valid. + */ +typedef enum CachedSubExprType +{ + CACHED_FUNCEXPR, /* cached FuncExpr */ + CACHED_OPEXPR, /* cached OpExpr */ + CACHED_DISTINCTEXPR, /* cached DistinctExpr */ + CACHED_NULLIFEXPR, /* cached NullIfExpr */ + CACHED_SCALARARRAYOPEXPR /* cached ScalarArrayOpExpr */ +} CachedSubExprType; + +/* + * CachedExpr - expression node for precalculated stable and immutable functions + * (= they are calculated once for all output rows, but as many times as + * function is mentioned in query), if they don't return a set and their + * arguments are constants or recursively precalculated functions. The same for + * operators' functions. + */ +typedef struct CachedExpr +{ + Expr xpr; + CachedSubExprType subexprtype; /* expression to be cached */ + + union SubExpr + { + FuncExpr *funcexpr; /* for CACHED_FUNCEXPR */ + OpExpr *opexpr; /* for CACHED_OPEXPR */ + DistinctExpr *distinctexpr; /* for CACHED_DISTINCTEXPR */ + NullIfExpr *nullifexpr; /* for CACHED_NULLIFEXPR */ + ScalarArrayOpExpr *saopexpr; /* for CACHED_SCALARARRAYOPEXPR */ + } subexpr; +} CachedExpr; + #endif /* PRIMNODES_H */ -- 1.9.1