diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 59 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 64 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 66 |
3 files changed, 180 insertions, 9 deletions
diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index c729a99f8b6..b6456485599 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -5923,10 +5923,16 @@ adjust_paths_for_srfs(PlannerInfo *root, RelOptInfo *rel, * side-effect that is useful when the expression will get evaluated more than * once. Also, we must fix operator function IDs. * + * This does not return any information about dependencies of the expression. + * Hence callers should use the results only for the duration of the current + * query. Callers that would like to cache the results for longer should use + * expression_planner_with_deps, probably via the plancache. + * * Note: this must not make any damaging changes to the passed-in expression * tree. (It would actually be okay to apply fix_opfuncids to it, but since * we first do an expression_tree_mutator-based walk, what is returned will - * be a new node tree.) + * be a new node tree.) The result is constructed in the current memory + * context; beware that this can leak a lot of additional stuff there, too. */ Expr * expression_planner(Expr *expr) @@ -5945,6 +5951,57 @@ expression_planner(Expr *expr) return (Expr *) result; } +/* + * expression_planner_with_deps + * Perform planner's transformations on a standalone expression, + * returning expression dependency information along with the result. + * + * This is identical to expression_planner() except that it also returns + * information about possible dependencies of the expression, ie identities of + * objects whose definitions affect the result. As in a PlannedStmt, these + * are expressed as a list of relation Oids and a list of PlanInvalItems. + */ +Expr * +expression_planner_with_deps(Expr *expr, + List **relationOids, + List **invalItems) +{ + Node *result; + PlannerGlobal glob; + PlannerInfo root; + + /* Make up dummy planner state so we can use setrefs machinery */ + MemSet(&glob, 0, sizeof(glob)); + glob.type = T_PlannerGlobal; + glob.relationOids = NIL; + glob.invalItems = NIL; + + MemSet(&root, 0, sizeof(root)); + root.type = T_PlannerInfo; + root.glob = &glob; + + /* + * Convert named-argument function calls, insert default arguments and + * simplify constant subexprs. Collect identities of inlined functions + * and elided domains, too. + */ + result = eval_const_expressions(&root, (Node *) expr); + + /* Fill in opfuncid values if missing */ + fix_opfuncids(result); + + /* + * Now walk the finished expression to find anything else we ought to + * record as an expression dependency. + */ + (void) extract_query_dependencies_walker(result, &root); + + *relationOids = glob.relationOids; + *invalItems = glob.invalItems; + + return (Expr *) result; +} + /* * plan_cluster_use_sort diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index a181aad8309..920a99d0d95 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -138,8 +138,7 @@ static List *set_returning_clause_references(PlannerInfo *root, Plan *topplan, Index resultRelation, int rtoffset); -static bool extract_query_dependencies_walker(Node *node, - PlannerInfo *context); + /***************************************************************************** * @@ -175,8 +174,8 @@ static bool extract_query_dependencies_walker(Node *node, * This will be used by plancache.c to drive invalidation of cached plans. * Relation dependencies are represented by OIDs, and everything else by * PlanInvalItems (this distinction is motivated by the shared-inval APIs). - * Currently, relations and user-defined functions are the only types of - * objects that are explicitly tracked this way. + * Currently, relations, user-defined functions, and domains are the only + * types of objects that are explicitly tracked this way. * * 8. We assign every plan node in the tree a unique ID. * @@ -2578,6 +2577,42 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid) } /* + * record_plan_type_dependency + * Mark the current plan as depending on a particular type. + * + * This is exported so that eval_const_expressions can record a + * dependency on a domain that it's removed a CoerceToDomain node for. + * + * We don't currently need to record dependencies on domains that the + * plan contains CoerceToDomain nodes for, though that might change in + * future. Hence, this isn't actually called in this module, though + * someday fix_expr_common might call it. + */ +void +record_plan_type_dependency(PlannerInfo *root, Oid typeid) +{ + /* + * As in record_plan_function_dependency, ignore the possibility that + * someone would change a built-in domain. + */ + if (typeid >= (Oid) FirstBootstrapObjectId) + { + PlanInvalItem *inval_item = makeNode(PlanInvalItem); + + /* + * It would work to use any syscache on pg_type, but the easiest is + * TYPEOID since we already have the type's OID at hand. Note that + * plancache.c knows we use TYPEOID. + */ + inval_item->cacheId = TYPEOID; + inval_item->hashValue = GetSysCacheHashValue1(TYPEOID, + ObjectIdGetDatum(typeid)); + + root->glob->invalItems = lappend(root->glob->invalItems, inval_item); + } +} + +/* * extract_query_dependencies * Given a rewritten, but not yet planned, query or queries * (i.e. a Query node or list of Query nodes), extract dependencies @@ -2586,6 +2621,13 @@ record_plan_function_dependency(PlannerInfo *root, Oid funcid) * * This is needed by plancache.c to handle invalidation of cached unplanned * queries. + * + * Note: this does not go through eval_const_expressions, and hence doesn't + * reflect its additions of inlined functions and elided CoerceToDomain nodes + * to the invalItems list. This is obviously OK for functions, since we'll + * see them in the original query tree anyway. For domains, it's OK because + * we don't care about domains unless they get elided. That is, a plan might + * have domain dependencies that the query tree doesn't. */ void extract_query_dependencies(Node *query, @@ -2615,14 +2657,20 @@ extract_query_dependencies(Node *query, *hasRowSecurity = glob.dependsOnRole; } -static bool +/* + * Tree walker for extract_query_dependencies. + * + * This is exported so that expression_planner_with_deps can call it on + * simple expressions (post-planning, not before planning, in that case). + * In that usage, glob.dependsOnRole isn't meaningful, but the relationOids + * and invalItems lists are added to as needed. + */ +bool extract_query_dependencies_walker(Node *node, PlannerInfo *context) { if (node == NULL) return false; Assert(!IsA(node, PlaceHolderVar)); - /* Extract function dependencies and check for regclass Consts */ - fix_expr_common(context, node); if (IsA(node, Query)) { Query *query = (Query *) node; @@ -2662,6 +2710,8 @@ extract_query_dependencies_walker(Node *node, PlannerInfo *context) return query_tree_walker(query, extract_query_dependencies_walker, (void *) context, 0); } + /* Extract function dependencies and check for regclass Consts */ + fix_expr_common(context, node); return expression_tree_walker(node, extract_query_dependencies_walker, (void *) context); } diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 8df369315bf..f4446169f5e 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -3699,6 +3699,70 @@ eval_const_expressions_mutator(Node *node, newbtest->location = btest->location; return (Node *) newbtest; } + case T_CoerceToDomain: + { + /* + * If the domain currently has no constraints, we replace the + * CoerceToDomain node with a simple RelabelType, which is + * both far faster to execute and more amenable to later + * optimization. We must then mark the plan as needing to be + * rebuilt if the domain's constraints change. + * + * Also, in estimation mode, always replace CoerceToDomain + * nodes, effectively assuming that the coercion will succeed. + */ + CoerceToDomain *cdomain = (CoerceToDomain *) node; + CoerceToDomain *newcdomain; + Node *arg; + + arg = eval_const_expressions_mutator((Node *) cdomain->arg, + context); + if (context->estimate || + !DomainHasConstraints(cdomain->resulttype)) + { + /* Record dependency, if this isn't estimation mode */ + if (context->root && !context->estimate) + record_plan_type_dependency(context->root, + cdomain->resulttype); + + /* Generate RelabelType to substitute for CoerceToDomain */ + /* This should match the RelabelType logic above */ + + while (arg && IsA(arg, RelabelType)) + arg = (Node *) ((RelabelType *) arg)->arg; + + if (arg && IsA(arg, Const)) + { + Const *con = (Const *) arg; + + con->consttype = cdomain->resulttype; + con->consttypmod = cdomain->resulttypmod; + con->constcollid = cdomain->resultcollid; + return (Node *) con; + } + else + { + RelabelType *newrelabel = makeNode(RelabelType); + + newrelabel->arg = (Expr *) arg; + newrelabel->resulttype = cdomain->resulttype; + newrelabel->resulttypmod = cdomain->resulttypmod; + newrelabel->resultcollid = cdomain->resultcollid; + newrelabel->relabelformat = cdomain->coercionformat; + newrelabel->location = cdomain->location; + return (Node *) newrelabel; + } + } + + newcdomain = makeNode(CoerceToDomain); + newcdomain->arg = (Expr *) arg; + newcdomain->resulttype = cdomain->resulttype; + newcdomain->resulttypmod = cdomain->resulttypmod; + newcdomain->resultcollid = cdomain->resultcollid; + newcdomain->coercionformat = cdomain->coercionformat; + newcdomain->location = cdomain->location; + return (Node *) newcdomain; + } case T_PlaceHolderVar: /* @@ -3770,7 +3834,7 @@ eval_const_expressions_mutator(Node *node, * For any node type not handled above, copy the node unchanged but * const-simplify its subexpressions. This is the correct thing for node * types whose behavior might change between planning and execution, such - * as CoerceToDomain. It's also a safe default for new node types not + * as CurrentOfExpr. It's also a safe default for new node types not * known to this routine. */ return ece_generic_processing(node); |