aboutsummaryrefslogtreecommitdiff
path: root/src/backend/optimizer/plan/initsplan.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/optimizer/plan/initsplan.c')
-rw-r--r--src/backend/optimizer/plan/initsplan.c293
1 files changed, 92 insertions, 201 deletions
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;
}