Commit cd9f0ca5 authored by Tom Lane's avatar Tom Lane

Deduce equality constraints that are implied by transitivity of

mergejoinable qual clauses, and add them to the query quals.  For
example, WHERE a = b AND b = c will cause us to add AND a = c.
This is necessary to ensure that it's safe to use these variables
as interchangeable sort keys, which is something 7.0 knows how to do.
Should provide a useful improvement in planning ability, too.
parent c39c198b
This diff is collapsed.
This diff is collapsed.
......@@ -8,13 +8,14 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.46 2000/04/12 17:15:21 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.47 2000/07/24 03:11:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include "postgres.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
......@@ -25,6 +26,9 @@
#include "optimizer/planmain.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parse_expr.h"
#include "parser/parse_oper.h"
#include "parser/parse_type.h"
#include "utils/lsyscache.h"
......@@ -122,6 +126,7 @@ add_missing_rels_to_query(Query *root)
}
}
/*****************************************************************************
*
* QUALIFICATIONS
......@@ -129,7 +134,6 @@ add_missing_rels_to_query(Query *root)
*****************************************************************************/
/*
* add_restrict_and_join_to_rels
* Fill RestrictInfo and JoinInfo lists of relation entries for all
......@@ -280,6 +284,113 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
}
}
/*
* process_implied_equality
* Check to see whether we already have a restrictinfo item that says
* item1 = item2, and create one if not. This is a consequence of
* transitivity of mergejoin equality: if we have mergejoinable
* clauses A = B and B = C, we can deduce A = C (where = is an
* appropriate mergejoinable operator).
*/
void
process_implied_equality(Query *root, Node *item1, Node *item2,
Oid sortop1, Oid sortop2)
{
Index irel1;
Index irel2;
RelOptInfo *rel1;
List *restrictlist;
List *itm;
Oid ltype,
rtype;
Operator eq_operator;
Form_pg_operator pgopform;
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 = get_base_rel(root, irel1);
if (irel1 == irel2)
restrictlist = rel1->baserestrictinfo;
else
{
JoinInfo *joininfo = find_joininfo_node(rel1,
lconsi(irel2, NIL));
restrictlist = joininfo->jinfo_restrictinfo;
}
/*
* Scan to see if equality is already known.
*/
foreach(itm, restrictlist)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(itm);
Node *left,
*right;
if (restrictinfo->mergejoinoperator == InvalidOid)
continue; /* ignore non-mergejoinable clauses */
/* We now know the restrictinfo clause is a binary opclause */
left = (Node *) get_leftop(restrictinfo->clause);
right = (Node *) get_rightop(restrictinfo->clause);
if ((equal(item1, left) && equal(item2, right)) ||
(equal(item2, left) && equal(item1, right)))
return; /* found a matching clause */
}
/*
* This equality is new information, so construct a clause
* representing it to add to the query data structures.
*/
ltype = exprType(item1);
rtype = exprType(item2);
eq_operator = oper("=", ltype, rtype, true);
if (!HeapTupleIsValid(eq_operator))
{
/*
* Would it be safe to just not add the equality to the query if
* we have no suitable equality operator for the combination of
* datatypes? NO, because sortkey selection may screw up anyway.
*/
elog(ERROR, "Unable to identify an equality operator for types '%s' and '%s'",
typeidTypeName(ltype), typeidTypeName(rtype));
}
pgopform = (Form_pg_operator) GETSTRUCT(eq_operator);
/*
* Let's just make sure this appears to be a compatible operator.
*/
if (pgopform->oprlsortop != sortop1 ||
pgopform->oprrsortop != sortop2 ||
pgopform->oprresult != BOOLOID)
elog(ERROR, "Equality operator for types '%s' and '%s' should be mergejoinable, but isn't",
typeidTypeName(ltype), typeidTypeName(rtype));
clause = makeNode(Expr);
clause->typeOid = BOOLOID;
clause->opType = OP_EXPR;
clause->oper = (Node *) makeOper(oprid(eq_operator), /* opno */
InvalidOid, /* opid */
BOOLOID, /* operator result type */
0,
NULL);
clause->args = lcons(item1, lcons(item2, NIL));
add_restrict_and_join_to_rel(root, (Node *) clause);
}
/*****************************************************************************
*
* CHECKS FOR MERGEJOINABLE AND HASHJOINABLE CLAUSES
......
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.55 2000/04/12 17:15:22 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.56 2000/07/24 03:11:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -184,7 +184,7 @@ subplanner(Query *root,
* base_rel_list as relation references are found (e.g., in the
* qualification, the targetlist, etc.). Restrict and join clauses
* are added to appropriate lists belonging to the mentioned
* relations, and we also build lists of equijoined keys for pathkey
* relations. We also build lists of equijoined keys for pathkey
* construction.
*/
root->base_rel_list = NIL;
......@@ -193,8 +193,18 @@ subplanner(Query *root,
make_var_only_tlist(root, flat_tlist);
add_restrict_and_join_to_rels(root, qual);
/*
* Make sure we have RelOptInfo nodes for all relations used.
*/
add_missing_rels_to_query(root);
/*
* Use the completed lists of equijoined keys to deduce any implied
* but unstated equalities (for example, A=B and B=C imply A=C).
*/
generate_implied_equalities(root);
/*
* We should now have all the pathkey equivalence sets built, so it's
* now possible to convert the requested query_pathkeys to canonical
......
......@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: paths.h,v 1.45 2000/05/31 00:28:38 petere Exp $
* $Id: paths.h,v 1.46 2000/07/24 03:10:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -90,6 +90,7 @@ typedef enum
} PathKeysComparison;
extern void add_equijoined_keys(Query *root, RestrictInfo *restrictinfo);
extern void generate_implied_equalities(Query *root);
extern List *canonicalize_pathkeys(Query *root, List *pathkeys);
extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
extern bool pathkeys_contained_in(List *keys1, List *keys2);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: planmain.h,v 1.42 2000/06/18 22:44:33 tgl Exp $
* $Id: planmain.h,v 1.43 2000/07/24 03:10:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -43,6 +43,8 @@ extern Result *make_result(List *tlist, Node *resconstantqual, Plan *subplan);
extern void make_var_only_tlist(Query *root, List *tlist);
extern void add_restrict_and_join_to_rels(Query *root, List *clauses);
extern void add_missing_rels_to_query(Query *root);
extern void process_implied_equality(Query *root, Node *item1, Node *item2,
Oid sortop1, Oid sortop2);
/*
* prototypes for plan/setrefs.c
......
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