<!--
-$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">
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>
</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
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
*
* 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*
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);
*
*
* 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
{
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");
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
}
* 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 $
*
*-------------------------------------------------------------------------
*/
Cost cpu_per_tuple;
QualCost restrict_qual_cost;
RestrictInfo *firstclause;
- Var *leftvar;
double outer_rows,
inner_rows;
double ntuples;
&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;
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
* 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;
}
* 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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
Oid opclass,
Expr *clause)
{
- Var *leftop,
+ Node *leftop,
*rightop;
/* Clause must be a binary opclause. */
* 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;
}
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;
Oid opclass,
Expr *clause)
{
- Var *leftop,
+ Node *leftop,
*rightop;
/* Clause must be a binary opclause. */
*/
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;
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;
*/
static bool
match_index_to_operand(int indexkey,
- Var *operand,
+ Node *operand,
RelOptInfo *rel,
IndexOptInfo *index)
{
* 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)
{
* 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;
bool indexkey_on_left)
{
bool isIndexable = false;
- Var *leftop,
+ Node *leftop,
*rightop;
Oid expr_op;
Const *patt = NULL;
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;
* 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;
* 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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
/*
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 */
}
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
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:
}
}
- 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;
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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;
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);
}
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 */
}
/*
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
List *mergeclauses,
Plan *lefttree, Plan *righttree,
JoinType jointype);
+static Sort *make_sort_from_pathkeys(Query *root, Plan *lefttree,
+ List *relids, List *pathkeys);
+
/*
* create_plan
{
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))
{
joinclauses,
otherclauses,
outer_plan,
- outer_tlist,
- inner_plan,
- inner_tlist);
+ inner_plan);
break;
case T_HashJoin:
plan = (Join *) create_hashjoin_plan(root,
joinclauses,
otherclauses,
outer_plan,
- outer_tlist,
- inner_plan,
- inner_tlist);
+ inner_plan);
break;
case T_NestLoop:
plan = (Join *) create_nestloop_plan(root,
joinclauses,
otherclauses,
outer_plan,
- outer_tlist,
- inner_plan,
- inner_tlist);
+ inner_plan);
break;
default:
elog(ERROR, "create_join_plan: unknown node type: %d",
*
* 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).
*
*****************************************************************************/
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))
List *joinclauses,
List *otherclauses,
Plan *outer_plan,
- List *outer_tlist,
- Plan *inner_plan,
- List *inner_tlist)
+ Plan *inner_plan)
{
+ List *outer_tlist = outer_plan->targetlist;
+ List *inner_tlist = inner_plan->targetlist;
List *mergeclauses;
MergeJoin *join_plan;
+ /*
+ * Remove the mergeclauses from the list of join qual clauses, leaving
+ * the list of quals that must be checked as qpquals.
+ */
mergeclauses = get_actual_clauses(best_path->path_mergeclauses);
+ joinclauses = set_difference(joinclauses, mergeclauses);
/*
- * Remove the mergeclauses from the list of join qual clauses, leaving
- * the list of quals that must be checked as qpquals. Set those
- * clauses to contain INNER/OUTER var references.
+ * Rearrange mergeclauses, if needed, so that the outer variable
+ * is always on the left.
*/
- joinclauses = join_references(set_difference(joinclauses, mergeclauses),
+ mergeclauses = get_switched_clauses(best_path->path_mergeclauses,
+ best_path->jpath.outerjoinpath->parent->relids);
+
+ /*
+ * Fix all the join clauses to contain INNER/OUTER var references.
+ */
+ joinclauses = join_references(joinclauses,
root->rtable,
outer_tlist,
inner_tlist,
(Index) 0);
-
- /*
- * Fix the additional qpquals too.
- */
otherclauses = join_references(otherclauses,
root->rtable,
outer_tlist,
inner_tlist,
(Index) 0);
-
- /*
- * Now set the references in the mergeclauses and rearrange them so
- * that the outer variable is always on the left.
- */
- mergeclauses = switch_outer(join_references(mergeclauses,
- root->rtable,
- outer_tlist,
- inner_tlist,
- (Index) 0));
+ mergeclauses = join_references(mergeclauses,
+ root->rtable,
+ outer_tlist,
+ inner_tlist,
+ (Index) 0);
/*
* Create explicit sort nodes for the outer and inner join paths if
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);
/*
List *joinclauses,
List *otherclauses,
Plan *outer_plan,
- List *outer_tlist,
- Plan *inner_plan,
- List *inner_tlist)
+ Plan *inner_plan)
{
+ List *outer_tlist = outer_plan->targetlist;
+ List *inner_tlist = inner_plan->targetlist;
List *hashclauses;
HashJoin *join_plan;
Hash *hash_plan;
List *innerhashkeys;
List *hcl;
+ /*
+ * Remove the hashclauses from the list of join qual clauses, leaving
+ * the list of quals that must be checked as qpquals.
+ */
hashclauses = get_actual_clauses(best_path->path_hashclauses);
+ joinclauses = set_difference(joinclauses, hashclauses);
/*
- * Remove the hashclauses from the list of join qual clauses, leaving
- * the list of quals that must be checked as qpquals. Set those
- * clauses to contain INNER/OUTER var references.
+ * Rearrange hashclauses, if needed, so that the outer variable
+ * is always on the left.
+ */
+ hashclauses = get_switched_clauses(best_path->path_hashclauses,
+ best_path->jpath.outerjoinpath->parent->relids);
+
+ /*
+ * Fix all the join clauses to contain INNER/OUTER var references.
*/
- joinclauses = join_references(set_difference(joinclauses, hashclauses),
+ joinclauses = join_references(joinclauses,
root->rtable,
outer_tlist,
inner_tlist,
(Index) 0);
-
- /*
- * Fix the additional qpquals too.
- */
otherclauses = join_references(otherclauses,
root->rtable,
outer_tlist,
inner_tlist,
(Index) 0);
-
- /*
- * Now set the references in the hashclauses and rearrange them so
- * that the outer variable is always on the left.
- */
- hashclauses = switch_outer(join_references(hashclauses,
- root->rtable,
- outer_tlist,
- inner_tlist,
- (Index) 0));
+ hashclauses = join_references(hashclauses,
+ root->rtable,
+ outer_tlist,
+ inner_tlist,
+ (Index) 0);
/*
* Extract the inner hash keys (right-hand operands of the hashclauses)
}
/*
- * 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
* 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;
/*
* 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
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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))
{
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
*/
else
elog(ERROR, "add_base_rels_to_query: unexpected node type %d",
nodeTag(jtnode));
- return result;
}
* 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)
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);
- }
}
}
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;
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
/*
* '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
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;
Expr *clause;
/*
- * Currently, since check_mergejoinable only accepts Var = Var
- * clauses, we should only see Var nodes here. Would have to work a
- * little harder to locate the right rel(s) if more-general mergejoin
- * clauses were accepted.
- */
- Assert(IsA(item1, Var));
- irel1 = ((Var *) item1)->varno;
- Assert(IsA(item2, Var));
- irel2 = ((Var *) item2)->varno;
-
- /*
- * If both vars belong to same rel, we need to look at that rel's
- * baserestrictinfo list. If different rels, each will have a
- * joininfo node for the other, and we can scan either list.
- */
- rel1 = find_base_rel(root, irel1);
- if (irel1 == irel2)
- restrictlist = rel1->baserestrictinfo;
- else
- {
- JoinInfo *joininfo = find_joininfo_node(rel1,
- makeListi1(irel2));
-
- restrictlist = joininfo->jinfo_restrictinfo;
- }
-
- /*
- * Scan to see if equality is already known.
+ * Forget it if this equality is already recorded.
+ *
+ * Note: if only a single relation is involved, we may fall through
+ * here and end up rejecting the equality later on in qual_is_redundant.
+ * This is a tad slow but should be okay.
*/
- foreach(itm, restrictlist)
- {
- RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
- Node *left,
- *right;
-
- if (restrictinfo->mergejoinoperator == InvalidOid)
- continue; /* ignore non-mergejoinable clauses */
- /* We now know the restrictinfo clause is a binary opclause */
- left = (Node *) get_leftop(restrictinfo->clause);
- right = (Node *) get_rightop(restrictinfo->clause);
- if ((equal(item1, left) && equal(item2, right)) ||
- (equal(item2, left) && equal(item1, right)))
- return; /* found a matching clause */
- }
+ if (exprs_known_equal(root, item1, item2))
+ return;
/*
* This equality is new information, so construct a clause
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.
*/
}
/*
- * vars_known_equal
- * Detect whether two Vars are known equal due to equijoin clauses.
+ * exprs_known_equal
+ * Detect whether two expressions are known equal due to equijoin clauses.
*
* This is not completely accurate since we avoid adding redundant restriction
* clauses to individual base rels (see qual_is_redundant). However, after
- * the implied-equality-deduction phase, it is complete for Vars of different
- * rels; that's sufficient for planned uses.
+ * the implied-equality-deduction phase, it is complete for expressions
+ * involving Vars of multiple rels; that's sufficient for planned uses.
*/
bool
-vars_known_equal(Query *root, Var *var1, Var *var2)
+exprs_known_equal(Query *root, Node *item1, Node *item2)
{
- Index irel1;
- Index irel2;
+ List *relids;
RelOptInfo *rel1;
List *restrictlist;
List *itm;
+ /* Get list of relids referenced in the two expressions */
+ relids = set_unioni(pull_varnos(item1), pull_varnos(item2));
+
/*
- * Would need more work here if we wanted to check for known equality
- * of general clauses: there might be multiple base rels involved.
+ * If there are no Vars at all, say "true". This prevents
+ * process_implied_equality from trying to store "const = const"
+ * deductions.
*/
- Assert(IsA(var1, Var));
- irel1 = var1->varno;
- Assert(IsA(var2, Var));
- irel2 = var2->varno;
+ if (relids == NIL)
+ return true;
/*
- * If both vars belong to same rel, we need to look at that rel's
- * baserestrictinfo list. If different rels, each will have a
- * joininfo node for the other, and we can scan either list.
+ * If the exprs involve a single rel, we need to look at that rel's
+ * baserestrictinfo list. If multiple rels, any one will have a
+ * joininfo node for the rest, and we can scan any of 'em.
*/
- rel1 = find_base_rel(root, irel1);
- if (irel1 == irel2)
+ rel1 = find_base_rel(root, lfirsti(relids));
+ relids = lnext(relids);
+ if (relids == NIL)
restrictlist = rel1->baserestrictinfo;
else
{
- JoinInfo *joininfo = find_joininfo_node(rel1,
- makeListi1(irel2));
+ JoinInfo *joininfo = find_joininfo_node(rel1, relids);
restrictlist = joininfo->jinfo_restrictinfo;
}
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 */
}
List *olditem;
Node *newleft;
Node *newright;
- List *equalvars;
+ List *equalexprs;
bool someadded;
/*
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;
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;
/*
* 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;
* 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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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
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.
*
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;
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
* 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;
{
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);
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");
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/*
- * 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))
{
/* 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 */
* 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
*
*
* 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
*
* 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;
* 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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
/* 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);
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;
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);
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),
{
List *joinrelids;
RelOptInfo *joinrel;
- RelOptInfo *joinrterel;
List *restrictlist;
List *new_outer_tlist;
List *new_inner_tlist;
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;
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
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
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
#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;
typedef struct
{
List *rtable;
- bool force;
+ int sublevels_up;
} flatten_join_alias_vars_context;
static bool pull_varnos_walker(Node *node,
* 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);
}
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);
}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
/* Verify mergejoinability and get left and right "<" operators */
if (!op_mergejoinable(opno,
- left->vartype,
- right->vartype,
&lsortop,
&rsortop))
return; /* shouldn't happen */
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
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)
* 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.
/*
* 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;
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;
/*
* 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),
{
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;
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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
*
* 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
* 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.
*
* 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:
* 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
{
RELOPT_BASEREL,
RELOPT_JOINREL,
- RELOPT_OTHER_JOIN_REL,
RELOPT_OTHER_CHILD_REL
} RelOptKind;
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) */
} 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
*
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
/*
* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
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 */
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
-----+---------+-----+---------
(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.
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
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
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
(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.
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?
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
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