Allow merge and hash joins to occur on arbitrary expressions (anything not

containing a volatile function), rather than only on 'Var = Var' clauses
as before.  This makes it practical to do flatten_join_alias_vars at the
start of planning, which in turn eliminates a bunch of klugery inside the
planner to deal with alias vars.  As a free side effect, we now detect
implied equality of non-Var expressions; for example in
	SELECT ... WHERE a.x = b.y and b.y = 42
we will deduce a.x = 42 and use that as a restriction qual on a.  Also,
we can remove the restriction introduced 12/5/02 to prevent pullup of
subqueries whose targetlists contain sublinks.
Still TODO: make statistical estimation routines in selfuncs.c and costsize.c
smarter about expressions that are more complex than plain Vars.  The need
for this is considerably greater now that we have to be able to estimate
the suitability of merge and hash join techniques on such expressions.
This commit is contained in:
Tom Lane 2003-01-15 19:35:48 +00:00
parent 0eed62f34d
commit de97072e3c
32 changed files with 523 additions and 662 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.21 2003/01/06 01:20:40 tgl Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/xoper.sgml,v 1.22 2003/01/15 19:35:35 tgl Exp $
-->
<Chapter Id="xoper">
@ -375,6 +375,27 @@ table1.column1 OP table2.column2
equality operators that are (or could be) implemented by <function>memcmp()</function>.
</para>
<note>
<para>
The function underlying a hashjoinable operator must be marked
immutable or stable. If it is volatile, the system will never
attempt to use the operator for a hash join.
</para>
</note>
<note>
<para>
If a hashjoinable operator has an underlying function that is marked
strict, the
function must also be complete: that is, it should return TRUE or
FALSE, never NULL, for any two non-NULL inputs. If this rule is
not followed, hash-optimization of <literal>IN</> operations may
generate wrong results. (Specifically, <literal>IN</> might return
FALSE where the correct answer per spec would be NULL; or it might
yield an error complaining that it wasn't prepared for a NULL result.)
</para>
</note>
</sect2>
<sect2>
@ -472,6 +493,14 @@ table1.column1 OP table2.column2
</itemizedlist>
</para>
<note>
<para>
The function underlying a mergejoinable operator must be marked
immutable or stable. If it is volatile, the system will never
attempt to use the operator for a merge join.
</para>
</note>
<note>
<para>
<literal>GROUP BY</> and <literal>DISTINCT</> operations require each

View File

@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.235 2003/01/10 21:08:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.236 2003/01/15 19:35:35 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1059,6 +1059,8 @@ _copyRestrictInfo(RestrictInfo *from)
COPY_NODE_FIELD(subclauseindices); /* XXX probably bad */
COPY_SCALAR_FIELD(eval_cost);
COPY_SCALAR_FIELD(this_selec);
COPY_INTLIST_FIELD(left_relids);
COPY_INTLIST_FIELD(right_relids);
COPY_SCALAR_FIELD(mergejoinoperator);
COPY_SCALAR_FIELD(left_sortop);
COPY_SCALAR_FIELD(right_sortop);

View File

@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.179 2003/01/10 21:08:10 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.180 2003/01/15 19:35:37 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -464,10 +464,10 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
COMPARE_NODE_FIELD(clause);
COMPARE_SCALAR_FIELD(ispusheddown);
/*
* We ignore subclauseindices, eval_cost, this_selec, left/right_pathkey,
* and left/right_bucketsize, since they may not be set yet, and should be
* derivable from the clause anyway. Probably it's not really necessary
* to compare any of these remaining fields ...
* We ignore subclauseindices, eval_cost, this_selec, left/right_relids,
* left/right_pathkey, and left/right_bucketsize, since they may not be
* set yet, and should be derivable from the clause anyway. Probably it's
* not really necessary to compare any of these remaining fields ...
*/
COMPARE_SCALAR_FIELD(mergejoinoperator);
COMPARE_SCALAR_FIELD(left_sortop);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.192 2003/01/10 21:08:11 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.193 2003/01/15 19:35:39 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
@ -952,6 +952,8 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
WRITE_NODE_FIELD(clause);
WRITE_BOOL_FIELD(ispusheddown);
WRITE_NODE_FIELD(subclauseindices);
WRITE_INTLIST_FIELD(left_relids);
WRITE_INTLIST_FIELD(right_relids);
WRITE_OID_FIELD(mergejoinoperator);
WRITE_OID_FIELD(left_sortop);
WRITE_OID_FIELD(right_sortop);

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.58 2002/12/12 15:49:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.59 2003/01/15 19:35:39 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -370,10 +370,10 @@ print_expr(Node *expr, List *rtable)
{
char *opname;
print_expr((Node *) get_leftop(e), rtable);
print_expr(get_leftop(e), rtable);
opname = get_opname(((OpExpr *) e)->opno);
printf(" %s ", ((opname != NULL) ? opname : "(invalid operator)"));
print_expr((Node *) get_rightop(e), rtable);
print_expr(get_rightop(e), rtable);
}
else
printf("an expr");

View File

@ -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

View File

@ -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 */
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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 */
}
/*

View File

@ -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;
mergeclauses = get_actual_clauses(best_path->path_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.
* the list of quals that must be checked as qpquals.
*/
joinclauses = join_references(set_difference(joinclauses, mergeclauses),
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
joinclauses = set_difference(joinclauses, mergeclauses);
/*
* Rearrange mergeclauses, if needed, so that the outer variable
* is always on the left.
*/
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;
hashclauses = get_actual_clauses(best_path->path_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.
* the list of quals that must be checked as qpquals.
*/
joinclauses = join_references(set_difference(joinclauses, hashclauses),
hashclauses = get_actual_clauses(best_path->path_hashclauses);
joinclauses = set_difference(joinclauses, hashclauses);
/*
* 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(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

View File

@ -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.
* 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.
*/
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.
*/
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;
/*
* Would need more work here if we wanted to check for known equality
* of general clauses: there might be multiple base rels involved.
*/
Assert(IsA(var1, Var));
irel1 = var1->varno;
Assert(IsA(var2, Var));
irel2 = var2->varno;
/* Get list of relids referenced in the two expressions */
relids = set_unioni(pull_varnos(item1), pull_varnos(item2));
/*
* 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 there are no Vars at all, say "true". This prevents
* process_implied_equality from trying to store "const = const"
* deductions.
*/
rel1 = find_base_rel(root, irel1);
if (irel1 == irel2)
if (relids == NIL)
return true;
/*
* 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, 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;
}

View File

@ -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

View File

@ -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 $
*
*-------------------------------------------------------------------------
*/
@ -559,20 +559,6 @@ is_simple_subquery(Query *subquery)
if (expression_returns_set((Node *) subquery->targetList))
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
@ -750,6 +736,14 @@ preprocess_jointree(Query *parse, Node *jtnode)
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.
*
@ -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;
}

View File

@ -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");
}

View File

@ -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

View File

@ -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;

View File

@ -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);
@ -220,47 +214,6 @@ find_base_rel(Query *root, int relid)
return NULL; /* keep compiler quiet */
}
/*
* 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),
@ -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
@ -400,23 +340,6 @@ build_join_rel(Query *root,
length(new_outer_tlist) + 1);
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

View File

@ -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);
}

View File

@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.125 2003/01/12 22:35:29 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.126 2003/01/15 19:35:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1754,8 +1754,8 @@ mergejoinscansel(Query *root, Node *clause,
if (!is_opclause(clause))
return; /* shouldn't happen */
opno = ((OpExpr *) clause)->opno;
left = get_leftop((Expr *) clause);
right = get_rightop((Expr *) clause);
left = (Var *) get_leftop((Expr *) clause);
right = (Var *) get_rightop((Expr *) clause);
if (!right)
return; /* shouldn't happen */
@ -1766,8 +1766,6 @@ mergejoinscansel(Query *root, Node *clause,
/* Verify mergejoinability and get left and right "<" operators */
if (!op_mergejoinable(opno,
left->vartype,
right->vartype,
&lsortop,
&rsortop))
return; /* shouldn't happen */
@ -1892,17 +1890,6 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows)
List *varshere;
varshere = pull_var_clause(groupexpr, false);
/*
* Replace any JOIN alias Vars with the underlying Vars. (This
* is not really right for FULL JOIN ...)
*/
if (root->hasJoinRTEs)
{
varshere = (List *) flatten_join_alias_vars((Node *) varshere,
root->rtable,
true);
varshere = pull_var_clause((Node *) varshere, false);
}
/*
* If we find any variable-free GROUP BY item, then either it is
* a constant (and we can ignore it) or it contains a volatile
@ -1963,7 +1950,7 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows)
l2 = lnext(l2);
if (var->varno != varinfo->var->varno &&
vars_known_equal(root, var, varinfo->var))
exprs_known_equal(root, (Node *) var, (Node *) varinfo->var))
{
/* Found a match */
if (varinfo->ndistinct <= ndistinct)

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.88 2002/12/05 04:04:44 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/utils/cache/lsyscache.c,v 1.89 2003/01/15 19:35:44 tgl Exp $
*
* NOTES
* Eventually, the index information should go through here, too.
@ -315,11 +315,11 @@ get_opname(Oid opno)
/*
* op_mergejoinable
*
* Returns the left and right sort operators and types corresponding to a
* mergejoinable operator, or nil if the operator is not mergejoinable.
* Returns the left and right sort operators corresponding to a
* mergejoinable operator, or false if the operator is not mergejoinable.
*/
bool
op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp)
op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp)
{
HeapTuple tp;
bool result = false;
@ -332,9 +332,7 @@ op_mergejoinable(Oid opno, Oid ltype, Oid rtype, Oid *leftOp, Oid *rightOp)
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
if (optup->oprlsortop &&
optup->oprrsortop &&
optup->oprleft == ltype &&
optup->oprright == rtype)
optup->oprrsortop)
{
*leftOp = optup->oprlsortop;
*rightOp = optup->oprrsortop;
@ -391,14 +389,13 @@ op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
/*
* op_hashjoinable
*
* Returns the hash operator corresponding to a hashjoinable operator,
* or InvalidOid if the operator is not hashjoinable.
* Returns true if the operator is hashjoinable.
*/
Oid
op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
bool
op_hashjoinable(Oid opno)
{
HeapTuple tp;
Oid result = InvalidOid;
bool result = false;
tp = SearchSysCache(OPEROID,
ObjectIdGetDatum(opno),
@ -407,10 +404,7 @@ op_hashjoinable(Oid opno, Oid ltype, Oid rtype)
{
Form_pg_operator optup = (Form_pg_operator) GETSTRUCT(tp);
if (optup->oprcanhash &&
optup->oprleft == ltype &&
optup->oprright == rtype)
result = opno;
result = optup->oprcanhash;
ReleaseSysCache(tp);
}
return result;

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.75 2003/01/12 22:35:29 tgl Exp $
* $Id: relation.h,v 1.76 2003/01/15 19:35:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -50,7 +50,7 @@ typedef struct QualCost
* Per-relation information for planning/optimization
*
* For planning purposes, a "base rel" is either a plain relation (a table)
* or the output of a sub-SELECT that appears in the range table.
* or the output of a sub-SELECT or function that appears in the range table.
* In either case it is uniquely identified by an RT index. A "joinrel"
* is the joining of two or more base rels. A joinrel is identified by
* the set of RT indexes for its component baserels. We create RelOptInfo
@ -63,14 +63,10 @@ typedef struct QualCost
*
* We also have "other rels", which are like base rels in that they refer to
* single RT indexes; but they are not part of the join tree, and are stored
* in other_rel_list not base_rel_list. An otherrel is created for each
* join RTE as an aid in processing Vars that refer to the join's outputs,
* but it serves no other purpose in planning. It is important not to
* confuse this otherrel with the joinrel that represents the matching set
* of base relations.
* in other_rel_list not base_rel_list.
*
* A second category of otherrels are those made for child relations of an
* inheritance scan (SELECT FROM foo*). The parent table's RTE and
* Currently the only kind of otherrels are those made for child relations
* of an inheritance scan (SELECT FROM foo*). The parent table's RTE and
* corresponding baserel represent the whole result of the inheritance scan.
* The planner creates separate RTEs and associated RelOptInfos for each child
* table (including the parent table, in its capacity as a member of the
@ -80,6 +76,10 @@ typedef struct QualCost
* the inheritance set; then the parent baserel is given an Append plan
* comprising the best plans for the individual child tables.
*
* At one time we also made otherrels to represent join RTEs, for use in
* handling join alias Vars. Currently this is not needed because all join
* alias Vars are expanded to non-aliased form during preprocess_expression.
*
* Parts of this data structure are specific to various scan and join
* mechanisms. It didn't seem worth creating new node types for them.
*
@ -114,15 +114,7 @@ typedef struct QualCost
* set_base_rel_pathlist processes the object.
*
* For otherrels that are inheritance children, these fields are filled
* in just as for a baserel. In otherrels for join RTEs, these fields
* are empty --- the only useful field of a join otherrel is its
* outerjoinset.
*
* If the relation is a join relation it will have these fields set:
*
* joinrti - RT index of corresponding JOIN RTE, if any; 0 if none
* joinrteids - List of RT indexes of JOIN RTEs included in this join
* (including joinrti)
* in just as for a baserel.
*
* The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in:
@ -135,8 +127,7 @@ typedef struct QualCost
* outerjoinset - For a base rel: if the rel appears within the nullable
* side of an outer join, the list of all relids
* participating in the highest such outer join; else NIL.
* For a join otherrel: the list of all baserel relids
* syntactically within the join. Otherwise, unused.
* Otherwise, unused.
* joininfo - List of JoinInfo nodes, containing info about each join
* clause in which this relation participates
* index_outer_relids - only used for base rels; list of outer relids
@ -170,7 +161,6 @@ typedef enum RelOptKind
{
RELOPT_BASEREL,
RELOPT_JOINREL,
RELOPT_OTHER_JOIN_REL,
RELOPT_OTHER_CHILD_REL
} RelOptKind;
@ -202,10 +192,6 @@ typedef struct RelOptInfo
double tuples;
struct Plan *subplan; /* if subquery */
/* information about a join rel (not set for base rels!) */
Index joinrti;
List *joinrteids;
/* used by various scans and joins: */
List *baserestrictinfo; /* RestrictInfo structures (if
* base rel) */
@ -275,15 +261,6 @@ typedef struct IndexOptInfo
} IndexOptInfo;
/*
* A Var is considered to belong to a relation if it's either from one
* of the actual base rels making up the relation, or it's a join alias
* var that is included in the relation.
*/
#define VARISRELMEMBER(varno,rel) (intMember((varno), (rel)->relids) || \
intMember((varno), (rel)->joinrteids))
/*
* PathKeys
*
@ -583,6 +560,15 @@ typedef struct RestrictInfo
QualCost eval_cost; /* eval cost of clause; -1 if not yet set */
Selectivity this_selec; /* selectivity; -1 if not yet set */
/*
* If the clause looks useful for joining --- that is, it is a binary
* opclause with nonoverlapping sets of relids referenced in the left
* and right sides --- then these two fields are set to lists of the
* referenced relids. Otherwise they are both NIL.
*/
List *left_relids; /* relids in left side of join clause */
List *right_relids; /* relids in right side of join clause */
/* valid if clause is mergejoinable, else InvalidOid: */
Oid mergejoinoperator; /* copy of clause operator */
Oid left_sortop; /* leftside sortop needed for mergejoin */

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.58 2002/12/14 00:17:59 tgl Exp $
* $Id: clauses.h,v 1.59 2003/01/15 19:35:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -25,8 +25,8 @@
extern Expr *make_opclause(Oid opno, Oid opresulttype, bool opretset,
Expr *leftop, Expr *rightop);
extern Var *get_leftop(Expr *clause);
extern Var *get_rightop(Expr *clause);
extern Node *get_leftop(Expr *clause);
extern Node *get_rightop(Expr *clause);
extern Expr *make_funcclause(Oid funcid, Oid funcresulttype, bool funcretset,
CoercionForm funcformat, List *funcargs);

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pathnode.h,v 1.46 2002/11/30 05:21:03 tgl Exp $
* $Id: pathnode.h,v 1.47 2003/01/15 19:35:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -74,8 +74,6 @@ extern HashPath *create_hashjoin_path(Query *root,
extern void build_base_rel(Query *root, int relid);
extern RelOptInfo *build_other_rel(Query *root, int relid);
extern RelOptInfo *find_base_rel(Query *root, int relid);
extern RelOptInfo *find_other_rel(Query *root, int relid);
extern RelOptInfo *find_other_rel_for_join(Query *root, List *relids);
extern RelOptInfo *build_join_rel(Query *root,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.64 2002/12/12 15:49:41 tgl Exp $
* $Id: planmain.h,v 1.65 2003/01/15 19:35:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -32,8 +32,6 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
extern Sort *make_sort(Query *root, List *tlist,
Plan *lefttree, int keycount);
extern Sort *make_sort_from_pathkeys(Query *root, List *tlist,
Plan *lefttree, List *pathkeys);
extern Agg *make_agg(Query *root, List *tlist, List *qual,
AggStrategy aggstrategy,
int numGroupCols, AttrNumber *grpColIdx,
@ -54,12 +52,12 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
/*
* prototypes for plan/initsplan.c
*/
extern List *add_base_rels_to_query(Query *root, Node *jtnode);
extern void add_base_rels_to_query(Query *root, Node *jtnode);
extern void build_base_rel_tlists(Query *root, List *tlist);
extern Relids distribute_quals_to_rels(Query *root, Node *jtnode);
extern void process_implied_equality(Query *root, Node *item1, Node *item2,
Oid sortop1, Oid sortop2);
extern bool vars_known_equal(Query *root, Var *var1, Var *var2);
extern bool exprs_known_equal(Query *root, Node *item1, Node *item2);
/*
* prototypes for plan/setrefs.c

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: var.h,v 1.23 2002/12/12 20:35:16 tgl Exp $
* $Id: var.h,v 1.24 2003/01/15 19:35:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,6 +23,6 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node);
extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, List *rtable, bool force);
extern Node *flatten_join_alias_vars(Node *node, List *rtable);
#endif /* VAR_H */

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: lsyscache.h,v 1.65 2002/12/01 21:05:14 tgl Exp $
* $Id: lsyscache.h,v 1.66 2003/01/15 19:35:47 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -26,11 +26,10 @@ extern void get_atttypetypmod(Oid relid, AttrNumber attnum,
extern bool opclass_is_btree(Oid opclass);
extern RegProcedure get_opcode(Oid opno);
extern char *get_opname(Oid opno);
extern bool op_mergejoinable(Oid opno, Oid ltype, Oid rtype,
Oid *leftOp, Oid *rightOp);
extern bool op_mergejoinable(Oid opno, Oid *leftOp, Oid *rightOp);
extern void op_mergejoin_crossops(Oid opno, Oid *ltop, Oid *gtop,
RegProcedure *ltproc, RegProcedure *gtproc);
extern Oid op_hashjoinable(Oid opno, Oid ltype, Oid rtype);
extern bool op_hashjoinable(Oid opno);
extern bool op_strict(Oid opno);
extern char op_volatile(Oid opno);
extern Oid get_commutator(Oid opno);

View File

@ -498,6 +498,17 @@ WHERE p1.oprcode = p2.oid AND
-----+---------+-----+---------
(0 rows)
-- If the operator is mergejoinable or hashjoinable, its underlying function
-- should not be volatile.
SELECT p1.oid, p1.oprname, p2.oid, p2.proname
FROM pg_operator AS p1, pg_proc AS p2
WHERE p1.oprcode = p2.oid AND
(p1.oprlsortop != 0 OR p1.oprcanhash) AND
p2.provolatile = 'v';
oid | oprname | oid | proname
-----+---------+-----+---------
(0 rows)
-- If oprrest is set, the operator must return boolean,
-- and it must link to a proc with the right signature
-- to be a restriction selectivity estimator.
@ -583,7 +594,8 @@ WHERE a.aggfnoid = p.oid AND
a.aggtranstype != p2.prorettype OR
a.aggtranstype != p2.proargtypes[0] OR
NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
(p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)));
(p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
ORDER BY 1;
aggfnoid | proname | oid | proname
----------+---------+-----+-------------
2121 | max | 768 | int4larger

View File

@ -107,7 +107,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset);
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
oid | typname | oid | proname
------+-----------+-----+-----------
32 | SET | 109 | unknownin
@ -132,7 +133,8 @@ FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1));
p1.typelem != 0 AND p1.typlen = -1))
ORDER BY 1;
oid | typname | oid | proname
------+-----------+-----+------------
32 | SET | 110 | unknownout

