mirror of
https://github.com/postgres/postgres.git
synced 2025-05-22 00:02:02 -04:00
Extend code that deduces implied equality clauses to detect whether a
clause being added to a particular restriction-clause list is redundant with those already in the list. This avoids useless work at runtime, and (perhaps more importantly) keeps the selectivity estimation routines from generating too-small estimates of numbers of output rows. Also some minor improvements in OPTIMIZER_DEBUG displays.
This commit is contained in:
parent
5045004958
commit
6254465d06
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.47 2001/03/22 03:59:32 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.48 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
* HISTORY
|
* HISTORY
|
||||||
* AUTHOR DATE MAJOR EVENT
|
* AUTHOR DATE MAJOR EVENT
|
||||||
@ -20,10 +20,12 @@
|
|||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
#include "access/printtup.h"
|
#include "access/printtup.h"
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
#include "nodes/print.h"
|
#include "nodes/print.h"
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
#include "utils/lsyscache.h"
|
#include "utils/lsyscache.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
static char *plannode_type(Plan *p);
|
static char *plannode_type(Plan *p);
|
||||||
|
|
||||||
@ -188,6 +190,36 @@ print_expr(Node *expr, List *rtable)
|
|||||||
}
|
}
|
||||||
printf("%s.%s", relname, attname);
|
printf("%s.%s", relname, attname);
|
||||||
}
|
}
|
||||||
|
else if (IsA(expr, Const))
|
||||||
|
{
|
||||||
|
Const *c = (Const *) expr;
|
||||||
|
HeapTuple typeTup;
|
||||||
|
Oid typoutput;
|
||||||
|
Oid typelem;
|
||||||
|
char *outputstr;
|
||||||
|
|
||||||
|
if (c->constisnull)
|
||||||
|
{
|
||||||
|
printf("NULL");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
typeTup = SearchSysCache(TYPEOID,
|
||||||
|
ObjectIdGetDatum(c->consttype),
|
||||||
|
0, 0, 0);
|
||||||
|
if (!HeapTupleIsValid(typeTup))
|
||||||
|
elog(ERROR, "Cache lookup for type %u failed", c->consttype);
|
||||||
|
typoutput = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput;
|
||||||
|
typelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem;
|
||||||
|
ReleaseSysCache(typeTup);
|
||||||
|
|
||||||
|
outputstr = DatumGetCString(OidFunctionCall3(typoutput,
|
||||||
|
c->constvalue,
|
||||||
|
ObjectIdGetDatum(typelem),
|
||||||
|
Int32GetDatum(-1)));
|
||||||
|
printf("%s", outputstr);
|
||||||
|
pfree(outputstr);
|
||||||
|
}
|
||||||
else if (IsA(expr, Expr))
|
else if (IsA(expr, Expr))
|
||||||
{
|
{
|
||||||
Expr *e = (Expr *) expr;
|
Expr *e = (Expr *) expr;
|
||||||
@ -232,7 +264,7 @@ print_pathkeys(List *pathkeys, List *rtable)
|
|||||||
if (lnext(k))
|
if (lnext(k))
|
||||||
printf(", ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
printf(") ");
|
printf(")");
|
||||||
if (lnext(i))
|
if (lnext(i))
|
||||||
printf(", ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
|
@ -461,6 +461,23 @@ code than it would otherwise have, and reduces the number of tuples
|
|||||||
processed in join stages, so it's a win to make these deductions even
|
processed in join stages, so it's a win to make these deductions even
|
||||||
if we weren't forced to.
|
if we weren't forced to.
|
||||||
|
|
||||||
|
When we generate implied equality constraints, we may find ourselves
|
||||||
|
adding redundant clauses to specific relations. For example, consider
|
||||||
|
SELECT * FROM t1, t2, t3 WHERE t1.a = t2.b AND t2.b = t3.c;
|
||||||
|
We will generate the implied clause t1.a = t3.c and add it to the tree.
|
||||||
|
This is good since it allows us to consider joining t1 and t3 directly,
|
||||||
|
which we otherwise wouldn't do. But when we reach the stage of joining
|
||||||
|
all three relations, we will have redundant join clauses --- eg, if we
|
||||||
|
join t1 and t2 first, then the path that joins (t1 t2) to t3 will have
|
||||||
|
both t2.b = t3.c and t1.a = t3.c as restriction clauses. This is bad;
|
||||||
|
not only is evaluation of the extra clause useless work at runtime,
|
||||||
|
but the selectivity estimator routines will underestimate the number
|
||||||
|
of tuples produced since they won't know that the two clauses are
|
||||||
|
perfectly redundant. We fix this by detecting and removing redundant
|
||||||
|
clauses as the restriction clause list is built for each join. (We
|
||||||
|
can't do it sooner, since which clauses are redundant will vary depending
|
||||||
|
on the join order.)
|
||||||
|
|
||||||
Yet another implication of all this is that mergejoinable operators
|
Yet another implication of all this is that mergejoinable operators
|
||||||
must form closed equivalence sets. For example, if "int2 = int4"
|
must form closed equivalence sets. For example, if "int2 = int4"
|
||||||
and "int4 = int8" are both marked mergejoinable, then there had better
|
and "int4 = int8" are both marked mergejoinable, then there had better
|
||||||
|
@ -8,13 +8,16 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.78 2001/07/31 17:56:30 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.79 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "postgres.h"
|
#include "postgres.h"
|
||||||
|
|
||||||
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
#include "nodes/print.h"
|
||||||
|
#endif
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
#include "optimizer/geqo.h"
|
#include "optimizer/geqo.h"
|
||||||
@ -42,11 +45,6 @@ static void set_subquery_pathlist(Query *root, RelOptInfo *rel,
|
|||||||
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
|
static RelOptInfo *make_one_rel_by_joins(Query *root, int levels_needed,
|
||||||
List *initial_rels);
|
List *initial_rels);
|
||||||
|
|
||||||
#ifdef OPTIMIZER_DEBUG
|
|
||||||
static void debug_print_rel(Query *root, RelOptInfo *rel);
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* make_one_rel
|
* make_one_rel
|
||||||
@ -116,6 +114,10 @@ set_base_rel_pathlists(Query *root)
|
|||||||
/* Plain relation */
|
/* Plain relation */
|
||||||
set_plain_rel_pathlist(root, rel, rte);
|
set_plain_rel_pathlist(root, rel, rte);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
debug_print_rel(root, rel);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -520,10 +522,22 @@ make_one_rel_by_joins(Query *root, int levels_needed, List *initial_rels)
|
|||||||
#ifdef OPTIMIZER_DEBUG
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_joinclauses(Query *root, List *clauses)
|
print_relids(Relids relids)
|
||||||
|
{
|
||||||
|
List *l;
|
||||||
|
|
||||||
|
foreach(l, relids)
|
||||||
|
{
|
||||||
|
printf("%d", lfirsti(l));
|
||||||
|
if (lnext(l))
|
||||||
|
printf(" ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
print_restrictclauses(Query *root, List *clauses)
|
||||||
{
|
{
|
||||||
List *l;
|
List *l;
|
||||||
extern void print_expr(Node *expr, List *rtable); /* in print.c */
|
|
||||||
|
|
||||||
foreach(l, clauses)
|
foreach(l, clauses)
|
||||||
{
|
{
|
||||||
@ -531,21 +545,17 @@ print_joinclauses(Query *root, List *clauses)
|
|||||||
|
|
||||||
print_expr((Node *) c->clause, root->rtable);
|
print_expr((Node *) c->clause, root->rtable);
|
||||||
if (lnext(l))
|
if (lnext(l))
|
||||||
printf(" ");
|
printf(", ");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
print_path(Query *root, Path *path, int indent)
|
print_path(Query *root, Path *path, int indent)
|
||||||
{
|
{
|
||||||
char *ptype = NULL;
|
const char *ptype;
|
||||||
JoinPath *jp;
|
bool join;
|
||||||
bool join = false;
|
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < indent; i++)
|
|
||||||
printf("\t");
|
|
||||||
|
|
||||||
switch (nodeTag(path))
|
switch (nodeTag(path))
|
||||||
{
|
{
|
||||||
case T_Path:
|
case T_Path:
|
||||||
@ -556,6 +566,10 @@ print_path(Query *root, Path *path, int indent)
|
|||||||
ptype = "IdxScan";
|
ptype = "IdxScan";
|
||||||
join = false;
|
join = false;
|
||||||
break;
|
break;
|
||||||
|
case T_TidPath:
|
||||||
|
ptype = "TidScan";
|
||||||
|
join = false;
|
||||||
|
break;
|
||||||
case T_NestPath:
|
case T_NestPath:
|
||||||
ptype = "Nestloop";
|
ptype = "Nestloop";
|
||||||
join = true;
|
join = true;
|
||||||
@ -569,82 +583,82 @@ print_path(Query *root, Path *path, int indent)
|
|||||||
join = true;
|
join = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
ptype = "???Path";
|
||||||
|
join = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < indent; i++)
|
||||||
|
printf("\t");
|
||||||
|
printf("%s(", ptype);
|
||||||
|
print_relids(path->parent->relids);
|
||||||
|
printf(") rows=%.0f cost=%.2f..%.2f\n",
|
||||||
|
path->parent->rows, path->startup_cost, path->total_cost);
|
||||||
|
|
||||||
|
if (path->pathkeys)
|
||||||
|
{
|
||||||
|
for (i = 0; i < indent; i++)
|
||||||
|
printf("\t");
|
||||||
|
printf(" pathkeys: ");
|
||||||
|
print_pathkeys(path->pathkeys, root->rtable);
|
||||||
|
}
|
||||||
|
|
||||||
if (join)
|
if (join)
|
||||||
{
|
{
|
||||||
jp = (JoinPath *) path;
|
JoinPath *jp = (JoinPath *) path;
|
||||||
|
|
||||||
printf("%s rows=%.0f cost=%.2f..%.2f\n",
|
for (i = 0; i < indent; i++)
|
||||||
ptype, path->parent->rows,
|
printf("\t");
|
||||||
path->startup_cost, path->total_cost);
|
printf(" clauses: ");
|
||||||
|
print_restrictclauses(root, jp->joinrestrictinfo);
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
if (path->pathkeys)
|
if (nodeTag(path) == T_MergePath)
|
||||||
{
|
{
|
||||||
for (i = 0; i < indent; i++)
|
MergePath *mp = (MergePath *) path;
|
||||||
printf("\t");
|
|
||||||
printf(" pathkeys=");
|
|
||||||
print_pathkeys(path->pathkeys, root->rtable);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (nodeTag(path))
|
if (mp->outersortkeys || mp->innersortkeys)
|
||||||
{
|
{
|
||||||
case T_MergePath:
|
|
||||||
case T_HashPath:
|
|
||||||
for (i = 0; i < indent; i++)
|
for (i = 0; i < indent; i++)
|
||||||
printf("\t");
|
printf("\t");
|
||||||
printf(" clauses=(");
|
printf(" sortouter=%d sortinner=%d\n",
|
||||||
print_joinclauses(root, jp->joinrestrictinfo);
|
((mp->outersortkeys) ? 1 : 0),
|
||||||
printf(")\n");
|
((mp->innersortkeys) ? 1 : 0));
|
||||||
|
}
|
||||||
if (nodeTag(path) == T_MergePath)
|
|
||||||
{
|
|
||||||
MergePath *mp = (MergePath *) path;
|
|
||||||
|
|
||||||
if (mp->outersortkeys || mp->innersortkeys)
|
|
||||||
{
|
|
||||||
for (i = 0; i < indent; i++)
|
|
||||||
printf("\t");
|
|
||||||
printf(" sortouter=%d sortinner=%d\n",
|
|
||||||
((mp->outersortkeys) ? 1 : 0),
|
|
||||||
((mp->innersortkeys) ? 1 : 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print_path(root, jp->outerjoinpath, indent + 1);
|
print_path(root, jp->outerjoinpath, indent + 1);
|
||||||
print_path(root, jp->innerjoinpath, indent + 1);
|
print_path(root, jp->innerjoinpath, indent + 1);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
int relid = lfirsti(path->parent->relids);
|
|
||||||
|
|
||||||
printf("%s(%d) rows=%.0f cost=%.2f..%.2f\n",
|
|
||||||
ptype, relid, path->parent->rows,
|
|
||||||
path->startup_cost, path->total_cost);
|
|
||||||
|
|
||||||
if (path->pathkeys)
|
|
||||||
{
|
|
||||||
for (i = 0; i < indent; i++)
|
|
||||||
printf("\t");
|
|
||||||
printf(" pathkeys=");
|
|
||||||
print_pathkeys(path->pathkeys, root->rtable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
void
|
||||||
debug_print_rel(Query *root, RelOptInfo *rel)
|
debug_print_rel(Query *root, RelOptInfo *rel)
|
||||||
{
|
{
|
||||||
List *l;
|
List *l;
|
||||||
|
|
||||||
printf("(");
|
printf("RELOPTINFO (");
|
||||||
foreach(l, rel->relids)
|
print_relids(rel->relids);
|
||||||
printf("%d ", lfirsti(l));
|
|
||||||
printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
|
printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
|
||||||
|
|
||||||
|
if (rel->baserestrictinfo)
|
||||||
|
{
|
||||||
|
printf("\tbaserestrictinfo: ");
|
||||||
|
print_restrictclauses(root, rel->baserestrictinfo);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(l, rel->joininfo)
|
||||||
|
{
|
||||||
|
JoinInfo *j = (JoinInfo *) lfirst(l);
|
||||||
|
|
||||||
|
printf("\tjoininfo (");
|
||||||
|
print_relids(j->unjoined_relids);
|
||||||
|
printf("): ");
|
||||||
|
print_restrictclauses(root, j->jinfo_restrictinfo);
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
printf("\tpath list:\n");
|
printf("\tpath list:\n");
|
||||||
foreach(l, rel->pathlist)
|
foreach(l, rel->pathlist)
|
||||||
print_path(root, lfirst(l), 1);
|
print_path(root, lfirst(l), 1);
|
||||||
@ -652,6 +666,8 @@ debug_print_rel(Query *root, RelOptInfo *rel)
|
|||||||
print_path(root, rel->cheapest_startup_path, 1);
|
print_path(root, rel->cheapest_startup_path, 1);
|
||||||
printf("\n\tcheapest total path:\n");
|
printf("\n\tcheapest total path:\n");
|
||||||
print_path(root, rel->cheapest_total_path, 1);
|
print_path(root, rel->cheapest_total_path, 1);
|
||||||
|
printf("\n");
|
||||||
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* OPTIMIZER_DEBUG */
|
#endif /* OPTIMIZER_DEBUG */
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.53 2001/05/20 20:28:18 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.54 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -374,6 +374,10 @@ make_jointree_rel(Query *root, Node *jtnode)
|
|||||||
*/
|
*/
|
||||||
set_cheapest(rel);
|
set_cheapest(rel);
|
||||||
|
|
||||||
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
debug_print_rel(root, rel);
|
||||||
|
#endif
|
||||||
|
|
||||||
return rel;
|
return rel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.32 2001/03/22 06:16:14 momjian Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.33 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -705,7 +705,7 @@ make_pathkeys_for_sortclauses(List *sortclauses,
|
|||||||
* This is a worthwhile savings because these routines will be invoked
|
* This is a worthwhile savings because these routines will be invoked
|
||||||
* many times when dealing with a many-relation query.
|
* many times when dealing with a many-relation query.
|
||||||
*/
|
*/
|
||||||
static void
|
void
|
||||||
cache_mergeclause_pathkeys(Query *root, RestrictInfo *restrictinfo)
|
cache_mergeclause_pathkeys(Query *root, RestrictInfo *restrictinfo)
|
||||||
{
|
{
|
||||||
Node *key;
|
Node *key;
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.63 2001/06/05 05:26:04 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.64 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -40,10 +40,13 @@ static void mark_baserels_for_outer_join(Query *root, Relids rels,
|
|||||||
static void distribute_qual_to_rels(Query *root, Node *clause,
|
static void distribute_qual_to_rels(Query *root, Node *clause,
|
||||||
bool ispusheddown,
|
bool ispusheddown,
|
||||||
bool isouterjoin,
|
bool isouterjoin,
|
||||||
|
bool isdeduced,
|
||||||
Relids qualscope);
|
Relids qualscope);
|
||||||
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
|
||||||
Relids join_relids);
|
Relids join_relids);
|
||||||
static void add_vars_to_targetlist(Query *root, List *vars);
|
static void add_vars_to_targetlist(Query *root, List *vars);
|
||||||
|
static bool qual_is_redundant(Query *root, RestrictInfo *restrictinfo,
|
||||||
|
List *restrictlist);
|
||||||
static void check_mergejoinable(RestrictInfo *restrictinfo);
|
static void check_mergejoinable(RestrictInfo *restrictinfo);
|
||||||
static void check_hashjoinable(RestrictInfo *restrictinfo);
|
static void check_hashjoinable(RestrictInfo *restrictinfo);
|
||||||
|
|
||||||
@ -219,7 +222,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
|
|||||||
*/
|
*/
|
||||||
foreach(qual, (List *) f->quals)
|
foreach(qual, (List *) f->quals)
|
||||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||||
true, false, result);
|
true, false, false, result);
|
||||||
}
|
}
|
||||||
else if (IsA(jtnode, JoinExpr))
|
else if (IsA(jtnode, JoinExpr))
|
||||||
{
|
{
|
||||||
@ -279,7 +282,7 @@ distribute_quals_to_rels(Query *root, Node *jtnode)
|
|||||||
|
|
||||||
foreach(qual, (List *) j->quals)
|
foreach(qual, (List *) j->quals)
|
||||||
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
distribute_qual_to_rels(root, (Node *) lfirst(qual),
|
||||||
false, isouterjoin, result);
|
false, isouterjoin, false, result);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
|
elog(ERROR, "distribute_quals_to_rels: unexpected node type %d",
|
||||||
@ -339,6 +342,7 @@ mark_baserels_for_outer_join(Query *root, Relids rels, Relids outerrels)
|
|||||||
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
|
* 'ispusheddown': if TRUE, force the clause to be marked 'ispusheddown'
|
||||||
* (this indicates the clause came from a FromExpr, not a JoinExpr)
|
* (this indicates the clause came from a FromExpr, not a JoinExpr)
|
||||||
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
|
* 'isouterjoin': TRUE if the qual came from an OUTER JOIN's ON-clause
|
||||||
|
* 'isdeduced': TRUE if the qual came from implied-equality deduction
|
||||||
* 'qualscope': list of baserels the qual's syntactic scope covers
|
* 'qualscope': list of baserels the qual's syntactic scope covers
|
||||||
*
|
*
|
||||||
* 'qualscope' identifies what level of JOIN the qual came from. For a top
|
* 'qualscope' identifies what level of JOIN the qual came from. For a top
|
||||||
@ -349,6 +353,7 @@ static void
|
|||||||
distribute_qual_to_rels(Query *root, Node *clause,
|
distribute_qual_to_rels(Query *root, Node *clause,
|
||||||
bool ispusheddown,
|
bool ispusheddown,
|
||||||
bool isouterjoin,
|
bool isouterjoin,
|
||||||
|
bool isdeduced,
|
||||||
Relids qualscope)
|
Relids qualscope)
|
||||||
{
|
{
|
||||||
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
RestrictInfo *restrictinfo = makeNode(RestrictInfo);
|
||||||
@ -405,8 +410,17 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
|||||||
* clause's own reference list. At the time we are called, the
|
* clause's own reference list. At the time we are called, the
|
||||||
* outerjoinset list of each baserel will show exactly those outer
|
* outerjoinset list of each baserel will show exactly those outer
|
||||||
* joins that are below the qual in the join tree.
|
* joins that are below the qual in the join tree.
|
||||||
|
*
|
||||||
|
* If the qual came from implied-equality deduction, we can evaluate
|
||||||
|
* the qual at its natural semantic level.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
if (isouterjoin)
|
if (isdeduced)
|
||||||
|
{
|
||||||
|
Assert(sameseti(relids, qualscope));
|
||||||
|
can_be_equijoin = true;
|
||||||
|
}
|
||||||
|
else if (isouterjoin)
|
||||||
{
|
{
|
||||||
relids = qualscope;
|
relids = qualscope;
|
||||||
can_be_equijoin = false;
|
can_be_equijoin = false;
|
||||||
@ -461,9 +475,6 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
|||||||
*/
|
*/
|
||||||
RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
|
RelOptInfo *rel = build_base_rel(root, lfirsti(relids));
|
||||||
|
|
||||||
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
|
|
||||||
restrictinfo);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Check for a "mergejoinable" clause even though it's not a join
|
* Check for a "mergejoinable" clause even though it's not a join
|
||||||
* clause. This is so that we can recognize that "a.x = a.y"
|
* clause. This is so that we can recognize that "a.x = a.y"
|
||||||
@ -474,10 +485,26 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
|||||||
*/
|
*/
|
||||||
if (can_be_equijoin)
|
if (can_be_equijoin)
|
||||||
check_mergejoinable(restrictinfo);
|
check_mergejoinable(restrictinfo);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the clause was deduced from implied equality, check to see
|
||||||
|
* whether it is redundant with restriction clauses we already have
|
||||||
|
* for this rel. Note we cannot apply this check to user-written
|
||||||
|
* clauses, since we haven't found the canonical pathkey sets yet
|
||||||
|
* while processing user clauses. (NB: no comparable check is done
|
||||||
|
* in the join-clause case; redundancy will be detected when the
|
||||||
|
* join clause is moved into a join rel's restriction list.)
|
||||||
|
*/
|
||||||
|
if (!isdeduced ||
|
||||||
|
!qual_is_redundant(root, restrictinfo, rel->baserestrictinfo))
|
||||||
|
{
|
||||||
|
/* Add clause to rel's restriction list */
|
||||||
|
rel->baserestrictinfo = lappend(rel->baserestrictinfo,
|
||||||
|
restrictinfo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (relids != NIL)
|
else if (relids != NIL)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 'clause' is a join clause, since there is more than one rel in
|
* 'clause' is a join clause, since there is more than one rel in
|
||||||
* the relid list. Set additional RestrictInfo fields for
|
* the relid list. Set additional RestrictInfo fields for
|
||||||
@ -524,9 +551,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
|
|||||||
* the two sides represent equivalent PathKeyItems for path keys: any
|
* the two sides represent equivalent PathKeyItems for path keys: any
|
||||||
* path that is sorted by one side will also be sorted by the other
|
* path that is sorted by one side will also be sorted by the other
|
||||||
* (as soon as the two rels are joined, that is). Record the key
|
* (as soon as the two rels are joined, that is). Record the key
|
||||||
* equivalence for future use.
|
* equivalence for future use. (We can skip this for a deduced clause,
|
||||||
|
* since the keys are already known equivalent in that case.)
|
||||||
*/
|
*/
|
||||||
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid)
|
if (can_be_equijoin && restrictinfo->mergejoinoperator != InvalidOid &&
|
||||||
|
!isdeduced)
|
||||||
add_equijoined_keys(root, restrictinfo);
|
add_equijoined_keys(root, restrictinfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,18 +713,106 @@ process_implied_equality(Query *root, Node *item1, Node *item2,
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Note: we mark the qual "pushed down" to ensure that it can never be
|
* Note: we mark the qual "pushed down" to ensure that it can never be
|
||||||
* taken for an original JOIN/ON clause. We also claim it is an
|
* taken for an original JOIN/ON clause.
|
||||||
* outer- join clause, which it isn't, but that keeps
|
|
||||||
* distribute_qual_to_rels from examining the outerjoinsets of the
|
|
||||||
* relevant rels (which are no longer of interest, but could keep the
|
|
||||||
* qual from being pushed down to where it should be). It'll also
|
|
||||||
* save a useless call to add_equijoined keys...
|
|
||||||
*/
|
*/
|
||||||
distribute_qual_to_rels(root, (Node *) clause,
|
distribute_qual_to_rels(root, (Node *) clause,
|
||||||
true, true,
|
true, false, true,
|
||||||
pull_varnos((Node *) clause));
|
pull_varnos((Node *) clause));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* qual_is_redundant
|
||||||
|
* Detect whether an implied-equality qual that turns out to be a
|
||||||
|
* restriction clause for a single base relation is redundant with
|
||||||
|
* already-known restriction clauses for that rel. This occurs with,
|
||||||
|
* for example,
|
||||||
|
* SELECT * FROM tab WHERE f1 = f2 AND f2 = f3;
|
||||||
|
* We need to suppress the redundant condition to avoid computing
|
||||||
|
* too-small selectivity, not to mention wasting time at execution.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
qual_is_redundant(Query *root,
|
||||||
|
RestrictInfo *restrictinfo,
|
||||||
|
List *restrictlist)
|
||||||
|
{
|
||||||
|
List *oldquals;
|
||||||
|
List *olditem;
|
||||||
|
Node *newleft;
|
||||||
|
Node *newright;
|
||||||
|
List *equalvars;
|
||||||
|
bool someadded;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set cached pathkeys. NB: it is okay to do this now because this
|
||||||
|
* routine is only invoked while we are generating implied equalities.
|
||||||
|
* Therefore, the equi_key_list is already complete and so we can
|
||||||
|
* correctly determine canonical pathkeys.
|
||||||
|
*/
|
||||||
|
cache_mergeclause_pathkeys(root, restrictinfo);
|
||||||
|
/* If different, say "not redundant" (should never happen) */
|
||||||
|
if (restrictinfo->left_pathkey != restrictinfo->right_pathkey)
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
* Scan existing quals to find those referencing same pathkeys.
|
||||||
|
* Usually there will be few, if any, so build a list of just the
|
||||||
|
* interesting ones.
|
||||||
|
*/
|
||||||
|
oldquals = NIL;
|
||||||
|
foreach(olditem, restrictlist)
|
||||||
|
{
|
||||||
|
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||||
|
|
||||||
|
if (oldrinfo->mergejoinoperator != InvalidOid)
|
||||||
|
{
|
||||||
|
cache_mergeclause_pathkeys(root, oldrinfo);
|
||||||
|
if (restrictinfo->left_pathkey == oldrinfo->left_pathkey &&
|
||||||
|
restrictinfo->right_pathkey == oldrinfo->right_pathkey)
|
||||||
|
oldquals = lcons(oldrinfo, oldquals);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldquals == NIL)
|
||||||
|
return false;
|
||||||
|
/*
|
||||||
|
* Now, we want to develop a list of Vars 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.
|
||||||
|
*/
|
||||||
|
newleft = (Node *) get_leftop(restrictinfo->clause);
|
||||||
|
newright = (Node *) get_rightop(restrictinfo->clause);
|
||||||
|
equalvars = makeList1(newleft);
|
||||||
|
do {
|
||||||
|
someadded = false;
|
||||||
|
foreach(olditem, oldquals)
|
||||||
|
{
|
||||||
|
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||||
|
Node *oldleft = (Node *) get_leftop(oldrinfo->clause);
|
||||||
|
Node *oldright = (Node *) get_rightop(oldrinfo->clause);
|
||||||
|
Node *newguy = NULL;
|
||||||
|
|
||||||
|
if (member(oldleft, equalvars))
|
||||||
|
newguy = oldright;
|
||||||
|
else if (member(oldright, equalvars))
|
||||||
|
newguy = oldleft;
|
||||||
|
else
|
||||||
|
continue;
|
||||||
|
if (equal(newguy, newright))
|
||||||
|
return true; /* we proved new clause is redundant */
|
||||||
|
equalvars = lcons(newguy, equalvars);
|
||||||
|
someadded = true;
|
||||||
|
/*
|
||||||
|
* Remove this qual from list, since we don't need it anymore.
|
||||||
|
* Note this doesn't break the foreach() loop, since lremove
|
||||||
|
* doesn't touch the next-link of the removed cons cell.
|
||||||
|
*/
|
||||||
|
oldquals = lremove(oldrinfo, oldquals);
|
||||||
|
}
|
||||||
|
} while (someadded);
|
||||||
|
|
||||||
|
return false; /* it's not redundant */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
*
|
*
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.108 2001/06/05 05:26:04 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.109 2001/10/18 16:11:41 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
#include "catalog/pg_type.h"
|
#include "catalog/pg_type.h"
|
||||||
#include "nodes/makefuncs.h"
|
#include "nodes/makefuncs.h"
|
||||||
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
#include "nodes/print.h"
|
||||||
|
#endif
|
||||||
#include "optimizer/clauses.h"
|
#include "optimizer/clauses.h"
|
||||||
#include "optimizer/paths.h"
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/planmain.h"
|
#include "optimizer/planmain.h"
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* IDENTIFICATION
|
||||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.33 2001/05/20 20:28:19 tgl Exp $
|
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.34 2001/10/18 16:11:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -17,6 +17,7 @@
|
|||||||
#include "optimizer/cost.h"
|
#include "optimizer/cost.h"
|
||||||
#include "optimizer/joininfo.h"
|
#include "optimizer/joininfo.h"
|
||||||
#include "optimizer/pathnode.h"
|
#include "optimizer/pathnode.h"
|
||||||
|
#include "optimizer/paths.h"
|
||||||
#include "optimizer/plancat.h"
|
#include "optimizer/plancat.h"
|
||||||
#include "optimizer/tlist.h"
|
#include "optimizer/tlist.h"
|
||||||
#include "parser/parsetree.h"
|
#include "parser/parsetree.h"
|
||||||
@ -24,9 +25,10 @@
|
|||||||
|
|
||||||
static RelOptInfo *make_base_rel(Query *root, int relid);
|
static RelOptInfo *make_base_rel(Query *root, int relid);
|
||||||
static List *new_join_tlist(List *tlist, int first_resdomno);
|
static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||||
static List *build_joinrel_restrictlist(RelOptInfo *joinrel,
|
static List *build_joinrel_restrictlist(Query *root,
|
||||||
RelOptInfo *outer_rel,
|
RelOptInfo *joinrel,
|
||||||
RelOptInfo *inner_rel);
|
RelOptInfo *outer_rel,
|
||||||
|
RelOptInfo *inner_rel);
|
||||||
static void build_joinrel_joinlist(RelOptInfo *joinrel,
|
static void build_joinrel_joinlist(RelOptInfo *joinrel,
|
||||||
RelOptInfo *outer_rel,
|
RelOptInfo *outer_rel,
|
||||||
RelOptInfo *inner_rel);
|
RelOptInfo *inner_rel);
|
||||||
@ -274,7 +276,8 @@ build_join_rel(Query *root,
|
|||||||
* particular pair of component relations.
|
* particular pair of component relations.
|
||||||
*/
|
*/
|
||||||
if (restrictlist_ptr)
|
if (restrictlist_ptr)
|
||||||
*restrictlist_ptr = build_joinrel_restrictlist(joinrel,
|
*restrictlist_ptr = build_joinrel_restrictlist(root,
|
||||||
|
joinrel,
|
||||||
outer_rel,
|
outer_rel,
|
||||||
inner_rel);
|
inner_rel);
|
||||||
return joinrel;
|
return joinrel;
|
||||||
@ -326,7 +329,10 @@ build_join_rel(Query *root,
|
|||||||
* caller might or might not need the restrictlist, but I need it
|
* caller might or might not need the restrictlist, but I need it
|
||||||
* anyway for set_joinrel_size_estimates().)
|
* anyway for set_joinrel_size_estimates().)
|
||||||
*/
|
*/
|
||||||
restrictlist = build_joinrel_restrictlist(joinrel, outer_rel, inner_rel);
|
restrictlist = build_joinrel_restrictlist(root,
|
||||||
|
joinrel,
|
||||||
|
outer_rel,
|
||||||
|
inner_rel);
|
||||||
if (restrictlist_ptr)
|
if (restrictlist_ptr)
|
||||||
*restrictlist_ptr = restrictlist;
|
*restrictlist_ptr = restrictlist;
|
||||||
build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
|
build_joinrel_joinlist(joinrel, outer_rel, inner_rel);
|
||||||
@ -407,6 +413,11 @@ new_join_tlist(List *tlist,
|
|||||||
* join paths made from this pair of sub-relations. (It will not need to
|
* join paths made from this pair of sub-relations. (It will not need to
|
||||||
* be considered further up the join tree.)
|
* be considered further up the join tree.)
|
||||||
*
|
*
|
||||||
|
* When building a restriction list, we eliminate redundant clauses.
|
||||||
|
* We don't try to do that for join clause lists, since the join clauses
|
||||||
|
* aren't really doing anything, just waiting to become part of higher
|
||||||
|
* levels' restriction lists.
|
||||||
|
*
|
||||||
* 'joinrel' is a join relation node
|
* 'joinrel' is a join relation node
|
||||||
* 'outer_rel' and 'inner_rel' are a pair of relations that can be joined
|
* 'outer_rel' and 'inner_rel' are a pair of relations that can be joined
|
||||||
* to form joinrel.
|
* to form joinrel.
|
||||||
@ -421,19 +432,80 @@ new_join_tlist(List *tlist,
|
|||||||
* the original nodes in the lists made for the join relation.
|
* the original nodes in the lists made for the join relation.
|
||||||
*/
|
*/
|
||||||
static List *
|
static List *
|
||||||
build_joinrel_restrictlist(RelOptInfo *joinrel,
|
build_joinrel_restrictlist(Query *root,
|
||||||
|
RelOptInfo *joinrel,
|
||||||
RelOptInfo *outer_rel,
|
RelOptInfo *outer_rel,
|
||||||
RelOptInfo *inner_rel)
|
RelOptInfo *inner_rel)
|
||||||
{
|
{
|
||||||
|
List *result = NIL;
|
||||||
|
List *rlist;
|
||||||
|
List *item;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We must eliminate duplicates, since we will see the same clauses
|
* Collect all the clauses that syntactically belong at this level.
|
||||||
* arriving from both input relations...
|
|
||||||
*/
|
*/
|
||||||
return set_union(subbuild_joinrel_restrictlist(joinrel,
|
rlist = nconc(subbuild_joinrel_restrictlist(joinrel,
|
||||||
outer_rel->joininfo),
|
outer_rel->joininfo),
|
||||||
subbuild_joinrel_restrictlist(joinrel,
|
subbuild_joinrel_restrictlist(joinrel,
|
||||||
inner_rel->joininfo));
|
inner_rel->joininfo));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Eliminate duplicate and redundant clauses.
|
||||||
|
*
|
||||||
|
* We must eliminate duplicates, since we will see many of the same
|
||||||
|
* clauses arriving from both input relations. Also, if a clause is
|
||||||
|
* a mergejoinable clause, it's possible that it is redundant with
|
||||||
|
* previous clauses (see optimizer/README for discussion). We detect
|
||||||
|
* that case and omit the redundant clause from the result list.
|
||||||
|
*
|
||||||
|
* We can detect redundant mergejoinable clauses very cheaply by using
|
||||||
|
* their left and right pathkeys, which uniquely identify the sets of
|
||||||
|
* equijoined variables in question. All the members of a pathkey set
|
||||||
|
* that are in the left relation have already been forced to be equal;
|
||||||
|
* likewise for those in the right relation. So, we need to have only
|
||||||
|
* one clause that checks equality between any set member on the left and
|
||||||
|
* any member on the right; by transitivity, all the rest are then equal.
|
||||||
|
*/
|
||||||
|
foreach(item, rlist)
|
||||||
|
{
|
||||||
|
RestrictInfo *rinfo = (RestrictInfo *) lfirst(item);
|
||||||
|
|
||||||
|
/* eliminate duplicates */
|
||||||
|
if (member(rinfo, result))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
/* check for redundant merge clauses */
|
||||||
|
if (rinfo->mergejoinoperator != InvalidOid)
|
||||||
|
{
|
||||||
|
bool redundant = false;
|
||||||
|
List *olditem;
|
||||||
|
|
||||||
|
cache_mergeclause_pathkeys(root, rinfo);
|
||||||
|
|
||||||
|
foreach(olditem, result)
|
||||||
|
{
|
||||||
|
RestrictInfo *oldrinfo = (RestrictInfo *) lfirst(olditem);
|
||||||
|
|
||||||
|
if (oldrinfo->mergejoinoperator != InvalidOid &&
|
||||||
|
rinfo->left_pathkey == oldrinfo->left_pathkey &&
|
||||||
|
rinfo->right_pathkey == oldrinfo->right_pathkey)
|
||||||
|
{
|
||||||
|
redundant = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (redundant)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* otherwise, add it to result list */
|
||||||
|
result = lappend(result, rinfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeList(rlist);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -458,7 +530,6 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
|
|||||||
|
|
||||||
if (is_subseti(joininfo->unjoined_relids, joinrel->relids))
|
if (is_subseti(joininfo->unjoined_relids, joinrel->relids))
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clauses in this JoinInfo list become restriction clauses
|
* Clauses in this JoinInfo list become restriction clauses
|
||||||
* for the joinrel, since they refer to no outside rels.
|
* for the joinrel, since they refer to no outside rels.
|
||||||
@ -471,7 +542,6 @@ subbuild_joinrel_restrictlist(RelOptInfo *joinrel,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These clauses are still join clauses at this level, so we
|
* These clauses are still join clauses at this level, so we
|
||||||
* ignore them in this routine.
|
* ignore them in this routine.
|
||||||
@ -497,7 +567,6 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
|||||||
joinrel->relids);
|
joinrel->relids);
|
||||||
if (new_unjoined_relids == NIL)
|
if (new_unjoined_relids == NIL)
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Clauses in this JoinInfo list become restriction clauses
|
* Clauses in this JoinInfo list become restriction clauses
|
||||||
* for the joinrel, since they refer to no outside rels. So we
|
* for the joinrel, since they refer to no outside rels. So we
|
||||||
@ -506,7 +575,6 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* These clauses are still join clauses at this level, so find
|
* These clauses are still join clauses at this level, so find
|
||||||
* or make the appropriate JoinInfo item for the joinrel, and
|
* or make the appropriate JoinInfo item for the joinrel, and
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* Portions Copyright (c) 1994, Regents of the University of California
|
||||||
*
|
*
|
||||||
* $Id: paths.h,v 1.55 2001/08/21 16:36:06 tgl Exp $
|
* $Id: paths.h,v 1.56 2001/10/18 16:11:42 tgl Exp $
|
||||||
*
|
*
|
||||||
*-------------------------------------------------------------------------
|
*-------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
@ -31,6 +31,10 @@ extern int geqo_rels;
|
|||||||
extern RelOptInfo *make_one_rel(Query *root);
|
extern RelOptInfo *make_one_rel(Query *root);
|
||||||
extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from);
|
extern RelOptInfo *make_fromexpr_rel(Query *root, FromExpr *from);
|
||||||
|
|
||||||
|
#ifdef OPTIMIZER_DEBUG
|
||||||
|
extern void debug_print_rel(Query *root, RelOptInfo *rel);
|
||||||
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* indxpath.c
|
* indxpath.c
|
||||||
* routines to generate index paths
|
* routines to generate index paths
|
||||||
@ -111,6 +115,8 @@ extern List *build_join_pathkeys(Query *root,
|
|||||||
List *outer_pathkeys);
|
List *outer_pathkeys);
|
||||||
extern List *make_pathkeys_for_sortclauses(List *sortclauses,
|
extern List *make_pathkeys_for_sortclauses(List *sortclauses,
|
||||||
List *tlist);
|
List *tlist);
|
||||||
|
extern void cache_mergeclause_pathkeys(Query *root,
|
||||||
|
RestrictInfo *restrictinfo);
|
||||||
extern List *find_mergeclauses_for_pathkeys(Query *root,
|
extern List *find_mergeclauses_for_pathkeys(Query *root,
|
||||||
List *pathkeys,
|
List *pathkeys,
|
||||||
List *restrictinfos);
|
List *restrictinfos);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user