diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/README | 6 | ||||
-rw-r--r-- | src/backend/optimizer/path/clausesel.c | 6 | ||||
-rw-r--r-- | src/backend/optimizer/path/costsize.c | 25 | ||||
-rw-r--r-- | src/backend/optimizer/path/indxpath.c | 42 | ||||
-rw-r--r-- | src/backend/optimizer/path/joinpath.c | 62 | ||||
-rw-r--r-- | src/backend/optimizer/path/pathkeys.c | 42 | ||||
-rw-r--r-- | src/backend/optimizer/plan/createplan.c | 223 | ||||
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 293 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 4 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 33 | ||||
-rw-r--r-- | src/backend/optimizer/plan/setrefs.c | 23 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepunion.c | 38 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 14 | ||||
-rw-r--r-- | src/backend/optimizer/util/relnode.c | 85 | ||||
-rw-r--r-- | src/backend/optimizer/util/var.c | 58 |
15 files changed, 397 insertions, 557 deletions
diff --git a/src/backend/optimizer/README b/src/backend/optimizer/README index f4d64ebbcad..955e022d8f6 100644 --- a/src/backend/optimizer/README +++ b/src/backend/optimizer/README @@ -251,8 +251,10 @@ Optimizer Data Structures RelOptInfo - a relation or joined relations - RestrictInfo - restriction clauses, like "x = 3" - JoinInfo - join clauses, including the relids needed for the join + RestrictInfo - WHERE clauses, like "x = 3" or "y = z" + (note the same structure is used for restriction and + join clauses) + JoinInfo - join clauses associated with a particular pair of relations Path - every way to generate a RelOptInfo(sequential,index,joins) SeqScan - a plain Path node with pathtype = T_SeqScan diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 0294c828124..84041a566d1 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.54 2002/12/12 15:49:28 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.55 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -266,12 +266,12 @@ addRangeClause(RangeQueryClause **rqlist, Node *clause, if (varonleft) { - var = (Node *) get_leftop((Expr *) clause); + var = get_leftop((Expr *) clause); is_lobound = !isLTsel; /* x < something is high bound */ } else { - var = (Node *) get_rightop((Expr *) clause); + var = get_rightop((Expr *) clause); is_lobound = isLTsel; /* something < x is low bound */ } diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index 1d736b34b91..efd80dff1ed 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -42,7 +42,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.99 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.100 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -752,7 +752,6 @@ cost_mergejoin(Path *path, Query *root, Cost cpu_per_tuple; QualCost restrict_qual_cost; RestrictInfo *firstclause; - Var *leftvar; double outer_rows, inner_rows; double ntuples; @@ -779,9 +778,7 @@ cost_mergejoin(Path *path, Query *root, &firstclause->left_mergescansel, &firstclause->right_mergescansel); - leftvar = get_leftop(firstclause->clause); - Assert(IsA(leftvar, Var)); - if (VARISRELMEMBER(leftvar->varno, outer_path->parent)) + if (is_subseti(firstclause->left_relids, outer_path->parent->relids)) { /* left side of clause is outer */ outerscansel = firstclause->left_mergescansel; @@ -935,14 +932,9 @@ cost_hashjoin(Path *path, Query *root, foreach(hcl, hashclauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(hcl); - Var *left, - *right; Selectivity thisbucketsize; Assert(IsA(restrictinfo, RestrictInfo)); - /* these must be OK, since check_hashjoinable accepted the clause */ - left = get_leftop(restrictinfo->clause); - right = get_rightop(restrictinfo->clause); /* * First we have to figure out which side of the hashjoin clause is the @@ -952,27 +944,30 @@ cost_hashjoin(Path *path, Query *root, * a large query, we cache the bucketsize estimate in the RestrictInfo * node to avoid repeated lookups of statistics. */ - if (VARISRELMEMBER(right->varno, inner_path->parent)) + if (is_subseti(restrictinfo->right_relids, inner_path->parent->relids)) { /* righthand side is inner */ thisbucketsize = restrictinfo->right_bucketsize; if (thisbucketsize < 0) { /* not cached yet */ - thisbucketsize = estimate_hash_bucketsize(root, right, + thisbucketsize = estimate_hash_bucketsize(root, + (Var *) get_rightop(restrictinfo->clause), virtualbuckets); restrictinfo->right_bucketsize = thisbucketsize; } } else { - Assert(VARISRELMEMBER(left->varno, inner_path->parent)); + Assert(is_subseti(restrictinfo->left_relids, + inner_path->parent->relids)); /* lefthand side is inner */ thisbucketsize = restrictinfo->left_bucketsize; if (thisbucketsize < 0) { /* not cached yet */ - thisbucketsize = estimate_hash_bucketsize(root, left, + thisbucketsize = estimate_hash_bucketsize(root, + (Var *) get_leftop(restrictinfo->clause), virtualbuckets); restrictinfo->left_bucketsize = thisbucketsize; } @@ -1088,7 +1083,7 @@ estimate_hash_bucketsize(Query *root, Var *var, int nbuckets) * Lookup info about var's relation and attribute; if none available, * return default estimate. */ - if (!IsA(var, Var)) + if (var == NULL || !IsA(var, Var)) return 0.1; relid = getrelid(var->varno, root->rtable); diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c index 228a876f71a..7e68c41ef37 100644 --- a/src/backend/optimizer/path/indxpath.c +++ b/src/backend/optimizer/path/indxpath.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.130 2002/12/16 21:30:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.131 2003/01/15 19:35:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -85,15 +85,15 @@ static Relids indexable_outerrelids(RelOptInfo *rel, IndexOptInfo *index); static Path *make_innerjoin_index_path(Query *root, RelOptInfo *rel, IndexOptInfo *index, List *clausegroup); -static bool match_index_to_operand(int indexkey, Var *operand, +static bool match_index_to_operand(int indexkey, Node *operand, RelOptInfo *rel, IndexOptInfo *index); static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index); static bool match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left); -static List *prefix_quals(Var *leftop, Oid expr_op, +static List *prefix_quals(Node *leftop, Oid expr_op, Const *prefix, Pattern_Prefix_Status pstatus); -static List *network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop); +static List *network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop); static Oid find_operator(const char *opname, Oid datatype); static Datum string_to_datum(const char *str, Oid datatype); static Const *string_to_const(const char *str, Oid datatype); @@ -713,7 +713,7 @@ match_clause_to_indexkey(RelOptInfo *rel, Oid opclass, Expr *clause) { - Var *leftop, + Node *leftop, *rightop; /* Clause must be a binary opclause. */ @@ -730,7 +730,7 @@ match_clause_to_indexkey(RelOptInfo *rel, * Anything that is a "pseudo constant" expression will do. */ if (match_index_to_operand(indexkey, leftop, rel, index) && - is_pseudo_constant_clause((Node *) rightop)) + is_pseudo_constant_clause(rightop)) { if (is_indexable_operator(clause, opclass, true)) return true; @@ -745,7 +745,7 @@ match_clause_to_indexkey(RelOptInfo *rel, } if (match_index_to_operand(indexkey, rightop, rel, index) && - is_pseudo_constant_clause((Node *) leftop)) + is_pseudo_constant_clause(leftop)) { if (is_indexable_operator(clause, opclass, false)) return true; @@ -801,7 +801,7 @@ match_join_clause_to_indexkey(RelOptInfo *rel, Oid opclass, Expr *clause) { - Var *leftop, + Node *leftop, *rightop; /* Clause must be a binary opclause. */ @@ -820,12 +820,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel, */ if (match_index_to_operand(indexkey, leftop, rel, index)) { - List *othervarnos = pull_varnos((Node *) rightop); + List *othervarnos = pull_varnos(rightop); bool isIndexable; isIndexable = !intMember(lfirsti(rel->relids), othervarnos) && - !contain_volatile_functions((Node *) rightop) && + !contain_volatile_functions(rightop) && is_indexable_operator(clause, opclass, true); freeList(othervarnos); return isIndexable; @@ -833,12 +833,12 @@ match_join_clause_to_indexkey(RelOptInfo *rel, if (match_index_to_operand(indexkey, rightop, rel, index)) { - List *othervarnos = pull_varnos((Node *) leftop); + List *othervarnos = pull_varnos(leftop); bool isIndexable; isIndexable = !intMember(lfirsti(rel->relids), othervarnos) && - !contain_volatile_functions((Node *) leftop) && + !contain_volatile_functions(leftop) && is_indexable_operator(clause, opclass, false); freeList(othervarnos); return isIndexable; @@ -1622,7 +1622,7 @@ make_innerjoin_index_path(Query *root, */ static bool match_index_to_operand(int indexkey, - Var *operand, + Node *operand, RelOptInfo *rel, IndexOptInfo *index) { @@ -1633,7 +1633,7 @@ match_index_to_operand(int indexkey, * eval_const_expressions() will have simplified if more than one. */ if (operand && IsA(operand, RelabelType)) - operand = (Var *) ((RelabelType *) operand)->arg; + operand = (Node *) ((RelabelType *) operand)->arg; if (index->indproc == InvalidOid) { @@ -1641,8 +1641,8 @@ match_index_to_operand(int indexkey, * Simple index. */ if (operand && IsA(operand, Var) && - lfirsti(rel->relids) == operand->varno && - indexkey == operand->varattno) + lfirsti(rel->relids) == ((Var *) operand)->varno && + indexkey == ((Var *) operand)->varattno) return true; else return false; @@ -1764,7 +1764,7 @@ match_special_index_operator(Expr *clause, Oid opclass, bool indexkey_on_left) { bool isIndexable = false; - Var *leftop, + Node *leftop, *rightop; Oid expr_op; Const *patt = NULL; @@ -1944,8 +1944,8 @@ expand_indexqual_conditions(List *indexquals) Expr *clause = (Expr *) lfirst(q); /* we know these will succeed */ - Var *leftop = get_leftop(clause); - Var *rightop = get_rightop(clause); + Node *leftop = get_leftop(clause); + Node *rightop = get_rightop(clause); Oid expr_op = ((OpExpr *) clause)->opno; Const *patt = (Const *) rightop; Const *prefix = NULL; @@ -2033,7 +2033,7 @@ expand_indexqual_conditions(List *indexquals) * operators. */ static List * -prefix_quals(Var *leftop, Oid expr_op, +prefix_quals(Node *leftop, Oid expr_op, Const *prefix_const, Pattern_Prefix_Status pstatus) { List *result; @@ -2143,7 +2143,7 @@ prefix_quals(Var *leftop, Oid expr_op, * operator. */ static List * -network_prefix_quals(Var *leftop, Oid expr_op, Datum rightop) +network_prefix_quals(Node *leftop, Oid expr_op, Datum rightop) { bool is_eq; char *opr1name; diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c index 65d0d8fa358..8a6fcd3f060 100644 --- a/src/backend/optimizer/path/joinpath.c +++ b/src/backend/optimizer/path/joinpath.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.74 2002/11/30 05:21:02 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.75 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -774,10 +774,9 @@ hash_inner_and_outer(Query *root, foreach(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Var *left, - *right; - if (restrictinfo->hashjoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->hashjoinoperator == InvalidOid) continue; /* not hashjoinable */ /* @@ -787,26 +786,16 @@ hash_inner_and_outer(Query *root, if (isouterjoin && restrictinfo->ispusheddown) continue; - /* these must be OK, since check_hashjoinable accepted the clause */ - left = get_leftop(restrictinfo->clause); - right = get_rightop(restrictinfo->clause); - /* * Check if clause is usable with these input rels. - * - * Since we currently accept only var-op-var clauses as hashjoinable, - * we need only check the membership of the vars to determine whether - * a particular clause can be used with this pair of sub-relations. - * This code would need to be upgraded if we wanted to allow - * more-complex expressions in hash joins. */ - if (VARISRELMEMBER(left->varno, outerrel) && - VARISRELMEMBER(right->varno, innerrel)) + if (is_subseti(restrictinfo->left_relids, outerrel->relids) && + is_subseti(restrictinfo->right_relids, innerrel->relids)) { /* righthand side is inner */ } - else if (VARISRELMEMBER(left->varno, innerrel) && - VARISRELMEMBER(right->varno, outerrel)) + else if (is_subseti(restrictinfo->left_relids, innerrel->relids) && + is_subseti(restrictinfo->right_relids, outerrel->relids)) { /* lefthand side is inner */ } @@ -874,9 +863,6 @@ select_mergejoin_clauses(RelOptInfo *joinrel, foreach(i, restrictlist) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Expr *clause; - Var *left, - *right; /* * If processing an outer join, only use its own join clauses in @@ -896,11 +882,13 @@ select_mergejoin_clauses(RelOptInfo *joinrel, switch (jointype) { case JOIN_RIGHT: - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) return NIL; /* not mergejoinable */ break; case JOIN_FULL: - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) elog(ERROR, "FULL JOIN is only supported with mergejoinable join conditions"); break; default: @@ -909,19 +897,27 @@ select_mergejoin_clauses(RelOptInfo *joinrel, } } - if (restrictinfo->mergejoinoperator == InvalidOid) + if (restrictinfo->left_relids == NIL || + restrictinfo->mergejoinoperator == InvalidOid) continue; /* not mergejoinable */ - clause = restrictinfo->clause; - /* these must be OK, since check_mergejoinable accepted the clause */ - left = get_leftop(clause); - right = get_rightop(clause); + /* + * Check if clause is usable with these input rels. + */ + if (is_subseti(restrictinfo->left_relids, outerrel->relids) && + is_subseti(restrictinfo->right_relids, innerrel->relids)) + { + /* righthand side is inner */ + } + else if (is_subseti(restrictinfo->left_relids, innerrel->relids) && + is_subseti(restrictinfo->right_relids, outerrel->relids)) + { + /* lefthand side is inner */ + } + else + continue; /* no good for these input relations */ - if ((VARISRELMEMBER(left->varno, outerrel) && - VARISRELMEMBER(right->varno, innerrel)) || - (VARISRELMEMBER(left->varno, innerrel) && - VARISRELMEMBER(right->varno, outerrel))) - result_list = lcons(restrictinfo, result_list); + result_list = lcons(restrictinfo, result_list); } return result_list; diff --git a/src/backend/optimizer/path/pathkeys.c b/src/backend/optimizer/path/pathkeys.c index 9c6ed4d1bd3..194bdddc2f0 100644 --- a/src/backend/optimizer/path/pathkeys.c +++ b/src/backend/optimizer/path/pathkeys.c @@ -11,7 +11,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.43 2002/12/17 01:18:22 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.44 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,27 +53,23 @@ makePathKeyItem(Node *key, Oid sortop) * The given clause has a mergejoinable operator, so its two sides * can be considered equal after restriction clause application; in * particular, any pathkey mentioning one side (with the correct sortop) - * can be expanded to include the other as well. Record the vars and + * can be expanded to include the other as well. Record the exprs and * associated sortops in the query's equi_key_list for future use. * * The query's equi_key_list field points to a list of sublists of PathKeyItem - * nodes, where each sublist is a set of two or more vars+sortops that have + * nodes, where each sublist is a set of two or more exprs+sortops that have * been identified as logically equivalent (and, therefore, we may consider * any two in a set to be equal). As described above, we will subsequently * use direct pointers to one of these sublists to represent any pathkey * that involves an equijoined variable. - * - * This code would actually work fine with expressions more complex than - * a single Var, but currently it won't see any because check_mergejoinable - * won't accept such clauses as mergejoinable. */ void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - PathKeyItem *item1 = makePathKeyItem((Node *) get_leftop(clause), + PathKeyItem *item1 = makePathKeyItem(get_leftop(clause), restrictinfo->left_sortop); - PathKeyItem *item2 = makePathKeyItem((Node *) get_rightop(clause), + PathKeyItem *item2 = makePathKeyItem(get_rightop(clause), restrictinfo->right_sortop); List *newset, *cursetlink; @@ -717,13 +713,13 @@ cache_mergeclause_pathkeys(Query *root, RestrictInfo *restrictinfo) if (restrictinfo->left_pathkey == NIL) { - key = (Node *) get_leftop(restrictinfo->clause); + key = get_leftop(restrictinfo->clause); item = makePathKeyItem(key, restrictinfo->left_sortop); restrictinfo->left_pathkey = make_canonical_pathkey(root, item); } if (restrictinfo->right_pathkey == NIL) { - key = (Node *) get_rightop(restrictinfo->clause); + key = get_rightop(restrictinfo->clause); item = makePathKeyItem(key, restrictinfo->right_sortop); restrictinfo->right_pathkey = make_canonical_pathkey(root, item); } @@ -852,32 +848,24 @@ make_pathkeys_for_mergeclauses(Query *root, foreach(i, mergeclauses) { RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); - Node *key; List *pathkey; cache_mergeclause_pathkeys(root, restrictinfo); - key = (Node *) get_leftop(restrictinfo->clause); - if (IsA(key, Var) && - VARISRELMEMBER(((Var *) key)->varno, rel)) + if (is_subseti(restrictinfo->left_relids, rel->relids)) { /* Rel is left side of mergeclause */ pathkey = restrictinfo->left_pathkey; } + else if (is_subseti(restrictinfo->right_relids, rel->relids)) + { + /* Rel is right side of mergeclause */ + pathkey = restrictinfo->right_pathkey; + } else { - key = (Node *) get_rightop(restrictinfo->clause); - if (IsA(key, Var) && - VARISRELMEMBER(((Var *) key)->varno, rel)) - { - /* Rel is right side of mergeclause */ - pathkey = restrictinfo->right_pathkey; - } - else - { - elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use"); - pathkey = NIL; /* keep compiler quiet */ - } + elog(ERROR, "make_pathkeys_for_mergeclauses: can't identify which side of mergeclause to use"); + pathkey = NIL; /* keep compiler quiet */ } /* diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 3d9ee6e4f4f..03ec3538479 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.129 2003/01/13 00:29:25 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.130 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -49,18 +49,15 @@ static FunctionScan *create_functionscan_plan(Path *best_path, static NestLoop *create_nestloop_plan(Query *root, NestPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static MergeJoin *create_mergejoin_plan(Query *root, MergePath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static HashJoin *create_hashjoin_plan(Query *root, HashPath *best_path, List *tlist, List *joinclauses, List *otherclauses, - Plan *outer_plan, List *outer_tlist, - Plan *inner_plan, List *inner_tlist); + Plan *outer_plan, Plan *inner_plan); static void fix_indxqual_references(List *indexquals, IndexPath *index_path, List **fixed_indexquals, List **recheck_indexquals); @@ -70,7 +67,7 @@ static void fix_indxqual_sublist(List *indexqual, int baserelid, static Node *fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, Oid *opclass); -static List *switch_outer(List *clauses); +static List *get_switched_clauses(List *clauses, List *outerrelids); static List *order_qual_clauses(Query *root, List *clauses); static void copy_path_costsize(Plan *dest, Path *src); static void copy_plan_costsize(Plan *dest, Plan *src); @@ -98,6 +95,9 @@ static MergeJoin *make_mergejoin(List *tlist, List *mergeclauses, Plan *lefttree, Plan *righttree, JoinType jointype); +static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree, + List *relids, List *pathkeys); + /* * create_plan @@ -246,18 +246,13 @@ create_join_plan(Query *root, JoinPath *best_path) { List *join_tlist = best_path->path.parent->targetlist; Plan *outer_plan; - List *outer_tlist; Plan *inner_plan; - List *inner_tlist; List *joinclauses; List *otherclauses; Join *plan; outer_plan = create_plan(root, best_path->outerjoinpath); - outer_tlist = outer_plan->targetlist; - inner_plan = create_plan(root, best_path->innerjoinpath); - inner_tlist = inner_plan->targetlist; if (IS_OUTER_JOIN(best_path->jointype)) { @@ -280,9 +275,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; case T_HashJoin: plan = (Join *) create_hashjoin_plan(root, @@ -291,9 +284,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; case T_NestLoop: plan = (Join *) create_nestloop_plan(root, @@ -302,9 +293,7 @@ create_join_plan(Query *root, JoinPath *best_path) joinclauses, otherclauses, outer_plan, - outer_tlist, - inner_plan, - inner_tlist); + inner_plan); break; default: elog(ERROR, "create_join_plan: unknown node type: %d", @@ -672,10 +661,9 @@ create_functionscan_plan(Path *best_path, List *tlist, List *scan_clauses) * * A cleaner solution would be to not call join_references() here at all, * but leave it for setrefs.c to do at the end of plan tree construction. - * But that would make switch_outer() much more complicated, and some care - * would be needed to get setrefs.c to do the right thing with nestloop - * inner indexscan quals. So, we do subplan reference adjustment here for - * quals of join nodes (and *only* for quals of join nodes). + * But some care would be needed to get setrefs.c to do the right thing with + * nestloop inner indexscan quals. So, we do subplan reference adjustment + * here for quals of join nodes (and *only* for quals of join nodes). * *****************************************************************************/ @@ -686,10 +674,10 @@ create_nestloop_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; NestLoop *join_plan; if (IsA(inner_plan, IndexScan)) @@ -797,44 +785,45 @@ create_mergejoin_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; List *mergeclauses; MergeJoin *join_plan; + /* + * Remove the mergeclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. + */ mergeclauses = get_actual_clauses(best_path->path_mergeclauses); + joinclauses = set_difference(joinclauses, mergeclauses); /* - * Remove the mergeclauses from the list of join qual clauses, leaving - * the list of quals that must be checked as qpquals. Set those - * clauses to contain INNER/OUTER var references. + * Rearrange mergeclauses, if needed, so that the outer variable + * is always on the left. */ - joinclauses = join_references(set_difference(joinclauses, mergeclauses), + mergeclauses = get_switched_clauses(best_path->path_mergeclauses, + best_path->jpath.outerjoinpath->parent->relids); + + /* + * Fix all the join clauses to contain INNER/OUTER var references. + */ + joinclauses = join_references(joinclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Fix the additional qpquals too. - */ otherclauses = join_references(otherclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Now set the references in the mergeclauses and rearrange them so - * that the outer variable is always on the left. - */ - mergeclauses = switch_outer(join_references(mergeclauses, - root->rtable, - outer_tlist, - inner_tlist, - (Index) 0)); + mergeclauses = join_references(mergeclauses, + root->rtable, + outer_tlist, + inner_tlist, + (Index) 0); /* * Create explicit sort nodes for the outer and inner join paths if @@ -843,15 +832,15 @@ create_mergejoin_plan(Query *root, if (best_path->outersortkeys) outer_plan = (Plan *) make_sort_from_pathkeys(root, - outer_tlist, outer_plan, + best_path->jpath.outerjoinpath->parent->relids, best_path->outersortkeys); if (best_path->innersortkeys) inner_plan = (Plan *) make_sort_from_pathkeys(root, - inner_tlist, inner_plan, + best_path->jpath.innerjoinpath->parent->relids, best_path->innersortkeys); /* @@ -877,47 +866,48 @@ create_hashjoin_plan(Query *root, List *joinclauses, List *otherclauses, Plan *outer_plan, - List *outer_tlist, - Plan *inner_plan, - List *inner_tlist) + Plan *inner_plan) { + List *outer_tlist = outer_plan->targetlist; + List *inner_tlist = inner_plan->targetlist; List *hashclauses; HashJoin *join_plan; Hash *hash_plan; List *innerhashkeys; List *hcl; + /* + * Remove the hashclauses from the list of join qual clauses, leaving + * the list of quals that must be checked as qpquals. + */ hashclauses = get_actual_clauses(best_path->path_hashclauses); + joinclauses = set_difference(joinclauses, hashclauses); /* - * Remove the hashclauses from the list of join qual clauses, leaving - * the list of quals that must be checked as qpquals. Set those - * clauses to contain INNER/OUTER var references. + * Rearrange hashclauses, if needed, so that the outer variable + * is always on the left. + */ + hashclauses = get_switched_clauses(best_path->path_hashclauses, + best_path->jpath.outerjoinpath->parent->relids); + + /* + * Fix all the join clauses to contain INNER/OUTER var references. */ - joinclauses = join_references(set_difference(joinclauses, hashclauses), + joinclauses = join_references(joinclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Fix the additional qpquals too. - */ otherclauses = join_references(otherclauses, root->rtable, outer_tlist, inner_tlist, (Index) 0); - - /* - * Now set the references in the hashclauses and rearrange them so - * that the outer variable is always on the left. - */ - hashclauses = switch_outer(join_references(hashclauses, - root->rtable, - outer_tlist, - inner_tlist, - (Index) 0)); + hashclauses = join_references(hashclauses, + root->rtable, + outer_tlist, + inner_tlist, + (Index) 0); /* * Extract the inner hash keys (right-hand operands of the hashclauses) @@ -1154,27 +1144,26 @@ fix_indxqual_operand(Node *node, int baserelid, IndexOptInfo *index, } /* - * switch_outer - * Given a list of merge or hash joinclauses, rearrange the elements within - * the clauses so the outer join variable is on the left and the inner is - * on the right. The original list is not touched; a modified list - * is returned. + * get_switched_clauses + * Given a list of merge or hash joinclauses (as RestrictInfo nodes), + * extract the bare clauses, and rearrange the elements within the + * clauses, if needed, so the outer join variable is on the left and + * the inner is on the right. The original data structure is not touched; + * a modified list is returned. */ static List * -switch_outer(List *clauses) +get_switched_clauses(List *clauses, List *outerrelids) { List *t_list = NIL; List *i; foreach(i, clauses) { - OpExpr *clause = (OpExpr *) lfirst(i); - Var *op; + RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i); + OpExpr *clause = (OpExpr *) restrictinfo->clause; Assert(is_opclause(clause)); - op = get_rightop((Expr *) clause); - Assert(op && IsA(op, Var)); - if (var_is_outer(op)) + if (is_subseti(restrictinfo->right_relids, outerrelids)) { /* * Duplicate just enough of the structure to allow commuting @@ -1554,17 +1543,24 @@ make_sort(Query *root, List *tlist, Plan *lefttree, int keycount) * make_sort_from_pathkeys * Create sort plan to sort according to given pathkeys * - * 'tlist' is the target list of the input plan * 'lefttree' is the node which yields input tuples + * 'relids' is the set of relids represented by the input node * 'pathkeys' is the list of pathkeys by which the result is to be sorted * * We must convert the pathkey information into reskey and reskeyop fields * of resdom nodes in the sort plan's target list. + * + * If the pathkeys include expressions that aren't simple Vars, we will + * usually need to add resjunk items to the input plan's targetlist to + * compute these expressions (since the Sort node itself won't do it). + * If the input plan type isn't one that can do projections, this means + * adding a Result node just to do the projection. */ -Sort * -make_sort_from_pathkeys(Query *root, List *tlist, - Plan *lefttree, List *pathkeys) +static Sort * +make_sort_from_pathkeys(Query *root, Plan *lefttree, + List *relids, List *pathkeys) { + List *tlist = lefttree->targetlist; List *sort_tlist; List *i; int numsortkeys = 0; @@ -1582,7 +1578,8 @@ make_sort_from_pathkeys(Query *root, List *tlist, /* * We can sort by any one of the sort key items listed in this * sublist. For now, we take the first one that corresponds to an - * available Var in the sort_tlist. + * available Var in the sort_tlist. If there isn't any, use the + * first one that is an expression in the input's vars. * * XXX if we have a choice, is there any way of figuring out which * might be cheapest to execute? (For example, int4lt is likely @@ -1599,8 +1596,52 @@ make_sort_from_pathkeys(Query *root, List *tlist, break; } if (!resdom) - elog(ERROR, "make_sort_from_pathkeys: cannot find tlist item to sort"); - + { + /* No matching Var; look for an expression */ + foreach(j, keysublist) + { + pathkey = lfirst(j); + if (is_subseti(pull_varnos(pathkey->key), relids)) + break; + } + if (!j) + elog(ERROR, "make_sort_from_pathkeys: cannot find pathkey item to sort"); + /* + * Do we need to insert a Result node? + * + * Currently, the only non-projection-capable plan type + * we can see here is Append. + */ + if (IsA(lefttree, Append)) + { + tlist = new_unsorted_tlist(tlist); + lefttree = (Plan *) make_result(tlist, NULL, lefttree); + } + /* + * Add resjunk entry to input's tlist + */ + resdom = makeResdom(length(tlist) + 1, + exprType(pathkey->key), + exprTypmod(pathkey->key), + NULL, + true); + tlist = lappend(tlist, + makeTargetEntry(resdom, + (Expr *) pathkey->key)); + lefttree->targetlist = tlist; /* just in case NIL before */ + /* + * Add one to sort node's tlist too. This will be identical + * except we are going to set the sort key info in it. + */ + resdom = makeResdom(length(sort_tlist) + 1, + exprType(pathkey->key), + exprTypmod(pathkey->key), + NULL, + true); + sort_tlist = lappend(sort_tlist, + makeTargetEntry(resdom, + (Expr *) pathkey->key)); + } /* * The resdom might be already marked as a sort key, if the * pathkeys contain duplicate entries. (This can happen in diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 1c1a848ea72..87c77e52fc3 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.80 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.81 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -60,32 +60,24 @@ static void check_hashjoinable(RestrictInfo *restrictinfo); * add_base_rels_to_query * * Scan the query's jointree and create baserel RelOptInfos for all - * the base relations (ie, table and subquery RTEs) appearing in the - * jointree. Also, create otherrel RelOptInfos for join RTEs. - * - * The return value is a list of all the baserel indexes (but not join RTE - * indexes) included in the scanned jointree. This is actually just an - * internal convenience for marking join otherrels properly; no outside - * caller uses the result. + * the base relations (ie, table, subquery, and function RTEs) + * appearing in the jointree. * * At the end of this process, there should be one baserel RelOptInfo for * every non-join RTE that is used in the query. Therefore, this routine * is the only place that should call build_base_rel. But build_other_rel - * will be used again later to build rels for inheritance children. + * will be used later to build rels for inheritance children. */ -List * +void add_base_rels_to_query(Query *root, Node *jtnode) { - List *result = NIL; - if (jtnode == NULL) - return NIL; + return; if (IsA(jtnode, RangeTblRef)) { int varno = ((RangeTblRef *) jtnode)->rtindex; build_base_rel(root, varno); - result = makeListi1(varno); } else if (IsA(jtnode, FromExpr)) { @@ -94,29 +86,15 @@ add_base_rels_to_query(Query *root, Node *jtnode) foreach(l, f->fromlist) { - result = nconc(result, - add_base_rels_to_query(root, lfirst(l))); + add_base_rels_to_query(root, lfirst(l)); } } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; - RelOptInfo *jrel; - - result = add_base_rels_to_query(root, j->larg); - result = nconc(result, - add_base_rels_to_query(root, j->rarg)); - /* the join's own rtindex is NOT added to result */ - jrel = build_other_rel(root, j->rtindex); - - /* - * Mark the join's otherrel with outerjoinset = list of baserel - * ids included in the join. Note we must copy here because - * result list is destructively modified by nconcs at higher - * levels. - */ - jrel->outerjoinset = listCopy(result); + add_base_rels_to_query(root, j->larg); + add_base_rels_to_query(root, j->rarg); /* * Safety check: join RTEs should not be SELECT FOR UPDATE targets */ @@ -126,7 +104,6 @@ add_base_rels_to_query(Query *root, Node *jtnode) else elog(ERROR, "add_base_rels_to_query: unexpected node type %d", nodeTag(jtnode)); - return result; } @@ -154,11 +131,6 @@ build_base_rel_tlists(Query *root, List *tlist) * add_vars_to_targetlist * For each variable appearing in the list, add it to the owning * relation's targetlist if not already present. - * - * Note that join alias variables will be attached to the otherrel for - * the join RTE. They will later be transferred to the tlist of - * the corresponding joinrel. We will also cause entries to be made - * for the Vars that the alias will eventually depend on. */ static void add_vars_to_targetlist(Query *root, List *vars) @@ -171,19 +143,6 @@ add_vars_to_targetlist(Query *root, List *vars) RelOptInfo *rel = find_base_rel(root, var->varno); add_var_to_tlist(rel, var); - - if (rel->reloptkind == RELOPT_OTHER_JOIN_REL) - { - /* Var is an alias */ - Node *expansion; - List *varsused; - - expansion = flatten_join_alias_vars((Node *) var, - root->rtable, true); - varsused = pull_var_clause(expansion, false); - add_vars_to_targetlist(root, varsused); - freeList(varsused); - } } } @@ -398,6 +357,8 @@ distribute_qual_to_rels(Query *root, Node *clause, restrictinfo->subclauseindices = NIL; restrictinfo->eval_cost.startup = -1; /* not computed until needed */ restrictinfo->this_selec = -1; /* not computed until needed */ + restrictinfo->left_relids = NIL; /* set below, if join clause */ + restrictinfo->right_relids = NIL; restrictinfo->mergejoinoperator = InvalidOid; restrictinfo->left_sortop = InvalidOid; restrictinfo->right_sortop = InvalidOid; @@ -416,41 +377,11 @@ distribute_qual_to_rels(Query *root, Node *clause, clause_get_relids_vars(clause, &relids, &vars); /* - * The clause might contain some join alias vars; if so, we want to - * remove the join otherrelids from relids and add the referent joins' - * scope lists instead (thus ensuring that the clause can be evaluated - * no lower than that join node). We rely here on the marking done - * earlier by add_base_rels_to_query. - * - * We can combine this step with a cross-check that the clause contains - * no relids not within its scope. If the first crosscheck succeeds, - * the clause contains no aliases and we needn't look more closely. + * Cross-check: clause should contain no relids not within its scope. + * Otherwise the parser messed up. */ if (!is_subseti(relids, qualscope)) - { - Relids newrelids = NIL; - List *relid; - - foreach(relid, relids) - { - RelOptInfo *rel = find_other_rel(root, lfirsti(relid)); - - if (rel && rel->outerjoinset) - { - /* this relid is for a join RTE */ - newrelids = set_unioni(newrelids, rel->outerjoinset); - } - else - { - /* this relid is for a true baserel */ - newrelids = lappendi(newrelids, lfirsti(relid)); - } - } - relids = newrelids; - /* Now repeat the crosscheck */ - if (!is_subseti(relids, qualscope)) - elog(ERROR, "JOIN qualification may not refer to other relations"); - } + elog(ERROR, "JOIN qualification may not refer to other relations"); /* * If the clause is variable-free, we force it to be evaluated at its @@ -575,7 +506,27 @@ distribute_qual_to_rels(Query *root, Node *clause, /* * 'clause' is a join clause, since there is more than one rel in * the relid list. Set additional RestrictInfo fields for - * joining. + * joining. First, does it look like a normal join clause, i.e., + * a binary operator relating expressions that come from distinct + * relations? If so we might be able to use it in a join algorithm. + */ + if (is_opclause(clause) && length(((OpExpr *) clause)->args) == 2) + { + List *left_relids; + List *right_relids; + + left_relids = pull_varnos(get_leftop((Expr *) clause)); + right_relids = pull_varnos(get_rightop((Expr *) clause)); + if (left_relids && right_relids && + nonoverlap_setsi(left_relids, right_relids)) + { + restrictinfo->left_relids = left_relids; + restrictinfo->right_relids = right_relids; + } + } + + /* + * Now check for hash or mergejoinable operators. * * We don't bother setting the hashjoin info if we're not going * to need it. We do want to know about mergejoinable ops in all @@ -675,11 +626,6 @@ void process_implied_equality(Query *root, Node *item1, Node *item2, Oid sortop1, Oid sortop2) { - Index irel1; - Index irel2; - RelOptInfo *rel1; - List *restrictlist; - List *itm; Oid ltype, rtype; Operator eq_operator; @@ -687,50 +633,14 @@ process_implied_equality(Query *root, Node *item1, Node *item2, Expr *clause; /* - * Currently, since check_mergejoinable only accepts Var = Var - * clauses, we should only see Var nodes here. Would have to work a - * little harder to locate the right rel(s) if more-general mergejoin - * clauses were accepted. - */ - Assert(IsA(item1, Var)); - irel1 = ((Var *) item1)->varno; - Assert(IsA(item2, Var)); - irel2 = ((Var *) item2)->varno; - - /* - * If both vars belong to same rel, we need to look at that rel's - * baserestrictinfo list. If different rels, each will have a - * joininfo node for the other, and we can scan either list. - */ - rel1 = find_base_rel(root, irel1); - if (irel1 == irel2) - restrictlist = rel1->baserestrictinfo; - else - { - JoinInfo *joininfo = find_joininfo_node(rel1, - makeListi1(irel2)); - - restrictlist = joininfo->jinfo_restrictinfo; - } - - /* - * Scan to see if equality is already known. + * Forget it if this equality is already recorded. + * + * Note: if only a single relation is involved, we may fall through + * here and end up rejecting the equality later on in qual_is_redundant. + * This is a tad slow but should be okay. */ - foreach(itm, restrictlist) - { - RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm); - Node *left, - *right; - - if (restrictinfo->mergejoinoperator == InvalidOid) - continue; /* ignore non-mergejoinable clauses */ - /* We now know the restrictinfo clause is a binary opclause */ - left = (Node *) get_leftop(restrictinfo->clause); - right = (Node *) get_rightop(restrictinfo->clause); - if ((equal(item1, left) && equal(item2, right)) || - (equal(item2, left) && equal(item1, right))) - return; /* found a matching clause */ - } + if (exprs_known_equal(root, item1, item2)) + return; /* * This equality is new information, so construct a clause @@ -770,6 +680,8 @@ process_implied_equality(Query *root, Node *item1, Node *item2, ReleaseSysCache(eq_operator); /* + * Push the new clause into all the appropriate restrictinfo lists. + * * Note: we mark the qual "pushed down" to ensure that it can never be * taken for an original JOIN/ON clause. */ @@ -779,44 +691,45 @@ process_implied_equality(Query *root, Node *item1, Node *item2, } /* - * vars_known_equal - * Detect whether two Vars are known equal due to equijoin clauses. + * exprs_known_equal + * Detect whether two expressions are known equal due to equijoin clauses. * * This is not completely accurate since we avoid adding redundant restriction * clauses to individual base rels (see qual_is_redundant). However, after - * the implied-equality-deduction phase, it is complete for Vars of different - * rels; that's sufficient for planned uses. + * the implied-equality-deduction phase, it is complete for expressions + * involving Vars of multiple rels; that's sufficient for planned uses. */ bool -vars_known_equal(Query *root, Var *var1, Var *var2) +exprs_known_equal(Query *root, Node *item1, Node *item2) { - Index irel1; - Index irel2; + List *relids; RelOptInfo *rel1; List *restrictlist; List *itm; + /* Get list of relids referenced in the two expressions */ + relids = set_unioni(pull_varnos(item1), pull_varnos(item2)); + /* - * Would need more work here if we wanted to check for known equality - * of general clauses: there might be multiple base rels involved. + * If there are no Vars at all, say "true". This prevents + * process_implied_equality from trying to store "const = const" + * deductions. */ - Assert(IsA(var1, Var)); - irel1 = var1->varno; - Assert(IsA(var2, Var)); - irel2 = var2->varno; + if (relids == NIL) + return true; /* - * If both vars belong to same rel, we need to look at that rel's - * baserestrictinfo list. If different rels, each will have a - * joininfo node for the other, and we can scan either list. + * If the exprs involve a single rel, we need to look at that rel's + * baserestrictinfo list. If multiple rels, any one will have a + * joininfo node for the rest, and we can scan any of 'em. */ - rel1 = find_base_rel(root, irel1); - if (irel1 == irel2) + rel1 = find_base_rel(root, lfirsti(relids)); + relids = lnext(relids); + if (relids == NIL) restrictlist = rel1->baserestrictinfo; else { - JoinInfo *joininfo = find_joininfo_node(rel1, - makeListi1(irel2)); + JoinInfo *joininfo = find_joininfo_node(rel1, relids); restrictlist = joininfo->jinfo_restrictinfo; } @@ -833,10 +746,10 @@ vars_known_equal(Query *root, Var *var1, Var *var2) if (restrictinfo->mergejoinoperator == InvalidOid) continue; /* ignore non-mergejoinable clauses */ /* We now know the restrictinfo clause is a binary opclause */ - left = (Node *) get_leftop(restrictinfo->clause); - right = (Node *) get_rightop(restrictinfo->clause); - if ((equal(var1, left) && equal(var2, right)) || - (equal(var2, left) && equal(var1, right))) + left = get_leftop(restrictinfo->clause); + right = get_rightop(restrictinfo->clause); + if ((equal(item1, left) && equal(item2, right)) || + (equal(item2, left) && equal(item1, right))) return true; /* found a matching clause */ } @@ -862,7 +775,7 @@ qual_is_redundant(Query *root, List *olditem; Node *newleft; Node *newright; - List *equalvars; + List *equalexprs; bool someadded; /* @@ -898,15 +811,15 @@ qual_is_redundant(Query *root, return false; /* - * Now, we want to develop a list of Vars that are known equal to the + * Now, we want to develop a list of exprs that are known equal to the * left side of the new qual. We traverse the old-quals list - * repeatedly to transitively expand the Vars list. If at any point - * we find we can reach the right-side Var of the new qual, we are - * done. We give up when we can't expand the equalvars list any more. + * repeatedly to transitively expand the exprs list. If at any point + * we find we can reach the right-side expr of the new qual, we are + * done. We give up when we can't expand the equalexprs list any more. */ - newleft = (Node *) get_leftop(restrictinfo->clause); - newright = (Node *) get_rightop(restrictinfo->clause); - equalvars = makeList1(newleft); + newleft = get_leftop(restrictinfo->clause); + newright = get_rightop(restrictinfo->clause); + equalexprs = makeList1(newleft); do { someadded = false; @@ -915,22 +828,22 @@ qual_is_redundant(Query *root, while (olditem) { RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem); - Node *oldleft = (Node *) get_leftop(oldrinfo->clause); - Node *oldright = (Node *) get_rightop(oldrinfo->clause); + Node *oldleft = get_leftop(oldrinfo->clause); + Node *oldright = get_rightop(oldrinfo->clause); Node *newguy = NULL; /* must advance olditem before lremove possibly pfree's it */ olditem = lnext(olditem); - if (member(oldleft, equalvars)) + if (member(oldleft, equalexprs)) newguy = oldright; - else if (member(oldright, equalvars)) + else if (member(oldright, equalexprs)) newguy = oldleft; else continue; if (equal(newguy, newright)) return true; /* we proved new clause is redundant */ - equalvars = lcons(newguy, equalvars); + equalexprs = lcons(newguy, equalexprs); someadded = true; /* @@ -956,39 +869,28 @@ qual_is_redundant(Query *root, * info fields in the restrictinfo. * * Currently, we support mergejoin for binary opclauses where - * both operands are simple Vars and the operator is a mergejoinable - * operator. + * the operator is a mergejoinable operator. The arguments can be + * anything --- as long as there are no volatile functions in them. */ static void check_mergejoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - Var *left, - *right; Oid opno, leftOp, rightOp; if (!is_opclause(clause)) return; - - left = get_leftop(clause); - right = get_rightop(clause); - - /* caution: is_opclause accepts more than I do, so check it */ - if (!right) - return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || - !IsA(right, Var)) + if (length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; if (op_mergejoinable(opno, - left->vartype, - right->vartype, &leftOp, - &rightOp)) + &rightOp) && + !contain_volatile_functions((Node *) clause)) { restrictinfo->mergejoinoperator = opno; restrictinfo->left_sortop = leftOp; @@ -1002,34 +904,23 @@ check_mergejoinable(RestrictInfo *restrictinfo) * info fields in the restrictinfo. * * Currently, we support hashjoin for binary opclauses where - * both operands are simple Vars and the operator is a hashjoinable - * operator. + * the operator is a hashjoinable operator. The arguments can be + * anything --- as long as there are no volatile functions in them. */ static void check_hashjoinable(RestrictInfo *restrictinfo) { Expr *clause = restrictinfo->clause; - Var *left, - *right; Oid opno; if (!is_opclause(clause)) return; - - left = get_leftop(clause); - right = get_rightop(clause); - - /* caution: is_opclause accepts more than I do, so check it */ - if (!right) - return; /* unary opclauses need not apply */ - if (!IsA(left, Var) || - !IsA(right, Var)) + if (length(((OpExpr *) clause)->args) != 2) return; opno = ((OpExpr *) clause)->opno; - if (op_hashjoinable(opno, - left->vartype, - right->vartype)) + if (op_hashjoinable(opno) && + !contain_volatile_functions((Node *) clause)) restrictinfo->hashjoinoperator = opno; } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 5d2634bf82d..6e265931eb2 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.72 2002/11/21 00:42:19 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.73 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -117,7 +117,7 @@ query_planner(Query *root, List *tlist, double tuple_fraction, /* * Construct RelOptInfo nodes for all base relations in query. */ - (void) add_base_rels_to_query(root, (Node *) root->jointree); + add_base_rels_to_query(root, (Node *) root->jointree); /* * Examine the targetlist and qualifications, adding entries to diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 76a16cd833d..4cf6a09acfe 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.138 2003/01/13 18:10:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.139 2003/01/15 19:35:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -560,20 +560,6 @@ is_simple_subquery(Query *subquery) return false; /* - * Don't pull up a subquery that has any sublinks in its targetlist, - * either. As of PG 7.3 this creates problems because the pulled-up - * expressions may go into join alias lists, and the sublinks would - * not get fixed because we do flatten_join_alias_vars() too late. - * Eventually we should do a complete flatten_join_alias_vars as the - * first step of preprocess_expression, and then we could probably - * support this. (BUT: it might be a bad idea anyway, due to possibly - * causing multiple evaluations of an expensive sublink.) - */ - if (subquery->hasSubLinks && - contain_subplans((Node *) subquery->targetList)) - return false; - - /* * Hack: don't try to pull up a subquery with an empty jointree. * query_planner() will correctly generate a Result plan for a * jointree that's totally empty, but I don't think the right things @@ -751,6 +737,14 @@ static Node * preprocess_expression(Query *parse, Node *expr, int kind) { /* + * If the query has any join RTEs, replace join alias variables with + * base-relation variables. We must do this before sublink processing, + * else sublinks expanded out from join aliases wouldn't get processed. + */ + if (parse->hasJoinRTEs) + expr = flatten_join_alias_vars(expr, parse->rtable); + + /* * Simplify constant expressions. * * Note that at this point quals have not yet been converted to @@ -783,15 +777,6 @@ preprocess_expression(Query *parse, Node *expr, int kind) if (PlannerQueryLevel > 1) expr = SS_replace_correlation_vars(expr); - /* - * If the query has any join RTEs, try to replace join alias variables - * with base-relation variables, to allow quals to be pushed down. We - * must do this after sublink processing, since it does not recurse - * into sublinks. - */ - if (parse->hasJoinRTEs) - expr = flatten_join_alias_vars(expr, parse->rtable, false); - return expr; } diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 1b5c72e163c..1c9f8a27cff 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.88 2003/01/13 18:10:53 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.89 2003/01/15 19:35:43 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -278,8 +278,7 @@ fix_expr_references_walker(Node *node, void *context) * Note: this same transformation has already been applied to the quals * of the join by createplan.c. It's a little odd to do it here for the * targetlist and there for the quals, but it's easier that way. (Look - * at switch_outer() and the handling of nestloop inner indexscans to - * see why.) + * at the handling of nestloop inner indexscans to see why.) * * Because the quals are reference-adjusted sooner, we cannot do equal() * comparisons between qual and tlist var nodes during the time between @@ -379,8 +378,7 @@ set_uppernode_references(Plan *plan, Index subvarno) * Creates a new set of targetlist entries or join qual clauses by * changing the varno/varattno values of variables in the clauses * to reference target list values from the outer and inner join - * relation target lists. Also, any join alias variables in the - * clauses are expanded into references to their component variables. + * relation target lists. * * This is used in two different scenarios: a normal join clause, where * all the Vars in the clause *must* be replaced by OUTER or INNER references; @@ -428,7 +426,6 @@ join_references_mutator(Node *node, { Var *var = (Var *) node; Resdom *resdom; - Node *newnode; /* First look for the var in the input tlists */ resdom = tlist_member((Node *) var, context->outer_tlist); @@ -454,20 +451,6 @@ join_references_mutator(Node *node, if (var->varno == context->acceptable_rel) return (Node *) copyObject(var); - /* - * Perhaps it's a join alias that can be resolved to input vars? - * We try this last since it's relatively slow. - */ - newnode = flatten_join_alias_vars((Node *) var, - context->rtable, - true); - if (!equal(newnode, (Node *) var)) - { - /* Must now resolve the input vars... */ - newnode = join_references_mutator(newnode, context); - return newnode; - } - /* No referent found for Var */ elog(ERROR, "join_references: variable not in subplan target lists"); } diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 5177e210d3d..4af395b860c 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.85 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.86 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -837,9 +837,7 @@ adjust_inherited_attrs_mutator(Node *node, } /* - * We have to process RestrictInfo nodes specially: we do NOT want to - * copy the original subclauseindices list, since the new rel may have - * different indices. The list will be rebuilt during later planning. + * We have to process RestrictInfo nodes specially. */ if (IsA(node, RestrictInfo)) { @@ -849,10 +847,41 @@ adjust_inherited_attrs_mutator(Node *node, /* Copy all flat-copiable fields */ memcpy(newinfo, oldinfo, sizeof(RestrictInfo)); + /* Recursively fix the clause itself */ newinfo->clause = (Expr *) adjust_inherited_attrs_mutator((Node *) oldinfo->clause, context); + /* + * We do NOT want to copy the original subclauseindices list, since + * the new rel will have different indices. The list will be rebuilt + * when needed during later planning. + */ newinfo->subclauseindices = NIL; + + /* + * Adjust left/right relids lists too. + */ + if (intMember(context->old_rt_index, oldinfo->left_relids)) + { + newinfo->left_relids = listCopy(oldinfo->left_relids); + newinfo->left_relids = lremovei(context->old_rt_index, + newinfo->left_relids); + newinfo->left_relids = lconsi(context->new_rt_index, + newinfo->left_relids); + } + else + newinfo->left_relids = oldinfo->left_relids; + if (intMember(context->old_rt_index, oldinfo->right_relids)) + { + newinfo->right_relids = listCopy(oldinfo->right_relids); + newinfo->right_relids = lremovei(context->old_rt_index, + newinfo->right_relids); + newinfo->right_relids = lconsi(context->new_rt_index, + newinfo->right_relids); + } + else + newinfo->right_relids = oldinfo->right_relids; + newinfo->eval_cost.startup = -1; /* reset these too */ newinfo->this_selec = -1; newinfo->left_pathkey = NIL; /* and these */ @@ -869,6 +898,7 @@ adjust_inherited_attrs_mutator(Node *node, * NOTE: we do not need to recurse into sublinks, because they should * already have been converted to subplans before we see them. */ + Assert(!IsA(node, SubLink)); /* * BUT: although we don't need to recurse into subplans, we do need to diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index e38fc46821d..233a8cb8947 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.121 2003/01/10 21:08:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.122 2003/01/15 19:35:44 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -112,17 +112,13 @@ make_opclause(Oid opno, Oid opresulttype, bool opretset, * * Returns the left operand of a clause of the form (op expr expr) * or (op expr) - * - * NB: for historical reasons, the result is declared Var *, even - * though many callers can cope with results that are not Vars. - * The result really ought to be declared Expr * or Node *. */ -Var * +Node * get_leftop(Expr *clause) { OpExpr *expr = (OpExpr *) clause; - if (expr->args != NULL) + if (expr->args != NIL) return lfirst(expr->args); else return NULL; @@ -134,12 +130,12 @@ get_leftop(Expr *clause) * Returns the right operand in a clause of the form (op expr expr). * NB: result will be NULL if applied to a unary op clause. */ -Var * +Node * get_rightop(Expr *clause) { OpExpr *expr = (OpExpr *) clause; - if (expr->args != NULL && lnext(expr->args) != NULL) + if (expr->args != NIL && lnext(expr->args) != NIL) return lfirst(lnext(expr->args)); else return NULL; diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 296d211ad12..87207f617cc 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.42 2003/01/12 22:35:29 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.43 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -110,9 +110,9 @@ build_other_rel(Query *root, int relid) /* No existing RelOptInfo for this other rel, so make a new one */ rel = make_base_rel(root, relid); - /* if it's not a join rel, must be a child rel */ - if (rel->reloptkind == RELOPT_BASEREL) - rel->reloptkind = RELOPT_OTHER_CHILD_REL; + /* presently, must be an inheritance child rel */ + Assert(rel->reloptkind == RELOPT_BASEREL); + rel->reloptkind = RELOPT_OTHER_CHILD_REL; /* and add it to the list */ root->other_rel_list = lcons(rel, root->other_rel_list); @@ -146,8 +146,6 @@ make_base_rel(Query *root, int relid) rel->pages = 0; rel->tuples = 0; rel->subplan = NULL; - rel->joinrti = 0; - rel->joinrteids = NIL; rel->baserestrictinfo = NIL; rel->baserestrictcost.startup = 0; rel->baserestrictcost.per_tuple = 0; @@ -174,10 +172,6 @@ make_base_rel(Query *root, int relid) case RTE_FUNCTION: /* Subquery or function --- nothing to do here */ break; - case RTE_JOIN: - /* Join --- must be an otherrel */ - rel->reloptkind = RELOPT_OTHER_JOIN_REL; - break; default: elog(ERROR, "make_base_rel: unsupported RTE kind %d", (int) rte->rtekind); @@ -221,47 +215,6 @@ find_base_rel(Query *root, int relid) } /* - * find_other_rel - * Find an otherrel entry, if one exists for the given relid. - * Return NULL if no entry. - */ -RelOptInfo * -find_other_rel(Query *root, int relid) -{ - List *rels; - - foreach(rels, root->other_rel_list) - { - RelOptInfo *rel = (RelOptInfo *) lfirst(rels); - - if (lfirsti(rel->relids) == relid) - return rel; - } - return NULL; -} - -/* - * find_other_rel_for_join - * Look for an otherrel for a join RTE matching the given baserel set. - * Return NULL if no entry. - */ -RelOptInfo * -find_other_rel_for_join(Query *root, List *relids) -{ - List *rels; - - foreach(rels, root->other_rel_list) - { - RelOptInfo *rel = (RelOptInfo *) lfirst(rels); - - if (rel->reloptkind == RELOPT_OTHER_JOIN_REL - && sameseti(relids, rel->outerjoinset)) - return rel; - } - return NULL; -} - -/* * find_join_rel * Returns relation entry corresponding to 'relids' (a list of RT indexes), * or NULL if none exists. This is for join relations. @@ -310,7 +263,6 @@ build_join_rel(Query *root, { List *joinrelids; RelOptInfo *joinrel; - RelOptInfo *joinrterel; List *restrictlist; List *new_outer_tlist; List *new_inner_tlist; @@ -360,9 +312,6 @@ build_join_rel(Query *root, joinrel->pages = 0; joinrel->tuples = 0; joinrel->subplan = NULL; - joinrel->joinrti = 0; - joinrel->joinrteids = nconc(listCopy(outer_rel->joinrteids), - inner_rel->joinrteids); joinrel->baserestrictinfo = NIL; joinrel->baserestrictcost.startup = 0; joinrel->baserestrictcost.per_tuple = 0; @@ -371,15 +320,6 @@ build_join_rel(Query *root, joinrel->index_outer_relids = NIL; joinrel->index_inner_paths = NIL; - /* Is there a join RTE matching this join? */ - joinrterel = find_other_rel_for_join(root, joinrelids); - if (joinrterel) - { - /* Yes, remember its RT index */ - joinrel->joinrti = lfirsti(joinrterel->relids); - joinrel->joinrteids = lconsi(joinrel->joinrti, joinrel->joinrteids); - } - /* * Create a new tlist by removing irrelevant elements from both tlists * of the outer and inner join relations and then merging the results @@ -401,23 +341,6 @@ build_join_rel(Query *root, joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist); /* - * If there are any alias variables attached to the matching join RTE, - * attach them to the tlist too, so that they will be evaluated for - * use at higher plan levels. - */ - if (joinrterel) - { - List *jrtetl; - - foreach(jrtetl, joinrterel->targetlist) - { - TargetEntry *jrtete = lfirst(jrtetl); - - add_var_to_tlist(joinrel, (Var *) jrtete->expr); - } - } - - /* * Construct restrict and join clause lists for the new joinrel. (The * caller might or might not need the restrictlist, but I need it * anyway for set_joinrel_size_estimates().) diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 92ff0cd5b4c..11267428d7a 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.43 2003/01/10 21:08:13 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.44 2003/01/15 19:35:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,6 +20,16 @@ #include "parser/parsetree.h" +/* macros borrowed from expression_tree_mutator */ + +#define FLATCOPY(newnode, node, nodetype) \ + ( (newnode) = makeNode(nodetype), \ + memcpy((newnode), (node), sizeof(nodetype)) ) + +#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \ + ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) ) + + typedef struct { List *varlist; @@ -42,7 +52,7 @@ typedef struct typedef struct { List *rtable; - bool force; + int sublevels_up; } flatten_join_alias_vars_context; static bool pull_varnos_walker(Node *node, @@ -314,26 +324,16 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) * relation variables instead. This allows quals involving such vars to be * pushed down. * - * If force is TRUE then we will reduce all JOIN alias Vars to non-alias Vars - * or expressions thereof (there may be COALESCE and/or type conversions - * involved). If force is FALSE we will not expand a Var to a non-Var - * expression. This is a hack to avoid confusing mergejoin planning, which - * currently cannot cope with non-Var join items --- we leave the join vars - * as Vars till after planning is done, then expand them during setrefs.c. - * - * Upper-level vars (with varlevelsup > 0) are ignored; normally there - * should not be any by the time this routine is called. - * - * Does not examine subqueries, therefore must only be used after reduction - * of sublinks to subplans! + * NOTE: this is used on not-yet-planned expressions. We do not expect it + * to be applied directly to a Query node. */ Node * -flatten_join_alias_vars(Node *node, List *rtable, bool force) +flatten_join_alias_vars(Node *node, List *rtable) { flatten_join_alias_vars_context context; context.rtable = rtable; - context.force = force; + context.sublevels_up = 0; return flatten_join_alias_vars_mutator(node, &context); } @@ -350,21 +350,31 @@ flatten_join_alias_vars_mutator(Node *node, RangeTblEntry *rte; Node *newvar; - if (var->varlevelsup != 0) + if (var->varlevelsup != context->sublevels_up) return node; /* no need to copy, really */ rte = rt_fetch(var->varno, context->rtable); if (rte->rtekind != RTE_JOIN) return node; Assert(var->varattno > 0); newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars); - if (IsA(newvar, Var) ||context->force) - { - /* expand it; recurse in case join input is itself a join */ - return flatten_join_alias_vars_mutator(newvar, context); - } - /* we don't want to force expansion of this alias Var */ - return node; + /* expand it; recurse in case join input is itself a join */ + return flatten_join_alias_vars_mutator(newvar, context); + } + if (IsA(node, Query)) + { + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ + Query *query = (Query *) node; + Query *newnode; + + FLATCOPY(newnode, query, Query); + context->sublevels_up++; + query_tree_mutator(newnode, flatten_join_alias_vars_mutator, + (void *) context, QTW_IGNORE_JOINALIASES); + context->sublevels_up--; + return (Node *) newnode; } + /* Already-planned tree not supported */ + Assert(!is_subplan(node)); return expression_tree_mutator(node, flatten_join_alias_vars_mutator, (void *) context); } |