View File

@ -416,6 +416,15 @@ WHERE p1.oprcode = p2.oid AND
(p1.oprleft != p2.proargtypes[0] AND p2.proargtypes[0] != 0) OR
p1.oprright != 0);
-- If the operator is mergejoinable or hashjoinable, its underlying function
-- should not be volatile.
SELECT p1.oid, p1.oprname, p2.oid, p2.proname
FROM pg_operator AS p1, pg_proc AS p2
WHERE p1.oprcode = p2.oid AND
(p1.oprlsortop != 0 OR p1.oprcanhash) AND
p2.provolatile = 'v';
-- If oprrest is set, the operator must return boolean,
-- and it must link to a proc with the right signature
-- to be a restriction selectivity estimator.
@ -490,7 +499,8 @@ WHERE a.aggfnoid = p.oid AND
a.aggtranstype != p2.prorettype OR
a.aggtranstype != p2.proargtypes[0] OR
NOT ((p2.pronargs = 2 AND p.proargtypes[0] = p2.proargtypes[1]) OR
(p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)));
(p2.pronargs = 1 AND p.proargtypes[0] = '"any"'::regtype)))
ORDER BY 1;
-- Cross-check finalfn (if present) against its entry in pg_proc.
-- FIXME: what about binary-compatible types?

View File

@ -90,7 +90,8 @@ SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typinput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
(p1.typelem != 0 AND p1.typlen < 0) AND NOT
(p2.prorettype = p1.oid AND NOT p2.proretset);
(p2.prorettype = p1.oid AND NOT p2.proretset)
ORDER BY 1;
-- Varlena array types will point to array_in
SELECT p1.oid, p1.typname, p2.oid, p2.proname
@ -108,7 +109,8 @@ FROM pg_type AS p1, pg_proc AS p2
WHERE p1.typoutput = p2.oid AND p1.typtype in ('b', 'p') AND NOT
((p2.pronargs = 1 AND p2.proargtypes[0] = p1.oid) OR
(p2.oid = 'array_out'::regproc AND
p1.typelem != 0 AND p1.typlen = -1));
p1.typelem != 0 AND p1.typlen = -1))
ORDER BY 1;
SELECT p1.oid, p1.typname, p2.oid, p2.proname
FROM pg_type AS p1, pg_proc AS p2