diff options
| author | Bruce Momjian | 1997-09-07 05:04:48 +0000 |
|---|---|---|
| committer | Bruce Momjian | 1997-09-07 05:04:48 +0000 |
| commit | 1ccd423235a48739d6f7a4d7889705b5f9ecc69b (patch) | |
| tree | 8001c4e839dfad8f29ceda7f8c5f5dbb8759b564 /src/backend/optimizer/path | |
| parent | 8fecd4febf8357f3cc20383ed29ced484877d5ac (diff) | |
Massive commit to run PGINDENT on all *.c and *.h files.
Diffstat (limited to 'src/backend/optimizer/path')
| -rw-r--r-- | src/backend/optimizer/path/allpaths.c | 609 | ||||
| -rw-r--r-- | src/backend/optimizer/path/clausesel.c | 536 | ||||
| -rw-r--r-- | src/backend/optimizer/path/costsize.c | 624 | ||||
| -rw-r--r-- | src/backend/optimizer/path/hashutils.c | 174 | ||||
| -rw-r--r-- | src/backend/optimizer/path/indxpath.c | 2113 | ||||
| -rw-r--r-- | src/backend/optimizer/path/joinpath.c | 1097 | ||||
| -rw-r--r-- | src/backend/optimizer/path/joinrels.c | 884 | ||||
| -rw-r--r-- | src/backend/optimizer/path/joinutils.c | 679 | ||||
| -rw-r--r-- | src/backend/optimizer/path/mergeutils.c | 170 | ||||
| -rw-r--r-- | src/backend/optimizer/path/orindxpath.c | 430 | ||||
| -rw-r--r-- | src/backend/optimizer/path/predmig.c | 1064 | ||||
| -rw-r--r-- | src/backend/optimizer/path/prune.c | 284 | ||||
| -rw-r--r-- | src/backend/optimizer/path/xfunc.c | 2192 |
13 files changed, 5723 insertions, 5133 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index d27b31cfbd7..7c4576d6f02 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * allpaths.c-- - * Routines to find possible search paths for processing a query + * Routines to find possible search paths for processing a query * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.10 1997/06/10 07:55:45 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.11 1997/09/07 04:43:27 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -34,226 +34,245 @@ #include "optimizer/geqo.h" #ifdef GEQO -bool _use_geqo_ = true; +bool _use_geqo_ = true; + #else -bool _use_geqo_ = false; +bool _use_geqo_ = false; + #endif -int32 _use_geqo_rels_ = GEQO_RELS; +int32 _use_geqo_rels_ = GEQO_RELS; -static void find_rel_paths(Query *root, List *rels); -static List *find_join_paths(Query *root, List *outer_rels, int levels_left); +static void find_rel_paths(Query * root, List * rels); +static List *find_join_paths(Query * root, List * outer_rels, int levels_left); -/* +/* * find-paths-- - * Finds all possible access paths for executing a query, returning the - * top level list of relation entries. - * + * Finds all possible access paths for executing a query, returning the + * top level list of relation entries. + * * 'rels' is the list of single relation entries appearing in the query */ -List * -find_paths(Query *root, List *rels) +List * +find_paths(Query * root, List * rels) { - int levels_left; - - /* - * Set the number of join (not nesting) levels yet to be processed. - */ - levels_left = length(rels); - - if (levels_left <= 0) - return NIL; - - /* - * Find the base relation paths. - */ - find_rel_paths(root, rels); - - if (levels_left <= 1) { + int levels_left; + /* - * Unsorted single relation, no more processing is required. + * Set the number of join (not nesting) levels yet to be processed. */ - return (rels); - }else { - /* - * this means that joins or sorts are required. - * set selectivities of clauses that have not been set - * by an index. + levels_left = length(rels); + + if (levels_left <= 0) + return NIL; + + /* + * Find the base relation paths. */ - set_rest_relselec(root, rels); + find_rel_paths(root, rels); + + if (levels_left <= 1) + { - return(find_join_paths(root, rels, levels_left-1)); - } + /* + * Unsorted single relation, no more processing is required. + */ + return (rels); + } + else + { + + /* + * this means that joins or sorts are required. set selectivities + * of clauses that have not been set by an index. + */ + set_rest_relselec(root, rels); + + return (find_join_paths(root, rels, levels_left - 1)); + } } -/* +/* * find-rel-paths-- - * Finds all paths available for scanning each relation entry in - * 'rels'. Sequential scan and any available indices are considered - * if possible(indices are not considered for lower nesting levels). - * All unique paths are attached to the relation's 'pathlist' field. - * - * MODIFIES: rels + * Finds all paths available for scanning each relation entry in + * 'rels'. Sequential scan and any available indices are considered + * if possible(indices are not considered for lower nesting levels). + * All unique paths are attached to the relation's 'pathlist' field. + * + * MODIFIES: rels */ static void -find_rel_paths(Query *root, List *rels) +find_rel_paths(Query * root, List * rels) { - List *temp; - Rel *rel; - List *lastpath; - - foreach(temp, rels) { - List *sequential_scan_list; - List *rel_index_scan_list; - List *or_index_scan_list; - - rel = (Rel *)lfirst(temp); - sequential_scan_list = lcons(create_seqscan_path(rel), - NIL); - - rel_index_scan_list = - find_index_paths(root, - rel, - find_relation_indices(root,rel), - rel->clauseinfo, - rel->joininfo); - - or_index_scan_list = - create_or_index_paths(root, rel, rel->clauseinfo); - - rel->pathlist = add_pathlist(rel, - sequential_scan_list, - append(rel_index_scan_list, - or_index_scan_list)); - - /* The unordered path is always the last in the list. - * If it is not the cheapest path, prune it. - */ - lastpath = rel->pathlist; - while(lnext(lastpath)!=NIL) - lastpath=lnext(lastpath); - prune_rel_path(rel, (Path*)lfirst(lastpath)); - /* - * if there is a qualification of sequential scan the selec. - * value is not set -- so set it explicitly -- Sunita - */ - set_rest_selec(root, rel->clauseinfo); - rel->size = compute_rel_size(rel); - rel->width = compute_rel_width(rel); - } - return; + List *temp; + Rel *rel; + List *lastpath; + + foreach(temp, rels) + { + List *sequential_scan_list; + List *rel_index_scan_list; + List *or_index_scan_list; + + rel = (Rel *) lfirst(temp); + sequential_scan_list = lcons(create_seqscan_path(rel), + NIL); + + rel_index_scan_list = + find_index_paths(root, + rel, + find_relation_indices(root, rel), + rel->clauseinfo, + rel->joininfo); + + or_index_scan_list = + create_or_index_paths(root, rel, rel->clauseinfo); + + rel->pathlist = add_pathlist(rel, + sequential_scan_list, + append(rel_index_scan_list, + or_index_scan_list)); + + /* + * The unordered path is always the last in the list. If it is not + * the cheapest path, prune it. + */ + lastpath = rel->pathlist; + while (lnext(lastpath) != NIL) + lastpath = lnext(lastpath); + prune_rel_path(rel, (Path *) lfirst(lastpath)); + + /* + * if there is a qualification of sequential scan the selec. value + * is not set -- so set it explicitly -- Sunita + */ + set_rest_selec(root, rel->clauseinfo); + rel->size = compute_rel_size(rel); + rel->width = compute_rel_width(rel); + } + return; } -/* +/* * find-join-paths-- - * Find all possible joinpaths for a query by successively finding ways - * to join single relations into join relations. + * Find all possible joinpaths for a query by successively finding ways + * to join single relations into join relations. * - * if BushyPlanFlag is set, bushy tree plans will be generated: - * Find all possible joinpaths(bushy trees) for a query by systematically - * finding ways to join relations(both original and derived) together. - * - * 'outer-rels' is the current list of relations for which join paths - * are to be found, i.e., he current list of relations that - * have already been derived. + * if BushyPlanFlag is set, bushy tree plans will be generated: + * Find all possible joinpaths(bushy trees) for a query by systematically + * finding ways to join relations(both original and derived) together. + * + * 'outer-rels' is the current list of relations for which join paths + * are to be found, i.e., he current list of relations that + * have already been derived. * 'levels-left' is the current join level being processed, where '1' is - * the "last" level - * + * the "last" level + * * Returns the final level of join relations, i.e., the relation that is * the result of joining all the original relations togehter. */ -static List * -find_join_paths(Query *root, List *outer_rels, int levels_left) +static List * +find_join_paths(Query * root, List * outer_rels, int levels_left) { - List *x; - List *new_rels; - Rel *rel; - - /******************************************* - * genetic query optimizer entry point * - * <[email protected]> * - *******************************************/ - - if ( (_use_geqo_) && length(root->base_relation_list_) >= _use_geqo_rels_ ) - return lcons(geqo(root), NIL); /* returns *one* Rel, so lcons it */ - - /******************************************* - * rest will be deprecated in case of GEQO * - *******************************************/ - - /* - * Determine all possible pairs of relations to be joined at this level. - * Determine paths for joining these relation pairs and modify 'new-rels' - * accordingly, then eliminate redundant join relations. - */ - new_rels = find_join_rels(root, outer_rels); - - find_all_join_paths(root, new_rels); - - new_rels = prune_joinrels(new_rels); - -#if 0 - /* - ** for each expensive predicate in each path in each distinct rel, - ** consider doing pullup -- JMH - */ - if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF) - foreach(x, new_rels) - xfunc_trypullup((Rel*)lfirst(x)); -#endif + List *x; + List *new_rels; + Rel *rel; - prune_rel_paths(new_rels); + /******************************************* + * genetic query optimizer entry point * + * <[email protected]> * + *******************************************/ + + if ((_use_geqo_) && length(root->base_relation_list_) >= _use_geqo_rels_) + return lcons(geqo(root), NIL); /* returns *one* Rel, so lcons it */ + + /******************************************* + * rest will be deprecated in case of GEQO * + *******************************************/ - if(BushyPlanFlag) { /* - * In case of bushy trees - * if there is still a join between a join relation and another - * relation, add a new joininfo that involves the join relation - * to the joininfo list of the other relation + * Determine all possible pairs of relations to be joined at this + * level. Determine paths for joining these relation pairs and modify + * 'new-rels' accordingly, then eliminate redundant join relations. */ - add_new_joininfos(root, new_rels,outer_rels); - } + new_rels = find_join_rels(root, outer_rels); + + find_all_join_paths(root, new_rels); + + new_rels = prune_joinrels(new_rels); - foreach(x, new_rels) { - rel = (Rel*)lfirst(x); - if ( rel->size <= 0 ) - rel->size = compute_rel_size(rel); - rel->width = compute_rel_width(rel); +#if 0 + + /* + * * for each expensive predicate in each path in each distinct rel, * + * consider doing pullup -- JMH + */ + if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF) + foreach(x, new_rels) + xfunc_trypullup((Rel *) lfirst(x)); +#endif + + prune_rel_paths(new_rels); + + if (BushyPlanFlag) + { + + /* + * In case of bushy trees if there is still a join between a join + * relation and another relation, add a new joininfo that involves + * the join relation to the joininfo list of the other relation + */ + add_new_joininfos(root, new_rels, outer_rels); + } + + foreach(x, new_rels) + { + rel = (Rel *) lfirst(x); + if (rel->size <= 0) + rel->size = compute_rel_size(rel); + rel->width = compute_rel_width(rel); /*#define OPTIMIZER_DEBUG*/ #ifdef OPTIMIZER_DEBUG - printf("levels left: %d\n", levels_left); - debug_print_rel(root, rel); -#endif - } - - if(BushyPlanFlag) { - /* - * prune rels that have been completely incorporated into - * new join rels - */ - outer_rels = prune_oldrels(outer_rels); - /* - * merge join rels if then contain the same list of base rels - */ - outer_rels = merge_joinrels(new_rels,outer_rels); - root->join_relation_list_ = outer_rels; - } - else { - root->join_relation_list_ = new_rels; - } - - if(levels_left == 1) { - if(BushyPlanFlag) - return(final_join_rels(outer_rels)); + printf("levels left: %d\n", levels_left); + debug_print_rel(root, rel); +#endif + } + + if (BushyPlanFlag) + { + + /* + * prune rels that have been completely incorporated into new join + * rels + */ + outer_rels = prune_oldrels(outer_rels); + + /* + * merge join rels if then contain the same list of base rels + */ + outer_rels = merge_joinrels(new_rels, outer_rels); + root->join_relation_list_ = outer_rels; + } else - return(new_rels); - } else { - if(BushyPlanFlag) - return(find_join_paths(root, outer_rels, levels_left - 1)); + { + root->join_relation_list_ = new_rels; + } + + if (levels_left == 1) + { + if (BushyPlanFlag) + return (final_join_rels(outer_rels)); + else + return (new_rels); + } else - return(find_join_paths(root, new_rels, levels_left - 1)); - } + { + if (BushyPlanFlag) + return (find_join_paths(root, outer_rels, levels_left - 1)); + else + return (find_join_paths(root, new_rels, levels_left - 1)); + } } /***************************************************************************** @@ -262,115 +281,147 @@ find_join_paths(Query *root, List *outer_rels, int levels_left) #ifdef OPTIMIZER_DEBUG static void -print_joinclauses(Query *root, List *clauses) +print_joinclauses(Query * root, List * clauses) { - List *l; - extern void print_expr(Node *expr, List *rtable); /* in print.c */ + List *l; + extern void print_expr(Node * expr, List * rtable); /* in print.c */ - foreach(l, clauses) { - CInfo *c = lfirst(l); + foreach(l, clauses) + { + CInfo *c = lfirst(l); - print_expr((Node*)c->clause, root->rtable); - if (lnext(l)) printf(" "); - } + print_expr((Node *) c->clause, root->rtable); + if (lnext(l)) + printf(" "); + } } static void -print_path(Query *root, Path *path, int indent) +print_path(Query * root, Path * path, int indent) { - char *ptype = NULL; - JoinPath *jp; - bool join = false; - int i; - - for(i=0; i < indent; i++) - printf("\t"); - - switch(nodeTag(path)) { - case T_Path: - ptype = "SeqScan"; join=false; break; - case T_IndexPath: - ptype = "IdxScan"; join=false; break; - case T_JoinPath: - ptype = "Nestloop"; join=true; break; - case T_MergePath: - ptype = "MergeJoin"; join=true; break; - case T_HashPath: - ptype = "HashJoin"; join=true; break; - default: - break; - } - if (join) { - int size = path->parent->size; - jp = (JoinPath*)path; - printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); - switch(nodeTag(path)) { + char *ptype = NULL; + JoinPath *jp; + bool join = false; + int i; + + for (i = 0; i < indent; i++) + printf("\t"); + + switch (nodeTag(path)) + { + case T_Path: + ptype = "SeqScan"; + join = false; + break; + case T_IndexPath: + ptype = "IdxScan"; + join = false; + break; + case T_JoinPath: + ptype = "Nestloop"; + join = true; + break; case T_MergePath: + ptype = "MergeJoin"; + join = true; + break; case T_HashPath: - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" clauses=("); - print_joinclauses(root, - ((JoinPath*)path)->pathclauseinfo); - printf(")\n"); - - if (nodeTag(path)==T_MergePath) { - MergePath *mp = (MergePath*)path; - if (mp->outersortkeys || mp->innersortkeys) { - for(i=0; i < indent+1; i++) - printf("\t"); - printf(" sortouter=%d sortinner=%d\n", - ((mp->outersortkeys)?1:0), - ((mp->innersortkeys)?1:0)); - } - } - break; + ptype = "HashJoin"; + join = true; + break; default: - break; + break; } - print_path(root, jp->outerjoinpath, indent+1); - print_path(root, jp->innerjoinpath, indent+1); - } else { - int size = path->parent->size; - int relid = lfirsti(path->parent->relids); - printf("%s(%d) size=%d cost=%f", - ptype, relid, size, path->path_cost); - - if (nodeTag(path)==T_IndexPath) { - List *k, *l; - - printf(" keys="); - foreach (k, path->keys) { - printf("("); - foreach (l, lfirst(k)) { - Var *var = lfirst(l); - printf("%d.%d", var->varnoold, var->varoattno); - if (lnext(l)) printf(", "); + if (join) + { + int size = path->parent->size; + + jp = (JoinPath *) path; + printf("%s size=%d cost=%f\n", ptype, size, path->path_cost); + switch (nodeTag(path)) + { + case T_MergePath: + case T_HashPath: + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" clauses=("); + print_joinclauses(root, + ((JoinPath *) path)->pathclauseinfo); + printf(")\n"); + + if (nodeTag(path) == T_MergePath) + { + MergePath *mp = (MergePath *) path; + + if (mp->outersortkeys || mp->innersortkeys) + { + for (i = 0; i < indent + 1; i++) + printf("\t"); + printf(" sortouter=%d sortinner=%d\n", + ((mp->outersortkeys) ? 1 : 0), + ((mp->innersortkeys) ? 1 : 0)); + } + } + break; + default: + break; } - printf(")"); - if (lnext(k)) printf(", "); - } + print_path(root, jp->outerjoinpath, indent + 1); + print_path(root, jp->innerjoinpath, indent + 1); + } + else + { + int size = path->parent->size; + int relid = lfirsti(path->parent->relids); + + printf("%s(%d) size=%d cost=%f", + ptype, relid, size, path->path_cost); + + if (nodeTag(path) == T_IndexPath) + { + List *k, + *l; + + printf(" keys="); + foreach(k, path->keys) + { + printf("("); + foreach(l, lfirst(k)) + { + Var *var = lfirst(l); + + printf("%d.%d", var->varnoold, var->varoattno); + if (lnext(l)) + printf(", "); + } + printf(")"); + if (lnext(k)) + printf(", "); + } + } + printf("\n"); } - printf("\n"); - } } -static void -debug_print_rel(Query *root, Rel *rel) +static void +debug_print_rel(Query * root, Rel * rel) { - List *l; - - printf("("); - foreach(l, rel->relids) { - printf("%d ", lfirsti(l)); - } - printf("): size=%d width=%d\n", rel->size, rel->width); - - printf("\tpath list:\n"); - foreach (l, rel->pathlist) { - print_path(root, lfirst(l), 1); - } - printf("\tcheapest path:\n"); - print_path(root, rel->cheapestpath, 1); + List *l; + + printf("("); + foreach(l, rel->relids) + { + printf("%d ", lfirsti(l)); + } + printf("): size=%d width=%d\n", rel->size, rel->width); + + printf("\tpath list:\n"); + foreach(l, rel->pathlist) + { + print_path(root, lfirst(l), 1); + } + printf("\tcheapest path:\n"); + print_path(root, rel->cheapestpath, 1); } -#endif /* OPTIMIZER_DEBUG */ + +#endif /* OPTIMIZER_DEBUG */ diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 634e1130794..0ce580754e3 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * clausesel.c-- - * Routines to compute and set clause selectivities + * Routines to compute and set clause selectivities * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.2 1997/09/07 04:43:31 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,7 +23,7 @@ #include "optimizer/cost.h" #include "optimizer/plancat.h" -#include "parser/parsetree.h" /* for getrelid() */ +#include "parser/parsetree.h" /* for getrelid() */ #include "catalog/pg_proc.h" #include "catalog/pg_operator.h" @@ -31,301 +31,353 @@ #include "utils/elog.h" #include "utils/lsyscache.h" -static Cost compute_selec(Query *root, List *clauses, List *or_selectivities); +static Cost compute_selec(Query * root, List * clauses, List * or_selectivities); /**************************************************************************** - * ROUTINES TO SET CLAUSE SELECTIVITIES + * ROUTINES TO SET CLAUSE SELECTIVITIES ****************************************************************************/ -/* +/* * set_clause_selectivities - - * Sets the selectivity field for each of clause in 'clauseinfo-list' - * to 'new-selectivity'. If the selectivity has already been set, reset - * it only if the new one is better. - * + * Sets the selectivity field for each of clause in 'clauseinfo-list' + * to 'new-selectivity'. If the selectivity has already been set, reset + * it only if the new one is better. + * * Returns nothing of interest. * */ void -set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity) +set_clause_selectivities(List * clauseinfo_list, Cost new_selectivity) { - List *temp; - CInfo *clausenode; - Cost cost_clause; - - foreach (temp,clauseinfo_list) { - clausenode = (CInfo*)lfirst(temp); - cost_clause = clausenode->selectivity; - if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) { - clausenode->selectivity = new_selectivity; + List *temp; + CInfo *clausenode; + Cost cost_clause; + + foreach(temp, clauseinfo_list) + { + clausenode = (CInfo *) lfirst(temp); + cost_clause = clausenode->selectivity; + if (FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) + { + clausenode->selectivity = new_selectivity; + } } - } } -/* +/* * product_selec - - * Multiplies the selectivities of each clause in 'clauseinfo-list'. - * + * Multiplies the selectivities of each clause in 'clauseinfo-list'. + * * Returns a flonum corresponding to the selectivity of 'clauseinfo-list'. */ Cost -product_selec(List *clauseinfo_list) +product_selec(List * clauseinfo_list) { - Cost result = 1.0; - if (clauseinfo_list!=NIL) { - List *xclausenode = NIL; - Cost temp; - - foreach(xclausenode,clauseinfo_list) { - temp = ((CInfo *)lfirst(xclausenode))->selectivity; - result = result * temp; + Cost result = 1.0; + + if (clauseinfo_list != NIL) + { + List *xclausenode = NIL; + Cost temp; + + foreach(xclausenode, clauseinfo_list) + { + temp = ((CInfo *) lfirst(xclausenode))->selectivity; + result = result * temp; + } } - } - return(result); + return (result); } -/* +/* * set_rest_relselec - - * Scans through clauses on each relation and assigns a selectivity to - * those clauses that haven't been assigned a selectivity by an index. - * + * Scans through clauses on each relation and assigns a selectivity to + * those clauses that haven't been assigned a selectivity by an index. + * * Returns nothing of interest. * MODIFIES: selectivities of the various rel's clauseinfo - * slots. + * slots. */ void -set_rest_relselec(Query *root, List *rel_list) +set_rest_relselec(Query * root, List * rel_list) { - Rel *rel; - List *x; + Rel *rel; + List *x; - foreach (x,rel_list) { - rel = (Rel*)lfirst(x); - set_rest_selec(root, rel->clauseinfo); - } + foreach(x, rel_list) + { + rel = (Rel *) lfirst(x); + set_rest_selec(root, rel->clauseinfo); + } } -/* +/* * set_rest_selec - - * Sets the selectivity fields for those clauses within a single - * relation's 'clauseinfo-list' that haven't already been set. - * + * Sets the selectivity fields for those clauses within a single + * relation's 'clauseinfo-list' that haven't already been set. + * * Returns nothing of interest. - * + * */ void -set_rest_selec(Query *root, List *clauseinfo_list) +set_rest_selec(Query * root, List * clauseinfo_list) { - List *temp = NIL; - CInfo *clausenode = (CInfo*)NULL; - Cost cost_clause; - - foreach (temp,clauseinfo_list) { - clausenode = (CInfo*)lfirst(temp); - cost_clause = clausenode->selectivity; + List *temp = NIL; + CInfo *clausenode = (CInfo *) NULL; + Cost cost_clause; - /* - * Check to see if the selectivity of this clause or any 'or' - * subclauses (if any) haven't been set yet. - */ - if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) { - clausenode->selectivity = - compute_clause_selec(root, - (Node*)clausenode->clause, - lcons(makeFloat(cost_clause), NIL)); + foreach(temp, clauseinfo_list) + { + clausenode = (CInfo *) lfirst(temp); + cost_clause = clausenode->selectivity; + + /* + * Check to see if the selectivity of this clause or any 'or' + * subclauses (if any) haven't been set yet. + */ + if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) + { + clausenode->selectivity = + compute_clause_selec(root, + (Node *) clausenode->clause, + lcons(makeFloat(cost_clause), NIL)); + } } - } } /**************************************************************************** - * ROUTINES TO COMPUTE SELECTIVITIES + * ROUTINES TO COMPUTE SELECTIVITIES ****************************************************************************/ -/* +/* * compute_clause_selec - - * Given a clause, this routine will compute the selectivity of the - * clause by calling 'compute_selec' with the appropriate parameters - * and possibly use that return value to compute the real selectivity - * of a clause. - * + * Given a clause, this routine will compute the selectivity of the + * clause by calling 'compute_selec' with the appropriate parameters + * and possibly use that return value to compute the real selectivity + * of a clause. + * * 'or-selectivities' are selectivities that have already been assigned - * to subclauses of an 'or' clause. - * + * to subclauses of an 'or' clause. + * * Returns a flonum corresponding to the clause selectivity. - * + * */ Cost -compute_clause_selec(Query *root, Node *clause, List *or_selectivities) +compute_clause_selec(Query * root, Node * clause, List * or_selectivities) { - if (!is_opclause (clause)) { - /* if it's not an operator clause, then it is a boolean clause -jolly*/ - /* - * Boolean variables get a selectivity of 1/2. - */ - return(0.1); - } else if (not_clause (clause)) { - /* - * 'not' gets "1.0 - selectivity-of-inner-clause". - */ - return (1.000000 - compute_selec(root, - lcons(get_notclausearg((Expr*)clause), - NIL), - or_selectivities)); - } else if (or_clause(clause)) { - /* - * Both 'or' and 'and' clauses are evaluated as described in - * (compute_selec). - */ - return (compute_selec(root, - ((Expr*)clause)->args, or_selectivities)); - } else { - return(compute_selec(root, - lcons(clause,NIL),or_selectivities)); - } + if (!is_opclause(clause)) + { + + /* + * if it's not an operator clause, then it is a boolean clause + * -jolly + */ + + /* + * Boolean variables get a selectivity of 1/2. + */ + return (0.1); + } + else if (not_clause(clause)) + { + + /* + * 'not' gets "1.0 - selectivity-of-inner-clause". + */ + return (1.000000 - compute_selec(root, + lcons(get_notclausearg((Expr *) clause), + NIL), + or_selectivities)); + } + else if (or_clause(clause)) + { + + /* + * Both 'or' and 'and' clauses are evaluated as described in + * (compute_selec). + */ + return (compute_selec(root, + ((Expr *) clause)->args, or_selectivities)); + } + else + { + return (compute_selec(root, + lcons(clause, NIL), or_selectivities)); + } } -/* - * compute_selec - - * Computes the selectivity of a clause. - * - * If there is more than one clause in the argument 'clauses', then the - * desired selectivity is that of an 'or' clause. Selectivities for an - * 'or' clause such as (OR a b) are computed by finding the selectivity - * of a (s1) and b (s2) and computing s1+s2 - s1*s2. - * - * In addition, if the clause is an 'or' clause, individual selectivities - * may have already been assigned by indices to subclauses. These values - * are contained in the list 'or-selectivities'. - * +/* + * compute_selec - + * Computes the selectivity of a clause. + * + * If there is more than one clause in the argument 'clauses', then the + * desired selectivity is that of an 'or' clause. Selectivities for an + * 'or' clause such as (OR a b) are computed by finding the selectivity + * of a (s1) and b (s2) and computing s1+s2 - s1*s2. + * + * In addition, if the clause is an 'or' clause, individual selectivities + * may have already been assigned by indices to subclauses. These values + * are contained in the list 'or-selectivities'. + * * Returns the clause selectivity as a flonum. - * + * */ -static Cost -compute_selec(Query *root, List *clauses, List *or_selectivities) +static Cost +compute_selec(Query * root, List * clauses, List * or_selectivities) { - Cost s1 = 0; - List *clause = lfirst(clauses); - - if (clauses==NULL) { - s1 = 1.0; - } else if (IsA(clause,Param)) { - /* XXX How're we handling this before?? -ay */ - s1 = 1.0; - } else if (IsA(clause,Const)) { - s1 = ((bool) ((Const*) clause)->constvalue) ? 1.0 : 0.0; - } else if (IsA(clause,Var)) { - Oid relid = getrelid(((Var*)clause)->varno, - root->rtable); + Cost s1 = 0; + List *clause = lfirst(clauses); - /* - * we have a bool Var. This is exactly equivalent to the clause: - * reln.attribute = 't' - * so we compute the selectivity as if that is what we have. The - * magic #define constants are a hack. I didn't want to have to - * do system cache look ups to find out all of that info. - */ + if (clauses == NULL) + { + s1 = 1.0; + } + else if (IsA(clause, Param)) + { + /* XXX How're we handling this before?? -ay */ + s1 = 1.0; + } + else if (IsA(clause, Const)) + { + s1 = ((bool) ((Const *) clause)->constvalue) ? 1.0 : 0.0; + } + else if (IsA(clause, Var)) + { + Oid relid = getrelid(((Var *) clause)->varno, + root->rtable); - s1 = restriction_selectivity(EqualSelectivityProcedure, - BooleanEqualOperator, - relid, - ((Var*)clause)->varoattno, - "t", - _SELEC_CONSTANT_RIGHT_); - } else if (or_selectivities) { - /* If s1 has already been assigned by an index, use that value. */ - List *this_sel = lfirst(or_selectivities); - - s1 = floatVal(this_sel); - } else if (is_funcclause((Node*)clause)) { - /* this isn't an Oper, it's a Func!! */ - /* - ** This is not an operator, so we guess at the selectivity. - ** THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE - ** ABLE TO HAVE SELECTIVITIES THEMSELVES. - ** -- JMH 7/9/92 - */ - s1 = 0.1; - } else if (NumRelids((Node*) clause) == 1) { - /* ...otherwise, calculate s1 from 'clauses'. - * The clause is not a join clause, since there is - * only one relid in the clause. The clause - * selectivity will be based on the operator - * selectivity and operand values. - */ - Oid opno = ((Oper*)((Expr*)clause)->oper)->opno; - RegProcedure oprrest = get_oprrest(opno); - Oid relid; - int relidx; - AttrNumber attno; - Datum constval; - int flag; - - get_relattval((Node*)clause, &relidx, &attno, &constval, &flag); - relid = getrelid(relidx, root->rtable); - - /* if the oprrest procedure is missing for whatever reason, - use a selectivity of 0.5*/ - if (!oprrest) - s1 = (Cost) (0.5); + /* + * we have a bool Var. This is exactly equivalent to the clause: + * reln.attribute = 't' so we compute the selectivity as if that + * is what we have. The magic #define constants are a hack. I + * didn't want to have to do system cache look ups to find out all + * of that info. + */ + + s1 = restriction_selectivity(EqualSelectivityProcedure, + BooleanEqualOperator, + relid, + ((Var *) clause)->varoattno, + "t", + _SELEC_CONSTANT_RIGHT_); + } + else if (or_selectivities) + { + /* If s1 has already been assigned by an index, use that value. */ + List *this_sel = lfirst(or_selectivities); + + s1 = floatVal(this_sel); + } + else if (is_funcclause((Node *) clause)) + { + /* this isn't an Oper, it's a Func!! */ + + /* + * * This is not an operator, so we guess at the selectivity. * + * THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE * ABLE + * TO HAVE SELECTIVITIES THEMSELVES. * -- JMH 7/9/92 + */ + s1 = 0.1; + } + else if (NumRelids((Node *) clause) == 1) + { + + /* + * ...otherwise, calculate s1 from 'clauses'. The clause is not a + * join clause, since there is only one relid in the clause. The + * clause selectivity will be based on the operator selectivity + * and operand values. + */ + Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; + RegProcedure oprrest = get_oprrest(opno); + Oid relid; + int relidx; + AttrNumber attno; + Datum constval; + int flag; + + get_relattval((Node *) clause, &relidx, &attno, &constval, &flag); + relid = getrelid(relidx, root->rtable); + + /* + * if the oprrest procedure is missing for whatever reason, use a + * selectivity of 0.5 + */ + if (!oprrest) + s1 = (Cost) (0.5); + else if (attno == InvalidAttrNumber) + { + + /* + * attno can be Invalid if the clause had a function in it, + * i.e. WHERE myFunc(f) = 10 + */ + /* this should be FIXED somehow to use function selectivity */ + s1 = (Cost) (0.5); + } + else + s1 = (Cost) restriction_selectivity(oprrest, + opno, + relid, + attno, + (char *) constval, + flag); + + } else - if (attno == InvalidAttrNumber) { - /* attno can be Invalid if the clause had a function in it, - i.e. WHERE myFunc(f) = 10 */ - /* this should be FIXED somehow to use function selectivity */ - s1 = (Cost) (0.5); - } else - s1 = (Cost) restriction_selectivity(oprrest, - opno, - relid, - attno, - (char *)constval, - flag); - - } else { - /* The clause must be a join clause. The clause - * selectivity will be based on the relations to be - * scanned and the attributes they are to be joined - * on. + { + + /* + * The clause must be a join clause. The clause selectivity will + * be based on the relations to be scanned and the attributes they + * are to be joined on. + */ + Oid opno = ((Oper *) ((Expr *) clause)->oper)->opno; + RegProcedure oprjoin = get_oprjoin(opno); + int relid1, + relid2; + AttrNumber attno1, + attno2; + + get_rels_atts((Node *) clause, &relid1, &attno1, &relid2, &attno2); + relid1 = getrelid(relid1, root->rtable); + relid2 = getrelid(relid2, root->rtable); + + /* + * if the oprjoin procedure is missing for whatever reason, use a + * selectivity of 0.5 + */ + if (!oprjoin) + s1 = (Cost) (0.5); + else + s1 = (Cost) join_selectivity(oprjoin, + opno, + relid1, + attno1, + relid2, + attno2); + } + + /* + * A null clause list eliminates no tuples, so return a selectivity of + * 1.0. If there is only one clause, the selectivity is not that of + * an 'or' clause, but rather that of the single clause. */ - Oid opno = ((Oper*)((Expr*)clause)->oper)->opno; - RegProcedure oprjoin = get_oprjoin (opno); - int relid1, relid2; - AttrNumber attno1, attno2; - - get_rels_atts((Node*)clause, &relid1, &attno1, &relid2, &attno2); - relid1 = getrelid(relid1, root->rtable); - relid2 = getrelid(relid2, root->rtable); - - /* if the oprjoin procedure is missing for whatever reason, - use a selectivity of 0.5*/ - if (!oprjoin) - s1 = (Cost) (0.5); - else - s1 = (Cost) join_selectivity(oprjoin, - opno, - relid1, - attno1, - relid2, - attno2); - } - - /* A null clause list eliminates no tuples, so return a selectivity - * of 1.0. If there is only one clause, the selectivity is not - * that of an 'or' clause, but rather that of the single clause. - */ - - if (length (clauses) < 2) { - return(s1); - } else { - /* Compute selectivity of the 'or'ed subclauses. */ - /* Added check for taking lnext(NIL). -- JMH 3/9/92 */ - Cost s2; - - if (or_selectivities != NIL) - s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities)); + + if (length(clauses) < 2) + { + return (s1); + } else - s2 = compute_selec(root, lnext(clauses), NIL); - return(s1 + s2 - s1 * s2); - } -} + { + /* Compute selectivity of the 'or'ed subclauses. */ + /* Added check for taking lnext(NIL). -- JMH 3/9/92 */ + Cost s2; + if (or_selectivities != NIL) + s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities)); + else + s2 = compute_selec(root, lnext(clauses), NIL); + return (s1 + s2 - s1 * s2); + } +} diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 35453fb3870..2873e62c48c 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * costsize.c-- - * Routines to compute (and set) relation sizes and path costs + * Routines to compute (and set) relation sizes and path costs * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.16 1997/08/19 21:31:48 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.17 1997/09/07 04:43:33 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,15 +17,15 @@ #include <math.h> #ifdef HAVE_LIMITS_H -# include <limits.h> -# ifndef MAXINT -# define MAXINT INT_MAX -# endif +#include <limits.h> +#ifndef MAXINT +#define MAXINT INT_MAX +#endif #else -# ifdef HAVE_VALUES_H -# include <values.h> -# endif -#endif +#ifdef HAVE_VALUES_H +#include <values.h> +#endif +#endif #include <utils/lsyscache.h> #include "nodes/relation.h" @@ -35,77 +35,81 @@ #include "optimizer/keys.h" #include "optimizer/tlist.h" -#include "storage/bufmgr.h" /* for BLCKSZ */ +#include "storage/bufmgr.h" /* for BLCKSZ */ -extern int NBuffers; +extern int NBuffers; -static int compute_attribute_width(TargetEntry *tlistentry); -static double base_log(double x, double b); -static int compute_targetlist_width(List *targetlist); +static int compute_attribute_width(TargetEntry * tlistentry); +static double base_log(double x, double b); +static int compute_targetlist_width(List * targetlist); -int _disable_cost_ = 30000000; - -bool _enable_seqscan_ = true; -bool _enable_indexscan_ = true; -bool _enable_sort_ = true; -bool _enable_hash_ = true; -bool _enable_nestloop_ = true; -bool _enable_mergesort_ = true; -bool _enable_hashjoin_ = true; +int _disable_cost_ = 30000000; -Cost _cpu_page_wight_ = _CPU_PAGE_WEIGHT_; -Cost _cpu_index_page_wight_ = _CPU_INDEX_PAGE_WEIGHT_; +bool _enable_seqscan_ = true; +bool _enable_indexscan_ = true; +bool _enable_sort_ = true; +bool _enable_hash_ = true; +bool _enable_nestloop_ = true; +bool _enable_mergesort_ = true; +bool _enable_hashjoin_ = true; -/* +Cost _cpu_page_wight_ = _CPU_PAGE_WEIGHT_; +Cost _cpu_index_page_wight_ = _CPU_INDEX_PAGE_WEIGHT_; + +/* * cost_seqscan-- - * Determines and returns the cost of scanning a relation sequentially. - * If the relation is a temporary to be materialized from a query - * embedded within a data field (determined by 'relid' containing an - * attribute reference), then a predetermined constant is returned (we - * have NO IDEA how big the result of a POSTQUEL procedure is going to - * be). - * - * disk = p - * cpu = *CPU-PAGE-WEIGHT* * t - * + * Determines and returns the cost of scanning a relation sequentially. + * If the relation is a temporary to be materialized from a query + * embedded within a data field (determined by 'relid' containing an + * attribute reference), then a predetermined constant is returned (we + * have NO IDEA how big the result of a POSTQUEL procedure is going to + * be). + * + * disk = p + * cpu = *CPU-PAGE-WEIGHT* * t + * * 'relid' is the relid of the relation to be scanned * 'relpages' is the number of pages in the relation to be scanned - * (as determined from the system catalogs) + * (as determined from the system catalogs) * 'reltuples' is the number of tuples in the relation to be scanned - * + * * Returns a flonum. - * + * */ Cost cost_seqscan(int relid, int relpages, int reltuples) { - Cost temp = 0; + Cost temp = 0; - if ( !_enable_seqscan_ ) - temp += _disable_cost_; + if (!_enable_seqscan_) + temp += _disable_cost_; - if (relid < 0) { - /* - * cost of sequentially scanning a materialized temporary relation - */ - temp += _TEMP_SCAN_COST_; - } else { - temp += relpages; - temp += _cpu_page_wight_ * reltuples; - } - Assert(temp >= 0); - return(temp); + if (relid < 0) + { + + /* + * cost of sequentially scanning a materialized temporary relation + */ + temp += _TEMP_SCAN_COST_; + } + else + { + temp += relpages; + temp += _cpu_page_wight_ * reltuples; + } + Assert(temp >= 0); + return (temp); } -/* +/* * cost_index-- - * Determines and returns the cost of scanning a relation using an index. - * - * disk = expected-index-pages + expected-data-pages - * cpu = *CPU-PAGE-WEIGHT* * - * (expected-index-tuples + expected-data-tuples) - * + * Determines and returns the cost of scanning a relation using an index. + * + * disk = expected-index-pages + expected-data-pages + * cpu = *CPU-PAGE-WEIGHT* * + * (expected-index-tuples + expected-data-tuples) + * * 'indexid' is the index OID * 'expected-indexpages' is the number of index pages examined in the scan * 'selec' is the selectivity of the index @@ -113,100 +117,102 @@ cost_seqscan(int relid, int relpages, int reltuples) * 'reltuples' is the number of tuples in the main relation * 'indexpages' is the number of pages in the index relation * 'indextuples' is the number of tuples in the index relation - * + * * Returns a flonum. - * + * */ Cost cost_index(Oid indexid, - int expected_indexpages, - Cost selec, - int relpages, - int reltuples, - int indexpages, - int indextuples, - bool is_injoin) + int expected_indexpages, + Cost selec, + int relpages, + int reltuples, + int indexpages, + int indextuples, + bool is_injoin) { - Cost temp; - double temp2; + Cost temp; + double temp2; + + temp = (Cost) 0; - temp = (Cost) 0; + if (!_enable_indexscan_ && !is_injoin) + temp += _disable_cost_; - if (!_enable_indexscan_ && !is_injoin) - temp += _disable_cost_; + /* expected index relation pages */ + temp += expected_indexpages; - /* expected index relation pages */ - temp += expected_indexpages; + /* expected base relation pages */ + temp2 = (reltuples == 0) ? (double) 0 : (double) relpages / reltuples; + temp2 = temp2 * (double) selec *indextuples; - /* expected base relation pages */ - temp2 = ( reltuples == 0 ) ? (double)0 : (double)relpages/reltuples; - temp2 = temp2 * (double)selec * indextuples; - temp += Min (relpages, (int)ceil (temp2)); + temp += Min(relpages, (int) ceil(temp2)); - /* per index tuples */ - temp = temp + (_cpu_index_page_wight_ * selec * indextuples); + /* per index tuples */ + temp = temp + (_cpu_index_page_wight_ * selec * indextuples); - /* per heap tuples */ - temp = temp + (_cpu_page_wight_ * selec * reltuples); + /* per heap tuples */ + temp = temp + (_cpu_page_wight_ * selec * reltuples); - Assert(temp >= 0); - return(temp); + Assert(temp >= 0); + return (temp); } -/* +/* * cost_sort-- - * Determines and returns the cost of sorting a relation by considering - * 1. the cost of doing an external sort: XXX this is probably too low - * disk = (p lg p) - * cpu = *CPU-PAGE-WEIGHT* * (t lg t) - * 2. the cost of reading the sort result into memory (another seqscan) - * unless 'noread' is set - * + * Determines and returns the cost of sorting a relation by considering + * 1. the cost of doing an external sort: XXX this is probably too low + * disk = (p lg p) + * cpu = *CPU-PAGE-WEIGHT* * (t lg t) + * 2. the cost of reading the sort result into memory (another seqscan) + * unless 'noread' is set + * * 'keys' is a list of sort keys * 'tuples' is the number of tuples in the relation * 'width' is the average tuple width in bytes * 'noread' is a flag indicating that the sort result can remain on disk - * (i.e., the sort result is the result relation) - * + * (i.e., the sort result is the result relation) + * * Returns a flonum. - * + * */ Cost -cost_sort(List *keys, int tuples, int width, bool noread) +cost_sort(List * keys, int tuples, int width, bool noread) { - Cost temp = 0; - int npages = page_size (tuples,width); - Cost pages = (Cost)npages; - Cost numTuples = tuples; - - if ( !_enable_sort_ ) - temp += _disable_cost_ ; - if (tuples == 0 || keys==NULL) + Cost temp = 0; + int npages = page_size(tuples, width); + Cost pages = (Cost) npages; + Cost numTuples = tuples; + + if (!_enable_sort_) + temp += _disable_cost_; + if (tuples == 0 || keys == NULL) { - Assert(temp >= 0); - return(temp); + Assert(temp >= 0); + return (temp); } - temp += pages * base_log((double)pages, (double)2.0); + temp += pages * base_log((double) pages, (double) 2.0); - /* - * could be base_log(pages, NBuffers), but we are only doing 2-way merges - */ - temp += _cpu_page_wight_ * - numTuples * base_log((double)pages,(double)2.0); + /* + * could be base_log(pages, NBuffers), but we are only doing 2-way + * merges + */ + temp += _cpu_page_wight_ * + numTuples * base_log((double) pages, (double) 2.0); - if( !noread ) - temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples); - Assert(temp >= 0); + if (!noread) + temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples); + Assert(temp >= 0); - return(temp); + return (temp); } -/* +/* * cost_result-- - * Determines and returns the cost of writing a relation of 'tuples' - * tuples of 'width' bytes out to a result relation. - * + * Determines and returns the cost of writing a relation of 'tuples' + * tuples of 'width' bytes out to a result relation. + * * Returns a flonum. * */ @@ -214,257 +220,273 @@ cost_sort(List *keys, int tuples, int width, bool noread) Cost cost_result(int tuples, int width) { - Cost temp =0; - temp = temp + page_size(tuples,width); - temp = temp + _cpu_page_wight_ * tuples; - Assert(temp >= 0); - return(temp); + Cost temp = 0; + + temp = temp + page_size(tuples, width); + temp = temp + _cpu_page_wight_ * tuples; + Assert(temp >= 0); + return (temp); } + #endif -/* +/* * cost_nestloop-- - * Determines and returns the cost of joining two relations using the - * nested loop algorithm. - * + * Determines and returns the cost of joining two relations using the + * nested loop algorithm. + * * 'outercost' is the (disk+cpu) cost of scanning the outer relation * 'innercost' is the (disk+cpu) cost of scanning the inner relation * 'outertuples' is the number of tuples in the outer relation - * + * * Returns a flonum. * */ Cost cost_nestloop(Cost outercost, - Cost innercost, - int outertuples, - int innertuples, - int outerpages, - bool is_indexjoin) + Cost innercost, + int outertuples, + int innertuples, + int outerpages, + bool is_indexjoin) { - Cost temp =0; + Cost temp = 0; - if ( !_enable_nestloop_ ) - temp += _disable_cost_; - temp += outercost; - temp += outertuples * innercost; - Assert(temp >= 0); + if (!_enable_nestloop_) + temp += _disable_cost_; + temp += outercost; + temp += outertuples * innercost; + Assert(temp >= 0); - return(temp); + return (temp); } -/* +/* * cost_mergesort-- - * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the - * outer and inner relations - * 'outersortkeys' and 'innersortkeys' are lists of the keys to be used - * to sort the outer and inner relations - * 'outertuples' and 'innertuples' are the number of tuples in the outer - * and inner relations - * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) - * of the tuples of the outer and inner relations - * + * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the + * outer and inner relations + * 'outersortkeys' and 'innersortkeys' are lists of the keys to be used + * to sort the outer and inner relations + * 'outertuples' and 'innertuples' are the number of tuples in the outer + * and inner relations + * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) + * of the tuples of the outer and inner relations + * * Returns a flonum. - * + * */ Cost cost_mergesort(Cost outercost, - Cost innercost, - List *outersortkeys, - List *innersortkeys, - int outersize, - int innersize, - int outerwidth, - int innerwidth) + Cost innercost, + List * outersortkeys, + List * innersortkeys, + int outersize, + int innersize, + int outerwidth, + int innerwidth) { - Cost temp = 0; - - if ( !_enable_mergesort_ ) - temp += _disable_cost_; - - temp += outercost; - temp += innercost; - temp += cost_sort(outersortkeys,outersize,outerwidth,false); - temp += cost_sort(innersortkeys,innersize,innerwidth,false); - temp += _cpu_page_wight_ * (outersize + innersize); - Assert(temp >= 0); - - return(temp); + Cost temp = 0; + + if (!_enable_mergesort_) + temp += _disable_cost_; + + temp += outercost; + temp += innercost; + temp += cost_sort(outersortkeys, outersize, outerwidth, false); + temp += cost_sort(innersortkeys, innersize, innerwidth, false); + temp += _cpu_page_wight_ * (outersize + innersize); + Assert(temp >= 0); + + return (temp); } -/* - * cost_hashjoin-- XXX HASH - * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the - * outer and inner relations - * 'outerkeys' and 'innerkeys' are lists of the keys to be used - * to hash the outer and inner relations - * 'outersize' and 'innersize' are the number of tuples in the outer - * and inner relations - * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) - * of the tuples of the outer and inner relations - * +/* + * cost_hashjoin-- XXX HASH + * 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the + * outer and inner relations + * 'outerkeys' and 'innerkeys' are lists of the keys to be used + * to hash the outer and inner relations + * 'outersize' and 'innersize' are the number of tuples in the outer + * and inner relations + * 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes) + * of the tuples of the outer and inner relations + * * Returns a flonum. */ Cost cost_hashjoin(Cost outercost, - Cost innercost, - List *outerkeys, - List *innerkeys, - int outersize, - int innersize, - int outerwidth, - int innerwidth) + Cost innercost, + List * outerkeys, + List * innerkeys, + int outersize, + int innersize, + int outerwidth, + int innerwidth) { - Cost temp = 0; - int outerpages = page_size (outersize,outerwidth); - int innerpages = page_size (innersize,innerwidth); - int nrun = ceil((double)outerpages/(double)NBuffers); - - if (outerpages < innerpages) - return _disable_cost_; - if ( !_enable_hashjoin_ ) - temp += _disable_cost_; - /* - temp += outercost + (nrun + 1) * innercost; - * - * the innercost shouldn't be used it. Instead the - * cost of hashing the innerpath should be used - * - * ASSUME innercost is 1 for now -- a horrible hack - * - jolly - temp += outercost + (nrun + 1); - * - * But we must add innercost to result. - vadim 04/24/97 - */ - temp += outercost + innercost + (nrun + 1); - - temp += _cpu_page_wight_ * (outersize + nrun * innersize); - Assert(temp >= 0); - - return(temp); + Cost temp = 0; + int outerpages = page_size(outersize, outerwidth); + int innerpages = page_size(innersize, innerwidth); + int nrun = ceil((double) outerpages / (double) NBuffers); + + if (outerpages < innerpages) + return _disable_cost_; + if (!_enable_hashjoin_) + temp += _disable_cost_; + + /* + * temp += outercost + (nrun + 1) * innercost; + * + * the innercost shouldn't be used it. Instead the cost of hashing the + * innerpath should be used + * + * ASSUME innercost is 1 for now -- a horrible hack - jolly temp += + * outercost + (nrun + 1); + * + * But we must add innercost to result. - vadim 04/24/97 + */ + temp += outercost + innercost + (nrun + 1); + + temp += _cpu_page_wight_ * (outersize + nrun * innersize); + Assert(temp >= 0); + + return (temp); } -/* +/* * compute-rel-size-- - * Computes the size of each relation in 'rel-list' (after applying - * restrictions), by multiplying the selectivity of each restriction - * by the original size of the relation. - * - * Sets the 'size' field for each relation entry with this computed size. - * + * Computes the size of each relation in 'rel-list' (after applying + * restrictions), by multiplying the selectivity of each restriction + * by the original size of the relation. + * + * Sets the 'size' field for each relation entry with this computed size. + * * Returns the size. */ -int compute_rel_size(Rel *rel) +int +compute_rel_size(Rel * rel) { - Cost temp; - int temp1; - - temp = rel->tuples * product_selec(rel->clauseinfo); - Assert(temp >= 0); - if (temp >= (MAXINT - 1)) { - temp1 = MAXINT; - } else { - temp1 = ceil((double) temp); - } - Assert(temp1 >= 0); - Assert(temp1 <= MAXINT); - return(temp1); + Cost temp; + int temp1; + + temp = rel->tuples * product_selec(rel->clauseinfo); + Assert(temp >= 0); + if (temp >= (MAXINT - 1)) + { + temp1 = MAXINT; + } + else + { + temp1 = ceil((double) temp); + } + Assert(temp1 >= 0); + Assert(temp1 <= MAXINT); + return (temp1); } -/* +/* * compute-rel-width-- - * Computes the width in bytes of a tuple from 'rel'. - * + * Computes the width in bytes of a tuple from 'rel'. + * * Returns the width of the tuple as a fixnum. */ int -compute_rel_width(Rel *rel) +compute_rel_width(Rel * rel) { - return (compute_targetlist_width(get_actual_tlist(rel->targetlist))); + return (compute_targetlist_width(get_actual_tlist(rel->targetlist))); } -/* +/* * compute-targetlist-width-- - * Computes the width in bytes of a tuple made from 'targetlist'. - * + * Computes the width in bytes of a tuple made from 'targetlist'. + * * Returns the width of the tuple as a fixnum. */ static int -compute_targetlist_width(List *targetlist) +compute_targetlist_width(List * targetlist) { - List *temp_tl; - int tuple_width = 0; - - foreach (temp_tl, targetlist) { - tuple_width = tuple_width + - compute_attribute_width(lfirst(temp_tl)); - } - return(tuple_width); + List *temp_tl; + int tuple_width = 0; + + foreach(temp_tl, targetlist) + { + tuple_width = tuple_width + + compute_attribute_width(lfirst(temp_tl)); + } + return (tuple_width); } -/* +/* * compute-attribute-width-- - * Given a target list entry, find the size in bytes of the attribute. - * - * If a field is variable-length, it is assumed to be at least the size - * of a TID field. - * + * Given a target list entry, find the size in bytes of the attribute. + * + * If a field is variable-length, it is assumed to be at least the size + * of a TID field. + * * Returns the width of the attribute as a fixnum. */ static int -compute_attribute_width(TargetEntry *tlistentry) +compute_attribute_width(TargetEntry * tlistentry) { - int width = get_typlen(tlistentry->resdom->restype); - if (width < 0) - return(_DEFAULT_ATTRIBUTE_WIDTH_); - else - return(width); + int width = get_typlen(tlistentry->resdom->restype); + + if (width < 0) + return (_DEFAULT_ATTRIBUTE_WIDTH_); + else + return (width); } -/* +/* * compute-joinrel-size-- - * Computes the size of the join relation 'joinrel'. - * + * Computes the size of the join relation 'joinrel'. + * * Returns a fixnum. */ int -compute_joinrel_size(JoinPath *joinpath) +compute_joinrel_size(JoinPath * joinpath) { - Cost temp = 1.0; - int temp1 = 0; - - temp *= ((Path*)joinpath->outerjoinpath)->parent->size; - temp *= ((Path*)joinpath->innerjoinpath)->parent->size; - - temp = temp * product_selec(joinpath->pathclauseinfo); - if (temp >= (MAXINT -1)) { - temp1 = MAXINT; - } else { - /* should be ceil here, we don't want joinrel size's of one, do we? */ - temp1 = ceil((double)temp); - } - Assert(temp1 >= 0); - - return(temp1); + Cost temp = 1.0; + int temp1 = 0; + + temp *= ((Path *) joinpath->outerjoinpath)->parent->size; + temp *= ((Path *) joinpath->innerjoinpath)->parent->size; + + temp = temp * product_selec(joinpath->pathclauseinfo); + if (temp >= (MAXINT - 1)) + { + temp1 = MAXINT; + } + else + { + + /* + * should be ceil here, we don't want joinrel size's of one, do + * we? + */ + temp1 = ceil((double) temp); + } + Assert(temp1 >= 0); + + return (temp1); } -/* +/* * page-size-- - * Returns an estimate of the number of pages covered by a given - * number of tuples of a given width (size in bytes). + * Returns an estimate of the number of pages covered by a given + * number of tuples of a given width (size in bytes). */ -int page_size(int tuples, int width) +int +page_size(int tuples, int width) { - int temp =0; + int temp = 0; - temp = ceil((double)(tuples * (width + sizeof(HeapTupleData))) - / BLCKSZ); - Assert(temp >= 0); - return(temp); + temp = ceil((double) (tuples * (width + sizeof(HeapTupleData))) + / BLCKSZ); + Assert(temp >= 0); + return (temp); } static double base_log(double x, double b) { - return(log(x)/log(b)); + return (log(x) / log(b)); } diff --git a/src/backend/optimizer/path/hashutils.c b/src/backend/optimizer/path/hashutils.c index cdbd9b6d901..5ec592ad1f9 100644 --- a/src/backend/optimizer/path/hashutils.c +++ b/src/backend/optimizer/path/hashutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * hashutils.c-- - * Utilities for finding applicable merge clauses and pathkeys + * Utilities for finding applicable merge clauses and pathkeys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.2 1997/09/07 04:43:34 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,101 +20,109 @@ #include "optimizer/clauses.h" -static HInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list); +static HInfo *match_hashop_hashinfo(Oid hashop, List * hashinfo_list); -/* +/* * group-clauses-by-hashop-- - * If a join clause node in 'clauseinfo-list' is hashjoinable, store - * it within a hashinfo node containing other clause nodes with the same - * hash operator. - * + * If a join clause node in 'clauseinfo-list' is hashjoinable, store + * it within a hashinfo node containing other clause nodes with the same + * hash operator. + * * 'clauseinfo-list' is the list of clauseinfo nodes * 'inner-relid' is the relid of the inner join relation - * + * * Returns the new list of hashinfo nodes. - * + * */ -List * -group_clauses_by_hashop(List *clauseinfo_list, - int inner_relid) +List * +group_clauses_by_hashop(List * clauseinfo_list, + int inner_relid) { - List *hashinfo_list = NIL; - CInfo *clauseinfo = (CInfo*)NULL; - List *i = NIL; - Oid hashjoinop = 0; - - foreach (i,clauseinfo_list) { - clauseinfo = (CInfo*)lfirst(i); - hashjoinop = clauseinfo->hashjoinoperator; - - /* - * Create a new hashinfo node and add it to 'hashinfo-list' if one - * does not yet exist for this hash operator. - */ - if (hashjoinop ) { - HInfo *xhashinfo = (HInfo*)NULL; - Expr *clause = clauseinfo->clause; - Var *leftop = get_leftop(clause); - Var *rightop = get_rightop(clause); - JoinKey *keys = (JoinKey*)NULL; - - xhashinfo = - match_hashop_hashinfo(hashjoinop,hashinfo_list); - - if (inner_relid == leftop->varno){ - keys = makeNode(JoinKey); - keys->outer = rightop; - keys->inner = leftop; - } else { - keys = makeNode(JoinKey); - keys->outer = leftop; - keys->inner = rightop; - } - - if (xhashinfo==NULL) { - xhashinfo = makeNode(HInfo); - xhashinfo->hashop = hashjoinop; - - xhashinfo->jmethod.jmkeys = NIL; - xhashinfo->jmethod.clauses = NIL; - - /* XXX was push */ - hashinfo_list = lappend(hashinfo_list,xhashinfo); - hashinfo_list = nreverse(hashinfo_list); - } - - xhashinfo->jmethod.clauses = - lcons(clause, xhashinfo->jmethod.clauses); - - xhashinfo->jmethod.jmkeys = - lcons(keys, xhashinfo->jmethod.jmkeys); + List *hashinfo_list = NIL; + CInfo *clauseinfo = (CInfo *) NULL; + List *i = NIL; + Oid hashjoinop = 0; + + foreach(i, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(i); + hashjoinop = clauseinfo->hashjoinoperator; + + /* + * Create a new hashinfo node and add it to 'hashinfo-list' if one + * does not yet exist for this hash operator. + */ + if (hashjoinop) + { + HInfo *xhashinfo = (HInfo *) NULL; + Expr *clause = clauseinfo->clause; + Var *leftop = get_leftop(clause); + Var *rightop = get_rightop(clause); + JoinKey *keys = (JoinKey *) NULL; + + xhashinfo = + match_hashop_hashinfo(hashjoinop, hashinfo_list); + + if (inner_relid == leftop->varno) + { + keys = makeNode(JoinKey); + keys->outer = rightop; + keys->inner = leftop; + } + else + { + keys = makeNode(JoinKey); + keys->outer = leftop; + keys->inner = rightop; + } + + if (xhashinfo == NULL) + { + xhashinfo = makeNode(HInfo); + xhashinfo->hashop = hashjoinop; + + xhashinfo->jmethod.jmkeys = NIL; + xhashinfo->jmethod.clauses = NIL; + + /* XXX was push */ + hashinfo_list = lappend(hashinfo_list, xhashinfo); + hashinfo_list = nreverse(hashinfo_list); + } + + xhashinfo->jmethod.clauses = + lcons(clause, xhashinfo->jmethod.clauses); + + xhashinfo->jmethod.jmkeys = + lcons(keys, xhashinfo->jmethod.jmkeys); + } } - } - return(hashinfo_list); + return (hashinfo_list); } -/* +/* * match-hashop-hashinfo-- - * Searches the list 'hashinfo-list' for a hashinfo node whose hash op - * field equals 'hashop'. - * + * Searches the list 'hashinfo-list' for a hashinfo node whose hash op + * field equals 'hashop'. + * * Returns the node if it exists. - * + * */ -static HInfo * -match_hashop_hashinfo(Oid hashop, List *hashinfo_list) +static HInfo * +match_hashop_hashinfo(Oid hashop, List * hashinfo_list) { - Oid key = 0; - HInfo *xhashinfo = (HInfo*)NULL; - List *i = NIL; - - foreach( i, hashinfo_list) { - xhashinfo = (HInfo*)lfirst(i); - key = xhashinfo->hashop; - if (hashop == key) { /* found */ - return(xhashinfo); /* should be a hashinfo node ! */ + Oid key = 0; + HInfo *xhashinfo = (HInfo *) NULL; + List *i = NIL; + + foreach(i, hashinfo_list) + { + xhashinfo = (HInfo *) lfirst(i); + key = xhashinfo->hashop; + if (hashop == key) + { /* found */ + return (xhashinfo); /* should be a hashinfo node ! */ + } } - } - return((HInfo*)NIL); + return ((HInfo *) NIL); } diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index f5b70e43a0f..bd9bc15ace0 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -1,14 +1,14 @@ /*------------------------------------------------------------------------- * * indxpath.c-- - * Routines to determine which indices are usable for scanning a - * given relation + * Routines to determine which indices are usable for scanning a + * given relation * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.8 1997/08/12 22:53:12 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.9 1997/09/07 04:43:36 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -43,308 +43,321 @@ #include "catalog/pg_proc.h" #include "executor/executor.h" -#include "parser/parsetree.h" /* for getrelid() */ - - -static void match_index_orclauses(Rel *rel, Rel *index, int indexkey, - int xclass, List *clauseinfo_list); -static bool match_index_to_operand(int indexkey, Expr *operand, - Rel *rel, Rel *index); -static List *match_index_orclause(Rel *rel, Rel *index, int indexkey, - int xclass, List *or_clauses, List *other_matching_indices); -static List *group_clauses_by_indexkey(Rel *rel, Rel *index, - int *indexkeys, Oid *classes, List *clauseinfo_list); -static List *group_clauses_by_ikey_for_joins(Rel *rel, Rel *index, - int *indexkeys, Oid *classes, List *join_cinfo_list, List *restr_cinfo_list); -static CInfo *match_clause_to_indexkey(Rel *rel, Rel *index, int indexkey, - int xclass, CInfo *clauseInfo, bool join); -static bool pred_test(List *predicate_list, List *clauseinfo_list, - List *joininfo_list); -static bool one_pred_test(Expr *predicate, List *clauseinfo_list); -static bool one_pred_clause_expr_test(Expr *predicate, Node *clause); -static bool one_pred_clause_test(Expr *predicate, Node *clause); -static bool clause_pred_clause_test(Expr *predicate, Node *clause); -static List *indexable_joinclauses (Rel *rel, Rel *index, - List *joininfo_list, List *clauseinfo_list); -static List *index_innerjoin(Query *root, Rel *rel, - List *clausegroup_list, Rel *index); -static List *create_index_paths(Query *root, Rel *rel, Rel *index, - List *clausegroup_list, bool join); -static List *add_index_paths(List *indexpaths, List *new_indexpaths); -static bool function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index); -static bool SingleAttributeIndex(Rel *index); +#include "parser/parsetree.h" /* for getrelid() */ + + +static void +match_index_orclauses(Rel * rel, Rel * index, int indexkey, + int xclass, List * clauseinfo_list); +static bool +match_index_to_operand(int indexkey, Expr * operand, + Rel * rel, Rel * index); +static List * +match_index_orclause(Rel * rel, Rel * index, int indexkey, + int xclass, List * or_clauses, List * other_matching_indices); +static List * +group_clauses_by_indexkey(Rel * rel, Rel * index, + int *indexkeys, Oid * classes, List * clauseinfo_list); +static List * +group_clauses_by_ikey_for_joins(Rel * rel, Rel * index, + int *indexkeys, Oid * classes, List * join_cinfo_list, List * restr_cinfo_list); +static CInfo * +match_clause_to_indexkey(Rel * rel, Rel * index, int indexkey, + int xclass, CInfo * clauseInfo, bool join); +static bool +pred_test(List * predicate_list, List * clauseinfo_list, + List * joininfo_list); +static bool one_pred_test(Expr * predicate, List * clauseinfo_list); +static bool one_pred_clause_expr_test(Expr * predicate, Node * clause); +static bool one_pred_clause_test(Expr * predicate, Node * clause); +static bool clause_pred_clause_test(Expr * predicate, Node * clause); +static List * +indexable_joinclauses(Rel * rel, Rel * index, + List * joininfo_list, List * clauseinfo_list); +static List * +index_innerjoin(Query * root, Rel * rel, + List * clausegroup_list, Rel * index); +static List * +create_index_paths(Query * root, Rel * rel, Rel * index, + List * clausegroup_list, bool join); +static List *add_index_paths(List * indexpaths, List * new_indexpaths); +static bool function_index_operand(Expr * funcOpnd, Rel * rel, Rel * index); +static bool SingleAttributeIndex(Rel * index); /* If Spyros can use a constant PRS2_BOOL_TYPEID, I can use this */ #define BOOL_TYPEID ((Oid) 16) -/* +/* * find-index-paths-- - * Finds all possible index paths by determining which indices in the - * list 'indices' are usable. - * - * To be usable, an index must match against either a set of - * restriction clauses or join clauses. - * - * Note that the current implementation requires that there exist - * matching clauses for every key in the index (i.e., no partial - * matches are allowed). - * - * If an index can't be used with restriction clauses, but its keys - * match those of the result sort order (according to information stored - * within 'sortkeys'), then the index is also considered. + * Finds all possible index paths by determining which indices in the + * list 'indices' are usable. + * + * To be usable, an index must match against either a set of + * restriction clauses or join clauses. + * + * Note that the current implementation requires that there exist + * matching clauses for every key in the index (i.e., no partial + * matches are allowed). + * + * If an index can't be used with restriction clauses, but its keys + * match those of the result sort order (according to information stored + * within 'sortkeys'), then the index is also considered. * * 'rel' is the relation entry to which these index paths correspond * 'indices' is a list of possible index paths * 'clauseinfo-list' is a list of restriction clauseinfo nodes for 'rel' * 'joininfo-list' is a list of joininfo nodes for 'rel' * 'sortkeys' is a node describing the result sort order (from - * (find_sortkeys)) - * + * (find_sortkeys)) + * * Returns a list of index nodes. - * + * */ -List * -find_index_paths (Query *root, - Rel *rel, - List *indices, - List *clauseinfo_list, - List *joininfo_list) +List * +find_index_paths(Query * root, + Rel * rel, + List * indices, + List * clauseinfo_list, + List * joininfo_list) { - List *scanclausegroups = NIL; - List *scanpaths = NIL; - Rel *index = (Rel *)NULL; - List *joinclausegroups = NIL; - List *joinpaths = NIL; - List *retval = NIL; - - if(indices == NIL) - return(NULL); - - index = (Rel*)lfirst (indices); - - retval = find_index_paths(root, - rel, - lnext (indices), - clauseinfo_list, - joininfo_list); - - /* If this is a partial index, return if it fails the predicate test */ - if (index->indpred != NIL) - if (!pred_test(index->indpred, clauseinfo_list, joininfo_list)) - return retval; - - /* 1. If this index has only one key, try matching it against - * subclauses of an 'or' clause. The fields of the clauseinfo - * nodes are marked with lists of the matching indices no path - * are actually created. - * - * XXX NOTE: Currently btrees dos not support indices with - * > 1 key, so the following test will always be true for - * now but we have decided not to support index-scans - * on disjunction . -- lp - */ - if (SingleAttributeIndex(index)) - { - match_index_orclauses (rel, - index, - index->indexkeys[0], - index->classlist[0], - clauseinfo_list); - } + List *scanclausegroups = NIL; + List *scanpaths = NIL; + Rel *index = (Rel *) NULL; + List *joinclausegroups = NIL; + List *joinpaths = NIL; + List *retval = NIL; + + if (indices == NIL) + return (NULL); + + index = (Rel *) lfirst(indices); + + retval = find_index_paths(root, + rel, + lnext(indices), + clauseinfo_list, + joininfo_list); - /* - * 2. If the keys of this index match any of the available - * restriction clauses, then create pathnodes corresponding - * to each group of usable clauses. - */ - scanclausegroups = group_clauses_by_indexkey(rel, - index, - index->indexkeys, - index->classlist, - clauseinfo_list); - - scanpaths = NIL; - if (scanclausegroups != NIL) - scanpaths = create_index_paths (root, - rel, - index, - scanclausegroups, - false); - - /* - * 3. If this index can be used with any join clause, then - * create pathnodes for each group of usable clauses. An - * index can be used with a join clause if its ordering is - * useful for a mergejoin, or if the index can possibly be - * used for scanning the inner relation of a nestloop join. - */ - joinclausegroups = indexable_joinclauses(rel,index,joininfo_list, clauseinfo_list); - joinpaths = NIL; - - if (joinclausegroups != NIL) + /* If this is a partial index, return if it fails the predicate test */ + if (index->indpred != NIL) + if (!pred_test(index->indpred, clauseinfo_list, joininfo_list)) + return retval; + + /* + * 1. If this index has only one key, try matching it against + * subclauses of an 'or' clause. The fields of the clauseinfo nodes + * are marked with lists of the matching indices no path are actually + * created. + * + * XXX NOTE: Currently btrees dos not support indices with > 1 key, so + * the following test will always be true for now but we have decided + * not to support index-scans on disjunction . -- lp + */ + if (SingleAttributeIndex(index)) { - List *new_join_paths = create_index_paths(root, rel, + match_index_orclauses(rel, index, - joinclausegroups, - true); - List *innerjoin_paths = index_innerjoin(root, rel,joinclausegroups,index); + index->indexkeys[0], + index->classlist[0], + clauseinfo_list); + } + + /* + * 2. If the keys of this index match any of the available restriction + * clauses, then create pathnodes corresponding to each group of + * usable clauses. + */ + scanclausegroups = group_clauses_by_indexkey(rel, + index, + index->indexkeys, + index->classlist, + clauseinfo_list); + + scanpaths = NIL; + if (scanclausegroups != NIL) + scanpaths = create_index_paths(root, + rel, + index, + scanclausegroups, + false); + + /* + * 3. If this index can be used with any join clause, then create + * pathnodes for each group of usable clauses. An index can be used + * with a join clause if its ordering is useful for a mergejoin, or if + * the index can possibly be used for scanning the inner relation of a + * nestloop join. + */ + joinclausegroups = indexable_joinclauses(rel, index, joininfo_list, clauseinfo_list); + joinpaths = NIL; - rel->innerjoin = nconc (rel->innerjoin, innerjoin_paths); - joinpaths = new_join_paths; + if (joinclausegroups != NIL) + { + List *new_join_paths = create_index_paths(root, rel, + index, + joinclausegroups, + true); + List *innerjoin_paths = index_innerjoin(root, rel, joinclausegroups, index); + + rel->innerjoin = nconc(rel->innerjoin, innerjoin_paths); + joinpaths = new_join_paths; } - - /* - * Some sanity checks to make sure that - * the indexpath is valid. - */ - if (joinpaths!=NULL) - retval = add_index_paths(joinpaths,retval); - if (scanpaths!=NULL) - retval = add_index_paths(scanpaths,retval); - - return retval; + + /* + * Some sanity checks to make sure that the indexpath is valid. + */ + if (joinpaths != NULL) + retval = add_index_paths(joinpaths, retval); + if (scanpaths != NULL) + retval = add_index_paths(scanpaths, retval); + + return retval; } /**************************************************************************** - * ---- ROUTINES TO MATCH 'OR' CLAUSES ---- + * ---- ROUTINES TO MATCH 'OR' CLAUSES ---- ****************************************************************************/ -/* +/* * match-index-orclauses-- - * Attempt to match an index against subclauses within 'or' clauses. - * If the index does match, then the clause is marked with information - * about the index. - * - * Essentially, this adds 'index' to the list of indices in the - * ClauseInfo field of each of the clauses which it matches. - * + * Attempt to match an index against subclauses within 'or' clauses. + * If the index does match, then the clause is marked with information + * about the index. + * + * Essentially, this adds 'index' to the list of indices in the + * ClauseInfo field of each of the clauses which it matches. + * * 'rel' is the node of the relation on which the index is defined. * 'index' is the index node. * 'indexkey' is the (single) key of the index * 'class' is the class of the operator corresponding to 'indexkey'. * 'clauseinfo-list' is the list of available restriction clauses. - * + * * Returns nothing. - * + * */ static void -match_index_orclauses(Rel *rel, - Rel *index, - int indexkey, - int xclass, - List *clauseinfo_list) +match_index_orclauses(Rel * rel, + Rel * index, + int indexkey, + int xclass, + List * clauseinfo_list) { - CInfo *clauseinfo = (CInfo*)NULL; - List *i = NIL; - - foreach (i, clauseinfo_list) { - clauseinfo = (CInfo*)lfirst(i); - if (valid_or_clause(clauseinfo)) { - - /* Mark the 'or' clause with a list of indices which - * match each of its subclauses. The list is - * generated by adding 'index' to the existing - * list where appropriate. - */ - clauseinfo->indexids = - match_index_orclause (rel,index,indexkey, - xclass, - clauseinfo->clause->args, - clauseinfo->indexids); + CInfo *clauseinfo = (CInfo *) NULL; + List *i = NIL; + + foreach(i, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(i); + if (valid_or_clause(clauseinfo)) + { + + /* + * Mark the 'or' clause with a list of indices which match + * each of its subclauses. The list is generated by adding + * 'index' to the existing list where appropriate. + */ + clauseinfo->indexids = + match_index_orclause(rel, index, indexkey, + xclass, + clauseinfo->clause->args, + clauseinfo->indexids); + } } - } } /* * match_index_operand-- - * Generalize test for a match between an existing index's key - * and the operand on the rhs of a restriction clause. Now check - * for functional indices as well. + * Generalize test for a match between an existing index's key + * and the operand on the rhs of a restriction clause. Now check + * for functional indices as well. */ -static bool +static bool match_index_to_operand(int indexkey, - Expr *operand, - Rel *rel, - Rel *index) + Expr * operand, + Rel * rel, + Rel * index) { - /* - * Normal index. - */ - if (index->indproc == InvalidOid) - return match_indexkey_operand(indexkey, (Var*)operand, rel); - - /* - * functional index check - */ - return (function_index_operand(operand, rel, index)); + + /* + * Normal index. + */ + if (index->indproc == InvalidOid) + return match_indexkey_operand(indexkey, (Var *) operand, rel); + + /* + * functional index check + */ + return (function_index_operand(operand, rel, index)); } -/* +/* * match-index-orclause-- - * Attempts to match an index against the subclauses of an 'or' clause. - * - * A match means that: - * (1) the operator within the subclause can be used with one - * of the index's operator classes, and - * (2) there is a usable key that matches the variable within a - * sargable clause. - * + * Attempts to match an index against the subclauses of an 'or' clause. + * + * A match means that: + * (1) the operator within the subclause can be used with one + * of the index's operator classes, and + * (2) there is a usable key that matches the variable within a + * sargable clause. + * * 'or-clauses' are the remaining subclauses within the 'or' clause * 'other-matching-indices' is the list of information on other indices - * that have already been matched to subclauses within this - * particular 'or' clause (i.e., a list previously generated by - * this routine) - * + * that have already been matched to subclauses within this + * particular 'or' clause (i.e., a list previously generated by + * this routine) + * * Returns a list of the form ((a b c) (d e f) nil (g h) ...) where * a,b,c are nodes of indices that match the first subclause in * 'or-clauses', d,e,f match the second subclause, no indices * match the third, g,h match the fourth, etc. */ -static List * -match_index_orclause(Rel *rel, - Rel *index, - int indexkey, - int xclass, - List *or_clauses, - List *other_matching_indices) +static List * +match_index_orclause(Rel * rel, + Rel * index, + int indexkey, + int xclass, + List * or_clauses, + List * other_matching_indices) { - Node *clause = NULL; - List *matched_indices = other_matching_indices; - List *index_list = NIL; - List *clist; - List *ind; - - if (!matched_indices) - matched_indices = lcons(NIL, NIL); - - for (clist = or_clauses, ind = matched_indices; - clist; - clist = lnext(clist), ind = lnext(ind)) + Node *clause = NULL; + List *matched_indices = other_matching_indices; + List *index_list = NIL; + List *clist; + List *ind; + + if (!matched_indices) + matched_indices = lcons(NIL, NIL); + + for (clist = or_clauses, ind = matched_indices; + clist; + clist = lnext(clist), ind = lnext(ind)) { - clause = lfirst(clist); - if (is_opclause (clause) && - op_class(((Oper*)((Expr*)clause)->oper)->opno, - xclass, index->relam) && - match_index_to_operand(indexkey, - (Expr*)get_leftop((Expr*)clause), - rel, - index) && - IsA(get_rightop((Expr*)clause),Const)) { - - matched_indices = lcons(index, matched_indices); - index_list = lappend(index_list, - matched_indices); - } + clause = lfirst(clist); + if (is_opclause(clause) && + op_class(((Oper *) ((Expr *) clause)->oper)->opno, + xclass, index->relam) && + match_index_to_operand(indexkey, + (Expr *) get_leftop((Expr *) clause), + rel, + index) && + IsA(get_rightop((Expr *) clause), Const)) + { + + matched_indices = lcons(index, matched_indices); + index_list = lappend(index_list, + matched_indices); + } } - return(index_list); - + return (index_list); + } /**************************************************************************** - * ---- ROUTINES TO CHECK RESTRICTIONS ---- + * ---- ROUTINES TO CHECK RESTRICTIONS ---- ****************************************************************************/ @@ -358,176 +371,177 @@ match_index_orclause(Rel *rel, * keys list represent the arguments to the function. -mer 3 Oct. 1991 */ #define DoneMatchingIndexKeys(indexkeys, index) \ - (indexkeys[0] == 0 || \ - (index->indproc != InvalidOid)) + (indexkeys[0] == 0 || \ + (index->indproc != InvalidOid)) -/* +/* * group-clauses-by-indexkey-- - * Determines whether there are clauses which will match each and every - * one of the remaining keys of an index. - * + * Determines whether there are clauses which will match each and every + * one of the remaining keys of an index. + * * 'rel' is the node of the relation corresponding to the index. * 'indexkeys' are the remaining index keys to be matched. * 'classes' are the classes of the index operators on those keys. * 'clauses' is either: - * (1) the list of available restriction clauses on a single - * relation, or - * (2) a list of join clauses between 'rel' and a fixed set of - * relations, - * depending on the value of 'join'. + * (1) the list of available restriction clauses on a single + * relation, or + * (2) a list of join clauses between 'rel' and a fixed set of + * relations, + * depending on the value of 'join'. + * + * NOTE: it works now for restriction clauses only. - vadim 03/18/97 * - * NOTE: it works now for restriction clauses only. - vadim 03/18/97 - * * Returns all possible groups of clauses that will match (given that * one or more clauses can match any of the remaining keys). - * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be + * E.g., if you have clauses A, B, and C, ((A B) (A C)) might be * returned for an index with 2 keys. - * + * */ -static List * -group_clauses_by_indexkey(Rel *rel, - Rel *index, - int *indexkeys, - Oid *classes, - List *clauseinfo_list) +static List * +group_clauses_by_indexkey(Rel * rel, + Rel * index, + int *indexkeys, + Oid * classes, + List * clauseinfo_list) { - List *curCinfo = NIL; - CInfo *matched_clause = (CInfo*)NULL; - List *clausegroup = NIL; - int curIndxKey; - Oid curClass; + List *curCinfo = NIL; + CInfo *matched_clause = (CInfo *) NULL; + List *clausegroup = NIL; + int curIndxKey; + Oid curClass; - if (clauseinfo_list == NIL) - return NIL; + if (clauseinfo_list == NIL) + return NIL; - while ( !DoneMatchingIndexKeys(indexkeys, index) ) - { - List *tempgroup = NIL; - - curIndxKey = indexkeys[0]; - curClass = classes[0]; - - foreach (curCinfo,clauseinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - false); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); - } - if ( tempgroup == NIL ) - break; + while (!DoneMatchingIndexKeys(indexkeys, index)) + { + List *tempgroup = NIL; + + curIndxKey = indexkeys[0]; + curClass = classes[0]; + + foreach(curCinfo, clauseinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + false); + if (!matched_clause) + continue; - clausegroup = nconc (clausegroup, tempgroup); - - indexkeys++; - classes++; - - } + tempgroup = lappend(tempgroup, matched_clause); + } + if (tempgroup == NIL) + break; - /* clausegroup holds all matched clauses ordered by indexkeys */ + clausegroup = nconc(clausegroup, tempgroup); + + indexkeys++; + classes++; + + } - if (clausegroup != NIL) - return(lcons(clausegroup, NIL)); - return NIL; + /* clausegroup holds all matched clauses ordered by indexkeys */ + + if (clausegroup != NIL) + return (lcons(clausegroup, NIL)); + return NIL; } -/* +/* * group-clauses-by-ikey-for-joins-- - * special edition of group-clauses-by-indexkey - will - * match join & restriction clauses. See comment in indexable_joinclauses. - * - vadim 03/18/97 - * + * special edition of group-clauses-by-indexkey - will + * match join & restriction clauses. See comment in indexable_joinclauses. + * - vadim 03/18/97 + * */ -static List * -group_clauses_by_ikey_for_joins(Rel *rel, - Rel *index, - int *indexkeys, - Oid *classes, - List *join_cinfo_list, - List *restr_cinfo_list) +static List * +group_clauses_by_ikey_for_joins(Rel * rel, + Rel * index, + int *indexkeys, + Oid * classes, + List * join_cinfo_list, + List * restr_cinfo_list) { - List *curCinfo = NIL; - CInfo *matched_clause = (CInfo*)NULL; - List *clausegroup = NIL; - int curIndxKey; - Oid curClass; - bool jfound = false; - - if (join_cinfo_list == NIL) - return NIL; + List *curCinfo = NIL; + CInfo *matched_clause = (CInfo *) NULL; + List *clausegroup = NIL; + int curIndxKey; + Oid curClass; + bool jfound = false; + + if (join_cinfo_list == NIL) + return NIL; + + while (!DoneMatchingIndexKeys(indexkeys, index)) + { + List *tempgroup = NIL; + + curIndxKey = indexkeys[0]; + curClass = classes[0]; + + foreach(curCinfo, join_cinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + true); + if (!matched_clause) + continue; + + tempgroup = lappend(tempgroup, matched_clause); + jfound = true; + } + foreach(curCinfo, restr_cinfo_list) + { + CInfo *temp = (CInfo *) lfirst(curCinfo); + + matched_clause = match_clause_to_indexkey(rel, + index, + curIndxKey, + curClass, + temp, + false); + if (!matched_clause) + continue; + + tempgroup = lappend(tempgroup, matched_clause); + } + if (tempgroup == NIL) + break; + + clausegroup = nconc(clausegroup, tempgroup); + + indexkeys++; + classes++; - while ( !DoneMatchingIndexKeys(indexkeys, index) ) - { - List *tempgroup = NIL; - - curIndxKey = indexkeys[0]; - curClass = classes[0]; - - foreach (curCinfo,join_cinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - true); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); - jfound = true; } - foreach (curCinfo,restr_cinfo_list) - { - CInfo *temp = (CInfo*)lfirst(curCinfo); - - matched_clause = match_clause_to_indexkey (rel, - index, - curIndxKey, - curClass, - temp, - false); - if (!matched_clause) - continue; - - tempgroup = lappend(tempgroup, matched_clause); + + /* clausegroup holds all matched clauses ordered by indexkeys */ + + if (clausegroup != NIL) + { + + /* + * if no one join clause was matched then there ain't clauses for + * joins at all. + */ + if (!jfound) + { + freeList(clausegroup); + return NIL; + } + return (lcons(clausegroup, NIL)); } - if ( tempgroup == NIL ) - break; - - clausegroup = nconc (clausegroup, tempgroup); - - indexkeys++; - classes++; - - } - - /* clausegroup holds all matched clauses ordered by indexkeys */ - - if (clausegroup != NIL) - { - /* - * if no one join clause was matched then there ain't clauses - * for joins at all. - */ - if ( !jfound ) - { - freeList (clausegroup); - return NIL; - } - return(lcons(clausegroup, NIL)); - } - return NIL; + return NIL; } /* @@ -537,798 +551,867 @@ group_clauses_by_ikey_for_joins(Rel *rel, * Now we can match with functional indices. */ #define IndexScanableOperand(opnd, indkeys, rel, index) \ - ((index->indproc == InvalidOid) ? \ - match_indexkey_operand(indkeys, opnd, rel) : \ - function_index_operand((Expr*)opnd,rel,index)) + ((index->indproc == InvalidOid) ? \ + match_indexkey_operand(indkeys, opnd, rel) : \ + function_index_operand((Expr*)opnd,rel,index)) /* * There was - * equal_indexkey_var(indkeys,opnd) : \ + * equal_indexkey_var(indkeys,opnd) : \ * above, and now - * match_indexkey_operand(indkeys, opnd, rel) : \ + * match_indexkey_operand(indkeys, opnd, rel) : \ * - vadim 01/22/97 */ -/* +/* * match_clause_to-indexkey-- - * Finds the first of a relation's available restriction clauses that - * matches a key of an index. - * - * To match, the clause must: - * (1) be in the form (op var const) if the clause is a single- - * relation clause, and - * (2) contain an operator which is in the same class as the index - * operator for this key. - * - * If the clause being matched is a join clause, then 'join' is t. - * - * Returns a single clauseinfo node corresponding to the matching + * Finds the first of a relation's available restriction clauses that + * matches a key of an index. + * + * To match, the clause must: + * (1) be in the form (op var const) if the clause is a single- + * relation clause, and + * (2) contain an operator which is in the same class as the index + * operator for this key. + * + * If the clause being matched is a join clause, then 'join' is t. + * + * Returns a single clauseinfo node corresponding to the matching * clause. * * NOTE: returns nil if clause is an or_clause. - * + * */ -static CInfo * -match_clause_to_indexkey(Rel *rel, - Rel *index, - int indexkey, - int xclass, - CInfo *clauseInfo, - bool join) +static CInfo * +match_clause_to_indexkey(Rel * rel, + Rel * index, + int indexkey, + int xclass, + CInfo * clauseInfo, + bool join) { - Expr *clause = clauseInfo->clause; - Var *leftop, *rightop; - Oid join_op = InvalidOid; - Oid restrict_op = InvalidOid; - bool isIndexable = false; - - if (or_clause((Node*)clause) || - not_clause((Node*)clause) || single_node((Node*)clause)) - return ((CInfo*)NULL); - - leftop = get_leftop(clause); - rightop = get_rightop(clause); - /* - * If this is not a join clause, check for clauses of the form: - * (operator var/func constant) and (operator constant var/func) - */ - if (!join) - { + Expr *clause = clauseInfo->clause; + Var *leftop, + *rightop; + Oid join_op = InvalidOid; + Oid restrict_op = InvalidOid; + bool isIndexable = false; + + if (or_clause((Node *) clause) || + not_clause((Node *) clause) || single_node((Node *) clause)) + return ((CInfo *) NULL); + + leftop = get_leftop(clause); + rightop = get_rightop(clause); + /* - * Check for standard s-argable clause + * If this is not a join clause, check for clauses of the form: + * (operator var/func constant) and (operator constant var/func) */ -#ifdef INDEXSCAN_PATCH - /* Handle also function parameters. DZ - 27-8-1996 */ - if ((rightop && IsA(rightop,Const)) || - (rightop && IsA(rightop,Param))) -#else - if (rightop && IsA(rightop,Const)) -#endif + if (!join) { - restrict_op = ((Oper*)((Expr*)clause)->oper)->opno; - isIndexable = - ( op_class(restrict_op, xclass, index->relam) && - IndexScanableOperand(leftop, - indexkey, - rel, - index) ); - } - /* - * Must try to commute the clause to standard s-arg format. - */ + /* + * Check for standard s-argable clause + */ #ifdef INDEXSCAN_PATCH - /* ...And here... - vadim 01/22/97 */ - else if ((leftop && IsA(leftop,Const)) || - (leftop && IsA(leftop,Param))) + /* Handle also function parameters. DZ - 27-8-1996 */ + if ((rightop && IsA(rightop, Const)) || + (rightop && IsA(rightop, Param))) #else - else if (leftop && IsA(leftop,Const)) + if (rightop && IsA(rightop, Const)) #endif - { - restrict_op = - get_commutator(((Oper*)((Expr*)clause)->oper)->opno); - - if ( (restrict_op != InvalidOid) && - op_class(restrict_op, xclass, index->relam) && - IndexScanableOperand(rightop, - indexkey,rel,index) ) - { - isIndexable = true; + { + restrict_op = ((Oper *) ((Expr *) clause)->oper)->opno; + isIndexable = + (op_class(restrict_op, xclass, index->relam) && + IndexScanableOperand(leftop, + indexkey, + rel, + index)); + } + /* - * In place list modification. - * (op const var/func) -> (op var/func const) + * Must try to commute the clause to standard s-arg format. */ - CommuteClause((Node*)clause); - } - } - } - /* - * Check for an indexable scan on one of the join relations. - * clause is of the form (operator var/func var/func) - */ - else - { - if (rightop - && match_index_to_operand(indexkey,(Expr*)rightop,rel,index)) - { - - join_op = get_commutator(((Oper*)((Expr*)clause)->oper)->opno); - - } else if (leftop - && match_index_to_operand(indexkey, - (Expr*)leftop,rel,index)) - { - join_op = ((Oper*)((Expr*)clause)->oper)->opno; +#ifdef INDEXSCAN_PATCH + /* ...And here... - vadim 01/22/97 */ + else if ((leftop && IsA(leftop, Const)) || + (leftop && IsA(leftop, Param))) +#else + else if (leftop && IsA(leftop, Const)) +#endif + { + restrict_op = + get_commutator(((Oper *) ((Expr *) clause)->oper)->opno); + + if ((restrict_op != InvalidOid) && + op_class(restrict_op, xclass, index->relam) && + IndexScanableOperand(rightop, + indexkey, rel, index)) + { + isIndexable = true; + + /* + * In place list modification. (op const var/func) -> (op + * var/func const) + */ + CommuteClause((Node *) clause); + } + } } - if ( join_op && op_class(join_op,xclass,index->relam) && - join_clause_p((Node*)clause)) + /* + * Check for an indexable scan on one of the join relations. clause is + * of the form (operator var/func var/func) + */ + else { - isIndexable = true; - - /* - * If we're using the operand's commutator we must - * commute the clause. - */ - if (join_op != ((Oper*)((Expr*)clause)->oper)->opno) - CommuteClause((Node*)clause); + if (rightop + && match_index_to_operand(indexkey, (Expr *) rightop, rel, index)) + { + + join_op = get_commutator(((Oper *) ((Expr *) clause)->oper)->opno); + + } + else if (leftop + && match_index_to_operand(indexkey, + (Expr *) leftop, rel, index)) + { + join_op = ((Oper *) ((Expr *) clause)->oper)->opno; + } + + if (join_op && op_class(join_op, xclass, index->relam) && + join_clause_p((Node *) clause)) + { + isIndexable = true; + + /* + * If we're using the operand's commutator we must commute the + * clause. + */ + if (join_op != ((Oper *) ((Expr *) clause)->oper)->opno) + CommuteClause((Node *) clause); + } } - } - if (isIndexable) - return(clauseInfo); + if (isIndexable) + return (clauseInfo); - return(NULL); + return (NULL); } /**************************************************************************** - * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- + * ---- ROUTINES TO DO PARTIAL INDEX PREDICATE TESTS ---- ****************************************************************************/ -/* +/* * pred_test-- - * Does the "predicate inclusion test" for partial indexes. + * Does the "predicate inclusion test" for partial indexes. * - * Recursively checks whether the clauses in clauseinfo_list imply - * that the given predicate is true. + * Recursively checks whether the clauses in clauseinfo_list imply + * that the given predicate is true. * - * This routine (together with the routines it calls) iterates over - * ANDs in the predicate first, then reduces the qualification - * clauses down to their constituent terms, and iterates over ORs - * in the predicate last. This order is important to make the test - * succeed whenever possible (assuming the predicate has been - * successfully cnfify()-ed). --Nels, Jan '93 + * This routine (together with the routines it calls) iterates over + * ANDs in the predicate first, then reduces the qualification + * clauses down to their constituent terms, and iterates over ORs + * in the predicate last. This order is important to make the test + * succeed whenever possible (assuming the predicate has been + * successfully cnfify()-ed). --Nels, Jan '93 */ -static bool -pred_test(List *predicate_list, List *clauseinfo_list, List *joininfo_list) +static bool +pred_test(List * predicate_list, List * clauseinfo_list, List * joininfo_list) { - List *pred, *items, *item; - - /* - * Note: if Postgres tried to optimize queries by forming equivalence - * classes over equi-joined attributes (i.e., if it recognized that a - * qualification such as "where a.b=c.d and a.b=5" could make use of - * an index on c.d), then we could use that equivalence class info - * here with joininfo_list to do more complete tests for the usability - * of a partial index. For now, the test only uses restriction - * clauses (those in clauseinfo_list). --Nels, Dec '92 - */ - - if (predicate_list == NULL) - return true; /* no predicate: the index is usable */ - if (clauseinfo_list == NULL) - return false; /* no restriction clauses: the test must fail */ - - foreach (pred, predicate_list) { - /* if any clause is not implied, the whole predicate is not implied */ - if (and_clause(lfirst(pred))) { - items = ((Expr*)lfirst(pred))->args; - foreach (item, items) { - if (!one_pred_test(lfirst(item), clauseinfo_list)) - return false; - } + List *pred, + *items, + *item; + + /* + * Note: if Postgres tried to optimize queries by forming equivalence + * classes over equi-joined attributes (i.e., if it recognized that a + * qualification such as "where a.b=c.d and a.b=5" could make use of + * an index on c.d), then we could use that equivalence class info + * here with joininfo_list to do more complete tests for the usability + * of a partial index. For now, the test only uses restriction + * clauses (those in clauseinfo_list). --Nels, Dec '92 + */ + + if (predicate_list == NULL) + return true; /* no predicate: the index is usable */ + if (clauseinfo_list == NULL) + return false; /* no restriction clauses: the test must + * fail */ + + foreach(pred, predicate_list) + { + + /* + * if any clause is not implied, the whole predicate is not + * implied + */ + if (and_clause(lfirst(pred))) + { + items = ((Expr *) lfirst(pred))->args; + foreach(item, items) + { + if (!one_pred_test(lfirst(item), clauseinfo_list)) + return false; + } + } + else if (!one_pred_test(lfirst(pred), clauseinfo_list)) + return false; } - else if (!one_pred_test(lfirst(pred), clauseinfo_list)) - return false; - } - return true; + return true; } -/* +/* * one_pred_test-- - * Does the "predicate inclusion test" for one conjunct of a predicate - * expression. + * Does the "predicate inclusion test" for one conjunct of a predicate + * expression. */ -static bool -one_pred_test(Expr *predicate, List *clauseinfo_list) +static bool +one_pred_test(Expr * predicate, List * clauseinfo_list) { - CInfo *clauseinfo; - List *item; - - Assert(predicate != NULL); - foreach (item, clauseinfo_list) { - clauseinfo = (CInfo *)lfirst(item); - /* if any clause implies the predicate, return true */ - if (one_pred_clause_expr_test(predicate, (Node*)clauseinfo->clause)) - return true; - } - return false; + CInfo *clauseinfo; + List *item; + + Assert(predicate != NULL); + foreach(item, clauseinfo_list) + { + clauseinfo = (CInfo *) lfirst(item); + /* if any clause implies the predicate, return true */ + if (one_pred_clause_expr_test(predicate, (Node *) clauseinfo->clause)) + return true; + } + return false; } -/* +/* * one_pred_clause_expr_test-- - * Does the "predicate inclusion test" for a general restriction-clause - * expression. + * Does the "predicate inclusion test" for a general restriction-clause + * expression. */ -static bool -one_pred_clause_expr_test(Expr *predicate, Node *clause) +static bool +one_pred_clause_expr_test(Expr * predicate, Node * clause) { - List *items, *item; - - if (is_opclause(clause)) - return one_pred_clause_test(predicate, clause); - else if (or_clause(clause)) { - items = ((Expr*)clause)->args; - foreach (item, items) { - /* if any OR item doesn't imply the predicate, clause doesn't */ - if (!one_pred_clause_expr_test(predicate, lfirst(item))) + List *items, + *item; + + if (is_opclause(clause)) + return one_pred_clause_test(predicate, clause); + else if (or_clause(clause)) + { + items = ((Expr *) clause)->args; + foreach(item, items) + { + /* if any OR item doesn't imply the predicate, clause doesn't */ + if (!one_pred_clause_expr_test(predicate, lfirst(item))) + return false; + } + return true; + } + else if (and_clause(clause)) + { + items = ((Expr *) clause)->args; + foreach(item, items) + { + + /* + * if any AND item implies the predicate, the whole clause + * does + */ + if (one_pred_clause_expr_test(predicate, lfirst(item))) + return true; + } return false; } - return true; - }else if (and_clause(clause)) { - items = ((Expr*)clause)->args; - foreach (item, items) { - /* if any AND item implies the predicate, the whole clause does */ - if (one_pred_clause_expr_test(predicate, lfirst(item))) - return true; + else + { + /* unknown clause type never implies the predicate */ + return false; } - return false; - }else { - /* unknown clause type never implies the predicate */ - return false; - } } -/* +/* * one_pred_clause_test-- - * Does the "predicate inclusion test" for one conjunct of a predicate - * expression for a simple restriction clause. + * Does the "predicate inclusion test" for one conjunct of a predicate + * expression for a simple restriction clause. */ -static bool -one_pred_clause_test(Expr *predicate, Node *clause) +static bool +one_pred_clause_test(Expr * predicate, Node * clause) { - List *items, *item; - - if (is_opclause((Node*)predicate)) - return clause_pred_clause_test(predicate, clause); - else if (or_clause((Node*)predicate)) { - items = predicate->args; - foreach (item, items) { - /* if any item is implied, the whole predicate is implied */ - if (one_pred_clause_test(lfirst(item), clause)) + List *items, + *item; + + if (is_opclause((Node *) predicate)) + return clause_pred_clause_test(predicate, clause); + else if (or_clause((Node *) predicate)) + { + items = predicate->args; + foreach(item, items) + { + /* if any item is implied, the whole predicate is implied */ + if (one_pred_clause_test(lfirst(item), clause)) + return true; + } + return false; + } + else if (and_clause((Node *) predicate)) + { + items = predicate->args; + foreach(item, items) + { + + /* + * if any item is not implied, the whole predicate is not + * implied + */ + if (!one_pred_clause_test(lfirst(item), clause)) + return false; + } return true; } - return false; - }else if (and_clause((Node*)predicate)) { - items = predicate->args; - foreach (item, items) { - /* - * if any item is not implied, the whole predicate is not - * implied - */ - if (!one_pred_clause_test(lfirst(item), clause)) + else + { + elog(DEBUG, "Unsupported predicate type, index will not be used"); return false; } - return true; - } - else { - elog(DEBUG, "Unsupported predicate type, index will not be used"); - return false; - } } /* * Define an "operator implication table" for btree operators ("strategies"). - * The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) > + * The "strategy numbers" are: (1) < (2) <= (3) = (4) >= (5) > * * The interpretation of: * - * test_op = BT_implic_table[given_op-1][target_op-1] + * test_op = BT_implic_table[given_op-1][target_op-1] * * where test_op, given_op and target_op are strategy numbers (from 1 to 5) * of btree operators, is as follows: * - * If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you - * want to determine whether "ATTR target_op CONST2" must also be true, then - * you can use "CONST1 test_op CONST2" as a test. If this test returns true, - * then the target expression must be true; if the test returns false, then - * the target expression may be false. + * If you know, for some ATTR, that "ATTR given_op CONST1" is true, and you + * want to determine whether "ATTR target_op CONST2" must also be true, then + * you can use "CONST1 test_op CONST2" as a test. If this test returns true, + * then the target expression must be true; if the test returns false, then + * the target expression may be false. * * An entry where test_op==0 means the implication cannot be determined, i.e., * this test should always be considered false. */ -StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { - {2, 2, 0, 0, 0}, - {1, 2, 0, 0, 0}, - {1, 2, 3, 4, 5}, - {0, 0, 0, 4, 5}, - {0, 0, 0, 4, 4} +StrategyNumber BT_implic_table[BTMaxStrategyNumber][BTMaxStrategyNumber] = { + {2, 2, 0, 0, 0}, + {1, 2, 0, 0, 0}, + {1, 2, 3, 4, 5}, + {0, 0, 0, 4, 5}, + {0, 0, 0, 4, 4} }; -/* +/* * clause_pred_clause_test-- - * Use operator class info to check whether clause implies predicate. - * - * Does the "predicate inclusion test" for a "simple clause" predicate - * for a single "simple clause" restriction. Currently, this only handles - * (binary boolean) operators that are in some btree operator class. - * Eventually, rtree operators could also be handled by defining an - * appropriate "RT_implic_table" array. + * Use operator class info to check whether clause implies predicate. + * + * Does the "predicate inclusion test" for a "simple clause" predicate + * for a single "simple clause" restriction. Currently, this only handles + * (binary boolean) operators that are in some btree operator class. + * Eventually, rtree operators could also be handled by defining an + * appropriate "RT_implic_table" array. */ -static bool -clause_pred_clause_test(Expr *predicate, Node *clause) +static bool +clause_pred_clause_test(Expr * predicate, Node * clause) { - Var *pred_var, *clause_var; - Const *pred_const, *clause_const; - Oid pred_op, clause_op, test_op; - Oid opclass_id; - StrategyNumber pred_strategy, clause_strategy, test_strategy; - Oper *test_oper; - Expr *test_expr; - bool test_result, isNull; - Relation relation; - HeapScanDesc scan; - HeapTuple tuple; - ScanKeyData entry[3]; - Form_pg_amop form; - - pred_var = (Var*)get_leftop(predicate); - pred_const = (Const*)get_rightop(predicate); - clause_var = (Var*)get_leftop((Expr*)clause); - clause_const = (Const*)get_rightop((Expr*)clause); - - /* Check the basic form; for now, only allow the simplest case */ - if (!is_opclause(clause) || - !IsA(clause_var,Var) || - !IsA(clause_const,Const) || - !IsA(predicate->oper,Oper) || - !IsA(pred_var,Var) || - !IsA(pred_const,Const)) { - return false; - } + Var *pred_var, + *clause_var; + Const *pred_const, + *clause_const; + Oid pred_op, + clause_op, + test_op; + Oid opclass_id; + StrategyNumber pred_strategy, + clause_strategy, + test_strategy; + Oper *test_oper; + Expr *test_expr; + bool test_result, + isNull; + Relation relation; + HeapScanDesc scan; + HeapTuple tuple; + ScanKeyData entry[3]; + Form_pg_amop form; + + pred_var = (Var *) get_leftop(predicate); + pred_const = (Const *) get_rightop(predicate); + clause_var = (Var *) get_leftop((Expr *) clause); + clause_const = (Const *) get_rightop((Expr *) clause); + + /* Check the basic form; for now, only allow the simplest case */ + if (!is_opclause(clause) || + !IsA(clause_var, Var) || + !IsA(clause_const, Const) || + !IsA(predicate->oper, Oper) || + !IsA(pred_var, Var) || + !IsA(pred_const, Const)) + { + return false; + } - /* - * The implication can't be determined unless the predicate and the clause - * refer to the same attribute. - */ - if (clause_var->varattno != pred_var->varattno) - return false; + /* + * The implication can't be determined unless the predicate and the + * clause refer to the same attribute. + */ + if (clause_var->varattno != pred_var->varattno) + return false; - /* Get the operators for the two clauses we're comparing */ - pred_op = ((Oper*)((Expr*)predicate)->oper)->opno; - clause_op = ((Oper*)((Expr*)clause)->oper)->opno; - - - /* - * 1. Find a "btree" strategy number for the pred_op - */ - /* XXX - hardcoded amopid value 403 to find "btree" operator classes */ - ScanKeyEntryInitialize(&entry[0], 0, - Anum_pg_amop_amopid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(403)); - - ScanKeyEntryInitialize(&entry[1], 0, - Anum_pg_amop_amopopr, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(pred_op)); - - relation = heap_openr(AccessMethodOperatorRelationName); - - /* - * The following assumes that any given operator will only be in a single - * btree operator class. This is true at least for all the pre-defined - * operator classes. If it isn't true, then whichever operator class - * happens to be returned first for the given operator will be used to - * find the associated strategy numbers for the test. --Nels, Jan '93 - */ - scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown pred_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); + /* Get the operators for the two clauses we're comparing */ + pred_op = ((Oper *) ((Expr *) predicate)->oper)->opno; + clause_op = ((Oper *) ((Expr *) clause)->oper)->opno; - /* Get the predicate operator's strategy number (1 to 5) */ - pred_strategy = (StrategyNumber)form->amopstrategy; - /* Remember which operator class this strategy number came from */ - opclass_id = form->amopclaid; + /* + * 1. Find a "btree" strategy number for the pred_op + */ + /* XXX - hardcoded amopid value 403 to find "btree" operator classes */ + ScanKeyEntryInitialize(&entry[0], 0, + Anum_pg_amop_amopid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(403)); - heap_endscan(scan); + ScanKeyEntryInitialize(&entry[1], 0, + Anum_pg_amop_amopopr, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(pred_op)); + relation = heap_openr(AccessMethodOperatorRelationName); - /* - * 2. From the same opclass, find a strategy num for the clause_op - */ - ScanKeyEntryInitialize(&entry[1], 0, - Anum_pg_amop_amopclaid, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(opclass_id)); + /* + * The following assumes that any given operator will only be in a + * single btree operator class. This is true at least for all the + * pre-defined operator classes. If it isn't true, then whichever + * operator class happens to be returned first for the given operator + * will be used to find the associated strategy numbers for the test. + * --Nels, Jan '93 + */ + scan = heap_beginscan(relation, false, NowTimeQual, 2, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown pred_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); - ScanKeyEntryInitialize(&entry[2], 0, - Anum_pg_amop_amopopr, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(clause_op)); + /* Get the predicate operator's strategy number (1 to 5) */ + pred_strategy = (StrategyNumber) form->amopstrategy; - scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown clause_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); + /* Remember which operator class this strategy number came from */ + opclass_id = form->amopclaid; - /* Get the restriction clause operator's strategy number (1 to 5) */ - clause_strategy = (StrategyNumber)form->amopstrategy; - heap_endscan(scan); + heap_endscan(scan); - /* - * 3. Look up the "test" strategy number in the implication table - */ + /* + * 2. From the same opclass, find a strategy num for the clause_op + */ + ScanKeyEntryInitialize(&entry[1], 0, + Anum_pg_amop_amopclaid, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(opclass_id)); + + ScanKeyEntryInitialize(&entry[2], 0, + Anum_pg_amop_amopopr, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(clause_op)); + + scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown clause_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); - test_strategy = BT_implic_table[clause_strategy-1][pred_strategy-1]; - if (test_strategy == 0) - return false; /* the implication cannot be determined */ + /* Get the restriction clause operator's strategy number (1 to 5) */ + clause_strategy = (StrategyNumber) form->amopstrategy; + heap_endscan(scan); - /* - * 4. From the same opclass, find the operator for the test strategy - */ + /* + * 3. Look up the "test" strategy number in the implication table + */ - ScanKeyEntryInitialize(&entry[2], 0, - Anum_pg_amop_amopstrategy, - Integer16EqualRegProcedure, - Int16GetDatum(test_strategy)); + test_strategy = BT_implic_table[clause_strategy - 1][pred_strategy - 1]; + if (test_strategy == 0) + return false; /* the implication cannot be determined */ - scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); - tuple = heap_getnext(scan, false, (Buffer *)NULL); - if (! HeapTupleIsValid(tuple)) { - elog(DEBUG, "clause_pred_clause_test: unknown test_op"); - return false; - } - form = (Form_pg_amop) GETSTRUCT(tuple); - /* Get the test operator */ - test_op = form->amopopr; - heap_endscan(scan); + /* + * 4. From the same opclass, find the operator for the test strategy + */ + ScanKeyEntryInitialize(&entry[2], 0, + Anum_pg_amop_amopstrategy, + Integer16EqualRegProcedure, + Int16GetDatum(test_strategy)); - /* - * 5. Evaluate the test - */ - test_oper = makeOper(test_op, /* opno */ - InvalidOid, /* opid */ - BOOL_TYPEID, /* opresulttype */ - 0, /* opsize */ - NULL); /* op_fcache */ - replace_opid(test_oper); + scan = heap_beginscan(relation, false, NowTimeQual, 3, entry); + tuple = heap_getnext(scan, false, (Buffer *) NULL); + if (!HeapTupleIsValid(tuple)) + { + elog(DEBUG, "clause_pred_clause_test: unknown test_op"); + return false; + } + form = (Form_pg_amop) GETSTRUCT(tuple); + + /* Get the test operator */ + test_op = form->amopopr; + heap_endscan(scan); - test_expr = make_opclause(test_oper, - copyObject(clause_const), - copyObject(pred_const)); + + /* + * 5. Evaluate the test + */ + test_oper = makeOper(test_op, /* opno */ + InvalidOid, /* opid */ + BOOL_TYPEID, /* opresulttype */ + 0, /* opsize */ + NULL); /* op_fcache */ + replace_opid(test_oper); + + test_expr = make_opclause(test_oper, + copyObject(clause_const), + copyObject(pred_const)); #ifndef OMIT_PARTIAL_INDEX - test_result = ExecEvalExpr((Node*)test_expr, NULL, &isNull, NULL); -#endif /* OMIT_PARTIAL_INDEX */ - if (isNull) { - elog(DEBUG, "clause_pred_clause_test: null test result"); - return false; - } - return test_result; + test_result = ExecEvalExpr((Node *) test_expr, NULL, &isNull, NULL); +#endif /* OMIT_PARTIAL_INDEX */ + if (isNull) + { + elog(DEBUG, "clause_pred_clause_test: null test result"); + return false; + } + return test_result; } /**************************************************************************** - * ---- ROUTINES TO CHECK JOIN CLAUSES ---- + * ---- ROUTINES TO CHECK JOIN CLAUSES ---- ****************************************************************************/ -/* +/* * indexable-joinclauses-- - * Finds all groups of join clauses from among 'joininfo-list' that can - * be used in conjunction with 'index'. - * - * The first clause in the group is marked as having the other relation - * in the join clause as its outer join relation. - * + * Finds all groups of join clauses from among 'joininfo-list' that can + * be used in conjunction with 'index'. + * + * The first clause in the group is marked as having the other relation + * in the join clause as its outer join relation. + * * Returns a list of these clause groups. * - * Added: clauseinfo_list - list of restriction CInfos. It's to - * support multi-column indices in joins and for cases - * when a key is in both join & restriction clauses. - vadim 03/18/97 - * + * Added: clauseinfo_list - list of restriction CInfos. It's to + * support multi-column indices in joins and for cases + * when a key is in both join & restriction clauses. - vadim 03/18/97 + * */ -static List * -indexable_joinclauses(Rel *rel, Rel *index, - List *joininfo_list, List *clauseinfo_list) +static List * +indexable_joinclauses(Rel * rel, Rel * index, + List * joininfo_list, List * clauseinfo_list) { - JInfo *joininfo = (JInfo*)NULL; - List *cg_list = NIL; - List *i = NIL; - List *clausegroups = NIL; - - foreach(i,joininfo_list) { - joininfo = (JInfo*)lfirst(i); - - if ( joininfo->jinfoclauseinfo == NIL ) - continue; - clausegroups = - group_clauses_by_ikey_for_joins (rel, - index, - index->indexkeys, - index->classlist, - joininfo->jinfoclauseinfo, - clauseinfo_list); - - if (clausegroups != NIL) { - List *clauses = lfirst(clausegroups); - - ((CInfo*)lfirst(clauses))->cinfojoinid = - joininfo->otherrels; + JInfo *joininfo = (JInfo *) NULL; + List *cg_list = NIL; + List *i = NIL; + List *clausegroups = NIL; + + foreach(i, joininfo_list) + { + joininfo = (JInfo *) lfirst(i); + + if (joininfo->jinfoclauseinfo == NIL) + continue; + clausegroups = + group_clauses_by_ikey_for_joins(rel, + index, + index->indexkeys, + index->classlist, + joininfo->jinfoclauseinfo, + clauseinfo_list); + + if (clausegroups != NIL) + { + List *clauses = lfirst(clausegroups); + + ((CInfo *) lfirst(clauses))->cinfojoinid = + joininfo->otherrels; + } + cg_list = nconc(cg_list, clausegroups); } - cg_list = nconc(cg_list,clausegroups); - } - return(cg_list); + return (cg_list); } /**************************************************************************** - * ---- PATH CREATION UTILITIES ---- + * ---- PATH CREATION UTILITIES ---- ****************************************************************************/ /* * extract_restrict_clauses - - * the list of clause info contains join clauses and restriction clauses. - * This routine returns the restriction clauses only. + * the list of clause info contains join clauses and restriction clauses. + * This routine returns the restriction clauses only. */ #ifdef NOT_USED -static List * -extract_restrict_clauses(List *clausegroup) +static List * +extract_restrict_clauses(List * clausegroup) { - List *restrict_cls = NIL; - List *l; - - foreach (l, clausegroup) { - CInfo *cinfo = lfirst(l); - - if (!join_clause_p((Node*)cinfo->clause)) { - restrict_cls = lappend(restrict_cls, cinfo); + List *restrict_cls = NIL; + List *l; + + foreach(l, clausegroup) + { + CInfo *cinfo = lfirst(l); + + if (!join_clause_p((Node *) cinfo->clause)) + { + restrict_cls = lappend(restrict_cls, cinfo); + } } - } - return restrict_cls; + return restrict_cls; } + #endif -/* +/* * index-innerjoin-- - * Creates index path nodes corresponding to paths to be used as inner - * relations in nestloop joins. + * Creates index path nodes corresponding to paths to be used as inner + * relations in nestloop joins. * * 'clausegroup-list' is a list of list of clauseinfo nodes which can use * 'index' on their inner relation. - * + * * Returns a list of index pathnodes. - * + * */ -static List * -index_innerjoin(Query *root, Rel *rel, List *clausegroup_list, Rel *index) +static List * +index_innerjoin(Query * root, Rel * rel, List * clausegroup_list, Rel * index) { - List *clausegroup = NIL; - List *cg_list = NIL; - List *i = NIL; - IndexPath *pathnode = (IndexPath*)NULL; - Cost temp_selec; - float temp_pages; - - foreach(i,clausegroup_list) { - List *attnos, *values, *flags; - - clausegroup = lfirst(i); - pathnode = makeNode(IndexPath); - - get_joinvars(lfirsti(rel->relids),clausegroup, - &attnos, &values, &flags); - index_selectivity(lfirsti(index->relids), - index->classlist, - get_opnos(clausegroup), - getrelid(lfirsti(rel->relids), - root->rtable), - attnos, - values, - flags, - length(clausegroup), - &temp_pages, - &temp_selec); - pathnode->path.pathtype = T_IndexScan; - pathnode->path.parent = rel; - pathnode->indexid = index->relids; - pathnode->indexkeys = index->indexkeys; - pathnode->indexqual = clausegroup; - - pathnode->path.joinid = ((CInfo*)lfirst(clausegroup))->cinfojoinid; - - pathnode->path.path_cost = - cost_index((Oid)lfirsti(index->relids), - (int)temp_pages, - temp_selec, - rel->pages, - rel->tuples, - index->pages, - index->tuples, - true); - - /* copy clauseinfo list into path for expensive function processing - -- JMH, 7/7/92 */ - pathnode->path.locclauseinfo = - set_difference(copyObject((Node*)rel->clauseinfo), - clausegroup); - -#if 0 /* fix xfunc */ - /* add in cost for expensive functions! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) { - ((Path*)pathnode)->path_cost += - xfunc_get_path_cost((Path*)pathnode); - } + List *clausegroup = NIL; + List *cg_list = NIL; + List *i = NIL; + IndexPath *pathnode = (IndexPath *) NULL; + Cost temp_selec; + float temp_pages; + + foreach(i, clausegroup_list) + { + List *attnos, + *values, + *flags; + + clausegroup = lfirst(i); + pathnode = makeNode(IndexPath); + + get_joinvars(lfirsti(rel->relids), clausegroup, + &attnos, &values, &flags); + index_selectivity(lfirsti(index->relids), + index->classlist, + get_opnos(clausegroup), + getrelid(lfirsti(rel->relids), + root->rtable), + attnos, + values, + flags, + length(clausegroup), + &temp_pages, + &temp_selec); + pathnode->path.pathtype = T_IndexScan; + pathnode->path.parent = rel; + pathnode->indexid = index->relids; + pathnode->indexkeys = index->indexkeys; + pathnode->indexqual = clausegroup; + + pathnode->path.joinid = ((CInfo *) lfirst(clausegroup))->cinfojoinid; + + pathnode->path.path_cost = + cost_index((Oid) lfirsti(index->relids), + (int) temp_pages, + temp_selec, + rel->pages, + rel->tuples, + index->pages, + index->tuples, + true); + + /* + * copy clauseinfo list into path for expensive function + * processing -- JMH, 7/7/92 + */ + pathnode->path.locclauseinfo = + set_difference(copyObject((Node *) rel->clauseinfo), + clausegroup); + +#if 0 /* fix xfunc */ + /* add in cost for expensive functions! -- JMH, 7/7/92 */ + if (XfuncMode != XFUNC_OFF) + { + ((Path *) pathnode)->path_cost += + xfunc_get_path_cost((Path *) pathnode); + } #endif - cg_list = lappend(cg_list,pathnode); - } - return(cg_list); + cg_list = lappend(cg_list, pathnode); + } + return (cg_list); } -/* +/* * create-index-paths-- - * Creates a list of index path nodes for each group of clauses - * (restriction or join) that can be used in conjunction with an index. - * + * Creates a list of index path nodes for each group of clauses + * (restriction or join) that can be used in conjunction with an index. + * * 'rel' is the relation for which 'index' is defined - * 'clausegroup-list' is the list of clause groups (lists of clauseinfo - * nodes) grouped by mergesortorder + * 'clausegroup-list' is the list of clause groups (lists of clauseinfo + * nodes) grouped by mergesortorder * 'join' is a flag indicating whether or not the clauses are join - * clauses - * + * clauses + * * Returns a list of new index path nodes. - * + * */ -static List * -create_index_paths(Query *root, - Rel *rel, - Rel *index, - List *clausegroup_list, - bool join) +static List * +create_index_paths(Query * root, + Rel * rel, + Rel * index, + List * clausegroup_list, + bool join) { - List *clausegroup = NIL; - List *ip_list = NIL; - List *i = NIL; - List *j = NIL; - IndexPath *temp_path; - - foreach(i, clausegroup_list) { - CInfo *clauseinfo; - List *temp_node = NIL; - bool temp = true; - - clausegroup = lfirst(i); - - foreach (j,clausegroup) { - clauseinfo = (CInfo*)lfirst(j); - if (!(join_clause_p((Node*)clauseinfo->clause) && - equal_path_merge_ordering(index->ordering, - clauseinfo->mergesortorder))) { - temp = false; - } - } + List *clausegroup = NIL; + List *ip_list = NIL; + List *i = NIL; + List *j = NIL; + IndexPath *temp_path; - if (!join || temp) { /* restriction, ordering scan */ - temp_path = create_index_path (root, rel,index,clausegroup,join); - temp_node = - lcons(temp_path, NIL); - ip_list = nconc(ip_list,temp_node); - } - } - return(ip_list); + foreach(i, clausegroup_list) + { + CInfo *clauseinfo; + List *temp_node = NIL; + bool temp = true; + + clausegroup = lfirst(i); + + foreach(j, clausegroup) + { + clauseinfo = (CInfo *) lfirst(j); + if (!(join_clause_p((Node *) clauseinfo->clause) && + equal_path_merge_ordering(index->ordering, + clauseinfo->mergesortorder))) + { + temp = false; + } + } + + if (!join || temp) + { /* restriction, ordering scan */ + temp_path = create_index_path(root, rel, index, clausegroup, join); + temp_node = + lcons(temp_path, NIL); + ip_list = nconc(ip_list, temp_node); + } + } + return (ip_list); } -static List * -add_index_paths(List *indexpaths, List *new_indexpaths) +static List * +add_index_paths(List * indexpaths, List * new_indexpaths) { - return append(indexpaths, new_indexpaths); + return append(indexpaths, new_indexpaths); } -static bool -function_index_operand(Expr *funcOpnd, Rel *rel, Rel *index) +static bool +function_index_operand(Expr * funcOpnd, Rel * rel, Rel * index) { - Oid heapRelid = (Oid)lfirsti(rel->relids); - Func *function; - List *funcargs; - int *indexKeys = index->indexkeys; - List *arg; - int i; - - /* - * sanity check, make sure we know what we're dealing with here. - */ - if (funcOpnd==NULL || - nodeTag(funcOpnd)!=T_Expr || funcOpnd->opType!=FUNC_EXPR || - funcOpnd->oper==NULL || indexKeys==NULL) - return false; + Oid heapRelid = (Oid) lfirsti(rel->relids); + Func *function; + List *funcargs; + int *indexKeys = index->indexkeys; + List *arg; + int i; - function = (Func*)funcOpnd->oper; - funcargs = funcOpnd->args; + /* + * sanity check, make sure we know what we're dealing with here. + */ + if (funcOpnd == NULL || + nodeTag(funcOpnd) != T_Expr || funcOpnd->opType != FUNC_EXPR || + funcOpnd->oper == NULL || indexKeys == NULL) + return false; - if (function->funcid != index->indproc) - return false; + function = (Func *) funcOpnd->oper; + funcargs = funcOpnd->args; + + if (function->funcid != index->indproc) + return false; + + /* + * Check that the arguments correspond to the same arguments used to + * create the functional index. To do this we must check that 1. + * refer to the right relatiion. 2. the args have the right attr. + * numbers in the right order. + * + * + * Check all args refer to the correct relation (i.e. the one with the + * functional index defined on it (rel). To do this we can simply + * compare range table entry numbers, they must be the same. + */ + foreach(arg, funcargs) + { + if (heapRelid != ((Var *) lfirst(arg))->varno) + return false; + } + + /* + * check attr numbers and order. + */ + i = 0; + foreach(arg, funcargs) + { + + if (indexKeys[i] == 0) + return (false); - /* - * Check that the arguments correspond to the same arguments used - * to create the functional index. To do this we must check that - * 1. refer to the right relatiion. - * 2. the args have the right attr. numbers in the right order. - * - * - * Check all args refer to the correct relation (i.e. the one with - * the functional index defined on it (rel). To do this we can - * simply compare range table entry numbers, they must be the same. - */ - foreach (arg, funcargs) { - if (heapRelid != ((Var*)lfirst(arg))->varno) - return false; - } - - /* - * check attr numbers and order. - */ - i = 0; - foreach (arg, funcargs) { - - if (indexKeys[i]==0) - return (false); - - if (((Var*)lfirst(arg))->varattno != indexKeys[i]) - return (false); - - i++; - } - - return true; + if (((Var *) lfirst(arg))->varattno != indexKeys[i]) + return (false); + + i++; + } + + return true; } -static bool -SingleAttributeIndex(Rel *index) +static bool +SingleAttributeIndex(Rel * index) { - /* - * return false for now as I don't know if we support index scans - * on disjunction and the code doesn't work - */ - return (false); + + /* + * return false for now as I don't know if we support index scans on + * disjunction and the code doesn't work + */ + return (false); #if 0 - /* - * Non-functional indices. - */ - if (index->indproc == InvalidOid) - return (index->indexkeys[0] != 0 && - index->indexkeys[1] == 0); - - /* - * We have a functional index which is a single attr index - */ - return true; + + /* + * Non-functional indices. + */ + if (index->indproc == InvalidOid) + return (index->indexkeys[0] != 0 && + index->indexkeys[1] == 0); + + /* + * We have a functional index which is a single attr index + */ + return true; #endif } diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 87365278ffa..c20558cf42b 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinpath.c-- - * Routines to find all possible paths for processing a set of joins + * Routines to find all possible paths for processing a set of joins * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.2 1996/10/31 10:59:00 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.3 1997/09/07 04:43:38 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,269 +26,286 @@ #include "optimizer/paths.h" #include "optimizer/pathnode.h" #include "optimizer/keys.h" -#include "optimizer/cost.h" /* for _enable_{hashjoin, _enable_mergesort} */ - -static Path *best_innerjoin(List *join_paths, List *outer_relid); -static List *sort_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *mergeinfo_list); -static List *match_unsorted_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin, - List *mergeinfo_list); -static List *match_unsorted_inner(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *innerpath_list, List *mergeinfo_list); -static bool EnoughMemoryForHashjoin(Rel *hashrel); -static List *hash_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel, - List *hashinfo_list); - -/* +#include "optimizer/cost.h" /* for _enable_{hashjoin, + * _enable_mergesort} */ + +static Path *best_innerjoin(List * join_paths, List * outer_relid); +static List * +sort_inner_and_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * mergeinfo_list); +static List * +match_unsorted_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * outerpath_list, Path * cheapest_inner, Path * best_innerjoin, + List * mergeinfo_list); +static List * +match_unsorted_inner(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * innerpath_list, List * mergeinfo_list); +static bool EnoughMemoryForHashjoin(Rel * hashrel); +static List * +hash_inner_and_outer(Rel * joinrel, Rel * outerrel, Rel * innerrel, + List * hashinfo_list); + +/* * find-all-join-paths-- - * Creates all possible ways to process joins for each of the join - * relations in the list 'joinrels.' Each unique path will be included - * in the join relation's 'pathlist' field. - * - * In postgres, n-way joins are handled left-only(permuting clauseless - * joins doesn't usually win much). - * - * if BushyPlanFlag is true, bushy tree plans will be generated + * Creates all possible ways to process joins for each of the join + * relations in the list 'joinrels.' Each unique path will be included + * in the join relation's 'pathlist' field. + * + * In postgres, n-way joins are handled left-only(permuting clauseless + * joins doesn't usually win much). + * + * if BushyPlanFlag is true, bushy tree plans will be generated * * 'joinrels' is the list of relation entries to be joined - * + * * Modifies the pathlist field of the appropriate rel node to contain * the unique join paths. * If bushy trees are considered, may modify the relid field of the * join rel nodes to flatten the lists. - * - * Returns nothing of interest. (?) + * + * Returns nothing of interest. (?) * It does a destructive modification. */ void -find_all_join_paths(Query *root, List *joinrels) +find_all_join_paths(Query * root, List * joinrels) { - List *mergeinfo_list = NIL; - List *hashinfo_list = NIL; - List *temp_list = NIL; - List *path = NIL; - - while (joinrels != NIL) { - Rel *joinrel = (Rel *)lfirst(joinrels); - List *innerrelids; - List *outerrelids; - Rel *innerrel; - Rel *outerrel; - Path *bestinnerjoin; - List *pathlist = NIL; - - innerrelids = lsecond(joinrel->relids); - outerrelids = lfirst(joinrel->relids); - - /* - * base relation id is an integer and join relation relid is a - * list of integers. - */ - innerrel = (length(innerrelids)==1)? - get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root,innerrelids); - outerrel = (length(outerrelids)==1)? - get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids); - - bestinnerjoin = best_innerjoin(innerrel->innerjoin, - outerrel->relids); - if( _enable_mergesort_ ) { - mergeinfo_list = - group_clauses_by_order(joinrel->clauseinfo, - lfirsti(innerrel->relids)); - } - - if( _enable_hashjoin_ ) { - hashinfo_list = - group_clauses_by_hashop(joinrel->clauseinfo, - lfirsti(innerrel->relids)); - } - - /* need to flatten the relids list */ - joinrel->relids = intAppend(outerrelids, innerrelids); - - /* - * 1. Consider mergesort paths where both relations must be - * explicitly sorted. - */ - pathlist = sort_inner_and_outer(joinrel,outerrel, - innerrel,mergeinfo_list); - - /* - * 2. Consider paths where the outer relation need not be explicitly - * sorted. This may include either nestloops and mergesorts where - * the outer path is already ordered. - */ - pathlist = - add_pathlist(joinrel, pathlist, - match_unsorted_outer(joinrel, - outerrel, - innerrel, - outerrel->pathlist, - (Path*)innerrel->cheapestpath, - bestinnerjoin, - mergeinfo_list)); - - /* - * 3. Consider paths where the inner relation need not be explicitly - * sorted. This may include nestloops and mergesorts the actual - * nestloop nodes were constructed in (match-unsorted-outer). - */ - pathlist = - add_pathlist(joinrel,pathlist, - match_unsorted_inner(joinrel,outerrel, - innerrel, - innerrel->pathlist, - mergeinfo_list)); - - /* - * 4. Consider paths where both outer and inner relations must be - * hashed before being joined. - */ - - pathlist = - add_pathlist(joinrel, pathlist, - hash_inner_and_outer(joinrel,outerrel, - innerrel,hashinfo_list)); - - joinrel->pathlist = pathlist; - - /* - * 'OuterJoinCost is only valid when calling (match-unsorted-inner) - * with the same arguments as the previous invokation of - * (match-unsorted-outer), so clear the field before going on. - */ - temp_list = innerrel->pathlist; - foreach(path, temp_list) { - - /* - * XXX - * - * This gross hack is to get around an apparent optimizer bug on - * Sparc (or maybe it is a bug of ours?) that causes really wierd - * behavior. - */ - if (IsA_JoinPath(path)) { - ((Path*)lfirst(path))->outerjoincost = (Cost) 0; - } - - /* do it iff it is a join path, which is not always - true, esp since the base level */ + List *mergeinfo_list = NIL; + List *hashinfo_list = NIL; + List *temp_list = NIL; + List *path = NIL; + + while (joinrels != NIL) + { + Rel *joinrel = (Rel *) lfirst(joinrels); + List *innerrelids; + List *outerrelids; + Rel *innerrel; + Rel *outerrel; + Path *bestinnerjoin; + List *pathlist = NIL; + + innerrelids = lsecond(joinrel->relids); + outerrelids = lfirst(joinrel->relids); + + /* + * base relation id is an integer and join relation relid is a + * list of integers. + */ + innerrel = (length(innerrelids) == 1) ? + get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root, innerrelids); + outerrel = (length(outerrelids) == 1) ? + get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids); + + bestinnerjoin = best_innerjoin(innerrel->innerjoin, + outerrel->relids); + if (_enable_mergesort_) + { + mergeinfo_list = + group_clauses_by_order(joinrel->clauseinfo, + lfirsti(innerrel->relids)); + } + + if (_enable_hashjoin_) + { + hashinfo_list = + group_clauses_by_hashop(joinrel->clauseinfo, + lfirsti(innerrel->relids)); + } + + /* need to flatten the relids list */ + joinrel->relids = intAppend(outerrelids, innerrelids); + + /* + * 1. Consider mergesort paths where both relations must be + * explicitly sorted. + */ + pathlist = sort_inner_and_outer(joinrel, outerrel, + innerrel, mergeinfo_list); + + /* + * 2. Consider paths where the outer relation need not be + * explicitly sorted. This may include either nestloops and + * mergesorts where the outer path is already ordered. + */ + pathlist = + add_pathlist(joinrel, pathlist, + match_unsorted_outer(joinrel, + outerrel, + innerrel, + outerrel->pathlist, + (Path *) innerrel->cheapestpath, + bestinnerjoin, + mergeinfo_list)); + + /* + * 3. Consider paths where the inner relation need not be + * explicitly sorted. This may include nestloops and mergesorts + * the actual nestloop nodes were constructed in + * (match-unsorted-outer). + */ + pathlist = + add_pathlist(joinrel, pathlist, + match_unsorted_inner(joinrel, outerrel, + innerrel, + innerrel->pathlist, + mergeinfo_list)); + + /* + * 4. Consider paths where both outer and inner relations must be + * hashed before being joined. + */ + + pathlist = + add_pathlist(joinrel, pathlist, + hash_inner_and_outer(joinrel, outerrel, + innerrel, hashinfo_list)); + + joinrel->pathlist = pathlist; + + /* + * 'OuterJoinCost is only valid when calling + * (match-unsorted-inner) with the same arguments as the previous + * invokation of (match-unsorted-outer), so clear the field before + * going on. + */ + temp_list = innerrel->pathlist; + foreach(path, temp_list) + { + + /* + * XXX + * + * This gross hack is to get around an apparent optimizer bug on + * Sparc (or maybe it is a bug of ours?) that causes really + * wierd behavior. + */ + if (IsA_JoinPath(path)) + { + ((Path *) lfirst(path))->outerjoincost = (Cost) 0; + } + + /* + * do it iff it is a join path, which is not always true, esp + * since the base level + */ + } + + joinrels = lnext(joinrels); } - - joinrels = lnext(joinrels); - } } -/* +/* * best-innerjoin-- - * Find the cheapest index path that has already been identified by - * (indexable_joinclauses) as being a possible inner path for the given - * outer relation in a nestloop join. - * + * Find the cheapest index path that has already been identified by + * (indexable_joinclauses) as being a possible inner path for the given + * outer relation in a nestloop join. + * * 'join-paths' is a list of join nodes * 'outer-relid' is the relid of the outer join relation - * + * * Returns the pathnode of the selected path. */ -static Path * -best_innerjoin(List *join_paths, List *outer_relids) +static Path * +best_innerjoin(List * join_paths, List * outer_relids) { - Path *cheapest = (Path*)NULL; - List *join_path; - - foreach(join_path, join_paths) { - Path *path = (Path *)lfirst(join_path); + Path *cheapest = (Path *) NULL; + List *join_path; + + foreach(join_path, join_paths) + { + Path *path = (Path *) lfirst(join_path); - if (intMember(lfirsti(path->joinid), outer_relids) - && ((cheapest==NULL || - path_is_cheaper((Path*)lfirst(join_path),cheapest)))) { + if (intMember(lfirsti(path->joinid), outer_relids) + && ((cheapest == NULL || + path_is_cheaper((Path *) lfirst(join_path), cheapest)))) + { - cheapest = (Path*)lfirst(join_path); + cheapest = (Path *) lfirst(join_path); + } } - } - return(cheapest); + return (cheapest); } -/* +/* * sort-inner-and-outer-- - * Create mergesort join paths by explicitly sorting both the outer and - * inner join relations on each available merge ordering. - * + * Create mergesort join paths by explicitly sorting both the outer and + * inner join relations on each available merge ordering. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'mergeinfo-list' is a list of nodes containing info on(mergesortable) - * clauses for joining the relations - * + * clauses for joining the relations + * * Returns a list of mergesort paths. */ -static List * -sort_inner_and_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *mergeinfo_list) +static List * +sort_inner_and_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * mergeinfo_list) { - List *ms_list = NIL; - MInfo *xmergeinfo = (MInfo*)NULL; - MergePath *temp_node = (MergePath*)NULL; - List *i; - List *outerkeys = NIL; - List *innerkeys = NIL; - List *merge_pathkeys = NIL; - - foreach(i, mergeinfo_list) { - xmergeinfo = (MInfo *)lfirst(i); - - outerkeys = - extract_path_keys(xmergeinfo->jmethod.jmkeys, - outerrel->targetlist, - OUTER); - - innerkeys = - extract_path_keys(xmergeinfo->jmethod.jmkeys, - innerrel->targetlist, - INNER); - - merge_pathkeys = - new_join_pathkeys(outerkeys, joinrel->targetlist, - xmergeinfo->jmethod.clauses); - - temp_node = - create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - (Path*)innerrel->cheapestpath, - merge_pathkeys, - xmergeinfo->m_ordering, - xmergeinfo->jmethod.clauses, - outerkeys, - innerkeys); - - ms_list = lappend(ms_list, temp_node); - } - return(ms_list); + List *ms_list = NIL; + MInfo *xmergeinfo = (MInfo *) NULL; + MergePath *temp_node = (MergePath *) NULL; + List *i; + List *outerkeys = NIL; + List *innerkeys = NIL; + List *merge_pathkeys = NIL; + + foreach(i, mergeinfo_list) + { + xmergeinfo = (MInfo *) lfirst(i); + + outerkeys = + extract_path_keys(xmergeinfo->jmethod.jmkeys, + outerrel->targetlist, + OUTER); + + innerkeys = + extract_path_keys(xmergeinfo->jmethod.jmkeys, + innerrel->targetlist, + INNER); + + merge_pathkeys = + new_join_pathkeys(outerkeys, joinrel->targetlist, + xmergeinfo->jmethod.clauses); + + temp_node = + create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + (Path *) innerrel->cheapestpath, + merge_pathkeys, + xmergeinfo->m_ordering, + xmergeinfo->jmethod.clauses, + outerkeys, + innerkeys); + + ms_list = lappend(ms_list, temp_node); + } + return (ms_list); } -/* +/* * match-unsorted-outer-- - * Creates possible join paths for processing a single join relation - * 'joinrel' by employing either iterative substitution or - * mergesorting on each of its possible outer paths(assuming that the - * outer relation need not be explicitly sorted). - * - * 1. The inner path is the cheapest available inner path. - * 2. Mergesort wherever possible. Mergesorts are considered if there - * are mergesortable join clauses between the outer and inner join - * relations such that the outer path is keyed on the variables - * appearing in the clauses. The corresponding inner merge path is - * either a path whose keys match those of the outer path(if such a - * path is available) or an explicit sort on the appropriate inner - * join keys, whichever is cheaper. - * + * Creates possible join paths for processing a single join relation + * 'joinrel' by employing either iterative substitution or + * mergesorting on each of its possible outer paths(assuming that the + * outer relation need not be explicitly sorted). + * + * 1. The inner path is the cheapest available inner path. + * 2. Mergesort wherever possible. Mergesorts are considered if there + * are mergesortable join clauses between the outer and inner join + * relations such that the outer path is keyed on the variables + * appearing in the clauses. The corresponding inner merge path is + * either a path whose keys match those of the outer path(if such a + * path is available) or an explicit sort on the appropriate inner + * join keys, whichever is cheaper. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation @@ -296,331 +313,355 @@ sort_inner_and_outer(Rel *joinrel, * 'cheapest-inner' is the cheapest inner path * 'best-innerjoin' is the best inner index path(if any) * 'mergeinfo-list' is a list of nodes containing info on mergesortable - * clauses - * + * clauses + * * Returns a list of possible join path nodes. */ -static List * -match_unsorted_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *outerpath_list, - Path *cheapest_inner, - Path *best_innerjoin, - List *mergeinfo_list) +static List * +match_unsorted_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * outerpath_list, + Path * cheapest_inner, + Path * best_innerjoin, + List * mergeinfo_list) { - Path *outerpath = (Path*)NULL; - List *jp_list = NIL; - List *temp_node = NIL; - List *merge_pathkeys = NIL; - Path *nestinnerpath =(Path*)NULL; - List *paths = NIL; - List *i = NIL; - PathOrder *outerpath_ordering = NULL; - - foreach(i,outerpath_list) { - List *clauses = NIL; - List *matchedJoinKeys = NIL; - List *matchedJoinClauses = NIL; - MInfo *xmergeinfo = (MInfo*)NULL; - - outerpath = (Path*)lfirst(i); - - outerpath_ordering = &outerpath->p_ordering; - - if (outerpath_ordering) { - xmergeinfo = - match_order_mergeinfo(outerpath_ordering, - mergeinfo_list); - } - - if (xmergeinfo) { - clauses = xmergeinfo->jmethod.clauses; - } - - if (clauses) { - List *keys = xmergeinfo->jmethod.jmkeys; - List *clauses = xmergeinfo->jmethod.clauses; - - matchedJoinKeys = - match_pathkeys_joinkeys(outerpath->keys, - keys, - clauses, - OUTER, - &matchedJoinClauses); - merge_pathkeys = - new_join_pathkeys(outerpath->keys, - joinrel->targetlist, clauses); - } else { - merge_pathkeys = outerpath->keys; - } - - if(best_innerjoin && - path_is_cheaper(best_innerjoin, cheapest_inner)) { - nestinnerpath = best_innerjoin; - } else { - nestinnerpath = cheapest_inner; - } - - paths = lcons(create_nestloop_path(joinrel, - outerrel, - outerpath, - nestinnerpath, - merge_pathkeys), - NIL); - - if (clauses && matchedJoinKeys) { - bool path_is_cheaper_than_sort; - List *varkeys = NIL; - Path *mergeinnerpath = - match_paths_joinkeys(matchedJoinKeys, - outerpath_ordering, - innerrel->pathlist, - INNER); - - path_is_cheaper_than_sort = - (bool) (mergeinnerpath && - (mergeinnerpath->path_cost < - (cheapest_inner->path_cost + - cost_sort(matchedJoinKeys, - innerrel->size, - innerrel->width, - false)))); - if(!path_is_cheaper_than_sort) { - varkeys = - extract_path_keys(matchedJoinKeys, - innerrel->targetlist, - INNER); - } - - - /* - * Keep track of the cost of the outer path used with - * this ordered inner path for later processing in - * (match-unsorted-inner), since it isn't a sort and - * thus wouldn't otherwise be considered. - */ - if (path_is_cheaper_than_sort) { - mergeinnerpath->outerjoincost = outerpath->path_cost; - } else { - mergeinnerpath = cheapest_inner; - } - - temp_node = - lcons(create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - outerpath, - mergeinnerpath, - merge_pathkeys, - xmergeinfo->m_ordering, - matchedJoinClauses, - NIL, - varkeys), - paths); - } else { - temp_node = paths; - } - jp_list = nconc(jp_list, temp_node); - } - return(jp_list); + Path *outerpath = (Path *) NULL; + List *jp_list = NIL; + List *temp_node = NIL; + List *merge_pathkeys = NIL; + Path *nestinnerpath = (Path *) NULL; + List *paths = NIL; + List *i = NIL; + PathOrder *outerpath_ordering = NULL; + + foreach(i, outerpath_list) + { + List *clauses = NIL; + List *matchedJoinKeys = NIL; + List *matchedJoinClauses = NIL; + MInfo *xmergeinfo = (MInfo *) NULL; + + outerpath = (Path *) lfirst(i); + + outerpath_ordering = &outerpath->p_ordering; + + if (outerpath_ordering) + { + xmergeinfo = + match_order_mergeinfo(outerpath_ordering, + mergeinfo_list); + } + + if (xmergeinfo) + { + clauses = xmergeinfo->jmethod.clauses; + } + + if (clauses) + { + List *keys = xmergeinfo->jmethod.jmkeys; + List *clauses = xmergeinfo->jmethod.clauses; + + matchedJoinKeys = + match_pathkeys_joinkeys(outerpath->keys, + keys, + clauses, + OUTER, + &matchedJoinClauses); + merge_pathkeys = + new_join_pathkeys(outerpath->keys, + joinrel->targetlist, clauses); + } + else + { + merge_pathkeys = outerpath->keys; + } + + if (best_innerjoin && + path_is_cheaper(best_innerjoin, cheapest_inner)) + { + nestinnerpath = best_innerjoin; + } + else + { + nestinnerpath = cheapest_inner; + } + + paths = lcons(create_nestloop_path(joinrel, + outerrel, + outerpath, + nestinnerpath, + merge_pathkeys), + NIL); + + if (clauses && matchedJoinKeys) + { + bool path_is_cheaper_than_sort; + List *varkeys = NIL; + Path *mergeinnerpath = + match_paths_joinkeys(matchedJoinKeys, + outerpath_ordering, + innerrel->pathlist, + INNER); + + path_is_cheaper_than_sort = + (bool) (mergeinnerpath && + (mergeinnerpath->path_cost < + (cheapest_inner->path_cost + + cost_sort(matchedJoinKeys, + innerrel->size, + innerrel->width, + false)))); + if (!path_is_cheaper_than_sort) + { + varkeys = + extract_path_keys(matchedJoinKeys, + innerrel->targetlist, + INNER); + } + + + /* + * Keep track of the cost of the outer path used with this + * ordered inner path for later processing in + * (match-unsorted-inner), since it isn't a sort and thus + * wouldn't otherwise be considered. + */ + if (path_is_cheaper_than_sort) + { + mergeinnerpath->outerjoincost = outerpath->path_cost; + } + else + { + mergeinnerpath = cheapest_inner; + } + + temp_node = + lcons(create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + outerpath, + mergeinnerpath, + merge_pathkeys, + xmergeinfo->m_ordering, + matchedJoinClauses, + NIL, + varkeys), + paths); + } + else + { + temp_node = paths; + } + jp_list = nconc(jp_list, temp_node); + } + return (jp_list); } -/* +/* * match-unsorted-inner -- - * Find the cheapest ordered join path for a given(ordered, unsorted) - * inner join path. - * - * Scans through each path available on an inner join relation and tries - * matching its ordering keys against those of mergejoin clauses. - * If 1. an appropriately-ordered inner path and matching mergeclause are - * found, and - * 2. sorting the cheapest outer path is cheaper than using an ordered - * but unsorted outer path(as was considered in - * (match-unsorted-outer)), - * then this merge path is considered. - * + * Find the cheapest ordered join path for a given(ordered, unsorted) + * inner join path. + * + * Scans through each path available on an inner join relation and tries + * matching its ordering keys against those of mergejoin clauses. + * If 1. an appropriately-ordered inner path and matching mergeclause are + * found, and + * 2. sorting the cheapest outer path is cheaper than using an ordered + * but unsorted outer path(as was considered in + * (match-unsorted-outer)), + * then this merge path is considered. + * * 'joinrel' is the join result relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'innerpath-list' is the list of possible inner join paths * 'mergeinfo-list' is a list of nodes containing info on mergesortable - * clauses - * + * clauses + * * Returns a list of possible merge paths. */ -static List * -match_unsorted_inner(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *innerpath_list, - List *mergeinfo_list) +static List * +match_unsorted_inner(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * innerpath_list, + List * mergeinfo_list) { - Path *innerpath = (Path*)NULL; - List *mp_list = NIL; - List *temp_node = NIL; - PathOrder *innerpath_ordering = NULL; - Cost temp1 = 0.0; - bool temp2 = false; - List *i = NIL; - - foreach (i, innerpath_list) { - MInfo *xmergeinfo = (MInfo*)NULL; - List *clauses = NIL; - List *matchedJoinKeys = NIL; - List *matchedJoinClauses = NIL; - - innerpath = (Path*)lfirst(i); - - innerpath_ordering = &innerpath->p_ordering; - - if (innerpath_ordering) { - xmergeinfo = - match_order_mergeinfo(innerpath_ordering, - mergeinfo_list); - } - - if (xmergeinfo) { - clauses = ((JoinMethod*)xmergeinfo)->clauses; - } - - if (clauses) { - List *keys = xmergeinfo->jmethod.jmkeys; - List *cls = xmergeinfo->jmethod.clauses; - - matchedJoinKeys = - match_pathkeys_joinkeys(innerpath->keys, - keys, - cls, - INNER, - &matchedJoinClauses); - } - - /* - * (match-unsorted-outer) if it is applicable. - * 'OuterJoinCost was set above in - */ - if (clauses && matchedJoinKeys) { - temp1 = outerrel->cheapestpath->path_cost + - cost_sort(matchedJoinKeys, outerrel->size, outerrel->width, - false); - - temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost) - || (innerpath->outerjoincost > temp1)); - - if(temp2) { - List *outerkeys = - extract_path_keys(matchedJoinKeys, - outerrel->targetlist, - OUTER); - List *merge_pathkeys = - new_join_pathkeys(outerkeys, - joinrel->targetlist, - clauses); - - temp_node = - lcons(create_mergesort_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - innerpath, - merge_pathkeys, - xmergeinfo->m_ordering, - matchedJoinClauses, - outerkeys, - NIL), - NIL); - - mp_list = nconc(mp_list,temp_node); - } + Path *innerpath = (Path *) NULL; + List *mp_list = NIL; + List *temp_node = NIL; + PathOrder *innerpath_ordering = NULL; + Cost temp1 = 0.0; + bool temp2 = false; + List *i = NIL; + + foreach(i, innerpath_list) + { + MInfo *xmergeinfo = (MInfo *) NULL; + List *clauses = NIL; + List *matchedJoinKeys = NIL; + List *matchedJoinClauses = NIL; + + innerpath = (Path *) lfirst(i); + + innerpath_ordering = &innerpath->p_ordering; + + if (innerpath_ordering) + { + xmergeinfo = + match_order_mergeinfo(innerpath_ordering, + mergeinfo_list); + } + + if (xmergeinfo) + { + clauses = ((JoinMethod *) xmergeinfo)->clauses; + } + + if (clauses) + { + List *keys = xmergeinfo->jmethod.jmkeys; + List *cls = xmergeinfo->jmethod.clauses; + + matchedJoinKeys = + match_pathkeys_joinkeys(innerpath->keys, + keys, + cls, + INNER, + &matchedJoinClauses); + } + + /* + * (match-unsorted-outer) if it is applicable. 'OuterJoinCost was + * set above in + */ + if (clauses && matchedJoinKeys) + { + temp1 = outerrel->cheapestpath->path_cost + + cost_sort(matchedJoinKeys, outerrel->size, outerrel->width, + false); + + temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost) + || (innerpath->outerjoincost > temp1)); + + if (temp2) + { + List *outerkeys = + extract_path_keys(matchedJoinKeys, + outerrel->targetlist, + OUTER); + List *merge_pathkeys = + new_join_pathkeys(outerkeys, + joinrel->targetlist, + clauses); + + temp_node = + lcons(create_mergesort_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + innerpath, + merge_pathkeys, + xmergeinfo->m_ordering, + matchedJoinClauses, + outerkeys, + NIL), + NIL); + + mp_list = nconc(mp_list, temp_node); + } + } } - } - return(mp_list); - + return (mp_list); + } -static bool -EnoughMemoryForHashjoin(Rel *hashrel) +static bool +EnoughMemoryForHashjoin(Rel * hashrel) { - int ntuples; - int tupsize; - int pages; - - ntuples = hashrel->size; - if (ntuples == 0) ntuples = 1000; - tupsize = hashrel->width + sizeof(HeapTupleData); - pages = page_size(ntuples, tupsize); - /* - * if amount of buffer space below hashjoin threshold, - * return false - */ - if (ceil(sqrt((double)pages)) > NBuffers) - return false; - return true; + int ntuples; + int tupsize; + int pages; + + ntuples = hashrel->size; + if (ntuples == 0) + ntuples = 1000; + tupsize = hashrel->width + sizeof(HeapTupleData); + pages = page_size(ntuples, tupsize); + + /* + * if amount of buffer space below hashjoin threshold, return false + */ + if (ceil(sqrt((double) pages)) > NBuffers) + return false; + return true; } -/* - * hash-inner-and-outer-- XXX HASH - * Create hashjoin join paths by explicitly hashing both the outer and - * inner join relations on each available hash op. - * +/* + * hash-inner-and-outer-- XXX HASH + * Create hashjoin join paths by explicitly hashing both the outer and + * inner join relations on each available hash op. + * * 'joinrel' is the join relation * 'outerrel' is the outer join relation * 'innerrel' is the inner join relation * 'hashinfo-list' is a list of nodes containing info on(hashjoinable) - * clauses for joining the relations - * + * clauses for joining the relations + * * Returns a list of hashjoin paths. */ -static List * -hash_inner_and_outer(Rel *joinrel, - Rel *outerrel, - Rel *innerrel, - List *hashinfo_list) +static List * +hash_inner_and_outer(Rel * joinrel, + Rel * outerrel, + Rel * innerrel, + List * hashinfo_list) { - HInfo *xhashinfo = (HInfo*)NULL; - List *hjoin_list = NIL; - HashPath *temp_node = (HashPath*)NULL; - List *i = NIL; - List *outerkeys = NIL; - List *innerkeys = NIL; - List *hash_pathkeys = NIL; - - foreach (i, hashinfo_list) { - xhashinfo = (HInfo*)lfirst(i); - outerkeys = - extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys, - outerrel->targetlist, - OUTER); - innerkeys = - extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys, - innerrel->targetlist, - INNER); - hash_pathkeys = - new_join_pathkeys(outerkeys, - joinrel->targetlist, - ((JoinMethod*)xhashinfo)->clauses); - - if (EnoughMemoryForHashjoin(innerrel)) { - temp_node = create_hashjoin_path(joinrel, - outerrel->size, - innerrel->size, - outerrel->width, - innerrel->width, - (Path*)outerrel->cheapestpath, - (Path*)innerrel->cheapestpath, - hash_pathkeys, - xhashinfo->hashop, - ((JoinMethod*)xhashinfo)->clauses, - outerkeys, - innerkeys); - hjoin_list = lappend(hjoin_list, temp_node); + HInfo *xhashinfo = (HInfo *) NULL; + List *hjoin_list = NIL; + HashPath *temp_node = (HashPath *) NULL; + List *i = NIL; + List *outerkeys = NIL; + List *innerkeys = NIL; + List *hash_pathkeys = NIL; + + foreach(i, hashinfo_list) + { + xhashinfo = (HInfo *) lfirst(i); + outerkeys = + extract_path_keys(((JoinMethod *) xhashinfo)->jmkeys, + outerrel->targetlist, + OUTER); + innerkeys = + extract_path_keys(((JoinMethod *) xhashinfo)->jmkeys, + innerrel->targetlist, + INNER); + hash_pathkeys = + new_join_pathkeys(outerkeys, + joinrel->targetlist, + ((JoinMethod *) xhashinfo)->clauses); + + if (EnoughMemoryForHashjoin(innerrel)) + { + temp_node = create_hashjoin_path(joinrel, + outerrel->size, + innerrel->size, + outerrel->width, + innerrel->width, + (Path *) outerrel->cheapestpath, + (Path *) innerrel->cheapestpath, + hash_pathkeys, + xhashinfo->hashop, + ((JoinMethod *) xhashinfo)->clauses, + outerkeys, + innerkeys); + hjoin_list = lappend(hjoin_list, temp_node); + } } - } - return(hjoin_list); + return (hjoin_list); } - diff --git a/src/backend/optimizer/path/joinrels.c b/src/backend/optimizer/path/joinrels.c index 00f8a04a050..98762f9800c 100644 --- a/src/backend/optimizer/path/joinrels.c +++ b/src/backend/optimizer/path/joinrels.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinrels.c-- - * Routines to determine which relations should be joined + * Routines to determine which relations should be joined * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.4 1997/06/05 09:33:52 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.5 1997/09/07 04:43:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,467 +24,508 @@ #include "optimizer/pathnode.h" #ifdef USE_RIGHT_SIDED_PLANS -bool _use_right_sided_plans_ = true; +bool _use_right_sided_plans_ = true; + #else -bool _use_right_sided_plans_ = false; +bool _use_right_sided_plans_ = false; + #endif -static List *find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list); -static List *find_clauseless_joins(Rel *outer_rel, List *inner_rels); -static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo); -static List *new_join_tlist(List *tlist, List *other_relids, - int first_resdomno); -static List *new_joininfo_list(List *joininfo_list, List *join_relids); -static void add_superrels(Rel *rel, Rel *super_rel); -static bool nonoverlap_rels(Rel *rel1, Rel *rel2); -static bool nonoverlap_sets(List *s1, List *s2); -static void set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, - JInfo *jinfo); - -/* +static List *find_clause_joins(Query * root, Rel * outer_rel, List * joininfo_list); +static List *find_clauseless_joins(Rel * outer_rel, List * inner_rels); +static Rel *init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo); +static List * +new_join_tlist(List * tlist, List * other_relids, + int first_resdomno); +static List *new_joininfo_list(List * joininfo_list, List * join_relids); +static void add_superrels(Rel * rel, Rel * super_rel); +static bool nonoverlap_rels(Rel * rel1, Rel * rel2); +static bool nonoverlap_sets(List * s1, List * s2); +static void +set_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel, + JInfo * jinfo); + +/* * find-join-rels-- - * Find all possible joins for each of the outer join relations in - * 'outer-rels'. A rel node is created for each possible join relation, - * and the resulting list of nodes is returned. If at all possible, only - * those relations for which join clauses exist are considered. If none - * of these exist for a given relation, all remaining possibilities are - * considered. - * + * Find all possible joins for each of the outer join relations in + * 'outer-rels'. A rel node is created for each possible join relation, + * and the resulting list of nodes is returned. If at all possible, only + * those relations for which join clauses exist are considered. If none + * of these exist for a given relation, all remaining possibilities are + * considered. + * * 'outer-rels' is the list of rel nodes - * + * * Returns a list of rel nodes corresponding to the new join relations. */ -List * -find_join_rels(Query *root, List *outer_rels) +List * +find_join_rels(Query * root, List * outer_rels) { - List *joins = NIL; - List *join_list = NIL; - List *r = NIL; - - foreach(r, outer_rels) { - Rel *outer_rel = (Rel *)lfirst(r); - - if(!(joins = find_clause_joins(root, outer_rel,outer_rel->joininfo))) - if (BushyPlanFlag) - joins = find_clauseless_joins(outer_rel,outer_rels); - else - joins = find_clauseless_joins(outer_rel,root->base_relation_list_); - - join_list = nconc(join_list, joins); - } - - return(join_list); + List *joins = NIL; + List *join_list = NIL; + List *r = NIL; + + foreach(r, outer_rels) + { + Rel *outer_rel = (Rel *) lfirst(r); + + if (!(joins = find_clause_joins(root, outer_rel, outer_rel->joininfo))) + if (BushyPlanFlag) + joins = find_clauseless_joins(outer_rel, outer_rels); + else + joins = find_clauseless_joins(outer_rel, root->base_relation_list_); + + join_list = nconc(join_list, joins); + } + + return (join_list); } -/* +/* * find-clause-joins-- - * Determines whether joins can be performed between an outer relation - * 'outer-rel' and those relations within 'outer-rel's joininfo nodes - * (i.e., relations that participate in join clauses that 'outer-rel' - * participates in). This is possible if all but one of the relations - * contained within the join clauses of the joininfo node are already - * contained within 'outer-rel'. + * Determines whether joins can be performed between an outer relation + * 'outer-rel' and those relations within 'outer-rel's joininfo nodes + * (i.e., relations that participate in join clauses that 'outer-rel' + * participates in). This is possible if all but one of the relations + * contained within the join clauses of the joininfo node are already + * contained within 'outer-rel'. * * 'outer-rel' is the relation entry for the outer relation - * 'joininfo-list' is a list of join clauses which 'outer-rel' - * participates in - * + * 'joininfo-list' is a list of join clauses which 'outer-rel' + * participates in + * * Returns a list of new join relations. */ -static List * -find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list) +static List * +find_clause_joins(Query * root, Rel * outer_rel, List * joininfo_list) { - List *join_list = NIL; - List *i = NIL; - - foreach (i, joininfo_list) { - JInfo *joininfo = (JInfo*)lfirst(i); - Rel *rel; - - if(!joininfo->inactive) { - List *other_rels = joininfo->otherrels; - - if(other_rels != NIL) { - if(length(other_rels) == 1) { - rel = init_join_rel(outer_rel, - get_base_rel(root, lfirsti(other_rels)), - joininfo); - /* how about right-sided plan ? */ - if ( _use_right_sided_plans_ && - length (outer_rel->relids) > 1 ) - { - if (rel != NULL) - join_list = lappend(join_list, rel); - rel = init_join_rel(get_base_rel(root, lfirsti(other_rels)), - outer_rel, - joininfo); - } - } else if (BushyPlanFlag) { - rel = init_join_rel(outer_rel, - get_join_rel(root, other_rels), - joininfo); - } else { - rel = NULL; - } + List *join_list = NIL; + List *i = NIL; - if (rel != NULL) - join_list = lappend(join_list, rel); - } + foreach(i, joininfo_list) + { + JInfo *joininfo = (JInfo *) lfirst(i); + Rel *rel; + + if (!joininfo->inactive) + { + List *other_rels = joininfo->otherrels; + + if (other_rels != NIL) + { + if (length(other_rels) == 1) + { + rel = init_join_rel(outer_rel, + get_base_rel(root, lfirsti(other_rels)), + joininfo); + /* how about right-sided plan ? */ + if (_use_right_sided_plans_ && + length(outer_rel->relids) > 1) + { + if (rel != NULL) + join_list = lappend(join_list, rel); + rel = init_join_rel(get_base_rel(root, lfirsti(other_rels)), + outer_rel, + joininfo); + } + } + else if (BushyPlanFlag) + { + rel = init_join_rel(outer_rel, + get_join_rel(root, other_rels), + joininfo); + } + else + { + rel = NULL; + } + + if (rel != NULL) + join_list = lappend(join_list, rel); + } + } } - } - return(join_list); + return (join_list); } -/* +/* * find-clauseless-joins-- - * Given an outer relation 'outer-rel' and a list of inner relations - * 'inner-rels', create a join relation between 'outer-rel' and each - * member of 'inner-rels' that isn't already included in 'outer-rel'. - * + * Given an outer relation 'outer-rel' and a list of inner relations + * 'inner-rels', create a join relation between 'outer-rel' and each + * member of 'inner-rels' that isn't already included in 'outer-rel'. + * * Returns a list of new join relations. */ -static List * -find_clauseless_joins(Rel *outer_rel, List *inner_rels) +static List * +find_clauseless_joins(Rel * outer_rel, List * inner_rels) { - Rel *inner_rel; - List *t_list = NIL; - List *temp_node = NIL; - List *i = NIL; - - foreach (i, inner_rels) { - inner_rel = (Rel *)lfirst(i); - if(nonoverlap_rels(inner_rel, outer_rel)) { - temp_node = lcons(init_join_rel(outer_rel, - inner_rel, - (JInfo*)NULL), - NIL); - t_list = nconc(t_list,temp_node); - } - } - - return(t_list); + Rel *inner_rel; + List *t_list = NIL; + List *temp_node = NIL; + List *i = NIL; + + foreach(i, inner_rels) + { + inner_rel = (Rel *) lfirst(i); + if (nonoverlap_rels(inner_rel, outer_rel)) + { + temp_node = lcons(init_join_rel(outer_rel, + inner_rel, + (JInfo *) NULL), + NIL); + t_list = nconc(t_list, temp_node); + } + } + + return (t_list); } -/* +/* * init-join-rel-- - * Creates and initializes a new join relation. - * + * Creates and initializes a new join relation. + * * 'outer-rel' and 'inner-rel' are relation nodes for the relations to be - * joined + * joined * 'joininfo' is the joininfo node(join clause) containing both - * 'outer-rel' and 'inner-rel', if any exists - * + * 'outer-rel' and 'inner-rel', if any exists + * * Returns the new join relation node. */ -static Rel * -init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo) +static Rel * +init_join_rel(Rel * outer_rel, Rel * inner_rel, JInfo * joininfo) { - Rel *joinrel = makeNode(Rel); - List *joinrel_joininfo_list = NIL; - List *new_outer_tlist; - List *new_inner_tlist; - - /* - * Create a new tlist by removing irrelevant elements from both - * tlists of the outer and inner join relations and then merging - * the results together. - */ - new_outer_tlist = - new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ - inner_rel->relids, 1); - new_inner_tlist = - new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ - outer_rel->relids, - length(new_outer_tlist) + 1); - - joinrel->relids = NIL; - joinrel->indexed = false; - joinrel->pages = 0; - joinrel->tuples = 0; - joinrel->width = 0; -/* joinrel->targetlist = NIL;*/ - joinrel->pathlist = NIL; - joinrel->unorderedpath = (Path *)NULL; - joinrel->cheapestpath = (Path *)NULL; - joinrel->pruneable = true; - joinrel->classlist = NULL; - joinrel->relam = InvalidOid; - joinrel->ordering = NULL; - joinrel->clauseinfo = NIL; - joinrel->joininfo = NULL; - joinrel->innerjoin = NIL; - joinrel->superrels = NIL; - - joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? -ay */ - lcons(inner_rel->relids, NIL)); - - new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist); - joinrel->targetlist = new_outer_tlist; - - if (joininfo) { - joinrel->clauseinfo = joininfo->jinfoclauseinfo; - if (BushyPlanFlag) - joininfo->inactive = true; - } - - joinrel_joininfo_list = - new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), - intAppend(outer_rel->relids, inner_rel->relids)); - - joinrel->joininfo = joinrel_joininfo_list; - - set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo); - - return(joinrel); + Rel *joinrel = makeNode(Rel); + List *joinrel_joininfo_list = NIL; + List *new_outer_tlist; + List *new_inner_tlist; + + /* + * Create a new tlist by removing irrelevant elements from both tlists + * of the outer and inner join relations and then merging the results + * together. + */ + new_outer_tlist = + new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */ + inner_rel->relids, 1); + new_inner_tlist = + new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */ + outer_rel->relids, + length(new_outer_tlist) + 1); + + joinrel->relids = NIL; + joinrel->indexed = false; + joinrel->pages = 0; + joinrel->tuples = 0; + joinrel->width = 0; +/* joinrel->targetlist = NIL;*/ + joinrel->pathlist = NIL; + joinrel->unorderedpath = (Path *) NULL; + joinrel->cheapestpath = (Path *) NULL; + joinrel->pruneable = true; + joinrel->classlist = NULL; + joinrel->relam = InvalidOid; + joinrel->ordering = NULL; + joinrel->clauseinfo = NIL; + joinrel->joininfo = NULL; + joinrel->innerjoin = NIL; + joinrel->superrels = NIL; + + joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? + * -ay */ + lcons(inner_rel->relids, NIL)); + + new_outer_tlist = nconc(new_outer_tlist, new_inner_tlist); + joinrel->targetlist = new_outer_tlist; + + if (joininfo) + { + joinrel->clauseinfo = joininfo->jinfoclauseinfo; + if (BushyPlanFlag) + joininfo->inactive = true; + } + + joinrel_joininfo_list = + new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo), + intAppend(outer_rel->relids, inner_rel->relids)); + + joinrel->joininfo = joinrel_joininfo_list; + + set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo); + + return (joinrel); } -/* +/* * new-join-tlist-- - * Builds a join relations's target list by keeping those elements that - * will be in the final target list and any other elements that are still - * needed for future joins. For a target list entry to still be needed - * for future joins, its 'joinlist' field must not be empty after removal - * of all relids in 'other-relids'. - * + * Builds a join relations's target list by keeping those elements that + * will be in the final target list and any other elements that are still + * needed for future joins. For a target list entry to still be needed + * for future joins, its 'joinlist' field must not be empty after removal + * of all relids in 'other-relids'. + * * 'tlist' is the target list of one of the join relations * 'other-relids' is a list of relids contained within the other - * join relation + * join relation * 'first-resdomno' is the resdom number to use for the first created - * target list entry - * + * target list entry + * * Returns the new target list. */ -static List * -new_join_tlist(List *tlist, - List *other_relids, - int first_resdomno) +static List * +new_join_tlist(List * tlist, + List * other_relids, + int first_resdomno) { - int resdomno = first_resdomno - 1; - TargetEntry *xtl = NULL; - List *temp_node = NIL; - List *t_list = NIL; - List *i = NIL; - List *join_list = NIL; - bool in_final_tlist =false; - - - foreach(i,tlist) { - xtl= lfirst(i); - in_final_tlist = (join_list==NIL); - if( in_final_tlist) { - resdomno += 1; - temp_node = - lcons(create_tl_element(get_expr(xtl), - resdomno), - NIL); - t_list = nconc(t_list,temp_node); - } - } - - return(t_list); + int resdomno = first_resdomno - 1; + TargetEntry *xtl = NULL; + List *temp_node = NIL; + List *t_list = NIL; + List *i = NIL; + List *join_list = NIL; + bool in_final_tlist = false; + + + foreach(i, tlist) + { + xtl = lfirst(i); + in_final_tlist = (join_list == NIL); + if (in_final_tlist) + { + resdomno += 1; + temp_node = + lcons(create_tl_element(get_expr(xtl), + resdomno), + NIL); + t_list = nconc(t_list, temp_node); + } + } + + return (t_list); } -/* +/* * new-joininfo-list-- - * Builds a join relation's joininfo list by checking for join clauses - * which still need to used in future joins involving this relation. A - * join clause is still needed if there are still relations in the clause - * not contained in the list of relations comprising this join relation. - * New joininfo nodes are only created and added to - * 'current-joininfo-list' if a node for a particular join hasn't already - * been created. + * Builds a join relation's joininfo list by checking for join clauses + * which still need to used in future joins involving this relation. A + * join clause is still needed if there are still relations in the clause + * not contained in the list of relations comprising this join relation. + * New joininfo nodes are only created and added to + * 'current-joininfo-list' if a node for a particular join hasn't already + * been created. * - * 'current-joininfo-list' contains a list of those joininfo nodes that - * have already been built + * 'current-joininfo-list' contains a list of those joininfo nodes that + * have already been built * 'joininfo-list' is the list of join clauses involving this relation - * 'join-relids' is a list of relids corresponding to the relations - * currently being joined - * + * 'join-relids' is a list of relids corresponding to the relations + * currently being joined + * * Returns a list of joininfo nodes, new and old. */ -static List * -new_joininfo_list(List *joininfo_list, List *join_relids) +static List * +new_joininfo_list(List * joininfo_list, List * join_relids) { - List *current_joininfo_list = NIL; - List *new_otherrels = NIL; - JInfo *other_joininfo = (JInfo*)NULL; - List *xjoininfo = NIL; - - foreach (xjoininfo, joininfo_list) { - List *or; - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - new_otherrels = joininfo->otherrels; - foreach (or, new_otherrels) - { - if ( intMember (lfirsti(or), join_relids) ) - new_otherrels = lremove ((void*)lfirst(or), new_otherrels); - } - joininfo->otherrels = new_otherrels; - if ( new_otherrels != NIL ) + List *current_joininfo_list = NIL; + List *new_otherrels = NIL; + JInfo *other_joininfo = (JInfo *) NULL; + List *xjoininfo = NIL; + + foreach(xjoininfo, joininfo_list) { - other_joininfo = joininfo_member(new_otherrels, - current_joininfo_list); - if(other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(joininfo->jinfoclauseinfo, - other_joininfo->jinfoclauseinfo); - }else { - other_joininfo = makeNode(JInfo); - - other_joininfo->otherrels = - joininfo->otherrels; - other_joininfo->jinfoclauseinfo = - joininfo->jinfoclauseinfo; - other_joininfo->mergesortable = - joininfo->mergesortable; - other_joininfo->hashjoinable = - joininfo->hashjoinable; - other_joininfo->inactive = false; - - current_joininfo_list = lcons(other_joininfo, - current_joininfo_list); - } + List *or; + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + new_otherrels = joininfo->otherrels; + foreach(or, new_otherrels) + { + if (intMember(lfirsti(or), join_relids)) + new_otherrels = lremove((void *) lfirst(or), new_otherrels); + } + joininfo->otherrels = new_otherrels; + if (new_otherrels != NIL) + { + other_joininfo = joininfo_member(new_otherrels, + current_joininfo_list); + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(joininfo->jinfoclauseinfo, + other_joininfo->jinfoclauseinfo); + } + else + { + other_joininfo = makeNode(JInfo); + + other_joininfo->otherrels = + joininfo->otherrels; + other_joininfo->jinfoclauseinfo = + joininfo->jinfoclauseinfo; + other_joininfo->mergesortable = + joininfo->mergesortable; + other_joininfo->hashjoinable = + joininfo->hashjoinable; + other_joininfo->inactive = false; + + current_joininfo_list = lcons(other_joininfo, + current_joininfo_list); + } + } } - } - return(current_joininfo_list); + return (current_joininfo_list); } /* * add-new-joininfos-- - * For each new join relation, create new joininfos that - * use the join relation as inner relation, and add - * the new joininfos to those rel nodes that still - * have joins with the join relation. + * For each new join relation, create new joininfos that + * use the join relation as inner relation, and add + * the new joininfos to those rel nodes that still + * have joins with the join relation. * * 'joinrels' is a list of join relations. * * Modifies the joininfo field of appropriate rel nodes. */ void -add_new_joininfos(Query *root, List *joinrels, List *outerrels) +add_new_joininfos(Query * root, List * joinrels, List * outerrels) { - List *xjoinrel = NIL; - List *xrelid = NIL; - List *xrel = NIL; - List *xjoininfo = NIL; - - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - foreach(xrelid, joinrel->relids) { - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - add_superrels(rel,joinrel); + List *xjoinrel = NIL; + List *xrelid = NIL; + List *xrel = NIL; + List *xjoininfo = NIL; + + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xrelid, joinrel->relids) + { + Relid relid = (Relid) lfirst(xrelid); + Rel *rel = get_join_rel(root, relid); + + add_superrels(rel, joinrel); + } } - } - foreach(xjoinrel, joinrels) { - Rel *joinrel = (Rel *)lfirst(xjoinrel); - - foreach(xjoininfo, joinrel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - List *other_rels = joininfo->otherrels; - List *clause_info = joininfo->jinfoclauseinfo; - bool mergesortable = joininfo->mergesortable; - bool hashjoinable = joininfo->hashjoinable; - - foreach(xrelid, other_rels) { - Relid relid = (Relid)lfirst(xrelid); - Rel *rel = get_join_rel(root, relid); - List *super_rels = rel->superrels; - List *xsuper_rel = NIL; - JInfo *new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = joinrel->relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - rel->joininfo = - lappend(rel->joininfo, new_joininfo); - - foreach(xsuper_rel, super_rels) { - Rel *super_rel = (Rel *)lfirst(xsuper_rel); - - if( nonoverlap_rels(super_rel,joinrel) ) { - List *new_relids = super_rel->relids; - JInfo *other_joininfo = - joininfo_member(new_relids, - joinrel->joininfo); - - if (other_joininfo) { - other_joininfo->jinfoclauseinfo = - (List*)LispUnion(clause_info, - other_joininfo->jinfoclauseinfo); - } else { - JInfo *new_joininfo = makeNode(JInfo); - - new_joininfo->otherrels = new_relids; - new_joininfo->jinfoclauseinfo = clause_info; - new_joininfo->mergesortable = mergesortable; - new_joininfo->hashjoinable = hashjoinable; - new_joininfo->inactive = false; - joinrel->joininfo = - lappend(joinrel->joininfo, - new_joininfo); + foreach(xjoinrel, joinrels) + { + Rel *joinrel = (Rel *) lfirst(xjoinrel); + + foreach(xjoininfo, joinrel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + List *other_rels = joininfo->otherrels; + List *clause_info = joininfo->jinfoclauseinfo; + bool mergesortable = joininfo->mergesortable; + bool hashjoinable = joininfo->hashjoinable; + + foreach(xrelid, other_rels) + { + Relid relid = (Relid) lfirst(xrelid); + Rel *rel = get_join_rel(root, relid); + List *super_rels = rel->superrels; + List *xsuper_rel = NIL; + JInfo *new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = joinrel->relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + rel->joininfo = + lappend(rel->joininfo, new_joininfo); + + foreach(xsuper_rel, super_rels) + { + Rel *super_rel = (Rel *) lfirst(xsuper_rel); + + if (nonoverlap_rels(super_rel, joinrel)) + { + List *new_relids = super_rel->relids; + JInfo *other_joininfo = + joininfo_member(new_relids, + joinrel->joininfo); + + if (other_joininfo) + { + other_joininfo->jinfoclauseinfo = + (List *) LispUnion(clause_info, + other_joininfo->jinfoclauseinfo); + } + else + { + JInfo *new_joininfo = makeNode(JInfo); + + new_joininfo->otherrels = new_relids; + new_joininfo->jinfoclauseinfo = clause_info; + new_joininfo->mergesortable = mergesortable; + new_joininfo->hashjoinable = hashjoinable; + new_joininfo->inactive = false; + joinrel->joininfo = + lappend(joinrel->joininfo, + new_joininfo); + } + } + } } - } } - } } - } - foreach(xrel, outerrels) { - Rel *rel = (Rel *)lfirst(xrel); - rel->superrels = NIL; - } + foreach(xrel, outerrels) + { + Rel *rel = (Rel *) lfirst(xrel); + + rel->superrels = NIL; + } } /* * final-join-rels-- - * Find the join relation that includes all the original - * relations, i.e. the final join result. + * Find the join relation that includes all the original + * relations, i.e. the final join result. * * 'join-rel-list' is a list of join relations. * * Returns the list of final join relations. */ -List * -final_join_rels(List *join_rel_list) +List * +final_join_rels(List * join_rel_list) { - List *xrel = NIL; - List *temp = NIL; - List *t_list = NIL; - - /* - * find the relations that has no further joins, - * i.e., its joininfos all have otherrels nil. - */ - foreach(xrel,join_rel_list) { - Rel *rel = (Rel *)lfirst(xrel); - List *xjoininfo = NIL; - bool final = true; - - foreach (xjoininfo, rel->joininfo) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - - if (joininfo->otherrels != NIL) { - final = false; - break; - } - } - if (final) { - temp = lcons(rel, NIL); - t_list = nconc(t_list, temp); + List *xrel = NIL; + List *temp = NIL; + List *t_list = NIL; + + /* + * find the relations that has no further joins, i.e., its joininfos + * all have otherrels nil. + */ + foreach(xrel, join_rel_list) + { + Rel *rel = (Rel *) lfirst(xrel); + List *xjoininfo = NIL; + bool final = true; + + foreach(xjoininfo, rel->joininfo) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + if (joininfo->otherrels != NIL) + { + final = false; + break; + } + } + if (final) + { + temp = lcons(rel, NIL); + t_list = nconc(t_list, temp); + } } - } - return(t_list); + return (t_list); } /* * add_superrels-- - * add rel to the temporary property list superrels. + * add rel to the temporary property list superrels. * * 'rel' a rel node * 'super-rel' rel node of a join relation that includes rel @@ -492,60 +533,69 @@ final_join_rels(List *join_rel_list) * Modifies the superrels field of rel */ static void -add_superrels(Rel *rel, Rel *super_rel) +add_superrels(Rel * rel, Rel * super_rel) { - rel->superrels = lappend(rel->superrels, super_rel); + rel->superrels = lappend(rel->superrels, super_rel); } /* * nonoverlap-rels-- - * test if two join relations overlap, i.e., includes the same - * relation. + * test if two join relations overlap, i.e., includes the same + * relation. * * 'rel1' and 'rel2' are two join relations * * Returns non-nil if rel1 and rel2 do not overlap. */ -static bool -nonoverlap_rels(Rel *rel1, Rel *rel2) +static bool +nonoverlap_rels(Rel * rel1, Rel * rel2) { - return(nonoverlap_sets(rel1->relids, rel2->relids)); + return (nonoverlap_sets(rel1->relids, rel2->relids)); } -static bool -nonoverlap_sets(List *s1, List *s2) +static bool +nonoverlap_sets(List * s1, List * s2) { - List *x = NIL; - - foreach(x,s1) { - int e = lfirsti(x); - if(intMember(e,s2)) - return(false); - } - return(true); + List *x = NIL; + + foreach(x, s1) + { + int e = lfirsti(x); + + if (intMember(e, s2)) + return (false); + } + return (true); } static void -set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, JInfo *jinfo) +set_joinrel_size(Rel * joinrel, Rel * outer_rel, Rel * inner_rel, JInfo * jinfo) { - int ntuples; - float selec; - - /* voodoo magic. but better than a size of 0. I have no idea why - we didn't set the size before. -ay 2/95 */ - if (jinfo==NULL) { - /* worst case: the cartesian product */ - ntuples = outer_rel->tuples * inner_rel->tuples; - } else { - selec = product_selec(jinfo->jinfoclauseinfo); -/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */ - ntuples = outer_rel->tuples * inner_rel->tuples * selec; - } - - /* I bet sizes less than 1 will screw up optimization so - make the best case 1 instead of 0 - jolly*/ - if (ntuples < 1) - ntuples = 1; - - joinrel->tuples = ntuples; + int ntuples; + float selec; + + /* + * voodoo magic. but better than a size of 0. I have no idea why we + * didn't set the size before. -ay 2/95 + */ + if (jinfo == NULL) + { + /* worst case: the cartesian product */ + ntuples = outer_rel->tuples * inner_rel->tuples; + } + else + { + selec = product_selec(jinfo->jinfoclauseinfo); +/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */ + ntuples = outer_rel->tuples * inner_rel->tuples * selec; + } + + /* + * I bet sizes less than 1 will screw up optimization so make the best + * case 1 instead of 0 - jolly + */ + if (ntuples < 1) + ntuples = 1; + + joinrel->tuples = ntuples; } diff --git a/src/backend/optimizer/path/joinutils.c b/src/backend/optimizer/path/joinutils.c index 1be5a57f2ec..c88d3cf19e8 100644 --- a/src/backend/optimizer/path/joinutils.c +++ b/src/backend/optimizer/path/joinutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * joinutils.c-- - * Utilities for matching and building join and path keys + * Utilities for matching and building join and path keys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.2 1997/09/07 04:43:42 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,407 +26,440 @@ #include "optimizer/ordering.h" -static int match_pathkey_joinkeys(List *pathkey, List *joinkeys, - int which_subkey); -static bool every_func(List *joinkeys, List *pathkey, - int which_subkey); -static List *new_join_pathkey(List *subkeys, - List *considered_subkeys, List *join_rel_tlist, - List *joinclauses); -static List *new_matching_subkeys(Var *subkey, List *considered_subkeys, - List *join_rel_tlist, List *joinclauses); +static int +match_pathkey_joinkeys(List * pathkey, List * joinkeys, + int which_subkey); +static bool +every_func(List * joinkeys, List * pathkey, + int which_subkey); +static List * +new_join_pathkey(List * subkeys, + List * considered_subkeys, List * join_rel_tlist, + List * joinclauses); +static List * +new_matching_subkeys(Var * subkey, List * considered_subkeys, + List * join_rel_tlist, List * joinclauses); /**************************************************************************** - * KEY COMPARISONS + * KEY COMPARISONS ****************************************************************************/ -/* +/* * match-pathkeys-joinkeys-- - * Attempts to match the keys of a path against the keys of join clauses. - * This is done by looking for a matching join key in 'joinkeys' for - * every path key in the list 'pathkeys'. If there is a matching join key - * (not necessarily unique) for every path key, then the list of - * corresponding join keys and join clauses are returned in the order in - * which the keys matched the path keys. - * + * Attempts to match the keys of a path against the keys of join clauses. + * This is done by looking for a matching join key in 'joinkeys' for + * every path key in the list 'pathkeys'. If there is a matching join key + * (not necessarily unique) for every path key, then the list of + * corresponding join keys and join clauses are returned in the order in + * which the keys matched the path keys. + * * 'pathkeys' is a list of path keys: - * ( ( (var) (var) ... ) ( (var) ... ) ) + * ( ( (var) (var) ... ) ( (var) ... ) ) * 'joinkeys' is a list of join keys: - * ( (outer inner) (outer inner) ... ) + * ( (outer inner) (outer inner) ... ) * 'joinclauses' is a list of clauses corresponding to the join keys in - * 'joinkeys' + * 'joinkeys' * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns the join keys and corresponding join clauses in a list if all * of the path keys were matched: - * ( - * ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) ) - * ( clause0 ... clauseN ) - * ) + * ( + * ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) ) + * ( clause0 ... clauseN ) + * ) * and nil otherwise. - * + * * Returns a list of matched join keys and a list of matched join clauses * in matchedJoinClausesPtr. - ay 11/94 */ -List * -match_pathkeys_joinkeys(List *pathkeys, - List *joinkeys, - List *joinclauses, - int which_subkey, - List **matchedJoinClausesPtr) +List * +match_pathkeys_joinkeys(List * pathkeys, + List * joinkeys, + List * joinclauses, + int which_subkey, + List ** matchedJoinClausesPtr) { - List *matched_joinkeys = NIL; - List *matched_joinclauses = NIL; - List *pathkey = NIL; - List *i = NIL; - int matched_joinkey_index = -1; - - foreach(i, pathkeys) { - pathkey = lfirst(i); - matched_joinkey_index = - match_pathkey_joinkeys(pathkey, joinkeys, which_subkey); - - if (matched_joinkey_index != -1 ) { - List *xjoinkey = nth(matched_joinkey_index,joinkeys); - List *joinclause = nth(matched_joinkey_index,joinclauses); - - /* XXX was "push" function */ - matched_joinkeys = lappend(matched_joinkeys,xjoinkey); - matched_joinkeys = nreverse(matched_joinkeys); - - matched_joinclauses = lappend(matched_joinclauses,joinclause); - matched_joinclauses = nreverse(matched_joinclauses); - joinkeys = LispRemove(xjoinkey,joinkeys); - } else { - return(NIL); - } - - } - if(matched_joinkeys==NULL || - length(matched_joinkeys) != length(pathkeys)) { - return NIL; - } - - *matchedJoinClausesPtr = nreverse(matched_joinclauses); - return (nreverse(matched_joinkeys)); + List *matched_joinkeys = NIL; + List *matched_joinclauses = NIL; + List *pathkey = NIL; + List *i = NIL; + int matched_joinkey_index = -1; + + foreach(i, pathkeys) + { + pathkey = lfirst(i); + matched_joinkey_index = + match_pathkey_joinkeys(pathkey, joinkeys, which_subkey); + + if (matched_joinkey_index != -1) + { + List *xjoinkey = nth(matched_joinkey_index, joinkeys); + List *joinclause = nth(matched_joinkey_index, joinclauses); + + /* XXX was "push" function */ + matched_joinkeys = lappend(matched_joinkeys, xjoinkey); + matched_joinkeys = nreverse(matched_joinkeys); + + matched_joinclauses = lappend(matched_joinclauses, joinclause); + matched_joinclauses = nreverse(matched_joinclauses); + joinkeys = LispRemove(xjoinkey, joinkeys); + } + else + { + return (NIL); + } + + } + if (matched_joinkeys == NULL || + length(matched_joinkeys) != length(pathkeys)) + { + return NIL; + } + + *matchedJoinClausesPtr = nreverse(matched_joinclauses); + return (nreverse(matched_joinkeys)); } -/* +/* * match-pathkey-joinkeys-- - * Returns the 0-based index into 'joinkeys' of the first joinkey whose - * outer or inner subkey matches any subkey of 'pathkey'. + * Returns the 0-based index into 'joinkeys' of the first joinkey whose + * outer or inner subkey matches any subkey of 'pathkey'. */ static int -match_pathkey_joinkeys(List *pathkey, - List *joinkeys, - int which_subkey) +match_pathkey_joinkeys(List * pathkey, + List * joinkeys, + int which_subkey) { - Var *path_subkey; - int pos; - List *i = NIL; - List *x = NIL; - JoinKey *jk; - - foreach(i, pathkey) { - path_subkey = (Var *)lfirst(i); - pos = 0; - foreach(x, joinkeys) { - jk = (JoinKey*)lfirst(x); - if(var_equal(path_subkey, - extract_subkey(jk, which_subkey))) - return(pos); - pos++; + Var *path_subkey; + int pos; + List *i = NIL; + List *x = NIL; + JoinKey *jk; + + foreach(i, pathkey) + { + path_subkey = (Var *) lfirst(i); + pos = 0; + foreach(x, joinkeys) + { + jk = (JoinKey *) lfirst(x); + if (var_equal(path_subkey, + extract_subkey(jk, which_subkey))) + return (pos); + pos++; + } } - } - return(-1); /* no index found */ + return (-1); /* no index found */ } -/* +/* * match-paths-joinkeys-- - * Attempts to find a path in 'paths' whose keys match a set of join - * keys 'joinkeys'. To match, - * 1. the path node ordering must equal 'ordering'. - * 2. each subkey of a given path must match(i.e., be(var_equal) to) the - * appropriate subkey of the corresponding join key in 'joinkeys', - * i.e., the Nth path key must match its subkeys against the subkey of - * the Nth join key in 'joinkeys'. - * - * 'joinkeys' is the list of key pairs to which the path keys must be - * matched + * Attempts to find a path in 'paths' whose keys match a set of join + * keys 'joinkeys'. To match, + * 1. the path node ordering must equal 'ordering'. + * 2. each subkey of a given path must match(i.e., be(var_equal) to) the + * appropriate subkey of the corresponding join key in 'joinkeys', + * i.e., the Nth path key must match its subkeys against the subkey of + * the Nth join key in 'joinkeys'. + * + * 'joinkeys' is the list of key pairs to which the path keys must be + * matched * 'ordering' is the ordering of the(outer) path to which 'joinkeys' - * must correspond + * must correspond * 'paths' is a list of(inner) paths which are to be matched against - * each join key in 'joinkeys' + * each join key in 'joinkeys' * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns the matching path node if one exists, nil otherwise. */ -static bool -every_func(List *joinkeys, List *pathkey, int which_subkey) +static bool +every_func(List * joinkeys, List * pathkey, int which_subkey) { - JoinKey *xjoinkey; - Var *temp; - Var *tempkey = NULL; - bool found = false; - List *i = NIL; - List *j = NIL; - - foreach(i,joinkeys) { - xjoinkey = (JoinKey*)lfirst(i); - found = false; - foreach(j,pathkey) { - temp = (Var*)lfirst((List*)lfirst(j)); - if(temp == NULL) continue; - tempkey = extract_subkey(xjoinkey,which_subkey); - if(var_equal(tempkey, temp)) { - found = true; - break; - } + JoinKey *xjoinkey; + Var *temp; + Var *tempkey = NULL; + bool found = false; + List *i = NIL; + List *j = NIL; + + foreach(i, joinkeys) + { + xjoinkey = (JoinKey *) lfirst(i); + found = false; + foreach(j, pathkey) + { + temp = (Var *) lfirst((List *) lfirst(j)); + if (temp == NULL) + continue; + tempkey = extract_subkey(xjoinkey, which_subkey); + if (var_equal(tempkey, temp)) + { + found = true; + break; + } + } + if (found == false) + return (false); } - if(found == false) - return(false); - } - return(found); + return (found); } /* * match_paths_joinkeys - - * find the cheapest path that matches the join keys + * find the cheapest path that matches the join keys */ -Path * -match_paths_joinkeys(List *joinkeys, - PathOrder *ordering, - List *paths, - int which_subkey) +Path * +match_paths_joinkeys(List * joinkeys, + PathOrder * ordering, + List * paths, + int which_subkey) { - Path *matched_path = NULL ; - bool key_match = false; - List *i = NIL; - - foreach(i,paths) { - Path *path = (Path*)lfirst(i); - - key_match = every_func(joinkeys, path->keys, which_subkey); - - if (equal_path_path_ordering(ordering, - &path->p_ordering) && - length(joinkeys) == length(path->keys) && - key_match) { - - if (matched_path) { - if (path->path_cost < matched_path->path_cost) - matched_path = path; - } else { - matched_path = path; - } + Path *matched_path = NULL; + bool key_match = false; + List *i = NIL; + + foreach(i, paths) + { + Path *path = (Path *) lfirst(i); + + key_match = every_func(joinkeys, path->keys, which_subkey); + + if (equal_path_path_ordering(ordering, + &path->p_ordering) && + length(joinkeys) == length(path->keys) && + key_match) + { + + if (matched_path) + { + if (path->path_cost < matched_path->path_cost) + matched_path = path; + } + else + { + matched_path = path; + } + } } - } - return matched_path; + return matched_path; } -/* +/* * extract-path-keys-- - * Builds a subkey list for a path by pulling one of the subkeys from - * a list of join keys 'joinkeys' and then finding the var node in the - * target list 'tlist' that corresponds to that subkey. - * + * Builds a subkey list for a path by pulling one of the subkeys from + * a list of join keys 'joinkeys' and then finding the var node in the + * target list 'tlist' that corresponds to that subkey. + * * 'joinkeys' is a list of join key pairs * 'tlist' is a relation target list * 'which-subkey' is a flag that selects the desired subkey of a join key - * in 'joinkeys' - * + * in 'joinkeys' + * * Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)). * [I've no idea why they have to be list of lists. Should be fixed. -ay 12/94] */ -List * -extract_path_keys(List *joinkeys, - List *tlist, - int which_subkey) +List * +extract_path_keys(List * joinkeys, + List * tlist, + int which_subkey) { - List *pathkeys = NIL; - List *jk; - - foreach(jk, joinkeys) { - JoinKey *jkey = (JoinKey*)lfirst(jk); - Var *var, *key; - List *p; - - /* - * find the right Var in the target list for this key - */ - var = (Var*)extract_subkey(jkey, which_subkey); - key = (Var*)matching_tlvar(var, tlist); - - /* - * include it in the pathkeys list if we haven't already done so - */ - foreach(p, pathkeys) { - Var *pkey = lfirst((List*)lfirst(p)); /* XXX fix me */ - if (key == pkey) - break; - } - if (p!=NIL) - continue; /* key already in pathkeys */ + List *pathkeys = NIL; + List *jk; + + foreach(jk, joinkeys) + { + JoinKey *jkey = (JoinKey *) lfirst(jk); + Var *var, + *key; + List *p; - pathkeys = - lappend(pathkeys, lcons(key,NIL)); - } - return(pathkeys); + /* + * find the right Var in the target list for this key + */ + var = (Var *) extract_subkey(jkey, which_subkey); + key = (Var *) matching_tlvar(var, tlist); + + /* + * include it in the pathkeys list if we haven't already done so + */ + foreach(p, pathkeys) + { + Var *pkey = lfirst((List *) lfirst(p)); /* XXX fix me */ + + if (key == pkey) + break; + } + if (p != NIL) + continue; /* key already in pathkeys */ + + pathkeys = + lappend(pathkeys, lcons(key, NIL)); + } + return (pathkeys); } /**************************************************************************** - * NEW PATHKEY FORMATION + * NEW PATHKEY FORMATION ****************************************************************************/ -/* +/* * new-join-pathkeys-- - * Find the path keys for a join relation by finding all vars in the list - * of join clauses 'joinclauses' such that: - * (1) the var corresponding to the outer join relation is a - * key on the outer path - * (2) the var appears in the target list of the join relation - * In other words, add to each outer path key the inner path keys that - * are required for qualification. - * + * Find the path keys for a join relation by finding all vars in the list + * of join clauses 'joinclauses' such that: + * (1) the var corresponding to the outer join relation is a + * key on the outer path + * (2) the var appears in the target list of the join relation + * In other words, add to each outer path key the inner path keys that + * are required for qualification. + * * 'outer-pathkeys' is the list of the outer path's path keys * 'join-rel-tlist' is the target list of the join relation * 'joinclauses' is the list of restricting join clauses - * - * Returns the list of new path keys. - * + * + * Returns the list of new path keys. + * */ -List * -new_join_pathkeys(List *outer_pathkeys, - List *join_rel_tlist, - List *joinclauses) -{ - List *outer_pathkey = NIL; - List *t_list = NIL; - List *x; - List *i = NIL; - - foreach(i, outer_pathkeys) { - outer_pathkey = lfirst(i); - x = new_join_pathkey(outer_pathkey, NIL, - join_rel_tlist,joinclauses); - if (x!=NIL) { - t_list = lappend(t_list, x); +List * +new_join_pathkeys(List * outer_pathkeys, + List * join_rel_tlist, + List * joinclauses) +{ + List *outer_pathkey = NIL; + List *t_list = NIL; + List *x; + List *i = NIL; + + foreach(i, outer_pathkeys) + { + outer_pathkey = lfirst(i); + x = new_join_pathkey(outer_pathkey, NIL, + join_rel_tlist, joinclauses); + if (x != NIL) + { + t_list = lappend(t_list, x); + } } - } - return(t_list); + return (t_list); } -/* +/* * new-join-pathkey-- - * Finds new vars that become subkeys due to qualification clauses that - * contain any previously considered subkeys. These new subkeys plus the - * subkeys from 'subkeys' form a new pathkey for the join relation. - * - * Note that each returned subkey is the var node found in - * 'join-rel-tlist' rather than the joinclause var node. - * + * Finds new vars that become subkeys due to qualification clauses that + * contain any previously considered subkeys. These new subkeys plus the + * subkeys from 'subkeys' form a new pathkey for the join relation. + * + * Note that each returned subkey is the var node found in + * 'join-rel-tlist' rather than the joinclause var node. + * * 'subkeys' is a list of subkeys for which matching subkeys are to be - * found + * found * 'considered-subkeys' is the current list of all subkeys corresponding - * to a given pathkey - * + * to a given pathkey + * * Returns a new pathkey(list of subkeys). - * + * */ -static List * -new_join_pathkey(List *subkeys, - List *considered_subkeys, - List *join_rel_tlist, - List *joinclauses) +static List * +new_join_pathkey(List * subkeys, + List * considered_subkeys, + List * join_rel_tlist, + List * joinclauses) { - List *t_list = NIL; - Var *subkey; - List *i = NIL; - List *matched_subkeys = NIL; - Expr *tlist_key = (Expr*)NULL; - List *newly_considered_subkeys = NIL; - - foreach (i, subkeys) { - subkey = (Var *)lfirst(i); - if(subkey == NULL) - break; /* XXX something is wrong */ - matched_subkeys = - new_matching_subkeys(subkey,considered_subkeys, - join_rel_tlist,joinclauses); - tlist_key = matching_tlvar(subkey,join_rel_tlist); - newly_considered_subkeys = NIL; - - if (tlist_key) { - if(!member(tlist_key, matched_subkeys)) - newly_considered_subkeys = lcons(tlist_key, - matched_subkeys); - } - else { - newly_considered_subkeys = matched_subkeys; - } - - considered_subkeys = - append(considered_subkeys, newly_considered_subkeys); - - t_list = nconc(t_list,newly_considered_subkeys); - } - return(t_list); + List *t_list = NIL; + Var *subkey; + List *i = NIL; + List *matched_subkeys = NIL; + Expr *tlist_key = (Expr *) NULL; + List *newly_considered_subkeys = NIL; + + foreach(i, subkeys) + { + subkey = (Var *) lfirst(i); + if (subkey == NULL) + break; /* XXX something is wrong */ + matched_subkeys = + new_matching_subkeys(subkey, considered_subkeys, + join_rel_tlist, joinclauses); + tlist_key = matching_tlvar(subkey, join_rel_tlist); + newly_considered_subkeys = NIL; + + if (tlist_key) + { + if (!member(tlist_key, matched_subkeys)) + newly_considered_subkeys = lcons(tlist_key, + matched_subkeys); + } + else + { + newly_considered_subkeys = matched_subkeys; + } + + considered_subkeys = + append(considered_subkeys, newly_considered_subkeys); + + t_list = nconc(t_list, newly_considered_subkeys); + } + return (t_list); } -/* +/* * new-matching-subkeys-- - * Returns a list of new subkeys: - * (1) which are not listed in 'considered-subkeys' - * (2) for which the "other" variable in some clause in 'joinclauses' is - * 'subkey' - * (3) which are mentioned in 'join-rel-tlist' - * - * Note that each returned subkey is the var node found in - * 'join-rel-tlist' rather than the joinclause var node. - * + * Returns a list of new subkeys: + * (1) which are not listed in 'considered-subkeys' + * (2) for which the "other" variable in some clause in 'joinclauses' is + * 'subkey' + * (3) which are mentioned in 'join-rel-tlist' + * + * Note that each returned subkey is the var node found in + * 'join-rel-tlist' rather than the joinclause var node. + * * 'subkey' is the var node for which we are trying to find matching - * clauses - * + * clauses + * * Returns a list of new subkeys. * */ -static List * -new_matching_subkeys(Var *subkey, - List *considered_subkeys, - List *join_rel_tlist, - List *joinclauses) +static List * +new_matching_subkeys(Var * subkey, + List * considered_subkeys, + List * join_rel_tlist, + List * joinclauses) { - Expr *joinclause = NULL; - List *t_list = NIL; - List *temp = NIL; - List *i = NIL; - Expr *tlist_other_var = (Expr *)NULL; - - foreach(i,joinclauses) { - joinclause = lfirst(i); - tlist_other_var = - matching_tlvar(other_join_clause_var(subkey,joinclause), - join_rel_tlist); - - if(tlist_other_var && - !(member(tlist_other_var,considered_subkeys))) { - - /* XXX was "push" function */ - considered_subkeys = lappend(considered_subkeys, - tlist_other_var); - - /* considered_subkeys = nreverse(considered_subkeys); - XXX -- I am not sure of this. */ - - temp = lcons(tlist_other_var, NIL); - t_list = nconc(t_list,temp); - } - } - return(t_list); + Expr *joinclause = NULL; + List *t_list = NIL; + List *temp = NIL; + List *i = NIL; + Expr *tlist_other_var = (Expr *) NULL; + + foreach(i, joinclauses) + { + joinclause = lfirst(i); + tlist_other_var = + matching_tlvar(other_join_clause_var(subkey, joinclause), + join_rel_tlist); + + if (tlist_other_var && + !(member(tlist_other_var, considered_subkeys))) + { + + /* XXX was "push" function */ + considered_subkeys = lappend(considered_subkeys, + tlist_other_var); + + /* + * considered_subkeys = nreverse(considered_subkeys); XXX -- I + * am not sure of this. + */ + + temp = lcons(tlist_other_var, NIL); + t_list = nconc(t_list, temp); + } + } + return (t_list); } diff --git a/src/backend/optimizer/path/mergeutils.c b/src/backend/optimizer/path/mergeutils.c index d5f0fdcb65b..93004a6741e 100644 --- a/src/backend/optimizer/path/mergeutils.c +++ b/src/backend/optimizer/path/mergeutils.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * mergeutils.c-- - * Utilities for finding applicable merge clauses and pathkeys + * Utilities for finding applicable merge clauses and pathkeys * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.2 1997/09/07 04:43:45 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,102 +21,110 @@ #include "optimizer/clauses.h" #include "optimizer/ordering.h" -/* +/* * group-clauses-by-order-- - * If a join clause node in 'clauseinfo-list' is mergesortable, store - * it within a mergeinfo node containing other clause nodes with the same - * mergesort ordering. - * + * If a join clause node in 'clauseinfo-list' is mergesortable, store + * it within a mergeinfo node containing other clause nodes with the same + * mergesort ordering. + * * 'clauseinfo-list' is the list of clauseinfo nodes * 'inner-relid' is the relid of the inner join relation - * + * * Returns the new list of mergeinfo nodes. - * + * */ -List * -group_clauses_by_order(List *clauseinfo_list, - int inner_relid) +List * +group_clauses_by_order(List * clauseinfo_list, + int inner_relid) { - List *mergeinfo_list = NIL; - List *xclauseinfo = NIL; - - foreach (xclauseinfo, clauseinfo_list) { - CInfo *clauseinfo = (CInfo *)lfirst(xclauseinfo); - MergeOrder *merge_ordering = clauseinfo->mergesortorder; - - if (merge_ordering) { - /* - * Create a new mergeinfo node and add it to - * 'mergeinfo-list' if one does not yet exist for this - * merge ordering. - */ - PathOrder p_ordering; - MInfo *xmergeinfo; - Expr *clause = clauseinfo->clause; - Var *leftop = get_leftop (clause); - Var *rightop = get_rightop (clause); - JoinKey *keys; - - p_ordering.ordtype = MERGE_ORDER; - p_ordering.ord.merge = merge_ordering; - xmergeinfo = - match_order_mergeinfo(&p_ordering, mergeinfo_list); - if (inner_relid == leftop->varno) { - keys = makeNode(JoinKey); - keys->outer = rightop; - keys->inner = leftop; - } else { - keys = makeNode(JoinKey); - keys->outer = leftop; - keys->inner = rightop; - } - - if (xmergeinfo==NULL) { - xmergeinfo = makeNode(MInfo); - - xmergeinfo->m_ordering = merge_ordering; - mergeinfo_list = lcons(xmergeinfo, - mergeinfo_list); - } - - ((JoinMethod *)xmergeinfo)->clauses = - lcons(clause, - ((JoinMethod *)xmergeinfo)->clauses); - ((JoinMethod *)xmergeinfo)->jmkeys = - lcons(keys, - ((JoinMethod *)xmergeinfo)->jmkeys); + List *mergeinfo_list = NIL; + List *xclauseinfo = NIL; + + foreach(xclauseinfo, clauseinfo_list) + { + CInfo *clauseinfo = (CInfo *) lfirst(xclauseinfo); + MergeOrder *merge_ordering = clauseinfo->mergesortorder; + + if (merge_ordering) + { + + /* + * Create a new mergeinfo node and add it to 'mergeinfo-list' + * if one does not yet exist for this merge ordering. + */ + PathOrder p_ordering; + MInfo *xmergeinfo; + Expr *clause = clauseinfo->clause; + Var *leftop = get_leftop(clause); + Var *rightop = get_rightop(clause); + JoinKey *keys; + + p_ordering.ordtype = MERGE_ORDER; + p_ordering.ord.merge = merge_ordering; + xmergeinfo = + match_order_mergeinfo(&p_ordering, mergeinfo_list); + if (inner_relid == leftop->varno) + { + keys = makeNode(JoinKey); + keys->outer = rightop; + keys->inner = leftop; + } + else + { + keys = makeNode(JoinKey); + keys->outer = leftop; + keys->inner = rightop; + } + + if (xmergeinfo == NULL) + { + xmergeinfo = makeNode(MInfo); + + xmergeinfo->m_ordering = merge_ordering; + mergeinfo_list = lcons(xmergeinfo, + mergeinfo_list); + } + + ((JoinMethod *) xmergeinfo)->clauses = + lcons(clause, + ((JoinMethod *) xmergeinfo)->clauses); + ((JoinMethod *) xmergeinfo)->jmkeys = + lcons(keys, + ((JoinMethod *) xmergeinfo)->jmkeys); + } } - } - return(mergeinfo_list); + return (mergeinfo_list); } -/* +/* * match-order-mergeinfo-- - * Searches the list 'mergeinfo-list' for a mergeinfo node whose order - * field equals 'ordering'. - * + * Searches the list 'mergeinfo-list' for a mergeinfo node whose order + * field equals 'ordering'. + * * Returns the node if it exists. - * + * */ -MInfo * -match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list) +MInfo * +match_order_mergeinfo(PathOrder * ordering, List * mergeinfo_list) { - MergeOrder *xmergeorder; - List *xmergeinfo = NIL; + MergeOrder *xmergeorder; + List *xmergeinfo = NIL; - foreach(xmergeinfo, mergeinfo_list) { - MInfo *mergeinfo = (MInfo*)lfirst(xmergeinfo); + foreach(xmergeinfo, mergeinfo_list) + { + MInfo *mergeinfo = (MInfo *) lfirst(xmergeinfo); - xmergeorder = mergeinfo->m_ordering; + xmergeorder = mergeinfo->m_ordering; - if ((ordering->ordtype==MERGE_ORDER && - equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) || - (ordering->ordtype==SORTOP_ORDER && - equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) { + if ((ordering->ordtype == MERGE_ORDER && + equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) || + (ordering->ordtype == SORTOP_ORDER && + equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) + { - return (mergeinfo); + return (mergeinfo); + } } - } - return((MInfo*) NIL); + return ((MInfo *) NIL); } diff --git a/src/backend/optimizer/path/orindxpath.c b/src/backend/optimizer/path/orindxpath.c index e040675e6ec..96408b78905 100644 --- a/src/backend/optimizer/path/orindxpath.c +++ b/src/backend/optimizer/path/orindxpath.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * orindxpath.c-- - * Routines to find index paths that match a set of 'or' clauses + * Routines to find index paths that match a set of 'or' clauses * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.2 1997/09/07 04:43:46 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,241 +31,267 @@ #include "parser/parsetree.h" -static void best_or_subclause_indices(Query *root, Rel *rel, List *subclauses, - List *indices, List *examined_indexids, Cost subcost, List *selectivities, - List **indexids, Cost *cost, List **selecs); -static void best_or_subclause_index(Query *root, Rel *rel, Expr *subclause, - List *indices, int *indexid, Cost *cost, Cost *selec); +static void +best_or_subclause_indices(Query * root, Rel * rel, List * subclauses, + List * indices, List * examined_indexids, Cost subcost, List * selectivities, + List ** indexids, Cost * cost, List ** selecs); +static void +best_or_subclause_index(Query * root, Rel * rel, Expr * subclause, + List * indices, int *indexid, Cost * cost, Cost * selec); -/* +/* * create-or-index-paths-- - * Creates index paths for indices that match 'or' clauses. - * + * Creates index paths for indices that match 'or' clauses. + * * 'rel' is the relation entry for which the paths are to be defined on * 'clauses' is the list of available restriction clause nodes - * + * * Returns a list of these index path nodes. - * + * */ -List * -create_or_index_paths(Query *root, - Rel *rel, List *clauses) +List * +create_or_index_paths(Query * root, + Rel * rel, List * clauses) { - List *t_list = NIL; - - if (clauses != NIL) { - CInfo *clausenode = (CInfo *) (lfirst (clauses)); - - /* Check to see if this clause is an 'or' clause, and, if so, - * whether or not each of the subclauses within the 'or' clause has - * been matched by an index (the 'Index field was set in - * (match_or) if no index matches a given subclause, one of the - * lists of index nodes returned by (get_index) will be 'nil'). - */ - if (valid_or_clause(clausenode) && - clausenode->indexids) { - List *temp = NIL; - List *index_list = NIL; - bool index_flag = true; - - index_list = clausenode->indexids; - foreach(temp,index_list) { - if (!temp) - index_flag = false; - } - if (index_flag) { /* used to be a lisp every function */ - IndexPath *pathnode = makeNode(IndexPath); - List *indexids; - Cost cost; - List *selecs; + List *t_list = NIL; + + if (clauses != NIL) + { + CInfo *clausenode = (CInfo *) (lfirst(clauses)); + + /* + * Check to see if this clause is an 'or' clause, and, if so, + * whether or not each of the subclauses within the 'or' clause + * has been matched by an index (the 'Index field was set in + * (match_or) if no index matches a given subclause, one of the + * lists of index nodes returned by (get_index) will be 'nil'). + */ + if (valid_or_clause(clausenode) && + clausenode->indexids) + { + List *temp = NIL; + List *index_list = NIL; + bool index_flag = true; + + index_list = clausenode->indexids; + foreach(temp, index_list) + { + if (!temp) + index_flag = false; + } + if (index_flag) + { /* used to be a lisp every function */ + IndexPath *pathnode = makeNode(IndexPath); + List *indexids; + Cost cost; + List *selecs; - best_or_subclause_indices(root, - rel, - clausenode->clause->args, - clausenode->indexids, - NIL, - (Cost)0, - NIL, - &indexids, - &cost, - &selecs); - - pathnode->path.pathtype = T_IndexScan; - pathnode->path.parent = rel; - pathnode->indexqual = - lcons(clausenode,NIL); - pathnode->indexid = indexids; - pathnode->path.path_cost = cost; - - /* copy clauseinfo list into path for expensive - function processing -- JMH, 7/7/92 */ - pathnode->path.locclauseinfo = - set_difference(clauses, - copyObject((Node*) - rel->clauseinfo)); - -#if 0 /* fix xfunc */ - /* add in cost for expensive functions! -- JMH, 7/7/92 */ - if (XfuncMode != XFUNC_OFF) { - ((Path*)pathnode)->path_cost += - xfunc_get_path_cost((Path)pathnode); + best_or_subclause_indices(root, + rel, + clausenode->clause->args, + clausenode->indexids, + NIL, + (Cost) 0, + NIL, + &indexids, + &cost, + &selecs); + + pathnode->path.pathtype = T_IndexScan; + pathnode->path.parent = rel; + pathnode->indexqual = + lcons(clausenode, NIL); + pathnode->indexid = indexids; + pathnode->path.path_cost = cost; + + /* + * copy clauseinfo list into path for expensive function + * processing -- JMH, 7/7/92 + */ + pathnode->path.locclauseinfo = + set_difference(clauses, + copyObject((Node *) + rel->clauseinfo)); + +#if 0 /* fix xfunc */ + /* add in cost for expensive functions! -- JMH, 7/7/92 */ + if (XfuncMode != XFUNC_OFF) + { + ((Path *) pathnode)->path_cost += + xfunc_get_path_cost((Path) pathnode); + } +#endif + clausenode->selectivity = (Cost) floatVal(selecs); + t_list = + lcons(pathnode, + create_or_index_paths(root, rel, lnext(clauses))); + } + else + { + t_list = create_or_index_paths(root, rel, lnext(clauses)); + } } -#endif - clausenode->selectivity = (Cost)floatVal(selecs); - t_list = - lcons(pathnode, - create_or_index_paths(root, rel,lnext(clauses))); - } else { - t_list = create_or_index_paths(root, rel,lnext(clauses)); - } } - } - return(t_list); + return (t_list); } -/* +/* * best-or-subclause-indices-- - * Determines the best index to be used in conjunction with each subclause - * of an 'or' clause and the cost of scanning a relation using these - * indices. The cost is the sum of the individual index costs. - * + * Determines the best index to be used in conjunction with each subclause + * of an 'or' clause and the cost of scanning a relation using these + * indices. The cost is the sum of the individual index costs. + * * 'rel' is the node of the relation on which the index is defined * 'subclauses' are the subclauses of the 'or' clause * 'indices' are those index nodes that matched subclauses of the 'or' - * clause - * 'examined-indexids' is a list of those index ids to be used with - * subclauses that have already been examined + * clause + * 'examined-indexids' is a list of those index ids to be used with + * subclauses that have already been examined * 'subcost' is the cost of using the indices in 'examined-indexids' * 'selectivities' is a list of the selectivities of subclauses that - * have already been examined - * + * have already been examined + * * Returns a list of the indexids, cost, and selectivities of each * subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID, * 'cost' is a flonum, and 's' is a flonum. */ static void -best_or_subclause_indices(Query *root, - Rel *rel, - List *subclauses, - List *indices, - List *examined_indexids, - Cost subcost, - List *selectivities, - List **indexids, /* return value */ - Cost *cost, /* return value */ - List **selecs) /* return value */ +best_or_subclause_indices(Query * root, + Rel * rel, + List * subclauses, + List * indices, + List * examined_indexids, + Cost subcost, + List * selectivities, + List ** indexids, /* return value */ + Cost * cost, /* return value */ + List ** selecs) /* return value */ { - if (subclauses==NIL) { - *indexids = nreverse(examined_indexids); - *cost = subcost; - *selecs = nreverse(selectivities); - } else { - int best_indexid; - Cost best_cost; - Cost best_selec; - - best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices), - &best_indexid, &best_cost, &best_selec); - - best_or_subclause_indices(root, - rel, - lnext(subclauses), - lnext(indices), - lconsi(best_indexid, examined_indexids), - subcost + best_cost, - lcons(makeFloat(best_selec), selectivities), - indexids, - cost, - selecs); - } - return; -} + if (subclauses == NIL) + { + *indexids = nreverse(examined_indexids); + *cost = subcost; + *selecs = nreverse(selectivities); + } + else + { + int best_indexid; + Cost best_cost; + Cost best_selec; + + best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices), + &best_indexid, &best_cost, &best_selec); -/* + best_or_subclause_indices(root, + rel, + lnext(subclauses), + lnext(indices), + lconsi(best_indexid, examined_indexids), + subcost + best_cost, + lcons(makeFloat(best_selec), selectivities), + indexids, + cost, + selecs); + } + return; +} + +/* * best-or-subclause-index-- - * Determines which is the best index to be used with a subclause of - * an 'or' clause by estimating the cost of using each index and selecting - * the least expensive. - * + * Determines which is the best index to be used with a subclause of + * an 'or' clause by estimating the cost of using each index and selecting + * the least expensive. + * * 'rel' is the node of the relation on which the index is defined * 'subclause' is the subclause * 'indices' is a list of index nodes that match the subclause - * + * * Returns a list (index-id index-subcost index-selectivity) * (a fixnum, a fixnum, and a flonum respectively). - * + * */ static void -best_or_subclause_index(Query *root, - Rel *rel, - Expr *subclause, - List *indices, - int *retIndexid, /* return value */ - Cost *retCost, /* return value */ - Cost *retSelec) /* return value */ +best_or_subclause_index(Query * root, + Rel * rel, + Expr * subclause, + List * indices, + int *retIndexid, /* return value */ + Cost * retCost, /* return value */ + Cost * retSelec) /* return value */ { - if (indices != NIL) { - Datum value; - int flag = 0; - Cost subcost; - Rel *index = (Rel *)lfirst (indices); - AttrNumber attno = (get_leftop (subclause))->varattno ; - Oid opno = ((Oper*)subclause->oper)->opno; - bool constant_on_right = non_null((Expr*)get_rightop(subclause)); - float npages, selec; - int subclause_indexid; - Cost subclause_cost; - Cost subclause_selec; - - if(constant_on_right) { - value = ((Const*)get_rightop (subclause))->constvalue; - } else { - value = NameGetDatum(""); - } - if(constant_on_right) { - flag = (_SELEC_IS_CONSTANT_ ||_SELEC_CONSTANT_RIGHT_); - } else { - flag = _SELEC_CONSTANT_RIGHT_; - } - index_selectivity(lfirsti(index->relids), - index->classlist, - lconsi(opno,NIL), - getrelid(lfirsti(rel->relids), - root->rtable), - lconsi(attno,NIL), - lconsi(value,NIL), - lconsi(flag,NIL), - 1, - &npages, - &selec); - - subcost = cost_index((Oid) lfirsti(index->relids), - (int)npages, - (Cost)selec, - rel->pages, - rel->tuples, - index->pages, - index->tuples, - false); - best_or_subclause_index(root, - rel, - subclause, - lnext(indices), - &subclause_indexid, - &subclause_cost, - &subclause_selec); + if (indices != NIL) + { + Datum value; + int flag = 0; + Cost subcost; + Rel *index = (Rel *) lfirst(indices); + AttrNumber attno = (get_leftop(subclause))->varattno; + Oid opno = ((Oper *) subclause->oper)->opno; + bool constant_on_right = non_null((Expr *) get_rightop(subclause)); + float npages, + selec; + int subclause_indexid; + Cost subclause_cost; + Cost subclause_selec; - if (subclause_indexid==0 || subcost < subclause_cost) { - *retIndexid = lfirsti(index->relids); - *retCost = subcost; - *retSelec = selec; - } else { - *retIndexid = 0; - *retCost = 0.0; - *retSelec = 0.0; - } - } - return; + if (constant_on_right) + { + value = ((Const *) get_rightop(subclause))->constvalue; + } + else + { + value = NameGetDatum(""); + } + if (constant_on_right) + { + flag = (_SELEC_IS_CONSTANT_ || _SELEC_CONSTANT_RIGHT_); + } + else + { + flag = _SELEC_CONSTANT_RIGHT_; + } + index_selectivity(lfirsti(index->relids), + index->classlist, + lconsi(opno, NIL), + getrelid(lfirsti(rel->relids), + root->rtable), + lconsi(attno, NIL), + lconsi(value, NIL), + lconsi(flag, NIL), + 1, + &npages, + &selec); + + subcost = cost_index((Oid) lfirsti(index->relids), + (int) npages, + (Cost) selec, + rel->pages, + rel->tuples, + index->pages, + index->tuples, + false); + best_or_subclause_index(root, + rel, + subclause, + lnext(indices), + &subclause_indexid, + &subclause_cost, + &subclause_selec); + + if (subclause_indexid == 0 || subcost < subclause_cost) + { + *retIndexid = lfirsti(index->relids); + *retCost = subcost; + *retSelec = selec; + } + else + { + *retIndexid = 0; + *retCost = 0.0; + *retSelec = 0.0; + } + } + return; } diff --git a/src/backend/optimizer/path/predmig.c b/src/backend/optimizer/path/predmig.c index 241ab4a12d7..c302af3b581 100644 --- a/src/backend/optimizer/path/predmig.c +++ b/src/backend/optimizer/path/predmig.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * predmig.c-- - * + * * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.2 1996/10/23 07:14:41 bryanh Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.3 1997/09/07 04:43:47 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,23 +16,23 @@ ** Main Routines to handle Predicate Migration (i.e. correct optimization ** of queries with expensive functions.) ** -** The reasoning behind some of these algorithms is rather detailed. -** Have a look at Sequoia Tech Report 92/13 for more info. Also +** The reasoning behind some of these algorithms is rather detailed. +** Have a look at Sequoia Tech Report 92/13 for more info. Also ** see Monma and Sidney's paper "Sequencing with Series-Parallel ** Precedence Constraints", in "Mathematics of Operations Research", ** volume 4 (1979), pp. 215-224. ** -** The main thing that this code does that wasn't handled in xfunc.c is +** The main thing that this code does that wasn't handled in xfunc.c is ** it considers the possibility that two joins in a stream may not ** be ordered by ascending rank -- in such a scenario, it may be optimal ** to pullup more restrictions than we did via xfunc_try_pullup. ** -** This code in some sense generalizes xfunc_try_pullup; if you +** This code in some sense generalizes xfunc_try_pullup; if you ** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this ** code will do everything that xfunc_try_pullup would have, and maybe -** more. However, this results in no pruning, which may slow down the +** more. However, this results in no pruning, which may slow down the ** optimizer and/or cause the system to run out of memory. -** -- JMH, 11/13/92 +** -- JMH, 11/13/92 */ #include "nodes/pg_list.h" @@ -49,331 +49,350 @@ #include "optimizer/tlist.h" #include "lib/qsort.h" -#define is_clause(node) (get_cinfo(node)) /* a stream node represents a - clause (not a join) iff it - has a non-NULL cinfo field */ - -static void xfunc_predmig(JoinPath pathnode, Stream streamroot, - Stream laststream, bool *progressp); -static bool xfunc_series_llel(Stream stream); -static bool xfunc_llel_chains(Stream root, Stream bottom); -static Stream xfunc_complete_stream(Stream stream); -static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, - JoinPath joinpath); -static void xfunc_form_groups(Stream root, Stream bottom); -static void xfunc_free_stream(Stream root); -static Stream xfunc_add_clauses(Stream current); -static void xfunc_setup_group(Stream node, Stream bottom); -static Stream xfunc_streaminsert(CInfo clauseinfo, Stream current, - int clausetype); -static int xfunc_num_relids(Stream node); +#define is_clause(node) (get_cinfo(node)) /* a stream node + * represents a clause + * (not a join) iff it has + * a non-NULL cinfo field */ + +static void +xfunc_predmig(JoinPath pathnode, Stream streamroot, + Stream laststream, bool * progressp); +static bool xfunc_series_llel(Stream stream); +static bool xfunc_llel_chains(Stream root, Stream bottom); +static Stream xfunc_complete_stream(Stream stream); +static bool +xfunc_prdmig_pullup(Stream origstream, Stream pullme, + JoinPath joinpath); +static void xfunc_form_groups(Stream root, Stream bottom); +static void xfunc_free_stream(Stream root); +static Stream xfunc_add_clauses(Stream current); +static void xfunc_setup_group(Stream node, Stream bottom); +static Stream +xfunc_streaminsert(CInfo clauseinfo, Stream current, + int clausetype); +static int xfunc_num_relids(Stream node); static StreamPtr xfunc_get_downjoin(Stream node); static StreamPtr xfunc_get_upjoin(Stream node); -static Stream xfunc_stream_qsort(Stream root, Stream bottom); -static int xfunc_stream_compare(void *arg1, void *arg2); -static bool xfunc_check_stream(Stream node); -static bool xfunc_in_stream(Stream node, Stream stream); +static Stream xfunc_stream_qsort(Stream root, Stream bottom); +static int xfunc_stream_compare(void *arg1, void *arg2); +static bool xfunc_check_stream(Stream node); +static bool xfunc_in_stream(Stream node, Stream stream); -/* ----------------- MAIN FUNCTIONS ------------------------ */ +/* ----------------- MAIN FUNCTIONS ------------------------ */ /* ** xfunc_do_predmig -** wrapper for Predicate Migration. It calls xfunc_predmig until no +** wrapper for Predicate Migration. It calls xfunc_predmig until no ** more progress is made. -** return value says if any changes were ever made. +** return value says if any changes were ever made. */ -bool xfunc_do_predmig(Path root) +bool +xfunc_do_predmig(Path root) { - bool progress, changed = false; - - if (is_join(root)) - do - { - progress = false; - Assert(IsA(root,JoinPath)); - xfunc_predmig((JoinPath)root, (Stream)NULL, (Stream)NULL, - &progress); - if (changed && progress) - elog(DEBUG, "Needed to do a second round of predmig!\n"); - if (progress) changed = true; - } while (progress); - return(changed); + bool progress, + changed = false; + + if (is_join(root)) + do + { + progress = false; + Assert(IsA(root, JoinPath)); + xfunc_predmig((JoinPath) root, (Stream) NULL, (Stream) NULL, + &progress); + if (changed && progress) + elog(DEBUG, "Needed to do a second round of predmig!\n"); + if (progress) + changed = true; + } while (progress); + return (changed); } /* ** xfunc_predmig - ** The main routine for Predicate Migration. It traverses a join tree, - ** and for each root-to-leaf path in the plan tree it constructs a + ** The main routine for Predicate Migration. It traverses a join tree, + ** and for each root-to-leaf path in the plan tree it constructs a ** "Stream", which it passes to xfunc_series_llel for optimization. ** Destructively modifies the join tree (via predicate pullup). */ static void -xfunc_predmig(JoinPath pathnode, /* root of the join tree */ - Stream streamroot, - Stream laststream, /* for recursive calls -- these are - the root of the stream under - construction, and the lowest node - created so far */ - bool *progressp) +xfunc_predmig(JoinPath pathnode,/* root of the join tree */ + Stream streamroot, + Stream laststream,/* for recursive calls -- these are the + * root of the stream under construction, + * and the lowest node created so far */ + bool * progressp) { - Stream newstream; - - /* - ** traverse the join tree dfs-style, constructing a stream as you go. - ** When you hit a scan node, pass the stream off to xfunc_series_llel. - */ - - /* sanity check */ - if ((!streamroot && laststream) || - (streamroot && !laststream)) - elog(WARN, "called xfunc_predmig with bad inputs"); - if (streamroot) Assert(xfunc_check_stream(streamroot)); - - /* add path node to stream */ - newstream = RMakeStream(); - if (!streamroot) - streamroot = newstream; - set_upstream(newstream, (StreamPtr)laststream); - if (laststream) - set_downstream(laststream, (StreamPtr)newstream); - set_downstream(newstream, (StreamPtr)NULL); - set_pathptr(newstream, (pathPtr)pathnode); - set_cinfo(newstream, (CInfo)NULL); - set_clausetype(newstream, XFUNC_UNKNOWN); - - /* base case: we're at a leaf, call xfunc_series_llel */ - if (!is_join(pathnode)) + Stream newstream; + + /* + * * traverse the join tree dfs-style, constructing a stream as you + * go. * When you hit a scan node, pass the stream off to + * xfunc_series_llel. + */ + + /* sanity check */ + if ((!streamroot && laststream) || + (streamroot && !laststream)) + elog(WARN, "called xfunc_predmig with bad inputs"); + if (streamroot) + Assert(xfunc_check_stream(streamroot)); + + /* add path node to stream */ + newstream = RMakeStream(); + if (!streamroot) + streamroot = newstream; + set_upstream(newstream, (StreamPtr) laststream); + if (laststream) + set_downstream(laststream, (StreamPtr) newstream); + set_downstream(newstream, (StreamPtr) NULL); + set_pathptr(newstream, (pathPtr) pathnode); + set_cinfo(newstream, (CInfo) NULL); + set_clausetype(newstream, XFUNC_UNKNOWN); + + /* base case: we're at a leaf, call xfunc_series_llel */ + if (!is_join(pathnode)) { - /* form a fleshed-out copy of the stream */ - Stream fullstream = xfunc_complete_stream(streamroot); - - /* sort it via series-llel */ - if (xfunc_series_llel(fullstream)) - *progressp = true; - - /* free up the copy */ - xfunc_free_stream(fullstream); + /* form a fleshed-out copy of the stream */ + Stream fullstream = xfunc_complete_stream(streamroot); + + /* sort it via series-llel */ + if (xfunc_series_llel(fullstream)) + *progressp = true; + + /* free up the copy */ + xfunc_free_stream(fullstream); } - else + else { - /* visit left child */ - xfunc_predmig((JoinPath)get_outerjoinpath(pathnode), - streamroot, newstream, progressp); - - /* visit right child */ - xfunc_predmig((JoinPath)get_innerjoinpath(pathnode), - streamroot, newstream, progressp); + /* visit left child */ + xfunc_predmig((JoinPath) get_outerjoinpath(pathnode), + streamroot, newstream, progressp); + + /* visit right child */ + xfunc_predmig((JoinPath) get_innerjoinpath(pathnode), + streamroot, newstream, progressp); } - - /* remove this node */ - if (get_upstream(newstream)) - set_downstream((Stream)get_upstream(newstream), (StreamPtr)NULL); - pfree(newstream); + + /* remove this node */ + if (get_upstream(newstream)) + set_downstream((Stream) get_upstream(newstream), (StreamPtr) NULL); + pfree(newstream); } /* ** xfunc_series_llel ** A flavor of Monma and Sidney's Series-Parallel algorithm. - ** Traverse stream downwards. When you find a node with restrictions on it, + ** Traverse stream downwards. When you find a node with restrictions on it, ** call xfunc_llel_chains on the substream from root to that node. */ -static bool xfunc_series_llel(Stream stream) +static bool +xfunc_series_llel(Stream stream) { - Stream temp, next; - bool progress = false; - - for (temp = stream; temp != (Stream)NULL; temp = next) + Stream temp, + next; + bool progress = false; + + for (temp = stream; temp != (Stream) NULL; temp = next) { - next = (Stream)xfunc_get_downjoin(temp); - /* - ** if there are restrictions/secondary join clauses above this - ** node, call xfunc_llel_chains - */ - if (get_upstream(temp) && is_clause((Stream)get_upstream(temp))) - if (xfunc_llel_chains(stream, temp)) - progress = true; + next = (Stream) xfunc_get_downjoin(temp); + + /* + * * if there are restrictions/secondary join clauses above this * + * node, call xfunc_llel_chains + */ + if (get_upstream(temp) && is_clause((Stream) get_upstream(temp))) + if (xfunc_llel_chains(stream, temp)) + progress = true; } - return(progress); + return (progress); } /* ** xfunc_llel_chains ** A flavor of Monma and Sidney's Parallel Chains algorithm. ** Given a stream which has been well-ordered except for its lowermost - ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. + ** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate. ** What that means here is to form groups in the chain above the lowest - ** join node above bottom inclusive, and then take all the restrictions + ** join node above bottom inclusive, and then take all the restrictions ** following bottom, and try to pull them up as far as possible. */ -static bool xfunc_llel_chains(Stream root, Stream bottom) +static bool +xfunc_llel_chains(Stream root, Stream bottom) { - bool progress = false; - Stream origstream; - Stream tmpstream, pathstream; - Stream rootcopy = root; - - Assert(xfunc_check_stream(root)); - - /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ - origstream = (Stream)copyObject((Node)root); - - /* form groups among ill-ordered nodes */ - xfunc_form_groups(root, bottom); - - /* sort chain by rank */ - Assert(xfunc_in_stream(bottom, root)); - rootcopy = xfunc_stream_qsort(root, bottom); - - /* - ** traverse sorted stream -- if any restriction has moved above a join, - ** we must pull it up in the plan. That is, make plan tree - ** reflect order of sorted stream. - */ - for (tmpstream = rootcopy, - pathstream = (Stream)xfunc_get_downjoin(rootcopy); - tmpstream != (Stream)NULL && pathstream != (Stream)NULL; - tmpstream = (Stream)get_downstream(tmpstream)) + bool progress = false; + Stream origstream; + Stream tmpstream, + pathstream; + Stream rootcopy = root; + + Assert(xfunc_check_stream(root)); + + /* xfunc_prdmig_pullup will need an unmodified copy of the stream */ + origstream = (Stream) copyObject((Node) root); + + /* form groups among ill-ordered nodes */ + xfunc_form_groups(root, bottom); + + /* sort chain by rank */ + Assert(xfunc_in_stream(bottom, root)); + rootcopy = xfunc_stream_qsort(root, bottom); + + /* + * * traverse sorted stream -- if any restriction has moved above a + * join, * we must pull it up in the plan. That is, make plan tree * + * reflect order of sorted stream. + */ + for (tmpstream = rootcopy, + pathstream = (Stream) xfunc_get_downjoin(rootcopy); + tmpstream != (Stream) NULL && pathstream != (Stream) NULL; + tmpstream = (Stream) get_downstream(tmpstream)) { - if (is_clause(tmpstream) - && get_pathptr(pathstream) != get_pathptr(tmpstream)) + if (is_clause(tmpstream) + && get_pathptr(pathstream) != get_pathptr(tmpstream)) { - /* - ** If restriction moved above a Join after sort, we pull it - ** up in the join plan. - ** If restriction moved down, we ignore it. - ** This is because Joey's Sequoia paper proves that - ** restrictions should never move down. If this - ** one were moved down, it would violate "semantic correctness", - ** i.e. it would be lower than the attributes it references. - */ - Assert(xfunc_num_relids(pathstream)>xfunc_num_relids(tmpstream)); - progress = - xfunc_prdmig_pullup(origstream, tmpstream, - (JoinPath)get_pathptr(pathstream)); + + /* + * * If restriction moved above a Join after sort, we pull it * + * up in the join plan. * If restriction moved down, we + * ignore it. * This is because Joey's Sequoia paper proves + * that * restrictions should never move down. If this * one + * were moved down, it would violate "semantic correctness", * + * i.e. it would be lower than the attributes it references. + */ + Assert(xfunc_num_relids(pathstream) > xfunc_num_relids(tmpstream)); + progress = + xfunc_prdmig_pullup(origstream, tmpstream, + (JoinPath) get_pathptr(pathstream)); } - if (get_downstream(tmpstream)) - pathstream = - (Stream)xfunc_get_downjoin((Stream)get_downstream(tmpstream)); + if (get_downstream(tmpstream)) + pathstream = + (Stream) xfunc_get_downjoin((Stream) get_downstream(tmpstream)); } - - /* free up origstream */ - xfunc_free_stream(origstream); - return(progress); + + /* free up origstream */ + xfunc_free_stream(origstream); + return (progress); } /* ** xfunc_complete_stream -- ** Given a stream composed of join nodes only, make a copy containing the - ** join nodes along with the associated restriction nodes. + ** join nodes along with the associated restriction nodes. */ -static Stream xfunc_complete_stream(Stream stream) +static Stream +xfunc_complete_stream(Stream stream) { - Stream tmpstream, copystream, curstream = (Stream)NULL; - - copystream = (Stream)copyObject((Node)stream); - Assert(xfunc_check_stream(copystream)); - - curstream = copystream; - Assert(!is_clause(curstream)); - - /* curstream = (Stream)xfunc_get_downjoin(curstream); */ - - while(curstream != (Stream)NULL) + Stream tmpstream, + copystream, + curstream = (Stream) NULL; + + copystream = (Stream) copyObject((Node) stream); + Assert(xfunc_check_stream(copystream)); + + curstream = copystream; + Assert(!is_clause(curstream)); + + /* curstream = (Stream)xfunc_get_downjoin(curstream); */ + + while (curstream != (Stream) NULL) { - xfunc_add_clauses(curstream); - curstream = (Stream)xfunc_get_downjoin(curstream); + xfunc_add_clauses(curstream); + curstream = (Stream) xfunc_get_downjoin(curstream); } - - /* find top of stream and return it */ - for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr)NULL; - tmpstream = (Stream)get_upstream(tmpstream)) - /* no body in for loop */; - - return(tmpstream); + + /* find top of stream and return it */ + for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr) NULL; + tmpstream = (Stream) get_upstream(tmpstream)) + /* no body in for loop */ ; + + return (tmpstream); } /* ** xfunc_prdmig_pullup ** pullup a clause in a path above joinpath. Since the JoinPath tree - ** doesn't have upward pointers, it's difficult to deal with. Thus we + ** doesn't have upward pointers, it's difficult to deal with. Thus we ** require the original stream, which maintains pointers to all the path - ** nodes. We use the original stream to find out what joins are + ** nodes. We use the original stream to find out what joins are ** above the clause. */ -static bool +static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath) { - CInfo clauseinfo = get_cinfo(pullme); - bool progress = false; - Stream upjoin, orignode, temp; - int whichchild; - - /* find node in origstream that contains clause */ - for (orignode = origstream; - orignode != (Stream) NULL - && get_cinfo(orignode) != clauseinfo; - orignode = (Stream)get_downstream(orignode)) - /* empty body in for loop */ ; - if (!orignode) - elog(WARN, "Didn't find matching node in original stream"); - - - /* pull up this node as far as it should go */ - for (upjoin = (Stream)xfunc_get_upjoin(orignode); - upjoin != (Stream)NULL - && (JoinPath)get_pathptr((Stream)xfunc_get_downjoin(upjoin)) - != joinpath; - upjoin = (Stream)xfunc_get_upjoin(upjoin)) + CInfo clauseinfo = get_cinfo(pullme); + bool progress = false; + Stream upjoin, + orignode, + temp; + int whichchild; + + /* find node in origstream that contains clause */ + for (orignode = origstream; + orignode != (Stream) NULL + && get_cinfo(orignode) != clauseinfo; + orignode = (Stream) get_downstream(orignode)) + /* empty body in for loop */ ; + if (!orignode) + elog(WARN, "Didn't find matching node in original stream"); + + + /* pull up this node as far as it should go */ + for (upjoin = (Stream) xfunc_get_upjoin(orignode); + upjoin != (Stream) NULL + && (JoinPath) get_pathptr((Stream) xfunc_get_downjoin(upjoin)) + != joinpath; + upjoin = (Stream) xfunc_get_upjoin(upjoin)) { -#ifdef DEBUG - elog(DEBUG, "pulling up in xfunc_predmig_pullup!"); +#ifdef DEBUG + elog(DEBUG, "pulling up in xfunc_predmig_pullup!"); #endif - /* move clause up in path */ - if (get_pathptr((Stream)get_downstream(upjoin)) - == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin))) - whichchild = OUTER; - else whichchild = INNER; - clauseinfo = xfunc_pullup((Path)get_pathptr((Stream)get_downstream(upjoin)), - (JoinPath)get_pathptr(upjoin), - clauseinfo, - whichchild, - get_clausetype(orignode)); - set_pathptr(pullme, get_pathptr(upjoin)); - /* pullme has been moved into locclauseinfo */ - set_clausetype(pullme, XFUNC_LOCPRD); - - /* - ** xfunc_pullup makes new path nodes for children of - ** get_pathptr(current). We must modify the stream nodes to point - ** to these path nodes - */ - if (whichchild == OUTER) + /* move clause up in path */ + if (get_pathptr((Stream) get_downstream(upjoin)) + == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))) + whichchild = OUTER; + else + whichchild = INNER; + clauseinfo = xfunc_pullup((Path) get_pathptr((Stream) get_downstream(upjoin)), + (JoinPath) get_pathptr(upjoin), + clauseinfo, + whichchild, + get_clausetype(orignode)); + set_pathptr(pullme, get_pathptr(upjoin)); + /* pullme has been moved into locclauseinfo */ + set_clausetype(pullme, XFUNC_LOCPRD); + + /* + * * xfunc_pullup makes new path nodes for children of * + * get_pathptr(current). We must modify the stream nodes to point * + * to these path nodes + */ + if (whichchild == OUTER) { - for(temp = (Stream)get_downstream(upjoin); is_clause(temp); - temp = (Stream)get_downstream(temp)) + for (temp = (Stream) get_downstream(upjoin); is_clause(temp); + temp = (Stream) get_downstream(temp)) + set_pathptr + (temp, (pathPtr) + get_outerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr - (temp, (pathPtr) - get_outerjoinpath((JoinPath)get_pathptr(upjoin))); - set_pathptr - (temp, - (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin))); + (temp, + (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(upjoin))); } - else + else { - for(temp = (Stream)get_downstream(upjoin); is_clause(temp); - temp = (Stream)get_downstream(temp)) + for (temp = (Stream) get_downstream(upjoin); is_clause(temp); + temp = (Stream) get_downstream(temp)) + set_pathptr + (temp, (pathPtr) + get_innerjoinpath((JoinPath) get_pathptr(upjoin))); set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath)get_pathptr(upjoin))); - set_pathptr - (temp, (pathPtr) - get_innerjoinpath((JoinPath)get_pathptr(upjoin))); + (temp, (pathPtr) + get_innerjoinpath((JoinPath) get_pathptr(upjoin))); } - progress = true; + progress = true; } - if (!progress) - elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); - return(progress); + if (!progress) + elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup"); + return (progress); } /* @@ -386,143 +405,151 @@ xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath) ** equal to the cost of the first plus the selectivity of the first times the ** cost of the second. We define each node to be in a group by itself, ** and then repeatedly find adjacent groups which are ordered by descending - ** rank, and make larger groups. You know that two adjacent nodes are in a - ** group together if the lower has groupup set to true. They will both have + ** rank, and make larger groups. You know that two adjacent nodes are in a + ** group together if the lower has groupup set to true. They will both have ** the same groupcost and groupsel (since they're in the same group!) */ -static void xfunc_form_groups(Query* queryInfo, Stream root, Stream bottom) +static void +xfunc_form_groups(Query * queryInfo, Stream root, Stream bottom) { - Stream temp, parent; - int lowest = xfunc_num_relids((Stream)xfunc_get_upjoin(bottom)); - bool progress; - LispValue primjoin; - int whichchild; - - if (!lowest) return; /* no joins in stream, so no groups */ - - /* initialize groups to be single nodes */ - for (temp = root; - temp != (Stream)NULL && temp != bottom; - temp = (Stream)get_downstream(temp)) + Stream temp, + parent; + int lowest = xfunc_num_relids((Stream) xfunc_get_upjoin(bottom)); + bool progress; + LispValue primjoin; + int whichchild; + + if (!lowest) + return; /* no joins in stream, so no groups */ + + /* initialize groups to be single nodes */ + for (temp = root; + temp != (Stream) NULL && temp != bottom; + temp = (Stream) get_downstream(temp)) { - /* if a Join node */ - if (!is_clause(temp)) + /* if a Join node */ + if (!is_clause(temp)) { - if (get_pathptr((Stream)get_downstream(temp)) - == (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(temp))) - whichchild = OUTER; - else whichchild = INNER; - set_groupcost(temp, - xfunc_join_expense((JoinPath)get_pathptr(temp), - whichchild)); - if (primjoin = xfunc_primary_join((JoinPath)get_pathptr(temp))) + if (get_pathptr((Stream) get_downstream(temp)) + == (pathPtr) get_outerjoinpath((JoinPath) get_pathptr(temp))) + whichchild = OUTER; + else + whichchild = INNER; + set_groupcost(temp, + xfunc_join_expense((JoinPath) get_pathptr(temp), + whichchild)); + if (primjoin = xfunc_primary_join((JoinPath) get_pathptr(temp))) { - set_groupsel(temp, - compute_clause_selec(queryInfo, - primjoin, NIL)); + set_groupsel(temp, + compute_clause_selec(queryInfo, + primjoin, NIL)); } - else + else { - set_groupsel(temp,1.0); + set_groupsel(temp, 1.0); } } - else /* a restriction, or 2-ary join pred */ + else +/* a restriction, or 2-ary join pred */ { - set_groupcost(temp, - xfunc_expense(queryInfo, - get_clause(get_cinfo(temp)))); - set_groupsel(temp, - compute_clause_selec(queryInfo, - get_clause(get_cinfo(temp)), - NIL)); + set_groupcost(temp, + xfunc_expense(queryInfo, + get_clause(get_cinfo(temp)))); + set_groupsel(temp, + compute_clause_selec(queryInfo, + get_clause(get_cinfo(temp)), + NIL)); } - set_groupup(temp,false); + set_groupup(temp, false); } - - /* make passes upwards, forming groups */ - do + + /* make passes upwards, forming groups */ + do { - progress = false; - for (temp = (Stream)get_upstream(bottom); - temp != (Stream)NULL; - temp = (Stream)get_upstream(temp)) + progress = false; + for (temp = (Stream) get_upstream(bottom); + temp != (Stream) NULL; + temp = (Stream) get_upstream(temp)) { - /* check for grouping with node upstream */ - if (!get_groupup(temp) && /* not already grouped */ - (parent = (Stream)get_upstream(temp)) != (Stream)NULL && + /* check for grouping with node upstream */ + if (!get_groupup(temp) && /* not already grouped */ + (parent = (Stream) get_upstream(temp)) != (Stream) NULL && /* temp is a join or temp is the top of a group */ - (is_join((Path)get_pathptr(temp)) || - get_downstream(temp) && - get_groupup((Stream)get_downstream(temp))) && - get_grouprank(parent) < get_grouprank(temp)) + (is_join((Path) get_pathptr(temp)) || + get_downstream(temp) && + get_groupup((Stream) get_downstream(temp))) && + get_grouprank(parent) < get_grouprank(temp)) { - progress = true; /* we formed a new group */ - set_groupup(temp,true); - set_groupcost(temp, - get_groupcost(temp) + - get_groupsel(temp) * get_groupcost(parent)); - set_groupsel(temp,get_groupsel(temp) * get_groupsel(parent)); - - /* fix costs and sels of all members of group */ - xfunc_setup_group(temp, bottom); + progress = true;/* we formed a new group */ + set_groupup(temp, true); + set_groupcost(temp, + get_groupcost(temp) + + get_groupsel(temp) * get_groupcost(parent)); + set_groupsel(temp, get_groupsel(temp) * get_groupsel(parent)); + + /* fix costs and sels of all members of group */ + xfunc_setup_group(temp, bottom); } } - } while(progress); + } while (progress); } -/* ------------------- UTILITY FUNCTIONS ------------------------- */ +/* ------------------- UTILITY FUNCTIONS ------------------------- */ /* ** xfunc_free_stream -- ** walk down a stream and pfree it */ -static void xfunc_free_stream(Stream root) +static void +xfunc_free_stream(Stream root) { - Stream cur, next; - - Assert(xfunc_check_stream(root)); - - if (root != (Stream)NULL) - for (cur = root; cur != (Stream)NULL; cur = next) - { - next = (Stream)get_downstream(cur); - pfree(cur); - } + Stream cur, + next; + + Assert(xfunc_check_stream(root)); + + if (root != (Stream) NULL) + for (cur = root; cur != (Stream) NULL; cur = next) + { + next = (Stream) get_downstream(cur); + pfree(cur); + } } /* ** xfunc_add<_clauses - ** find any clauses above current, and insert them into stream as + ** find any clauses above current, and insert them into stream as ** appropriate. Return uppermost clause inserted, or current if none. */ -static Stream xfunc_add_clauses(Stream current) +static Stream +xfunc_add_clauses(Stream current) { - Stream topnode = current; - LispValue temp; - LispValue primjoin; - - /* first add in the local clauses */ - foreach(temp, get_locclauseinfo((Path)get_pathptr(current))) + Stream topnode = current; + LispValue temp; + LispValue primjoin; + + /* first add in the local clauses */ + foreach(temp, get_locclauseinfo((Path) get_pathptr(current))) { - topnode = - xfunc_streaminsert((CInfo)lfirst(temp), topnode, - XFUNC_LOCPRD); + topnode = + xfunc_streaminsert((CInfo) lfirst(temp), topnode, + XFUNC_LOCPRD); } - - /* and add in the join clauses */ - if (IsA(get_pathptr(current),JoinPath)) + + /* and add in the join clauses */ + if (IsA(get_pathptr(current), JoinPath)) { - primjoin = xfunc_primary_join((JoinPath)get_pathptr(current)); - foreach(temp, get_pathclauseinfo((JoinPath)get_pathptr(current))) + primjoin = xfunc_primary_join((JoinPath) get_pathptr(current)); + foreach(temp, get_pathclauseinfo((JoinPath) get_pathptr(current))) { - if (!equal(get_clause((CInfo)lfirst(temp)), primjoin)) - topnode = - xfunc_streaminsert((CInfo)lfirst(temp), topnode, - XFUNC_JOINPRD); + if (!equal(get_clause((CInfo) lfirst(temp)), primjoin)) + topnode = + xfunc_streaminsert((CInfo) lfirst(temp), topnode, + XFUNC_JOINPRD); } } - return(topnode); + return (topnode); } @@ -531,33 +558,36 @@ static Stream xfunc_add_clauses(Stream current) ** find all elements of stream that are grouped with node and are above ** bottom, and set their groupcost and groupsel to be the same as node's. */ -static void xfunc_setup_group(Stream node, Stream bottom) +static void +xfunc_setup_group(Stream node, Stream bottom) { - Stream temp; - - if (node != bottom) - /* traverse downwards */ - for (temp = (Stream)get_downstream(node); - temp != (Stream)NULL && temp != bottom; - temp = (Stream)get_downstream(temp)) - { - if (!get_groupup(temp)) break; - else - { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); - } - } - - /* traverse upwards */ - for (temp = (Stream)get_upstream(node); temp != (Stream)NULL; - temp = (Stream)get_upstream(temp)) + Stream temp; + + if (node != bottom) + /* traverse downwards */ + for (temp = (Stream) get_downstream(node); + temp != (Stream) NULL && temp != bottom; + temp = (Stream) get_downstream(temp)) + { + if (!get_groupup(temp)) + break; + else + { + set_groupcost(temp, get_groupcost(node)); + set_groupsel(temp, get_groupsel(node)); + } + } + + /* traverse upwards */ + for (temp = (Stream) get_upstream(node); temp != (Stream) NULL; + temp = (Stream) get_upstream(temp)) { - if (!get_groupup((Stream)get_downstream(temp))) break; - else + if (!get_groupup((Stream) get_downstream(temp))) + break; + else { - set_groupcost(temp, get_groupcost(node)); - set_groupsel(temp, get_groupsel(node)); + set_groupcost(temp, get_groupcost(node)); + set_groupsel(temp, get_groupsel(node)); } } } @@ -568,70 +598,75 @@ static void xfunc_setup_group(Stream node, Stream bottom) ** Make a new Stream node to hold clause, and insert it above current. ** Return new node. */ -static Stream +static Stream xfunc_streaminsert(CInfo clauseinfo, - Stream current, - int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */ + Stream current, + int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */ { - Stream newstream = RMakeStream(); - set_upstream(newstream, get_upstream(current)); - if (get_upstream(current)) - set_downstream((Stream)(get_upstream(current)), (StreamPtr)newstream); - set_upstream(current, (StreamPtr)newstream); - set_downstream(newstream, (StreamPtr)current); - set_pathptr(newstream, get_pathptr(current)); - set_cinfo(newstream, clauseinfo); - set_clausetype(newstream, clausetype); - return(newstream); + Stream newstream = RMakeStream(); + + set_upstream(newstream, get_upstream(current)); + if (get_upstream(current)) + set_downstream((Stream) (get_upstream(current)), (StreamPtr) newstream); + set_upstream(current, (StreamPtr) newstream); + set_downstream(newstream, (StreamPtr) current); + set_pathptr(newstream, get_pathptr(current)); + set_cinfo(newstream, clauseinfo); + set_clausetype(newstream, clausetype); + return (newstream); } /* ** Given a Stream node, find the number of relids referenced in the pathnode ** associated with the stream node. The number of relids gives a unique - ** ordering on the joins in a stream, which we use to compare the height of + ** ordering on the joins in a stream, which we use to compare the height of ** join nodes. */ -static int xfunc_num_relids(Stream node) +static int +xfunc_num_relids(Stream node) { - if (!node || !IsA(get_pathptr(node),JoinPath)) - return(0); - else return(length - (get_relids(get_parent((JoinPath)get_pathptr(node))))); + if (!node || !IsA(get_pathptr(node), JoinPath)) + return (0); + else + return (length + (get_relids(get_parent((JoinPath) get_pathptr(node))))); } -/* +/* ** xfunc_get_downjoin -- ** Given a stream node, find the next lowest node which points to a ** join predicate or a scan node. */ -static StreamPtr xfunc_get_downjoin(Stream node) +static StreamPtr +xfunc_get_downjoin(Stream node) { - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream)get_downstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream)get_downstream(temp)) - /* empty body in for loop */ ; - - return((StreamPtr)temp); + Stream temp; + + if (!is_clause(node)) /* if this is a join */ + node = (Stream) get_downstream(node); + for (temp = node; temp && is_clause(temp); + temp = (Stream) get_downstream(temp)) + /* empty body in for loop */ ; + + return ((StreamPtr) temp); } /* ** xfunc_get_upjoin -- ** same as above, but upwards. */ -static StreamPtr xfunc_get_upjoin(Stream node) +static StreamPtr +xfunc_get_upjoin(Stream node) { - Stream temp; - - if (!is_clause(node)) /* if this is a join */ - node = (Stream)get_upstream(node); - for (temp = node; temp && is_clause(temp); - temp = (Stream)get_upstream(temp)) - /* empty body in for loop */ ; - - return((StreamPtr)temp); + Stream temp; + + if (!is_clause(node)) /* if this is a join */ + node = (Stream) get_upstream(node); + for (temp = node; temp && is_clause(temp); + temp = (Stream) get_upstream(temp)) + /* empty body in for loop */ ; + + return ((StreamPtr) temp); } /* @@ -639,43 +674,46 @@ static StreamPtr xfunc_get_upjoin(Stream node) ** Given a stream, sort by group rank the elements in the stream from the ** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root. */ -static Stream xfunc_stream_qsort(Stream root, Stream bottom) +static Stream +xfunc_stream_qsort(Stream root, Stream bottom) { - int i; - size_t num; - Stream *nodearray, output; - Stream tmp; - - /* find size of list */ - for (num = 0, tmp = root; tmp != bottom; - tmp = (Stream)get_downstream(tmp)) - num ++; - if (num <= 1) return (root); - - /* copy elements of the list into an array */ - nodearray = (Stream *) palloc(num * sizeof(Stream)); - - for (tmp = root, i = 0; tmp != bottom; - tmp = (Stream)get_downstream(tmp), i++) - nodearray[i] = tmp; - - /* sort the array */ - pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare); - - /* paste together the array elements */ - output = nodearray[num - 1]; - set_upstream(output, (StreamPtr)NULL); - for (i = num - 2; i >= 0; i--) + int i; + size_t num; + Stream *nodearray, + output; + Stream tmp; + + /* find size of list */ + for (num = 0, tmp = root; tmp != bottom; + tmp = (Stream) get_downstream(tmp)) + num++; + if (num <= 1) + return (root); + + /* copy elements of the list into an array */ + nodearray = (Stream *) palloc(num * sizeof(Stream)); + + for (tmp = root, i = 0; tmp != bottom; + tmp = (Stream) get_downstream(tmp), i++) + nodearray[i] = tmp; + + /* sort the array */ + pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare); + + /* paste together the array elements */ + output = nodearray[num - 1]; + set_upstream(output, (StreamPtr) NULL); + for (i = num - 2; i >= 0; i--) { - set_downstream(nodearray[i+1], (StreamPtr)nodearray[i]); - set_upstream(nodearray[i], (StreamPtr)nodearray[i+1]); + set_downstream(nodearray[i + 1], (StreamPtr) nodearray[i]); + set_upstream(nodearray[i], (StreamPtr) nodearray[i + 1]); } - set_downstream(nodearray[0], (StreamPtr)bottom); - if (bottom) - set_upstream(bottom, (StreamPtr)nodearray[0]); - - Assert(xfunc_check_stream(output)); - return(output); + set_downstream(nodearray[0], (StreamPtr) bottom); + if (bottom) + set_upstream(bottom, (StreamPtr) nodearray[0]); + + Assert(xfunc_check_stream(output)); + return (output); } /* @@ -684,90 +722,102 @@ static Stream xfunc_stream_qsort(Stream root, Stream bottom) ** Compare nodes by group rank. If group ranks are equal, ensure that ** join nodes appear in same order as in plan tree. */ -static int xfunc_stream_compare(void *arg1, void *arg2) +static int +xfunc_stream_compare(void *arg1, void *arg2) { - Stream stream1 = *(Stream *) arg1; - Stream stream2 = *(Stream *) arg2; - Cost rank1, rank2; - - rank1 = get_grouprank(stream1); - rank2 = get_grouprank(stream2); - - if (rank1 > rank2) return(1); - else if (rank1 < rank2) return(-1); - else + Stream stream1 = *(Stream *) arg1; + Stream stream2 = *(Stream *) arg2; + Cost rank1, + rank2; + + rank1 = get_grouprank(stream1); + rank2 = get_grouprank(stream2); + + if (rank1 > rank2) + return (1); + else if (rank1 < rank2) + return (-1); + else { - if (is_clause(stream1) && is_clause(stream2)) - return(0); /* doesn't matter what order if both are restrictions */ - else if (!is_clause(stream1) && !is_clause(stream2)) + if (is_clause(stream1) && is_clause(stream2)) + return (0); /* doesn't matter what order if both are + * restrictions */ + else if (!is_clause(stream1) && !is_clause(stream2)) { - if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2)) - return(-1); - else return(1); + if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2)) + return (-1); + else + return (1); } - else if (is_clause(stream1) && !is_clause(stream2)) + else if (is_clause(stream1) && !is_clause(stream2)) { - if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2)) - /* stream1 is a restriction over stream2 */ - return(1); - else return(-1); + if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2)) + /* stream1 is a restriction over stream2 */ + return (1); + else + return (-1); } - else if (!is_clause(stream1) && is_clause(stream2)) + else if (!is_clause(stream1) && is_clause(stream2)) { - /* stream2 is a restriction over stream1: never push down */ - return(-1); + /* stream2 is a restriction over stream1: never push down */ + return (-1); } } } -/* ------------------ DEBUGGING ROUTINES ---------------------------- */ +/* ------------------ DEBUGGING ROUTINES ---------------------------- */ /* ** Make sure all pointers in stream make sense. Make sure no joins are ** out of order. */ -static bool xfunc_check_stream(Stream node) +static bool +xfunc_check_stream(Stream node) { - Stream temp; - int numrelids, tmp; - - /* set numrelids higher than max */ - if (!is_clause(node)) - numrelids = xfunc_num_relids(node) + 1; - else if (xfunc_get_downjoin(node)) - numrelids = xfunc_num_relids((Stream)xfunc_get_downjoin(node)) + 1; - else numrelids = 1; - - for (temp = node; get_downstream(temp); temp = (Stream)get_downstream(temp)) + Stream temp; + int numrelids, + tmp; + + /* set numrelids higher than max */ + if (!is_clause(node)) + numrelids = xfunc_num_relids(node) + 1; + else if (xfunc_get_downjoin(node)) + numrelids = xfunc_num_relids((Stream) xfunc_get_downjoin(node)) + 1; + else + numrelids = 1; + + for (temp = node; get_downstream(temp); temp = (Stream) get_downstream(temp)) { - if ((Stream)get_upstream((Stream)get_downstream(temp)) != temp) + if ((Stream) get_upstream((Stream) get_downstream(temp)) != temp) { - elog(WARN, "bad pointers in stream"); - return(false); + elog(WARN, "bad pointers in stream"); + return (false); } - if (!is_clause(temp)) + if (!is_clause(temp)) { - if ((tmp = xfunc_num_relids(temp)) >= numrelids) + if ((tmp = xfunc_num_relids(temp)) >= numrelids) { - elog(WARN, "Joins got reordered!"); - return(false); + elog(WARN, "Joins got reordered!"); + return (false); } - numrelids = tmp; + numrelids = tmp; } } - - return(true); + + return (true); } /* ** xfunc_in_stream ** check if node is in stream */ -static bool xfunc_in_stream(Stream node, Stream stream) +static bool +xfunc_in_stream(Stream node, Stream stream) { - Stream temp; - - for (temp = stream; temp; temp = (Stream)get_downstream(temp)) - if (temp == node) return(1); - return(0); + Stream temp; + + for (temp = stream; temp; temp = (Stream) get_downstream(temp)) + if (temp == node) + return (1); + return (0); } diff --git a/src/backend/optimizer/path/prune.c b/src/backend/optimizer/path/prune.c index 0b154e108fa..4f3ae2d15de 100644 --- a/src/backend/optimizer/path/prune.c +++ b/src/backend/optimizer/path/prune.c @@ -1,13 +1,13 @@ /*------------------------------------------------------------------------- * * prune.c-- - * Routines to prune redundant paths and relations + * Routines to prune redundant paths and relations * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.3 1997/06/10 07:55:47 vadim Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.4 1997/09/07 04:43:49 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,181 +24,199 @@ #include "utils/elog.h" -static List *prune_joinrel(Rel *rel, List *other_rels); +static List *prune_joinrel(Rel * rel, List * other_rels); -/* +/* * prune-joinrels-- - * Removes any redundant relation entries from a list of rel nodes - * 'rel-list'. - * - * Returns the resulting list. - * + * Removes any redundant relation entries from a list of rel nodes + * 'rel-list'. + * + * Returns the resulting list. + * */ -List *prune_joinrels(List *rel_list) +List * +prune_joinrels(List * rel_list) { - List *temp_list = NIL; - - if (rel_list != NIL) { - temp_list = lcons(lfirst(rel_list), - prune_joinrels(prune_joinrel((Rel*)lfirst(rel_list), - lnext(rel_list)))); - } - return(temp_list); + List *temp_list = NIL; + + if (rel_list != NIL) + { + temp_list = lcons(lfirst(rel_list), + prune_joinrels(prune_joinrel((Rel *) lfirst(rel_list), + lnext(rel_list)))); + } + return (temp_list); } -/* +/* * prune-joinrel-- - * Prunes those relations from 'other-rels' that are redundant with - * 'rel'. A relation is redundant if it is built up of the same - * relations as 'rel'. Paths for the redundant relation are merged into - * the pathlist of 'rel'. - * + * Prunes those relations from 'other-rels' that are redundant with + * 'rel'. A relation is redundant if it is built up of the same + * relations as 'rel'. Paths for the redundant relation are merged into + * the pathlist of 'rel'. + * * Returns a list of non-redundant relations, and sets the pathlist field * of 'rel' appropriately. - * + * */ -static List * -prune_joinrel(Rel *rel, List *other_rels) +static List * +prune_joinrel(Rel * rel, List * other_rels) { - List *i = NIL; - List *t_list = NIL; - List *temp_node = NIL; - Rel *other_rel = (Rel *)NULL; - - foreach(i, other_rels) { - other_rel = (Rel*)lfirst(i); - if(same(rel->relids, other_rel->relids)) { - rel->pathlist = add_pathlist(rel, - rel->pathlist, - other_rel->pathlist); - t_list = nconc(t_list, NIL); /* XXX is this right ? */ - } else { - temp_node = lcons(other_rel, NIL); - t_list = nconc(t_list,temp_node); - } - } - return(t_list); + List *i = NIL; + List *t_list = NIL; + List *temp_node = NIL; + Rel *other_rel = (Rel *) NULL; + + foreach(i, other_rels) + { + other_rel = (Rel *) lfirst(i); + if (same(rel->relids, other_rel->relids)) + { + rel->pathlist = add_pathlist(rel, + rel->pathlist, + other_rel->pathlist); + t_list = nconc(t_list, NIL); /* XXX is this right ? */ + } + else + { + temp_node = lcons(other_rel, NIL); + t_list = nconc(t_list, temp_node); + } + } + return (t_list); } -/* +/* * prune-rel-paths-- - * For each relation entry in 'rel-list' (which corresponds to a join - * relation), set pointers to the unordered path and cheapest paths - * (if the unordered path isn't the cheapest, it is pruned), and - * reset the relation's size field to reflect the join. - * + * For each relation entry in 'rel-list' (which corresponds to a join + * relation), set pointers to the unordered path and cheapest paths + * (if the unordered path isn't the cheapest, it is pruned), and + * reset the relation's size field to reflect the join. + * * Returns nothing of interest. - * + * */ void -prune_rel_paths(List *rel_list) +prune_rel_paths(List * rel_list) { - List *x = NIL; - List *y = NIL; - Path *path = NULL; - Rel *rel = (Rel*)NULL; - JoinPath *cheapest = (JoinPath*)NULL; - - foreach(x, rel_list) { - rel = (Rel*)lfirst(x); - rel->size = 0; - foreach(y, rel->pathlist) { - path = (Path*)lfirst(y); - - if(!path->p_ordering.ord.sortop) { - break; - } + List *x = NIL; + List *y = NIL; + Path *path = NULL; + Rel *rel = (Rel *) NULL; + JoinPath *cheapest = (JoinPath *) NULL; + + foreach(x, rel_list) + { + rel = (Rel *) lfirst(x); + rel->size = 0; + foreach(y, rel->pathlist) + { + path = (Path *) lfirst(y); + + if (!path->p_ordering.ord.sortop) + { + break; + } + } + cheapest = (JoinPath *) prune_rel_path(rel, path); + if (IsA_JoinPath(cheapest)) + { + rel->size = compute_joinrel_size(cheapest); + } + else + elog(WARN, "non JoinPath called"); } - cheapest = (JoinPath*)prune_rel_path(rel, path); - if (IsA_JoinPath(cheapest)) - { - rel->size = compute_joinrel_size(cheapest); - } - else - elog(WARN, "non JoinPath called"); - } } -/* +/* * prune-rel-path-- - * Compares the unordered path for a relation with the cheapest path. If - * the unordered path is not cheapest, it is pruned. - * - * Resets the pointers in 'rel' for unordered and cheapest paths. - * + * Compares the unordered path for a relation with the cheapest path. If + * the unordered path is not cheapest, it is pruned. + * + * Resets the pointers in 'rel' for unordered and cheapest paths. + * * Returns the cheapest path. - * + * */ -Path * -prune_rel_path(Rel *rel, Path *unorderedpath) +Path * +prune_rel_path(Rel * rel, Path * unorderedpath) { - Path *cheapest = set_cheapest(rel, rel->pathlist); - - /* don't prune if not pruneable -- JMH, 11/23/92 */ - if(unorderedpath != cheapest - && rel->pruneable) { - - rel->unorderedpath = (Path *)NULL; - rel->pathlist = lremove(unorderedpath, rel->pathlist); - } else { - rel->unorderedpath = (Path *)unorderedpath; - } - - return(cheapest); + Path *cheapest = set_cheapest(rel, rel->pathlist); + + /* don't prune if not pruneable -- JMH, 11/23/92 */ + if (unorderedpath != cheapest + && rel->pruneable) + { + + rel->unorderedpath = (Path *) NULL; + rel->pathlist = lremove(unorderedpath, rel->pathlist); + } + else + { + rel->unorderedpath = (Path *) unorderedpath; + } + + return (cheapest); } /* * merge-joinrels-- - * Given two lists of rel nodes that are already - * pruned, merge them into one pruned rel node list + * Given two lists of rel nodes that are already + * pruned, merge them into one pruned rel node list * * 'rel-list1' and * 'rel-list2' are the rel node lists * * Returns one pruned rel node list */ -List * -merge_joinrels(List *rel_list1, List *rel_list2) +List * +merge_joinrels(List * rel_list1, List * rel_list2) { - List *xrel = NIL; - - foreach(xrel,rel_list1) { - Rel *rel = (Rel*)lfirst(xrel); - rel_list2 = prune_joinrel(rel,rel_list2); - } - return(append(rel_list1, rel_list2)); + List *xrel = NIL; + + foreach(xrel, rel_list1) + { + Rel *rel = (Rel *) lfirst(xrel); + + rel_list2 = prune_joinrel(rel, rel_list2); + } + return (append(rel_list1, rel_list2)); } /* * prune_oldrels-- - * If all the joininfo's in a rel node are inactive, - * that means that this node has been joined into - * other nodes in all possible ways, therefore - * this node can be discarded. If not, it will cause - * extra complexity of the optimizer. + * If all the joininfo's in a rel node are inactive, + * that means that this node has been joined into + * other nodes in all possible ways, therefore + * this node can be discarded. If not, it will cause + * extra complexity of the optimizer. * * old_rels is a list of rel nodes - * + * * Returns a new list of rel nodes */ -List *prune_oldrels(List *old_rels) +List * +prune_oldrels(List * old_rels) { - Rel *rel; - List *joininfo_list, *xjoininfo; - - if(old_rels == NIL) - return(NIL); - - rel = (Rel*)lfirst(old_rels); - joininfo_list = rel->joininfo; - if(joininfo_list == NIL) - return (lcons(rel, prune_oldrels(lnext(old_rels)))); - - foreach(xjoininfo, joininfo_list) { - JInfo *joininfo = (JInfo*)lfirst(xjoininfo); - if(!joininfo->inactive) - return (lcons(rel, prune_oldrels(lnext(old_rels)))); - } - return(prune_oldrels(lnext(old_rels))); + Rel *rel; + List *joininfo_list, + *xjoininfo; + + if (old_rels == NIL) + return (NIL); + + rel = (Rel *) lfirst(old_rels); + joininfo_list = rel->joininfo; + if (joininfo_list == NIL) + return (lcons(rel, prune_oldrels(lnext(old_rels)))); + + foreach(xjoininfo, joininfo_list) + { + JInfo *joininfo = (JInfo *) lfirst(xjoininfo); + + if (!joininfo->inactive) + return (lcons(rel, prune_oldrels(lnext(old_rels)))); + } + return (prune_oldrels(lnext(old_rels))); } diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c index 3e3ee650f94..36135d4a823 100644 --- a/src/backend/optimizer/path/xfunc.c +++ b/src/backend/optimizer/path/xfunc.c @@ -1,21 +1,21 @@ /*------------------------------------------------------------------------- * * xfunc.c-- - * Utility routines to handle expensive function optimization. - * Includes xfunc_trypullup(), which attempts early pullup of predicates - * to allow for maximal pruning. - * + * Utility routines to handle expensive function optimization. + * Includes xfunc_trypullup(), which attempts early pullup of predicates + * to allow for maximal pruning. + * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.3 1997/02/14 04:15:39 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.4 1997/09/07 04:43:50 momjian Exp $ * *------------------------------------------------------------------------- */ -#include <math.h> /* for MAXFLOAT on most systems */ +#include <math.h> /* for MAXFLOAT on most systems */ -#include <values.h> /* for MAXFLOAT on SunOS */ +#include <values.h> /* for MAXFLOAT on SunOS */ #include <string.h> #include "postgres.h" @@ -40,82 +40,96 @@ #include "lib/lispsort.h" #include "access/heapam.h" #include "tcop/dest.h" -#include "storage/buf_internals.h" /* for NBuffers */ -#include "optimizer/tlist.h" /* for get_expr */ +#include "storage/buf_internals.h" /* for NBuffers */ +#include "optimizer/tlist.h" /* for get_expr */ #define ever ; 1 ; /* local funcs */ -static int xfunc_card_unreferenced(Query *queryInfo, - Expr *clause, Relid referenced); */ +static int +xfunc_card_unreferenced(Query * queryInfo, + Expr * clause, Relid referenced); + +*/ /* ** xfunc_trypullup -- -** Preliminary pullup of predicates, to allow for maximal pruning. +** Preliminary pullup of predicates, to allow for maximal pruning. ** Given a relation, check each of its paths and see if you can ** pullup clauses from its inner and outer. */ -void xfunc_trypullup(Rel rel) +void +xfunc_trypullup(Rel rel) { - LispValue y; /* list ptr */ - CInfo maxcinfo; /* The CInfo to pull up, as calculated by - xfunc_shouldpull() */ - JoinPath curpath; /* current path in list */ - int progress; /* has progress been made this time through? */ - int clausetype; - - do { - progress = false; /* no progress yet in this iteration */ - foreach(y, get_pathlist(rel)) { - curpath = (JoinPath)lfirst(y); - - /* - ** for each operand, attempt to pullup predicates until first - ** failure. - */ - for(ever) { - /* No, the following should NOT be '==' !! */ - if (clausetype = - xfunc_shouldpull((Path)get_innerjoinpath(curpath), - curpath, INNER, &maxcinfo)) { - - xfunc_pullup((Path)get_innerjoinpath(curpath), - curpath, maxcinfo, INNER, clausetype); - progress = true; - }else - break; - } - for(ever) { - - /* No, the following should NOT be '==' !! */ - if (clausetype = - xfunc_shouldpull((Path)get_outerjoinpath(curpath), - curpath, OUTER, &maxcinfo)) { - - xfunc_pullup((Path)get_outerjoinpath(curpath), - curpath, maxcinfo, OUTER, clausetype); - progress = true; - }else - break; - } - - /* - ** make sure the unpruneable flag bubbles up, i.e. - ** if anywhere below us in the path pruneable is false, - ** then pruneable should be false here - */ - if (get_pruneable(get_parent(curpath)) && - (!get_pruneable(get_parent - ((Path)get_innerjoinpath(curpath))) || - !get_pruneable(get_parent((Path) - get_outerjoinpath(curpath))))) { - - set_pruneable(get_parent(curpath),false); - progress = true; - } - } - } while(progress); + LispValue y; /* list ptr */ + CInfo maxcinfo; /* The CInfo to pull up, as calculated by + * xfunc_shouldpull() */ + JoinPath curpath; /* current path in list */ + int progress; /* has progress been made this time + * through? */ + int clausetype; + + do + { + progress = false; /* no progress yet in this iteration */ + foreach(y, get_pathlist(rel)) + { + curpath = (JoinPath) lfirst(y); + + /* + * * for each operand, attempt to pullup predicates until + * first * failure. + */ + for (ever) + { + /* No, the following should NOT be '==' !! */ + if (clausetype = + xfunc_shouldpull((Path) get_innerjoinpath(curpath), + curpath, INNER, &maxcinfo)) + { + + xfunc_pullup((Path) get_innerjoinpath(curpath), + curpath, maxcinfo, INNER, clausetype); + progress = true; + } + else + break; + } + for (ever) + { + + /* No, the following should NOT be '==' !! */ + if (clausetype = + xfunc_shouldpull((Path) get_outerjoinpath(curpath), + curpath, OUTER, &maxcinfo)) + { + + xfunc_pullup((Path) get_outerjoinpath(curpath), + curpath, maxcinfo, OUTER, clausetype); + progress = true; + } + else + break; + } + + /* + * * make sure the unpruneable flag bubbles up, i.e. * if + * anywhere below us in the path pruneable is false, * then + * pruneable should be false here + */ + if (get_pruneable(get_parent(curpath)) && + (!get_pruneable(get_parent + ((Path) get_innerjoinpath(curpath))) || + !get_pruneable(get_parent((Path) + get_outerjoinpath(curpath))))) + { + + set_pruneable(get_parent(curpath), false); + progress = true; + } + } + } while (progress); } /* @@ -128,108 +142,123 @@ void xfunc_trypullup(Rel rel) ** we'd better set the unpruneable flag. -- JMH, 11/11/92 ** ** Returns: 0 if nothing left to pullup - ** XFUNC_LOCPRD if a local predicate is to be pulled up - ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up + ** XFUNC_LOCPRD if a local predicate is to be pulled up + ** XFUNC_JOINPRD if a secondary join predicate is to be pulled up */ -int xfunc_shouldpull(Query* queryInfo, - Path childpath, - JoinPath parentpath, - int whichchild, - CInfo *maxcinfopt) /* Out: pointer to clause to pullup */ +int +xfunc_shouldpull(Query * queryInfo, + Path childpath, + JoinPath parentpath, + int whichchild, + CInfo * maxcinfopt) /* Out: pointer to clause to + * pullup */ { - LispValue clauselist, tmplist; /* lists of clauses */ - CInfo maxcinfo; /* clause to pullup */ - LispValue primjoinclause /* primary join clause */ + LispValue clauselist, + tmplist; /* lists of clauses */ + CInfo maxcinfo; /* clause to pullup */ + LispValue primjoinclause /* primary join clause */ = xfunc_primary_join(parentpath); - Cost tmprank, maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ - Cost joinselec = 0; /* selectivity of the join predicate */ - Cost joincost = 0; /* join cost + primjoinclause cost */ - int retval = XFUNC_LOCPRD; - - clauselist = get_locclauseinfo(childpath); - - if (clauselist != LispNil) { - /* find local predicate with maximum rank */ - for (tmplist = clauselist, - maxcinfo = (CInfo) lfirst(tmplist), - maxrank = xfunc_rank(get_clause(maxcinfo)); - tmplist != LispNil; - tmplist = lnext(tmplist)) { - - if ((tmprank = xfunc_rank(get_clause((CInfo)lfirst(tmplist)))) - > maxrank) { - maxcinfo = (CInfo) lfirst(tmplist); - maxrank = tmprank; - } + Cost tmprank, + maxrank = (-1 * MAXFLOAT); /* ranks of clauses */ + Cost joinselec = 0; /* selectivity of the join + * predicate */ + Cost joincost = 0; /* join cost + primjoinclause cost */ + int retval = XFUNC_LOCPRD; + + clauselist = get_locclauseinfo(childpath); + + if (clauselist != LispNil) + { + /* find local predicate with maximum rank */ + for (tmplist = clauselist, + maxcinfo = (CInfo) lfirst(tmplist), + maxrank = xfunc_rank(get_clause(maxcinfo)); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (CInfo) lfirst(tmplist); + maxrank = tmprank; + } + } } - } - - /* - ** If child is a join path, and there are multiple join clauses, - ** see if any join clause has even higher rank than the highest - ** local predicate - */ - if (is_join(childpath) && xfunc_num_join_clauses((JoinPath)childpath) > 1) - for (tmplist = get_pathclauseinfo((JoinPath)childpath); - tmplist != LispNil; - tmplist = lnext(tmplist)) { - - if (tmplist != LispNil && - (tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) - > maxrank) { - maxcinfo = (CInfo) lfirst(tmplist); - maxrank = tmprank; - retval = XFUNC_JOINPRD; - } - } - if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ - return(0); - - /* - ** Pullup over join if clause is higher rank than join, or if - ** join is nested loop and current path is inner child (note that - ** restrictions on the inner of a nested loop don't buy you anything -- - ** you still have to scan the entire inner relation each time). - ** Note that the cost of a secondary join clause is only what's - ** calculated by xfunc_expense(), since the actual joining - ** (i.e. the usual path_cost) is paid for by the primary join clause. - */ - if (primjoinclause != LispNil) { - joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); - joincost = xfunc_join_expense(parentpath, whichchild); - - if (XfuncMode == XFUNC_PULLALL || - (XfuncMode != XFUNC_WAIT && - ((joincost != 0 && - (maxrank = xfunc_rank(get_clause(maxcinfo))) > - ((joinselec - 1.0) / joincost)) - || (joincost == 0 && joinselec < 1) - || (!is_join(childpath) - && (whichchild == INNER) - && IsA(parentpath,JoinPath) - && !IsA(parentpath,HashPath) - && !IsA(parentpath,MergePath))))) { - - *maxcinfopt = maxcinfo; - return(retval); - - }else if (maxrank != -(MAXFLOAT)) { - /* - ** we've left an expensive restriction below a join. Since - ** we may pullup this restriction in predmig.c, we'd best - ** set the Rel of this join to be unpruneable - */ - set_pruneable(get_parent(parentpath), false); - /* and fall through */ + + /* + * * If child is a join path, and there are multiple join clauses, * + * see if any join clause has even higher rank than the highest * + * local predicate + */ + if (is_join(childpath) && xfunc_num_join_clauses((JoinPath) childpath) > 1) + for (tmplist = get_pathclauseinfo((JoinPath) childpath); + tmplist != LispNil; + tmplist = lnext(tmplist)) + { + + if (tmplist != LispNil && + (tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + > maxrank) + { + maxcinfo = (CInfo) lfirst(tmplist); + maxrank = tmprank; + retval = XFUNC_JOINPRD; + } + } + if (maxrank == (-1 * MAXFLOAT)) /* no expensive clauses */ + return (0); + + /* + * * Pullup over join if clause is higher rank than join, or if * join + * is nested loop and current path is inner child (note that * + * restrictions on the inner of a nested loop don't buy you anything + * -- * you still have to scan the entire inner relation each time). * + * Note that the cost of a secondary join clause is only what's * + * calculated by xfunc_expense(), since the actual joining * (i.e. the + * usual path_cost) is paid for by the primary join clause. + */ + if (primjoinclause != LispNil) + { + joinselec = compute_clause_selec(queryInfo, primjoinclause, LispNil); + joincost = xfunc_join_expense(parentpath, whichchild); + + if (XfuncMode == XFUNC_PULLALL || + (XfuncMode != XFUNC_WAIT && + ((joincost != 0 && + (maxrank = xfunc_rank(get_clause(maxcinfo))) > + ((joinselec - 1.0) / joincost)) + || (joincost == 0 && joinselec < 1) + || (!is_join(childpath) + && (whichchild == INNER) + && IsA(parentpath, JoinPath) + && !IsA(parentpath, HashPath) + && !IsA(parentpath, MergePath))))) + { + + *maxcinfopt = maxcinfo; + return (retval); + + } + else if (maxrank != -(MAXFLOAT)) + { + + /* + * * we've left an expensive restriction below a join. Since * + * we may pullup this restriction in predmig.c, we'd best * + * set the Rel of this join to be unpruneable + */ + set_pruneable(get_parent(parentpath), false); + /* and fall through */ + } } - } - return(0); + return (0); } /* ** xfunc_pullup -- - ** move clause from child pathnode to parent pathnode. This operation + ** move clause from child pathnode to parent pathnode. This operation ** makes the child pathnode produce a larger relation than it used to. ** This means that we must construct a new Rel just for the childpath, ** although this Rel will not be added to the list of Rels to be joined up @@ -238,101 +267,111 @@ int xfunc_shouldpull(Query* queryInfo, ** ** Now returns a pointer to the new pulled-up CInfo. -- JMH, 11/18/92 */ -CInfo xfunc_pullup(Query* queryInfo, - Path childpath, - JoinPath parentpath, - CInfo cinfo, /* clause to pull up */ - int whichchild,/* whether child is INNER or OUTER of join */ - int clausetype)/* whether clause to pull is join or local */ +CInfo +xfunc_pullup(Query * queryInfo, + Path childpath, + JoinPath parentpath, + CInfo cinfo, /* clause to pull up */ + int whichchild, /* whether child is INNER or OUTER of join */ + int clausetype) /* whether clause to pull is join or local */ { - Path newkid; - Rel newrel; - Cost pulled_selec; - Cost cost; - CInfo newinfo; - - /* remove clause from childpath */ - newkid = (Path)copyObject((Node)childpath); - if (clausetype == XFUNC_LOCPRD) { - set_locclauseinfo(newkid, - xfunc_LispRemove((LispValue)cinfo, - (List)get_locclauseinfo(newkid))); - }else { - set_pathclauseinfo - ((JoinPath)newkid, - xfunc_LispRemove((LispValue)cinfo, - (List)get_pathclauseinfo((JoinPath)newkid))); - } - - /* - ** give the new child path its own Rel node that reflects the - ** lack of the pulled-up predicate - */ - pulled_selec = compute_clause_selec(queryInfo, - get_clause(cinfo), LispNil); - xfunc_copyrel(get_parent(newkid), &newrel); - set_parent(newkid, newrel); - set_pathlist(newrel, lcons(newkid, NIL)); - set_unorderedpath(newrel, (PathPtr)newkid); - set_cheapestpath(newrel, (PathPtr)newkid); - set_size(newrel, - (Count)((Cost)get_size(get_parent(childpath)) / pulled_selec)); - - /* - ** fix up path cost of newkid. To do this we subtract away all the - ** xfunc_costs of childpath, then recompute the xfunc_costs of newkid - */ - cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); - Assert(cost >= 0); - set_path_cost(newkid, cost); - cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); - set_path_cost(newkid, cost); - - /* - ** We copy the cinfo, since it may appear in other plans, and we're going - ** to munge it. -- JMH, 7/22/92 - */ - newinfo = (CInfo)copyObject((Node)cinfo); - - /* - ** Fix all vars in the clause - ** to point to the right varno and varattno in parentpath - */ - xfunc_fixvars(get_clause(newinfo), newrel, whichchild); - - /* add clause to parentpath, and fix up its cost. */ - set_locclauseinfo(parentpath, - lispCons((LispValue)newinfo, - (LispValue)get_locclauseinfo(parentpath))); - /* put new childpath into the path tree */ - if (whichchild == INNER) { - set_innerjoinpath(parentpath, (pathPtr)newkid); - }else { - set_outerjoinpath(parentpath, (pathPtr)newkid); - } - - /* - ** recompute parentpath cost from scratch -- the cost - ** of the join method has changed - */ - cost = xfunc_total_path_cost(parentpath); - set_path_cost(parentpath, cost); - - return(newinfo); + Path newkid; + Rel newrel; + Cost pulled_selec; + Cost cost; + CInfo newinfo; + + /* remove clause from childpath */ + newkid = (Path) copyObject((Node) childpath); + if (clausetype == XFUNC_LOCPRD) + { + set_locclauseinfo(newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_locclauseinfo(newkid))); + } + else + { + set_pathclauseinfo + ((JoinPath) newkid, + xfunc_LispRemove((LispValue) cinfo, + (List) get_pathclauseinfo((JoinPath) newkid))); + } + + /* + * * give the new child path its own Rel node that reflects the * lack + * of the pulled-up predicate + */ + pulled_selec = compute_clause_selec(queryInfo, + get_clause(cinfo), LispNil); + xfunc_copyrel(get_parent(newkid), &newrel); + set_parent(newkid, newrel); + set_pathlist(newrel, lcons(newkid, NIL)); + set_unorderedpath(newrel, (PathPtr) newkid); + set_cheapestpath(newrel, (PathPtr) newkid); + set_size(newrel, + (Count) ((Cost) get_size(get_parent(childpath)) / pulled_selec)); + + /* + * * fix up path cost of newkid. To do this we subtract away all the * + * xfunc_costs of childpath, then recompute the xfunc_costs of newkid + */ + cost = get_path_cost(newkid) - xfunc_get_path_cost(childpath); + Assert(cost >= 0); + set_path_cost(newkid, cost); + cost = get_path_cost(newkid) + xfunc_get_path_cost(newkid); + set_path_cost(newkid, cost); + + /* + * * We copy the cinfo, since it may appear in other plans, and we're + * going * to munge it. -- JMH, 7/22/92 + */ + newinfo = (CInfo) copyObject((Node) cinfo); + + /* + * * Fix all vars in the clause * to point to the right varno and + * varattno in parentpath + */ + xfunc_fixvars(get_clause(newinfo), newrel, whichchild); + + /* add clause to parentpath, and fix up its cost. */ + set_locclauseinfo(parentpath, + lispCons((LispValue) newinfo, + (LispValue) get_locclauseinfo(parentpath))); + /* put new childpath into the path tree */ + if (whichchild == INNER) + { + set_innerjoinpath(parentpath, (pathPtr) newkid); + } + else + { + set_outerjoinpath(parentpath, (pathPtr) newkid); + } + + /* + * * recompute parentpath cost from scratch -- the cost * of the join + * method has changed + */ + cost = xfunc_total_path_cost(parentpath); + set_path_cost(parentpath, cost); + + return (newinfo); } /* - ** calculate (selectivity-1)/cost. + ** calculate (selectivity-1)/cost. */ -Cost xfunc_rank(Query *queryInfo,LispValue clause) +Cost +xfunc_rank(Query * queryInfo, LispValue clause) { - Cost selec = compute_clause_selec(queryInfo, clause, LispNil); - Cost cost = xfunc_expense(queryInfo,clause); - - if (cost == 0) - if (selec > 1) return(MAXFLOAT); - else return(-(MAXFLOAT)); - return((selec - 1)/cost); + Cost selec = compute_clause_selec(queryInfo, clause, LispNil); + Cost cost = xfunc_expense(queryInfo, clause); + + if (cost == 0) + if (selec > 1) + return (MAXFLOAT); + else + return (-(MAXFLOAT)); + return ((selec - 1) / cost); } /* @@ -340,91 +379,99 @@ Cost xfunc_rank(Query *queryInfo,LispValue clause) ** by the cardinalities of all the base relations of the query that are *not* ** referenced in the clause. */ -Cost xfunc_expense(Query* queryInfo, clause) - LispValue clause; +Cost +xfunc_expense(Query * queryInfo, clause) +LispValue clause; { - Cost cost = xfunc_local_expense(clause); - - if (cost) + Cost cost = xfunc_local_expense(clause); + + if (cost) { - Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); - if (card) - cost /= card; + Count card = xfunc_card_unreferenced(queryInfo, clause, LispNil); + + if (card) + cost /= card; } - - return(cost); + + return (cost); } /* ** xfunc_join_expense -- ** Find global expense of a join clause */ -Cost xfunc_join_expense(Query *queryInfo, JoinPath path, int whichchild) +Cost +xfunc_join_expense(Query * queryInfo, JoinPath path, int whichchild) { - LispValue primjoinclause = xfunc_primary_join(path); - - /* - ** the second argument to xfunc_card_unreferenced reflects all the - ** relations involved in the join clause, i.e. all the relids in the Rel - ** of the join clause - */ - Count card = 0; - Cost cost = xfunc_expense_per_tuple(path, whichchild); - - card = xfunc_card_unreferenced(queryInfo, - primjoinclause, - get_relids(get_parent(path))); - if (primjoinclause) - cost += xfunc_local_expense(primjoinclause); - - if (card) cost /= card; - - return(cost); + LispValue primjoinclause = xfunc_primary_join(path); + + /* + * * the second argument to xfunc_card_unreferenced reflects all the * + * relations involved in the join clause, i.e. all the relids in the + * Rel * of the join clause + */ + Count card = 0; + Cost cost = xfunc_expense_per_tuple(path, whichchild); + + card = xfunc_card_unreferenced(queryInfo, + primjoinclause, + get_relids(get_parent(path))); + if (primjoinclause) + cost += xfunc_local_expense(primjoinclause); + + if (card) + cost /= card; + + return (cost); } /* ** Recursively find the per-tuple expense of a clause. See ** xfunc_func_expense for more discussion. */ -Cost xfunc_local_expense(LispValue clause) +Cost +xfunc_local_expense(LispValue clause) { - Cost cost = 0; /* running expense */ - LispValue tmpclause; - - /* First handle the base case */ - if (IsA(clause,Const) || IsA(clause,Var) || IsA(clause,Param)) - return(0); - /* now other stuff */ - else if (IsA(clause,Iter)) - /* Too low. Should multiply by the expected number of iterations. */ - return(xfunc_local_expense(get_iterexpr((Iter)clause))); - else if (IsA(clause,ArrayRef)) - return(xfunc_local_expense(get_refexpr((ArrayRef)clause))); - else if (fast_is_clause(clause)) - return(xfunc_func_expense((LispValue)get_op(clause), - (LispValue)get_opargs(clause))); - else if (fast_is_funcclause(clause)) - return(xfunc_func_expense((LispValue)get_function(clause), - (LispValue)get_funcargs(clause))); - else if (fast_not_clause(clause)) - return(xfunc_local_expense(lsecond(clause))); - else if (fast_or_clause(clause)) { - /* find cost of evaluating each disjunct */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - cost += xfunc_local_expense(lfirst(tmpclause)); - return(cost); - }else { - elog(WARN, "Clause node of undetermined type"); - return(-1); - } + Cost cost = 0; /* running expense */ + LispValue tmpclause; + + /* First handle the base case */ + if (IsA(clause, Const) || IsA(clause, Var) || IsA(clause, Param)) + return (0); + /* now other stuff */ + else if (IsA(clause, Iter)) + /* Too low. Should multiply by the expected number of iterations. */ + return (xfunc_local_expense(get_iterexpr((Iter) clause))); + else if (IsA(clause, ArrayRef)) + return (xfunc_local_expense(get_refexpr((ArrayRef) clause))); + else if (fast_is_clause(clause)) + return (xfunc_func_expense((LispValue) get_op(clause), + (LispValue) get_opargs(clause))); + else if (fast_is_funcclause(clause)) + return (xfunc_func_expense((LispValue) get_function(clause), + (LispValue) get_funcargs(clause))); + else if (fast_not_clause(clause)) + return (xfunc_local_expense(lsecond(clause))); + else if (fast_or_clause(clause)) + { + /* find cost of evaluating each disjunct */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + cost += xfunc_local_expense(lfirst(tmpclause)); + return (cost); + } + else + { + elog(WARN, "Clause node of undetermined type"); + return (-1); + } } /* ** xfunc_func_expense -- ** given a Func or Oper and its args, find its expense. ** Note: in Stonebraker's SIGMOD '91 paper, he uses a more complicated metric - ** than the one here. We can ignore the expected number of tuples for + ** than the one here. We can ignore the expected number of tuples for ** our calculations; we just need the per-tuple expense. But he also ** proposes components to take into account the costs of accessing disk and ** archive. We didn't adopt that scheme here; eventually the vacuum @@ -434,268 +481,323 @@ Cost xfunc_local_expense(LispValue clause) ** accessing secondary or tertiary storage, since we don't have sufficient ** stats to do it right. */ -Cost xfunc_func_expense(LispValue node, LispValue args) +Cost +xfunc_func_expense(LispValue node, LispValue args) { - HeapTuple tupl; /* the pg_proc tuple for each function */ - Form_pg_proc proc; /* a data structure to hold the pg_proc tuple */ - int width = 0; /* byte width of the field referenced by each clause */ - RegProcedure funcid; /* ID of function associate with node */ - Cost cost = 0; /* running expense */ - LispValue tmpclause; - LispValue operand; /* one operand of an operator */ - - if (IsA(node,Oper)) { - /* don't trust the opid in the Oper node. Use the opno. */ - if (!(funcid = get_opcode(get_opno((Oper)node)))) - elog(WARN, "Oper's function is undefined"); - }else { - funcid = get_funcid((Func)node); - } - - /* look up tuple in cache */ - tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* - ** if it's a Postquel function, its cost is stored in the - ** associated plan. - */ - if (proc->prolang == SQLlanguageId) { - LispValue tmpplan; - List planlist; - - if (IsA(node,Oper) || get_func_planlist((Func)node) == LispNil) { - Oid *argOidVect; /* vector of argtypes */ - char *pq_src; /* text of PQ function */ - int nargs; /* num args to PQ function */ - QueryTreeList *queryTree_list; /* dummy variable */ - - /* - ** plan the function, storing it in the Func node for later - ** use by the executor. - */ - pq_src = (char *) textout(&(proc->prosrc)); - nargs = proc->pronargs; - if (nargs > 0) - argOidVect = proc->proargtypes; - planlist = (List)pg_plan(pq_src, argOidVect, nargs, - &parseTree_list, None); - if (IsA(node,Func)) - set_func_planlist((Func)node, planlist); - - }else {/* plan has been cached inside the Func node already */ - planlist = get_func_planlist((Func)node); + HeapTuple tupl; /* the pg_proc tuple for each function */ + Form_pg_proc proc; /* a data structure to hold the pg_proc + * tuple */ + int width = 0; /* byte width of the field referenced by + * each clause */ + RegProcedure funcid; /* ID of function associate with node */ + Cost cost = 0; /* running expense */ + LispValue tmpclause; + LispValue operand; /* one operand of an operator */ + + if (IsA(node, Oper)) + { + /* don't trust the opid in the Oper node. Use the opno. */ + if (!(funcid = get_opcode(get_opno((Oper) node)))) + elog(WARN, "Oper's function is undefined"); } - - /* - ** Return the sum of the costs of the plans (the PQ function - ** may have many queries in its body). - */ - foreach(tmpplan, planlist) - cost += get_cost((Plan)lfirst(tmpplan)); - return(cost); - }else { /* it's a C function */ + else + { + funcid = get_funcid((Func) node); + } + + /* look up tuple in cache */ + tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + /* - ** find the cost of evaluating the function's arguments - ** and the width of the operands + * * if it's a Postquel function, its cost is stored in the * + * associated plan. */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) { - - if ((operand = lfirst(tmpclause)) != LispNil) { - cost += xfunc_local_expense(operand); - width += xfunc_width(operand); - } + if (proc->prolang == SQLlanguageId) + { + LispValue tmpplan; + List planlist; + + if (IsA(node, Oper) || get_func_planlist((Func) node) == LispNil) + { + Oid *argOidVect; /* vector of argtypes */ + char *pq_src; /* text of PQ function */ + int nargs; /* num args to PQ function */ + QueryTreeList *queryTree_list; /* dummy variable */ + + /* + * * plan the function, storing it in the Func node for later * + * use by the executor. + */ + pq_src = (char *) textout(&(proc->prosrc)); + nargs = proc->pronargs; + if (nargs > 0) + argOidVect = proc->proargtypes; + planlist = (List) pg_plan(pq_src, argOidVect, nargs, + &parseTree_list, None); + if (IsA(node, Func)) + set_func_planlist((Func) node, planlist); + + } + else + { /* plan has been cached inside the Func + * node already */ + planlist = get_func_planlist((Func) node); + } + + /* + * * Return the sum of the costs of the plans (the PQ function * + * may have many queries in its body). + */ + foreach(tmpplan, planlist) + cost += get_cost((Plan) lfirst(tmpplan)); + return (cost); + } + else + { /* it's a C function */ + + /* + * * find the cost of evaluating the function's arguments * and + * the width of the operands + */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + { + + if ((operand = lfirst(tmpclause)) != LispNil) + { + cost += xfunc_local_expense(operand); + width += xfunc_width(operand); + } + } + + /* + * * when stats become available, add in cost of accessing + * secondary * and tertiary storage here. + */ + return (cost + + (Cost) proc->propercall_cpu + + (Cost) proc->properbyte_cpu * (Cost) proc->probyte_pct / 100.00 * + (Cost) width + + /* + * Pct_of_obj_in_mem DISK_COST * proc->probyte_pct/100.00 * width + * Pct_of_obj_on_disk + ARCH_COST * proc->probyte_pct/100.00 * + * width Pct_of_obj_on_arch + */ + ); } - - /* - ** when stats become available, add in cost of accessing secondary - ** and tertiary storage here. - */ - return(cost + - (Cost)proc->propercall_cpu + - (Cost)proc->properbyte_cpu * (Cost)proc->probyte_pct/100.00 * - (Cost)width - /* - * Pct_of_obj_in_mem - DISK_COST * proc->probyte_pct/100.00 * width - * Pct_of_obj_on_disk + - ARCH_COST * proc->probyte_pct/100.00 * width - * Pct_of_obj_on_arch - */ - ); - } } -/* +/* ** xfunc_width -- ** recursively find the width of a expression */ -int xfunc_width(LispValue clause) +int +xfunc_width(LispValue clause) { - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - TypeTupleForm type; /* structure to hold a type tuple */ - int retval = 0; - - if (IsA(clause,Const)) { - /* base case: width is the width of this constant */ - retval = get_constlen((Const) clause); - goto exit; - }else if (IsA(clause,ArrayRef)) { - /* base case: width is width of the refelem within the array */ - retval = get_refelemlength((ArrayRef)clause); - goto exit; - }else if (IsA(clause,Var)) { - /* base case: width is width of this attribute */ - tupl = SearchSysCacheTuple(TYPOID, - PointerGetDatum(get_vartype((Var)clause)), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for type %d", - get_vartype((Var)clause)); - type = (TypeTupleForm) GETSTRUCT(tupl); - if (get_varattno((Var)clause) == 0) { - /* clause is a tuple. Get its width */ - rd = heap_open(type->typrelid); - retval = xfunc_tuple_width(rd); - heap_close(rd); - }else { - /* attribute is a base type */ - retval = type->typlen; + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + TypeTupleForm type; /* structure to hold a type tuple */ + int retval = 0; + + if (IsA(clause, Const)) + { + /* base case: width is the width of this constant */ + retval = get_constlen((Const) clause); + goto exit; } - goto exit; - }else if (IsA(clause,Param)) { - if (typeid_get_relid(get_paramtype((Param)clause))) { - /* Param node returns a tuple. Find its width */ - rd = heap_open(typeid_get_relid(get_paramtype((Param)clause))); - retval = xfunc_tuple_width(rd); - heap_close(rd); - }else if (get_param_tlist((Param)clause) != LispNil) { - /* Param node projects a complex type */ - Assert(length(get_param_tlist((Param)clause)) == 1); /* sanity */ - retval = - xfunc_width((LispValue) - get_expr(lfirst(get_param_tlist((Param)clause)))); - }else { - /* Param node returns a base type */ - retval = tlen(get_id_type(get_paramtype((Param)clause))); + else if (IsA(clause, ArrayRef)) + { + /* base case: width is width of the refelem within the array */ + retval = get_refelemlength((ArrayRef) clause); + goto exit; } - goto exit; - }else if (IsA(clause,Iter)) { - /* - ** An Iter returns a setof things, so return the width of a single - ** thing. - ** Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET FIXED, - ** SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! - ** This whole Iter business is bogus, anyway. - */ - retval = xfunc_width(get_iterexpr((Iter)clause)); - goto exit; - }else if (fast_is_clause(clause)) { - /* - ** get function associated with this Oper, and treat this as - ** a Func - */ - tupl = SearchSysCacheTuple(OPROID, - ObjectIdGetDatum(get_opno((Oper)get_op(clause))), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", - get_opno((Oper)get_op(clause))); - return(xfunc_func_width - ((RegProcedure)(((OperatorTupleForm)(GETSTRUCT(tupl)))->oprcode), - (LispValue)get_opargs(clause))); - }else if (fast_is_funcclause(clause)) { - Func func = (Func)get_function(clause); - if (get_func_tlist(func) != LispNil) { - /* this function has a projection on it. Get the length - of the projected attribute */ - Assert(length(get_func_tlist(func)) == 1); /* sanity */ - retval = - xfunc_width((LispValue) - get_expr(lfirst(get_func_tlist(func)))); - goto exit; - }else { - return(xfunc_func_width((RegProcedure)get_funcid(func), - (LispValue)get_funcargs(clause))); + else if (IsA(clause, Var)) + { + /* base case: width is width of this attribute */ + tupl = SearchSysCacheTuple(TYPOID, + PointerGetDatum(get_vartype((Var) clause)), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for type %d", + get_vartype((Var) clause)); + type = (TypeTupleForm) GETSTRUCT(tupl); + if (get_varattno((Var) clause) == 0) + { + /* clause is a tuple. Get its width */ + rd = heap_open(type->typrelid); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else + { + /* attribute is a base type */ + retval = type->typlen; + } + goto exit; + } + else if (IsA(clause, Param)) + { + if (typeid_get_relid(get_paramtype((Param) clause))) + { + /* Param node returns a tuple. Find its width */ + rd = heap_open(typeid_get_relid(get_paramtype((Param) clause))); + retval = xfunc_tuple_width(rd); + heap_close(rd); + } + else if (get_param_tlist((Param) clause) != LispNil) + { + /* Param node projects a complex type */ + Assert(length(get_param_tlist((Param) clause)) == 1); /* sanity */ + retval = + xfunc_width((LispValue) + get_expr(lfirst(get_param_tlist((Param) clause)))); + } + else + { + /* Param node returns a base type */ + retval = tlen(get_id_type(get_paramtype((Param) clause))); + } + goto exit; + } + else if (IsA(clause, Iter)) + { + + /* + * * An Iter returns a setof things, so return the width of a + * single * thing. * Note: THIS MAY NOT WORK RIGHT WHEN AGGS GET + * FIXED, * SINCE AGG FUNCTIONS CHEW ON THE WHOLE SETOF THINGS!!!! * + * This whole Iter business is bogus, anyway. + */ + retval = xfunc_width(get_iterexpr((Iter) clause)); + goto exit; + } + else if (fast_is_clause(clause)) + { + + /* + * * get function associated with this Oper, and treat this as * a + * Func + */ + tupl = SearchSysCacheTuple(OPROID, + ObjectIdGetDatum(get_opno((Oper) get_op(clause))), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", + get_opno((Oper) get_op(clause))); + return (xfunc_func_width + ((RegProcedure) (((OperatorTupleForm) (GETSTRUCT(tupl)))->oprcode), + (LispValue) get_opargs(clause))); + } + else if (fast_is_funcclause(clause)) + { + Func func = (Func) get_function(clause); + + if (get_func_tlist(func) != LispNil) + { + + /* + * this function has a projection on it. Get the length of + * the projected attribute + */ + Assert(length(get_func_tlist(func)) == 1); /* sanity */ + retval = + xfunc_width((LispValue) + get_expr(lfirst(get_func_tlist(func)))); + goto exit; + } + else + { + return (xfunc_func_width((RegProcedure) get_funcid(func), + (LispValue) get_funcargs(clause))); + } + } + else + { + elog(WARN, "Clause node of undetermined type"); + return (-1); } - }else { - elog(WARN, "Clause node of undetermined type"); - return(-1); - } - - exit: - if (retval == -1) - retval = VARLEN_DEFAULT; - return(retval); + +exit: + if (retval == -1) + retval = VARLEN_DEFAULT; + return (retval); } /* ** xfunc_card_unreferenced: - ** find all relations not referenced in clause, and multiply their - ** cardinalities. Ignore relation of cardinality 0. + ** find all relations not referenced in clause, and multiply their + ** cardinalities. Ignore relation of cardinality 0. ** User may pass in referenced list, if they know it (useful ** for joins). */ -static Count -xfunc_card_unreferenced(Query *queryInfo, - LispValue clause, Relid referenced) +static Count +xfunc_card_unreferenced(Query * queryInfo, + LispValue clause, Relid referenced) { - Relid unreferenced, allrelids = LispNil; - LispValue temp; - - /* find all relids of base relations referenced in query */ - foreach (temp,queryInfo->base_relation_list_) + Relid unreferenced, + allrelids = LispNil; + LispValue temp; + + /* find all relids of base relations referenced in query */ + foreach(temp, queryInfo->base_relation_list_) { - Assert(lnext(get_relids((Rel)lfirst(temp))) == LispNil); - allrelids = lappend(allrelids, - lfirst(get_relids((Rel)lfirst(temp)))); + Assert(lnext(get_relids((Rel) lfirst(temp))) == LispNil); + allrelids = lappend(allrelids, + lfirst(get_relids((Rel) lfirst(temp)))); } - - /* find all relids referenced in query but not in clause */ - if (!referenced) - referenced = xfunc_find_references(clause); - unreferenced = set_difference(allrelids, referenced); - - return(xfunc_card_product(unreferenced)); + + /* find all relids referenced in query but not in clause */ + if (!referenced) + referenced = xfunc_find_references(clause); + unreferenced = set_difference(allrelids, referenced); + + return (xfunc_card_product(unreferenced)); } /* - ** xfunc_card_product + ** xfunc_card_product ** multiple together cardinalities of a list relations. */ -Count xfunc_card_product(Query *queryInfo, Relid relids) +Count +xfunc_card_product(Query * queryInfo, Relid relids) { - LispValue cinfonode; - LispValue temp; - Rel currel; - Cost tuples; - Count retval = 0; - - foreach(temp,relids) { - currel = get_rel(lfirst(temp)); - tuples = get_tuples(currel); - - if (tuples) { /* not of cardinality 0 */ - /* factor in the selectivity of all zero-cost clauses */ - foreach (cinfonode, get_clauseinfo(currel)) { - if (!xfunc_expense(queryInfo,get_clause((CInfo)lfirst(cinfonode)))) - tuples *= - compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(cinfonode)), - LispNil); - } - - if (retval == 0) retval = tuples; - else retval *= tuples; + LispValue cinfonode; + LispValue temp; + Rel currel; + Cost tuples; + Count retval = 0; + + foreach(temp, relids) + { + currel = get_rel(lfirst(temp)); + tuples = get_tuples(currel); + + if (tuples) + { /* not of cardinality 0 */ + /* factor in the selectivity of all zero-cost clauses */ + foreach(cinfonode, get_clauseinfo(currel)) + { + if (!xfunc_expense(queryInfo, get_clause((CInfo) lfirst(cinfonode)))) + tuples *= + compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(cinfonode)), + LispNil); + } + + if (retval == 0) + retval = tuples; + else + retval *= tuples; + } } - } - if (retval == 0) retval = 1; /* saves caller from dividing by zero */ - return(retval); + if (retval == 0) + retval = 1; /* saves caller from dividing by zero */ + return (retval); } @@ -703,48 +805,60 @@ Count xfunc_card_product(Query *queryInfo, Relid relids) ** xfunc_find_references: ** Traverse a clause and find all relids referenced in the clause. */ -List xfunc_find_references(LispValue clause) +List +xfunc_find_references(LispValue clause) { - List retval = (List)LispNil; - LispValue tmpclause; - - /* Base cases */ - if (IsA(clause,Var)) - return(lispCons(lfirst(get_varid((Var)clause)), LispNil)); - else if (IsA(clause,Const) || IsA(clause,Param)) - return((List)LispNil); - - /* recursion */ - else if (IsA(clause,Iter)) - /* Too low. Should multiply by the expected number of iterations. maybe */ - return(xfunc_find_references(get_iterexpr((Iter)clause))); - else if (IsA(clause,ArrayRef)) - return(xfunc_find_references(get_refexpr((ArrayRef)clause))); - else if (fast_is_clause(clause)) { - /* string together result of all operands of Oper */ - for (tmpclause = (LispValue)get_opargs(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else if (fast_is_funcclause(clause)) { - /* string together result of all args of Func */ - for (tmpclause = (LispValue)get_funcargs(clause); - tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else if (fast_not_clause(clause)) - return(xfunc_find_references(lsecond(clause))); - else if (fast_or_clause(clause)) { - /* string together result of all operands of OR */ - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); - return(retval); - }else { - elog(WARN, "Clause node of undetermined type"); - return((List)LispNil); - } + List retval = (List) LispNil; + LispValue tmpclause; + + /* Base cases */ + if (IsA(clause, Var)) + return (lispCons(lfirst(get_varid((Var) clause)), LispNil)); + else if (IsA(clause, Const) || IsA(clause, Param)) + return ((List) LispNil); + + /* recursion */ + else if (IsA(clause, Iter)) + + /* + * Too low. Should multiply by the expected number of iterations. + * maybe + */ + return (xfunc_find_references(get_iterexpr((Iter) clause))); + else if (IsA(clause, ArrayRef)) + return (xfunc_find_references(get_refexpr((ArrayRef) clause))); + else if (fast_is_clause(clause)) + { + /* string together result of all operands of Oper */ + for (tmpclause = (LispValue) get_opargs(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else if (fast_is_funcclause(clause)) + { + /* string together result of all args of Func */ + for (tmpclause = (LispValue) get_funcargs(clause); + tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else if (fast_not_clause(clause)) + return (xfunc_find_references(lsecond(clause))); + else if (fast_or_clause(clause)) + { + /* string together result of all operands of OR */ + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval = nconc(retval, xfunc_find_references(lfirst(tmpclause))); + return (retval); + } + else + { + elog(WARN, "Clause node of undetermined type"); + return ((List) LispNil); + } } /* @@ -753,212 +867,219 @@ List xfunc_find_references(LispValue clause) ** min rank Hash or Merge clause, while for Nested Loop it's the ** min rank pathclause */ -LispValue xfunc_primary_join(JoinPath pathnode) +LispValue +xfunc_primary_join(JoinPath pathnode) { - LispValue joinclauselist = get_pathclauseinfo(pathnode); - CInfo mincinfo; - LispValue tmplist; - LispValue minclause = LispNil; - Cost minrank, tmprank; - - if (IsA(pathnode,MergePath)) + LispValue joinclauselist = get_pathclauseinfo(pathnode); + CInfo mincinfo; + LispValue tmplist; + LispValue minclause = LispNil; + Cost minrank, + tmprank; + + if (IsA(pathnode, MergePath)) { - for(tmplist = get_path_mergeclauses((MergePath)pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return(minclause); + for (tmplist = get_path_mergeclauses((MergePath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return (minclause); } - else if (IsA(pathnode,HashPath)) + else if (IsA(pathnode, HashPath)) { - for(tmplist = get_path_hashclauses((HashPath)pathnode), - minclause = lfirst(tmplist), - minrank = xfunc_rank(minclause); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(lfirst(tmplist))) - < minrank) - { - minrank = tmprank; - minclause = lfirst(tmplist); - } - return(minclause); + for (tmplist = get_path_hashclauses((HashPath) pathnode), + minclause = lfirst(tmplist), + minrank = xfunc_rank(minclause); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(lfirst(tmplist))) + < minrank) + { + minrank = tmprank; + minclause = lfirst(tmplist); + } + return (minclause); } - - /* if we drop through, it's nested loop join */ - if (joinclauselist == LispNil) - return(LispNil); - - for(tmplist = joinclauselist, mincinfo = (CInfo) lfirst(joinclauselist), - minrank = xfunc_rank(get_clause((CInfo) lfirst(tmplist))); - tmplist != LispNil; - tmplist = lnext(tmplist)) - if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) - < minrank) - { - minrank = tmprank; - mincinfo = (CInfo) lfirst(tmplist); - } - return((LispValue)get_clause(mincinfo)); + + /* if we drop through, it's nested loop join */ + if (joinclauselist == LispNil) + return (LispNil); + + for (tmplist = joinclauselist, mincinfo = (CInfo) lfirst(joinclauselist), + minrank = xfunc_rank(get_clause((CInfo) lfirst(tmplist))); + tmplist != LispNil; + tmplist = lnext(tmplist)) + if ((tmprank = xfunc_rank(get_clause((CInfo) lfirst(tmplist)))) + < minrank) + { + minrank = tmprank; + mincinfo = (CInfo) lfirst(tmplist); + } + return ((LispValue) get_clause(mincinfo)); } /* ** xfunc_get_path_cost ** get the expensive function costs of the path */ -Cost xfunc_get_path_cost(Query *queryInfo, Path pathnode) +Cost +xfunc_get_path_cost(Query * queryInfo, Path pathnode) { - Cost cost = 0; - LispValue tmplist; - Cost selec = 1.0; - - /* - ** first add in the expensive local function costs. - ** We ensure that the clauses are sorted by rank, so that we - ** know (via selectivities) the number of tuples that will be checked - ** by each function. If we're not doing any optimization of expensive - ** functions, we don't sort. - */ - if (XfuncMode != XFUNC_OFF) - set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode), - xfunc_cinfo_compare)); - for(tmplist = get_locclauseinfo(pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + Cost cost = 0; + LispValue tmplist; + Cost selec = 1.0; + + /* + * * first add in the expensive local function costs. * We ensure that + * the clauses are sorted by rank, so that we * know (via + * selectivities) the number of tuples that will be checked * by each + * function. If we're not doing any optimization of expensive * + * functions, we don't sort. + */ + if (XfuncMode != XFUNC_OFF) + set_locclauseinfo(pathnode, lisp_qsort(get_locclauseinfo(pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_locclauseinfo(pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist))) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(tmplist)), - LispNil); + cost += (Cost) (xfunc_local_expense(get_clause((CInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(tmplist)), + LispNil); } - - /* - ** Now add in any node-specific expensive function costs. - ** Again, we must ensure that the clauses are sorted by rank. - */ - if (IsA(pathnode,JoinPath)) + + /* + * * Now add in any node-specific expensive function costs. * Again, + * we must ensure that the clauses are sorted by rank. + */ + if (IsA(pathnode, JoinPath)) { - if (XfuncMode != XFUNC_OFF) - set_pathclauseinfo((JoinPath)pathnode, lisp_qsort - (get_pathclauseinfo((JoinPath)pathnode), - xfunc_cinfo_compare)); - for(tmplist = get_pathclauseinfo((JoinPath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_pathclauseinfo((JoinPath) pathnode, lisp_qsort + (get_pathclauseinfo((JoinPath) pathnode), + xfunc_cinfo_compare)); + for (tmplist = get_pathclauseinfo((JoinPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(get_clause((CInfo)lfirst(tmplist))) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - get_clause((CInfo)lfirst(tmplist)), - LispNil); + cost += (Cost) (xfunc_local_expense(get_clause((CInfo) lfirst(tmplist))) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + get_clause((CInfo) lfirst(tmplist)), + LispNil); } } - if (IsA(pathnode,HashPath)) + if (IsA(pathnode, HashPath)) { - if (XfuncMode != XFUNC_OFF) - set_path_hashclauses - ((HashPath)pathnode, - lisp_qsort(get_path_hashclauses((HashPath)pathnode), - xfunc_clause_compare)); - for(tmplist = get_path_hashclauses((HashPath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_path_hashclauses + ((HashPath) pathnode, + lisp_qsort(get_path_hashclauses((HashPath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_hashclauses((HashPath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(lfirst(tmplist)) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); } } - if (IsA(pathnode,MergePath)) + if (IsA(pathnode, MergePath)) { - if (XfuncMode != XFUNC_OFF) - set_path_mergeclauses - ((MergePath)pathnode, - lisp_qsort(get_path_mergeclauses((MergePath)pathnode), - xfunc_clause_compare)); - for(tmplist = get_path_mergeclauses((MergePath)pathnode), selec = 1.0; - tmplist != LispNil; - tmplist = lnext(tmplist)) + if (XfuncMode != XFUNC_OFF) + set_path_mergeclauses + ((MergePath) pathnode, + lisp_qsort(get_path_mergeclauses((MergePath) pathnode), + xfunc_clause_compare)); + for (tmplist = get_path_mergeclauses((MergePath) pathnode), selec = 1.0; + tmplist != LispNil; + tmplist = lnext(tmplist)) { - cost += (Cost)(xfunc_local_expense(lfirst(tmplist)) - * (Cost)get_tuples(get_parent(pathnode)) * selec); - selec *= compute_clause_selec(queryInfo, - lfirst(tmplist), LispNil); + cost += (Cost) (xfunc_local_expense(lfirst(tmplist)) + * (Cost) get_tuples(get_parent(pathnode)) * selec); + selec *= compute_clause_selec(queryInfo, + lfirst(tmplist), LispNil); } } - Assert(cost >= 0); - return(cost); + Assert(cost >= 0); + return (cost); } /* - ** Recalculate the cost of a path node. This includes the basic cost of the + ** Recalculate the cost of a path node. This includes the basic cost of the ** node, as well as the cost of its expensive functions. ** We need to do this to the parent after pulling a clause from a child into a ** parent. Thus we should only be calling this function on JoinPaths. */ -Cost xfunc_total_path_cost(JoinPath pathnode) +Cost +xfunc_total_path_cost(JoinPath pathnode) { - Cost cost = xfunc_get_path_cost((Path)pathnode); - - Assert(IsA(pathnode,JoinPath)); - if (IsA(pathnode,MergePath)) + Cost cost = xfunc_get_path_cost((Path) pathnode); + + Assert(IsA(pathnode, JoinPath)); + if (IsA(pathnode, MergePath)) { - MergePath mrgnode = (MergePath)pathnode; - cost += cost_mergesort(get_path_cost((Path)get_outerjoinpath(mrgnode)), - get_path_cost((Path)get_innerjoinpath(mrgnode)), - get_outersortkeys(mrgnode), - get_innersortkeys(mrgnode), - get_tuples(get_parent((Path)get_outerjoinpath - (mrgnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (mrgnode))), - get_width(get_parent((Path)get_outerjoinpath - (mrgnode))), - get_width(get_parent((Path)get_innerjoinpath - (mrgnode)))); - Assert(cost >= 0); - return(cost); + MergePath mrgnode = (MergePath) pathnode; + + cost += cost_mergesort(get_path_cost((Path) get_outerjoinpath(mrgnode)), + get_path_cost((Path) get_innerjoinpath(mrgnode)), + get_outersortkeys(mrgnode), + get_innersortkeys(mrgnode), + get_tuples(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_outerjoinpath + (mrgnode))), + get_width(get_parent((Path) get_innerjoinpath + (mrgnode)))); + Assert(cost >= 0); + return (cost); } - else if (IsA(pathnode,HashPath)) + else if (IsA(pathnode, HashPath)) { - HashPath hashnode = (HashPath)pathnode; - cost += cost_hashjoin(get_path_cost((Path)get_outerjoinpath(hashnode)), - get_path_cost((Path)get_innerjoinpath(hashnode)), - get_outerhashkeys(hashnode), - get_innerhashkeys(hashnode), - get_tuples(get_parent((Path)get_outerjoinpath - (hashnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (hashnode))), - get_width(get_parent((Path)get_outerjoinpath - (hashnode))), - get_width(get_parent((Path)get_innerjoinpath - (hashnode)))); - Assert (cost >= 0); - return(cost); + HashPath hashnode = (HashPath) pathnode; + + cost += cost_hashjoin(get_path_cost((Path) get_outerjoinpath(hashnode)), + get_path_cost((Path) get_innerjoinpath(hashnode)), + get_outerhashkeys(hashnode), + get_innerhashkeys(hashnode), + get_tuples(get_parent((Path) get_outerjoinpath + (hashnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (hashnode))), + get_width(get_parent((Path) get_outerjoinpath + (hashnode))), + get_width(get_parent((Path) get_innerjoinpath + (hashnode)))); + Assert(cost >= 0); + return (cost); } - else /* Nested Loop Join */ + else +/* Nested Loop Join */ { - cost += cost_nestloop(get_path_cost((Path)get_outerjoinpath(pathnode)), - get_path_cost((Path)get_innerjoinpath(pathnode)), - get_tuples(get_parent((Path)get_outerjoinpath - (pathnode))), - get_tuples(get_parent((Path)get_innerjoinpath - (pathnode))), - get_pages(get_parent((Path)get_outerjoinpath - (pathnode))), - IsA(get_innerjoinpath(pathnode),IndexPath)); - Assert(cost >= 0); - return(cost); + cost += cost_nestloop(get_path_cost((Path) get_outerjoinpath(pathnode)), + get_path_cost((Path) get_innerjoinpath(pathnode)), + get_tuples(get_parent((Path) get_outerjoinpath + (pathnode))), + get_tuples(get_parent((Path) get_innerjoinpath + (pathnode))), + get_pages(get_parent((Path) get_outerjoinpath + (pathnode))), + IsA(get_innerjoinpath(pathnode), IndexPath)); + Assert(cost >= 0); + return (cost); } } @@ -967,7 +1088,7 @@ Cost xfunc_total_path_cost(JoinPath pathnode) ** xfunc_expense_per_tuple -- ** return the expense of the join *per-tuple* of the input relation. ** The cost model here is that a join costs - ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n + ** k*card(outer)*card(inner) + l*card(outer) + m*card(inner) + n ** ** We treat the l and m terms by considering them to be like restrictions ** constrained to be right under the join. Thus the cost per inner and @@ -975,138 +1096,146 @@ Cost xfunc_total_path_cost(JoinPath pathnode) ** ** The cost per tuple of outer is k + l/referenced(inner). Cost per tuple ** of inner is k + m/referenced(outer). - ** The constants k, l, m and n depend on the join method. Measures here are + ** The constants k, l, m and n depend on the join method. Measures here are ** based on the costs in costsize.c, with fudging for HashJoin and Sorts to ** make it fit our model (the 'q' in HashJoin results in a ** card(outer)/card(inner) term, and sorting results in a log term. - + */ -Cost xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) +Cost +xfunc_expense_per_tuple(JoinPath joinnode, int whichchild) { - Rel outerrel = get_parent((Path)get_outerjoinpath(joinnode)); - Rel innerrel = get_parent((Path)get_innerjoinpath(joinnode)); - Count outerwidth = get_width(outerrel); - Count outers_per_page = ceil(BLCKSZ/(outerwidth + sizeof(HeapTupleData))); - - if (IsA(joinnode,HashPath)) + Rel outerrel = get_parent((Path) get_outerjoinpath(joinnode)); + Rel innerrel = get_parent((Path) get_innerjoinpath(joinnode)); + Count outerwidth = get_width(outerrel); + Count outers_per_page = ceil(BLCKSZ / (outerwidth + sizeof(HeapTupleData))); + + if (IsA(joinnode, HashPath)) { - if (whichchild == INNER) - return((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers); - else - return(((1 + _CPU_PAGE_WEIGHT_)*outers_per_page/NBuffers) - + _CPU_PAGE_WEIGHT_ - / xfunc_card_product(get_relids(innerrel))); + if (whichchild == INNER) + return ((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers); + else + return (((1 + _CPU_PAGE_WEIGHT_) * outers_per_page / NBuffers) + + _CPU_PAGE_WEIGHT_ + / xfunc_card_product(get_relids(innerrel))); } - else if (IsA(joinnode,MergePath)) + else if (IsA(joinnode, MergePath)) { - /* assumes sort exists, and costs one (I/O + CPU) per tuple */ - if (whichchild == INNER) - return((2*_CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(outerrel))); - else - return((2*_CPU_PAGE_WEIGHT_ + 1) - / xfunc_card_product(get_relids(innerrel))); + /* assumes sort exists, and costs one (I/O + CPU) per tuple */ + if (whichchild == INNER) + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(outerrel))); + else + return ((2 * _CPU_PAGE_WEIGHT_ + 1) + / xfunc_card_product(get_relids(innerrel))); } - else /* nestloop */ + else +/* nestloop */ { - Assert(IsA(joinnode,JoinPath)); - return(_CPU_PAGE_WEIGHT_); + Assert(IsA(joinnode, JoinPath)); + return (_CPU_PAGE_WEIGHT_); } } /* ** xfunc_fixvars -- - ** After pulling up a clause, we must walk its expression tree, fixing Var + ** After pulling up a clause, we must walk its expression tree, fixing Var ** nodes to point to the correct varno (either INNER or OUTER, depending - ** on which child the clause was pulled from), and the right varattno in the + ** on which child the clause was pulled from), and the right varattno in the ** target list of the child's former relation. If the target list of the ** child Rel does not contain the attribute we need, we add it. */ -void xfunc_fixvars(LispValue clause, /* clause being pulled up */ - Rel rel, /* rel it's being pulled from */ - int varno) /* whether rel is INNER or OUTER of join */ +void +xfunc_fixvars(LispValue clause, /* clause being pulled up */ + Rel rel, /* rel it's being pulled from */ + int varno) /* whether rel is INNER or OUTER of join */ { - LispValue tmpclause; /* temporary variable */ - TargetEntry *tle; /* tlist member corresponding to var */ - - - if (IsA(clause,Const) || IsA(clause,Param)) return; - else if (IsA(clause,Var)) + LispValue tmpclause; /* temporary variable */ + TargetEntry *tle; /* tlist member corresponding to var */ + + + if (IsA(clause, Const) || IsA(clause, Param)) + return; + else if (IsA(clause, Var)) { - /* here's the meat */ - tle = tlistentry_member((Var)clause, get_targetlist(rel)); - if (tle == LispNil) + /* here's the meat */ + tle = tlistentry_member((Var) clause, get_targetlist(rel)); + if (tle == LispNil) { - /* - ** The attribute we need is not in the target list, - ** so we have to add it. - ** - */ - add_tl_element(rel, (Var)clause); - tle = tlistentry_member((Var)clause, get_targetlist(rel)); + + /* + * * The attribute we need is not in the target list, * so we + * have to add it. * + * + */ + add_tl_element(rel, (Var) clause); + tle = tlistentry_member((Var) clause, get_targetlist(rel)); } - set_varno(((Var)clause), varno); - set_varattno(((Var)clause), get_resno(get_resdom(get_entry(tle)))); + set_varno(((Var) clause), varno); + set_varattno(((Var) clause), get_resno(get_resdom(get_entry(tle)))); } - else if (IsA(clause,Iter)) - xfunc_fixvars(get_iterexpr((Iter)clause), rel, varno); - else if (fast_is_clause(clause)) + else if (IsA(clause, Iter)) + xfunc_fixvars(get_iterexpr((Iter) clause), rel, varno); + else if (fast_is_clause(clause)) { - xfunc_fixvars(lfirst(lnext(clause)), rel, varno); - xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); + xfunc_fixvars(lfirst(lnext(clause)), rel, varno); + xfunc_fixvars(lfirst(lnext(lnext(clause))), rel, varno); } - else if (fast_is_funcclause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else if (fast_not_clause(clause)) - xfunc_fixvars(lsecond(clause), rel, varno); - else if (fast_or_clause(clause)) - for (tmpclause = lnext(clause); tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - xfunc_fixvars(lfirst(tmpclause), rel, varno); - else + else if (fast_is_funcclause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else if (fast_not_clause(clause)) + xfunc_fixvars(lsecond(clause), rel, varno); + else if (fast_or_clause(clause)) + for (tmpclause = lnext(clause); tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + xfunc_fixvars(lfirst(tmpclause), rel, varno); + else { - elog(WARN, "Clause node of undetermined type"); + elog(WARN, "Clause node of undetermined type"); } } /* ** Comparison function for lisp_qsort() on a list of CInfo's. - ** arg1 and arg2 should really be of type (CInfo *). + ** arg1 and arg2 should really be of type (CInfo *). */ -int xfunc_cinfo_compare(void *arg1, void *arg2) +int +xfunc_cinfo_compare(void *arg1, void *arg2) { - CInfo info1 = *(CInfo *) arg1; - CInfo info2 = *(CInfo *) arg2; - - LispValue clause1 = (LispValue) get_clause(info1), - clause2 = (LispValue) get_clause(info2); - - return(xfunc_clause_compare((void *) &clause1, (void *) &clause2)); + CInfo info1 = *(CInfo *) arg1; + CInfo info2 = *(CInfo *) arg2; + + LispValue clause1 = (LispValue) get_clause(info1), + clause2 = (LispValue) get_clause(info2); + + return (xfunc_clause_compare((void *) &clause1, (void *) &clause2)); } /* - ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two + ** xfunc_clause_compare: comparison function for lisp_qsort() that compares two ** clauses based on expense/(1 - selectivity) ** arg1 and arg2 are really pointers to clauses. */ -int xfunc_clause_compare(void *arg1, void *arg2) +int +xfunc_clause_compare(void *arg1, void *arg2) { - LispValue clause1 = *(LispValue *) arg1; - LispValue clause2 = *(LispValue *) arg2; - Cost rank1, /* total xfunc rank of clause1 */ - rank2; /* total xfunc rank of clause2 */ - - rank1 = xfunc_rank(clause1); - rank2 = xfunc_rank(clause2); - - if ( rank1 < rank2) - return(-1); - else if (rank1 == rank2) - return(0); - else return(1); + LispValue clause1 = *(LispValue *) arg1; + LispValue clause2 = *(LispValue *) arg2; + Cost rank1, /* total xfunc rank of clause1 */ + rank2; /* total xfunc rank of clause2 */ + + rank1 = xfunc_rank(clause1); + rank2 = xfunc_rank(clause2); + + if (rank1 < rank2) + return (-1); + else if (rank1 == rank2) + return (0); + else + return (1); } /* @@ -1115,58 +1244,62 @@ int xfunc_clause_compare(void *arg1, void *arg2) ** (this assumes the predicates have been converted to Conjunctive NF) ** Modifies the clause list! */ -void xfunc_disjunct_sort(LispValue clause_list) +void +xfunc_disjunct_sort(LispValue clause_list) { - LispValue temp; - - foreach(temp, clause_list) - if(or_clause(lfirst(temp))) - lnext(lfirst(temp)) = - lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); + LispValue temp; + + foreach(temp, clause_list) + if (or_clause(lfirst(temp))) + lnext(lfirst(temp)) = + lisp_qsort(lnext(lfirst(temp)), xfunc_disjunct_compare); } /* - ** xfunc_disjunct_compare: comparison function for qsort() that compares two + ** xfunc_disjunct_compare: comparison function for qsort() that compares two ** disjuncts based on cost/selec. ** arg1 and arg2 are really pointers to disjuncts */ -int xfunc_disjunct_compare(Query* queryInfo, void *arg1, void *arg2) +int +xfunc_disjunct_compare(Query * queryInfo, void *arg1, void *arg2) { - LispValue disjunct1 = *(LispValue *) arg1; - LispValue disjunct2 = *(LispValue *) arg2; - Cost cost1, /* total cost of disjunct1 */ - cost2, /* total cost of disjunct2 */ - selec1, - selec2; - Cost rank1, rank2; - - cost1 = xfunc_expense(queryInfo, disjunct1); - cost2 = xfunc_expense(queryInfo, disjunct2); - selec1 = compute_clause_selec(queryInfo, - disjunct1, LispNil); - selec2 = compute_clause_selec(queryInfo, - disjunct2, LispNil); - - if (selec1 == 0) - rank1 = MAXFLOAT; - else if (cost1 == 0) - rank1 = 0; - else - rank1 = cost1/selec1; - - if (selec2 == 0) - rank2 = MAXFLOAT; - else if (cost2 == 0) - rank2 = 0; - else - rank2 = cost2/selec2; - - if ( rank1 < rank2) - return(-1); - else if (rank1 == rank2) - return(0); - else return(1); + LispValue disjunct1 = *(LispValue *) arg1; + LispValue disjunct2 = *(LispValue *) arg2; + Cost cost1, /* total cost of disjunct1 */ + cost2, /* total cost of disjunct2 */ + selec1, + selec2; + Cost rank1, + rank2; + + cost1 = xfunc_expense(queryInfo, disjunct1); + cost2 = xfunc_expense(queryInfo, disjunct2); + selec1 = compute_clause_selec(queryInfo, + disjunct1, LispNil); + selec2 = compute_clause_selec(queryInfo, + disjunct2, LispNil); + + if (selec1 == 0) + rank1 = MAXFLOAT; + else if (cost1 == 0) + rank1 = 0; + else + rank1 = cost1 / selec1; + + if (selec2 == 0) + rank2 = MAXFLOAT; + else if (cost2 == 0) + rank2 = 0; + else + rank2 = cost2 / selec2; + + if (rank1 < rank2) + return (-1); + else if (rank1 == rank2) + return (0); + else + return (1); } /* ------------------------ UTILITY FUNCTIONS ------------------------------- */ @@ -1174,182 +1307,197 @@ int xfunc_disjunct_compare(Query* queryInfo, void *arg1, void *arg2) ** xfunc_func_width -- ** Given a function OID and operands, find the width of the return value. */ -int xfunc_func_width(RegProcedure funcid, LispValue args) +int +xfunc_func_width(RegProcedure funcid, LispValue args) { - Relation rd; /* Relation Descriptor */ - HeapTuple tupl; /* structure to hold a cached tuple */ - Form_pg_proc proc; /* structure to hold the pg_proc tuple */ - TypeTupleForm type; /* structure to hold the pg_type tuple */ - LispValue tmpclause; - int retval; - - /* lookup function and find its return type */ - Assert(RegProcedureIsValid(funcid)); - tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for procedure %d", funcid); - proc = (Form_pg_proc) GETSTRUCT(tupl); - - /* if function returns a tuple, get the width of that */ - if (typeid_get_relid(proc->prorettype)) + Relation rd; /* Relation Descriptor */ + HeapTuple tupl; /* structure to hold a cached tuple */ + Form_pg_proc proc; /* structure to hold the pg_proc tuple */ + TypeTupleForm type; /* structure to hold the pg_type tuple */ + LispValue tmpclause; + int retval; + + /* lookup function and find its return type */ + Assert(RegProcedureIsValid(funcid)); + tupl = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for procedure %d", funcid); + proc = (Form_pg_proc) GETSTRUCT(tupl); + + /* if function returns a tuple, get the width of that */ + if (typeid_get_relid(proc->prorettype)) { - rd = heap_open(typeid_get_relid(proc->prorettype)); - retval = xfunc_tuple_width(rd); - heap_close(rd); - goto exit; + rd = heap_open(typeid_get_relid(proc->prorettype)); + retval = xfunc_tuple_width(rd); + heap_close(rd); + goto exit; } - else /* function returns a base type */ + else +/* function returns a base type */ { - tupl = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(proc->prorettype), - 0,0,0); - if (!HeapTupleIsValid(tupl)) - elog(WARN, "Cache lookup failed for type %d", proc->prorettype); - type = (TypeTupleForm) GETSTRUCT(tupl); - /* if the type length is known, return that */ - if (type->typlen != -1) + tupl = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(proc->prorettype), + 0, 0, 0); + if (!HeapTupleIsValid(tupl)) + elog(WARN, "Cache lookup failed for type %d", proc->prorettype); + type = (TypeTupleForm) GETSTRUCT(tupl); + /* if the type length is known, return that */ + if (type->typlen != -1) { - retval = type->typlen; - goto exit; + retval = type->typlen; + goto exit; } - else /* estimate the return size */ + else +/* estimate the return size */ { - /* find width of the function's arguments */ - for (tmpclause = args; tmpclause != LispNil; - tmpclause = lnext(tmpclause)) - retval += xfunc_width(lfirst(tmpclause)); - /* multiply by outin_ratio */ - retval = (int)(proc->prooutin_ratio/100.0 * retval); - goto exit; + /* find width of the function's arguments */ + for (tmpclause = args; tmpclause != LispNil; + tmpclause = lnext(tmpclause)) + retval += xfunc_width(lfirst(tmpclause)); + /* multiply by outin_ratio */ + retval = (int) (proc->prooutin_ratio / 100.0 * retval); + goto exit; } } - exit: - return(retval); +exit: + return (retval); } /* ** xfunc_tuple_width -- - ** Return the sum of the lengths of all the attributes of a given relation + ** Return the sum of the lengths of all the attributes of a given relation */ -int xfunc_tuple_width(Relation rd) +int +xfunc_tuple_width(Relation rd) { - int i; - int retval = 0; - TupleDesc tdesc = RelationGetTupleDescriptor(rd); - - for (i = 0; i < tdesc->natts; i++) + int i; + int retval = 0; + TupleDesc tdesc = RelationGetTupleDescriptor(rd); + + for (i = 0; i < tdesc->natts; i++) { - if (tdesc->attrs[i]->attlen != -1) - retval += tdesc->attrs[i]->attlen; - else retval += VARLEN_DEFAULT; + if (tdesc->attrs[i]->attlen != -1) + retval += tdesc->attrs[i]->attlen; + else + retval += VARLEN_DEFAULT; } - - return(retval); + + return (retval); } /* ** xfunc_num_join_clauses -- ** Find the number of join clauses associated with this join path */ -int xfunc_num_join_clauses(JoinPath path) +int +xfunc_num_join_clauses(JoinPath path) { - int num = length(get_pathclauseinfo(path)); - if (IsA(path,MergePath)) - return(num + length(get_path_mergeclauses((MergePath)path))); - else if (IsA(path,HashPath)) - return(num + length(get_path_hashclauses((HashPath)path))); - else return(num); + int num = length(get_pathclauseinfo(path)); + + if (IsA(path, MergePath)) + return (num + length(get_path_mergeclauses((MergePath) path))); + else if (IsA(path, HashPath)) + return (num + length(get_path_hashclauses((HashPath) path))); + else + return (num); } /* ** xfunc_LispRemove -- ** Just like LispRemove, but it whines if the item to be removed ain't there */ -LispValue xfunc_LispRemove(LispValue foo, List bar) +LispValue +xfunc_LispRemove(LispValue foo, List bar) { - LispValue temp = LispNil; - LispValue result = LispNil; - int sanity = false; - - for (temp = bar; !null(temp); temp = lnext(temp)) - if (! equal((Node)(foo),(Node)(lfirst(temp))) ) - { - result = lappend(result,lfirst(temp)); - } - else sanity = true; /* found a matching item to remove! */ - - if (!sanity) - elog(WARN, "xfunc_LispRemove: didn't find a match!"); - - return(result); + LispValue temp = LispNil; + LispValue result = LispNil; + int sanity = false; + + for (temp = bar; !null(temp); temp = lnext(temp)) + if (!equal((Node) (foo), (Node) (lfirst(temp)))) + { + result = lappend(result, lfirst(temp)); + } + else + sanity = true; /* found a matching item to remove! */ + + if (!sanity) + elog(WARN, "xfunc_LispRemove: didn't find a match!"); + + return (result); } #define Node_Copy(a, b, c, d) \ - if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) { \ - return false; \ - } + if (NodeCopy((Node)((a)->d), (Node*)&((b)->d), c) != true) { \ + return false; \ + } /* ** xfunc_copyrel -- ** Just like _copyRel, but doesn't copy the paths */ -bool xfunc_copyrel(Rel from, Rel *to) +bool +xfunc_copyrel(Rel from, Rel * to) { - Rel newnode; - Pointer (*alloc)() = palloc; - - /* COPY_CHECKARGS() */ - if (to == NULL) - { - return false; - } - - /* COPY_CHECKNULL() */ - if (from == NULL) + Rel newnode; + + Pointer(*alloc) () = palloc; + + /* COPY_CHECKARGS() */ + if (to == NULL) + { + return false; + } + + /* COPY_CHECKNULL() */ + if (from == NULL) { - (*to) = NULL; - return true; - } - - /* COPY_NEW(c) */ - newnode = (Rel)(*alloc)(classSize(Rel)); - if (newnode == NULL) - { - return false; - } - - /* ---------------- - * copy node superclass fields - * ---------------- - */ - CopyNodeFields((Node)from, (Node)newnode, alloc); - - /* ---------------- - * copy remainder of node - * ---------------- - */ - Node_Copy(from, newnode, alloc, relids); - - newnode->indexed = from->indexed; - newnode->pages = from->pages; - newnode->tuples = from->tuples; - newnode->size = from->size; - newnode->width = from->width; - - Node_Copy(from, newnode, alloc, targetlist); - /* No!!!! Node_Copy(from, newnode, alloc, pathlist); - Node_Copy(from, newnode, alloc, unorderedpath); - Node_Copy(from, newnode, alloc, cheapestpath); */ -#if 0 /* can't use Node_copy now. 2/95 -ay */ - Node_Copy(from, newnode, alloc, classlist); - Node_Copy(from, newnode, alloc, indexkeys); - Node_Copy(from, newnode, alloc, ordering); + (*to) = NULL; + return true; + } + + /* COPY_NEW(c) */ + newnode = (Rel) (*alloc) (classSize(Rel)); + if (newnode == NULL) + { + return false; + } + + /* ---------------- + * copy node superclass fields + * ---------------- + */ + CopyNodeFields((Node) from, (Node) newnode, alloc); + + /* ---------------- + * copy remainder of node + * ---------------- + */ + Node_Copy(from, newnode, alloc, relids); + + newnode->indexed = from->indexed; + newnode->pages = from->pages; + newnode->tuples = from->tuples; + newnode->size = from->size; + newnode->width = from->width; + + Node_Copy(from, newnode, alloc, targetlist); + + /* + * No!!!! Node_Copy(from, newnode, alloc, pathlist); + * Node_Copy(from, newnode, alloc, unorderedpath); Node_Copy(from, + * newnode, alloc, cheapestpath); + */ +#if 0 /* can't use Node_copy now. 2/95 -ay */ + Node_Copy(from, newnode, alloc, classlist); + Node_Copy(from, newnode, alloc, indexkeys); + Node_Copy(from, newnode, alloc, ordering); #endif - Node_Copy(from, newnode, alloc, clauseinfo); - Node_Copy(from, newnode, alloc, joininfo); - Node_Copy(from, newnode, alloc, innerjoin); - Node_Copy(from, newnode, alloc, superrels); - - (*to) = newnode; - return true; + Node_Copy(from, newnode, alloc, clauseinfo); + Node_Copy(from, newnode, alloc, joininfo); + Node_Copy(from, newnode, alloc, innerjoin); + Node_Copy(from, newnode, alloc, superrels); + + (*to) = newnode; + return true; } |
