Commit de97072e authored by Tom Lane's avatar Tom Lane

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.
parent 0eed62f3
<!--
$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
......
......@@ -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);
......
......@@ -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);
......
......@@ -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);
......
......@@ -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");
......
......@@ -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
......
......@@ -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 */
}
......
......@@ -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);
......
......@@ -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;
......
......@@ -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;
......
......@@ -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 */
}
/*
......
This diff is collapsed.
This diff is collapsed.
......@@ -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
......
......@@ -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;
}
......
......@@ -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");
}
......
......@@ -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
......
......@@ -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;
......
......@@ -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
......
......@@ -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);
}
......@@ -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)
......
......@@ -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;
......
......@@ -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.
*
* A second category of otherrels are those made for child relations of an
* inheritance scan (SELECT FROM foo*). The parent table's RTE and
* in other_rel_list not base_rel_list.
*
* 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 */
......
......@@ -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);
......
......@@ -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,
......
......@@ -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
......
......@@ -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 */
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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?
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment