From 39cee738895447cb09bcf9be04ab85b97531d951 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 10 Jun 2005 00:28:54 +0000 Subject: [PATCH] Revise searching of subplan target lists to use something more efficient than tlist_member calls. Building a large join tlist is still O(N^2), but with a much smaller constant factor than before. --- src/backend/optimizer/plan/setrefs.c | 442 +++++++++++++++------------ 1 file changed, 248 insertions(+), 194 deletions(-) diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index c3eb36bcfc..5f6e8301fc 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.110 2005/05/22 22:30:19 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.111 2005/06/10 00:28:54 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,20 +25,34 @@ #include "utils/lsyscache.h" +typedef struct +{ + Index varno; /* RT index of Var */ + AttrNumber varattno; /* attr number of Var */ + AttrNumber resno; /* TLE position of Var */ +} tlist_vinfo; + +typedef struct +{ + List *tlist; /* underlying target list */ + int num_vars; /* number of plain Var tlist entries */ + bool has_non_vars; /* are there non-plain-Var entries? */ + /* array of num_vars entries: */ + tlist_vinfo vars[1]; /* VARIABLE LENGTH ARRAY */ +} indexed_tlist; /* VARIABLE LENGTH STRUCT */ + typedef struct { List *rtable; - List *outer_tlist; - List *inner_tlist; + indexed_tlist *outer_itlist; + indexed_tlist *inner_itlist; Index acceptable_rel; - bool tlists_have_non_vars; } join_references_context; typedef struct { + indexed_tlist *subplan_itlist; Index subvarno; - List *subplan_targetlist; - bool tlist_has_non_vars; } replace_vars_with_subplan_refs_context; static Plan *set_subqueryscan_references(SubqueryScan *plan, List *rtable); @@ -51,22 +65,25 @@ static bool fix_expr_references_walker(Node *node, void *context); static void set_join_references(Join *join, List *rtable); static void set_inner_join_references(Plan *inner_plan, List *rtable, - List *outer_tlist, - bool tlists_have_non_vars); + indexed_tlist *outer_itlist); static void set_uppernode_references(Plan *plan, Index subvarno); -static bool targetlist_has_non_vars(List *tlist); +static indexed_tlist *build_tlist_index(List *tlist); +static Var *search_indexed_tlist_for_var(Var *var, + indexed_tlist *itlist, + Index newvarno); +static Var *search_indexed_tlist_for_non_var(Node *node, + indexed_tlist *itlist, + Index newvarno); static List *join_references(List *clauses, - List *rtable, - List *outer_tlist, - List *inner_tlist, - Index acceptable_rel, - bool tlists_have_non_vars); + List *rtable, + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel); static Node *join_references_mutator(Node *node, join_references_context *context); static Node *replace_vars_with_subplan_refs(Node *node, - Index subvarno, - List *subplan_targetlist, - bool tlist_has_non_vars); + indexed_tlist *subplan_itlist, + Index subvarno); static Node *replace_vars_with_subplan_refs_mutator(Node *node, replace_vars_with_subplan_refs_context *context); static bool fix_opfuncids_walker(Node *node, void *context); @@ -689,32 +706,28 @@ set_join_references(Join *join, List *rtable) { Plan *outer_plan = join->plan.lefttree; Plan *inner_plan = join->plan.righttree; - List *outer_tlist = outer_plan->targetlist; - List *inner_tlist = inner_plan->targetlist; - bool tlists_have_non_vars; + indexed_tlist *outer_itlist; + indexed_tlist *inner_itlist; - tlists_have_non_vars = targetlist_has_non_vars(outer_tlist) || - targetlist_has_non_vars(inner_tlist); + outer_itlist = build_tlist_index(outer_plan->targetlist); + inner_itlist = build_tlist_index(inner_plan->targetlist); /* All join plans have tlist, qual, and joinqual */ join->plan.targetlist = join_references(join->plan.targetlist, rtable, - outer_tlist, - inner_tlist, - (Index) 0, - tlists_have_non_vars); + outer_itlist, + inner_itlist, + (Index) 0); join->plan.qual = join_references(join->plan.qual, rtable, - outer_tlist, - inner_tlist, - (Index) 0, - tlists_have_non_vars); + outer_itlist, + inner_itlist, + (Index) 0); join->joinqual = join_references(join->joinqual, rtable, - outer_tlist, - inner_tlist, - (Index) 0, - tlists_have_non_vars); + outer_itlist, + inner_itlist, + (Index) 0); /* Now do join-type-specific stuff */ if (IsA(join, NestLoop)) @@ -722,8 +735,7 @@ set_join_references(Join *join, List *rtable) /* This processing is split out to handle possible recursion */ set_inner_join_references(inner_plan, rtable, - outer_tlist, - tlists_have_non_vars); + outer_itlist); } else if (IsA(join, MergeJoin)) { @@ -731,10 +743,9 @@ set_join_references(Join *join, List *rtable) mj->mergeclauses = join_references(mj->mergeclauses, rtable, - outer_tlist, - inner_tlist, - (Index) 0, - tlists_have_non_vars); + outer_itlist, + inner_itlist, + (Index) 0); } else if (IsA(join, HashJoin)) { @@ -742,11 +753,13 @@ set_join_references(Join *join, List *rtable) hj->hashclauses = join_references(hj->hashclauses, rtable, - outer_tlist, - inner_tlist, - (Index) 0, - tlists_have_non_vars); + outer_itlist, + inner_itlist, + (Index) 0); } + + pfree(outer_itlist); + pfree(inner_itlist); } /* @@ -760,8 +773,7 @@ set_join_references(Join *join, List *rtable) static void set_inner_join_references(Plan *inner_plan, List *rtable, - List *outer_tlist, - bool tlists_have_non_vars) + indexed_tlist *outer_itlist) { if (IsA(inner_plan, IndexScan)) { @@ -782,16 +794,14 @@ set_inner_join_references(Plan *inner_plan, /* only refs to outer vars get changed in the inner qual */ innerscan->indexqualorig = join_references(indexqualorig, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); innerscan->indexqual = join_references(innerscan->indexqual, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); /* * We must fix the inner qpqual too, if it has join @@ -801,10 +811,9 @@ set_inner_join_references(Plan *inner_plan, if (NumRelids((Node *) inner_plan->qual) > 1) inner_plan->qual = join_references(inner_plan->qual, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); } } else if (IsA(inner_plan, BitmapIndexScan)) @@ -823,16 +832,14 @@ set_inner_join_references(Plan *inner_plan, /* only refs to outer vars get changed in the inner qual */ innerscan->indexqualorig = join_references(indexqualorig, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); innerscan->indexqual = join_references(innerscan->indexqual, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); /* no need to fix inner qpqual */ Assert(inner_plan->qual == NIL); } @@ -854,10 +861,9 @@ set_inner_join_references(Plan *inner_plan, /* only refs to outer vars get changed in the inner qual */ innerscan->bitmapqualorig = join_references(bitmapqualorig, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); /* * We must fix the inner qpqual too, if it has join @@ -867,16 +873,14 @@ set_inner_join_references(Plan *inner_plan, if (NumRelids((Node *) inner_plan->qual) > 1) inner_plan->qual = join_references(inner_plan->qual, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); /* Now recurse */ set_inner_join_references(inner_plan->lefttree, rtable, - outer_tlist, - tlists_have_non_vars); + outer_itlist); } } else if (IsA(inner_plan, BitmapAnd)) @@ -889,8 +893,7 @@ set_inner_join_references(Plan *inner_plan, { set_inner_join_references((Plan *) lfirst(l), rtable, - outer_tlist, - tlists_have_non_vars); + outer_itlist); } } else if (IsA(inner_plan, BitmapOr)) @@ -903,8 +906,7 @@ set_inner_join_references(Plan *inner_plan, { set_inner_join_references((Plan *) lfirst(l), rtable, - outer_tlist, - tlists_have_non_vars); + outer_itlist); } } else if (IsA(inner_plan, TidScan)) @@ -914,10 +916,9 @@ set_inner_join_references(Plan *inner_plan, innerscan->tideval = join_references(innerscan->tideval, rtable, - outer_tlist, - NIL, - innerrel, - tlists_have_non_vars); + outer_itlist, + NULL, + innerrel); } } @@ -941,17 +942,14 @@ static void set_uppernode_references(Plan *plan, Index subvarno) { Plan *subplan = plan->lefttree; - List *subplan_targetlist, - *output_targetlist; + indexed_tlist *subplan_itlist; + List *output_targetlist; ListCell *l; - bool tlist_has_non_vars; if (subplan != NULL) - subplan_targetlist = subplan->targetlist; + subplan_itlist = build_tlist_index(subplan->targetlist); else - subplan_targetlist = NIL; - - tlist_has_non_vars = targetlist_has_non_vars(subplan_targetlist); + subplan_itlist = build_tlist_index(NIL); output_targetlist = NIL; foreach(l, plan->targetlist) @@ -960,9 +958,8 @@ set_uppernode_references(Plan *plan, Index subvarno) Node *newexpr; newexpr = replace_vars_with_subplan_refs((Node *) tle->expr, - subvarno, - subplan_targetlist, - tlist_has_non_vars); + subplan_itlist, + subvarno); tle = flatCopyTargetEntry(tle); tle->expr = (Expr *) newexpr; output_targetlist = lappend(output_targetlist, tle); @@ -971,30 +968,127 @@ set_uppernode_references(Plan *plan, Index subvarno) plan->qual = (List *) replace_vars_with_subplan_refs((Node *) plan->qual, - subvarno, - subplan_targetlist, - tlist_has_non_vars); + subplan_itlist, + subvarno); + + pfree(subplan_itlist); } /* - * targetlist_has_non_vars --- are there any non-Var entries in tlist? + * build_tlist_index --- build an index data structure for a child tlist * - * In most cases, subplan tlists will be "flat" tlists with only Vars. - * Checking for this allows us to save comparisons in common cases. + * In most cases, subplan tlists will be "flat" tlists with only Vars, + * so we try to optimize that case by extracting information about Vars + * in advance. Matching a parent tlist to a child is still an O(N^2) + * operation, but at least with a much smaller constant factor than plain + * tlist_member() searches. + * + * The result of this function is an indexed_tlist struct to pass to + * search_indexed_tlist_for_var() or search_indexed_tlist_for_non_var(). + * When done, the indexed_tlist may be freed with a single pfree(). */ -static bool -targetlist_has_non_vars(List *tlist) +static indexed_tlist * +build_tlist_index(List *tlist) { + indexed_tlist *itlist; + tlist_vinfo *vinfo; ListCell *l; + /* Create data structure with enough slots for all tlist entries */ + itlist = (indexed_tlist *) + palloc(offsetof(indexed_tlist, vars) + + list_length(tlist) * sizeof(tlist_vinfo)); + + itlist->tlist = tlist; + itlist->has_non_vars = false; + + /* Find the Vars and fill in the index array */ + vinfo = itlist->vars; foreach(l, tlist) { TargetEntry *tle = (TargetEntry *) lfirst(l); - if (tle->expr && !IsA(tle->expr, Var)) - return true; + if (tle->expr && IsA(tle->expr, Var)) + { + Var *var = (Var *) tle->expr; + + vinfo->varno = var->varno; + vinfo->varattno = var->varattno; + vinfo->resno = tle->resno; + vinfo++; + } + else + itlist->has_non_vars = true; } - return false; + + itlist->num_vars = (vinfo - itlist->vars); + + return itlist; +} + +/* + * search_indexed_tlist_for_var --- find a Var in an indexed tlist + * + * If a match is found, return a copy of the given Var with suitably + * modified varno/varattno (to wit, newvarno and the resno of the TLE entry). + * If no match, return NULL. + */ +static Var * +search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, Index newvarno) +{ + Index varno = var->varno; + AttrNumber varattno = var->varattno; + tlist_vinfo *vinfo; + int i; + + vinfo = itlist->vars; + i = itlist->num_vars; + while (i-- > 0) + { + if (vinfo->varno == varno && vinfo->varattno == varattno) + { + /* Found a match */ + Var *newvar = (Var *) copyObject(var); + + newvar->varno = newvarno; + newvar->varattno = vinfo->resno; + return newvar; + } + vinfo++; + } + return NULL; /* no match */ +} + +/* + * search_indexed_tlist_for_non_var --- find a non-Var in an indexed tlist + * + * If a match is found, return a Var constructed to reference the tlist item. + * If no match, return NULL. + * + * NOTE: it is a waste of time to call this if !itlist->has_non_vars + */ +static Var * +search_indexed_tlist_for_non_var(Node *node, + indexed_tlist *itlist, Index newvarno) +{ + TargetEntry *tle; + + tle = tlist_member(node, itlist->tlist); + if (tle) + { + /* Found a matching subplan output expression */ + Var *newvar; + + newvar = makeVar(newvarno, + tle->resno, + exprType((Node *) tle->expr), + exprTypmod((Node *) tle->expr), + 0); + newvar->varnoold = 0; /* wasn't ever a plain Var */ + newvar->varoattno = 0; + return newvar; + } + return NULL; /* no match */ } /* @@ -1012,12 +1106,13 @@ targetlist_has_non_vars(List *tlist) * * For a normal join, acceptable_rel should be zero so that any failure to * match a Var will be reported as an error. For the indexscan case, - * pass inner_tlist = NIL and acceptable_rel = the ID of the inner relation. + * pass inner_itlist = NULL and acceptable_rel = the ID of the inner relation. * * 'clauses' is the targetlist or list of join clauses * 'rtable' is the current range table - * 'outer_tlist' is the target list of the outer join relation - * 'inner_tlist' is the target list of the inner join relation, or NIL + * 'outer_itlist' is the indexed target list of the outer join relation + * 'inner_itlist' is the indexed target list of the inner join relation, + * or NULL * 'acceptable_rel' is either zero or the rangetable index of a relation * whose Vars may appear in the clause without provoking an error. * @@ -1027,18 +1122,16 @@ targetlist_has_non_vars(List *tlist) static List * join_references(List *clauses, List *rtable, - List *outer_tlist, - List *inner_tlist, - Index acceptable_rel, - bool tlists_have_non_vars) + indexed_tlist *outer_itlist, + indexed_tlist *inner_itlist, + Index acceptable_rel) { join_references_context context; context.rtable = rtable; - context.outer_tlist = outer_tlist; - context.inner_tlist = inner_tlist; + context.outer_itlist = outer_itlist; + context.inner_itlist = inner_itlist; context.acceptable_rel = acceptable_rel; - context.tlists_have_non_vars = tlists_have_non_vars; return (List *) join_references_mutator((Node *) clauses, &context); } @@ -1046,31 +1139,27 @@ static Node * join_references_mutator(Node *node, join_references_context *context) { + Var *newvar; + if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; - TargetEntry *tle; /* First look for the var in the input tlists */ - tle = tlist_member((Node *) var, context->outer_tlist); - if (tle) - { - Var *newvar = (Var *) copyObject(var); - - newvar->varno = OUTER; - newvar->varattno = tle->resno; + newvar = search_indexed_tlist_for_var(var, + context->outer_itlist, + OUTER); + if (newvar) return (Node *) newvar; - } - tle = tlist_member((Node *) var, context->inner_tlist); - if (tle) + if (context->inner_itlist) { - Var *newvar = (Var *) copyObject(var); - - newvar->varno = INNER; - newvar->varattno = tle->resno; - return (Node *) newvar; + newvar = search_indexed_tlist_for_var(var, + context->inner_itlist, + INNER); + if (newvar) + return (Node *) newvar; } /* Return the Var unmodified, if it's for acceptable_rel */ @@ -1081,40 +1170,21 @@ join_references_mutator(Node *node, elog(ERROR, "variable not found in subplan target lists"); } /* Try matching more complex expressions too, if tlists have any */ - if (context->tlists_have_non_vars) + if (context->outer_itlist->has_non_vars) { - TargetEntry *tle; - - tle = tlist_member(node, context->outer_tlist); - if (tle) - { - /* Found a matching subplan output expression */ - Var *newvar; - - newvar = makeVar(OUTER, - tle->resno, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), - 0); - newvar->varnoold = 0; /* wasn't ever a plain Var */ - newvar->varoattno = 0; + newvar = search_indexed_tlist_for_non_var(node, + context->outer_itlist, + OUTER); + if (newvar) return (Node *) newvar; - } - tle = tlist_member(node, context->inner_tlist); - if (tle) - { - /* Found a matching subplan output expression */ - Var *newvar; - - newvar = makeVar(INNER, - tle->resno, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), - 0); - newvar->varnoold = 0; /* wasn't ever a plain Var */ - newvar->varoattno = 0; + } + if (context->inner_itlist && context->inner_itlist->has_non_vars) + { + newvar = search_indexed_tlist_for_non_var(node, + context->inner_itlist, + INNER); + if (newvar) return (Node *) newvar; - } } return expression_tree_mutator(node, join_references_mutator, @@ -1131,16 +1201,15 @@ join_references_mutator(Node *node, * --- so this routine should only be applied to nodes whose subplans' * targetlists were generated via flatten_tlist() or some such method. * - * If tlist_has_non_vars is true, then we try to match whole subexpressions + * If itlist->has_non_vars is true, then we try to match whole subexpressions * against elements of the subplan tlist, so that we can avoid recomputing * expressions that were already computed by the subplan. (This is relatively * expensive, so we don't want to try it in the common case where the * subplan tlist is just a flattened list of Vars.) * * 'node': the tree to be fixed (a target item or qual) + * 'subplan_itlist': indexed target list for subplan * 'subvarno': varno to be assigned to all Vars - * 'subplan_targetlist': target list for subplan - * 'tlist_has_non_vars': true if subplan_targetlist contains non-Var exprs * * The resulting tree is a copy of the original in which all Var nodes have * varno = subvarno, varattno = resno of corresponding subplan target. @@ -1148,15 +1217,13 @@ join_references_mutator(Node *node, */ static Node * replace_vars_with_subplan_refs(Node *node, - Index subvarno, - List *subplan_targetlist, - bool tlist_has_non_vars) + indexed_tlist *subplan_itlist, + Index subvarno) { replace_vars_with_subplan_refs_context context; + context.subplan_itlist = subplan_itlist; context.subvarno = subvarno; - context.subplan_targetlist = subplan_targetlist; - context.tlist_has_non_vars = tlist_has_non_vars; return replace_vars_with_subplan_refs_mutator(node, &context); } @@ -1164,42 +1231,29 @@ static Node * replace_vars_with_subplan_refs_mutator(Node *node, replace_vars_with_subplan_refs_context *context) { + Var *newvar; + if (node == NULL) return NULL; if (IsA(node, Var)) { Var *var = (Var *) node; - TargetEntry *tle; - Var *newvar; - tle = tlist_member((Node *) var, context->subplan_targetlist); - if (!tle) + newvar = search_indexed_tlist_for_var(var, + context->subplan_itlist, + context->subvarno); + if (!newvar) elog(ERROR, "variable not found in subplan target list"); - newvar = (Var *) copyObject(var); - newvar->varno = context->subvarno; - newvar->varattno = tle->resno; return (Node *) newvar; } /* Try matching more complex expressions too, if tlist has any */ - if (context->tlist_has_non_vars) + if (context->subplan_itlist->has_non_vars) { - TargetEntry *tle; - - tle = tlist_member(node, context->subplan_targetlist); - if (tle) - { - /* Found a matching subplan output expression */ - Var *newvar; - - newvar = makeVar(context->subvarno, - tle->resno, - exprType((Node *) tle->expr), - exprTypmod((Node *) tle->expr), - 0); - newvar->varnoold = 0; /* wasn't ever a plain Var */ - newvar->varoattno = 0; + newvar = search_indexed_tlist_for_non_var(node, + context->subplan_itlist, + context->subvarno); + if (newvar) return (Node *) newvar; - } } return expression_tree_mutator(node, replace_vars_with_subplan_refs_mutator,