Commit d8733ce6 authored by Tom Lane's avatar Tom Lane

Repair planning bugs caused by my misguided removal of restrictinfo link

fields in JoinPaths --- turns out that we do need that after all :-(.
Also, rearrange planner so that only one RelOptInfo is created for a
particular set of joined base relations, no matter how many different
subsets of relations it can be created from.  This saves memory and
processing time compared to the old method of making a bunch of RelOptInfos
and then removing the duplicates.  Clean up the jointree iteration logic;
not sure if it's better, but I sure find it more readable and plausible
now, particularly for the case of 'bushy plans'.
parent 2bda7a44
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.103 2000/01/27 18:11:27 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.104 2000/02/07 04:40:56 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -977,7 +977,7 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->pages = from->pages;
newnode->tuples = from->tuples;
Node_Copy(from, newnode, restrictinfo);
Node_Copy(from, newnode, baserestrictinfo);
Node_Copy(from, newnode, joininfo);
Node_Copy(from, newnode, innerjoin);
......@@ -1137,6 +1137,7 @@ CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
{
Node_Copy(from, newnode, outerjoinpath);
Node_Copy(from, newnode, innerjoinpath);
Node_Copy(from, newnode, joinrestrictinfo);
}
/* ----------------
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.58 2000/01/31 01:21:39 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.59 2000/02/07 04:40:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -374,6 +374,8 @@ _equalJoinPath(JoinPath *a, JoinPath *b)
return false;
if (!equal(a->innerjoinpath, b->innerjoinpath))
return false;
if (!equal(a->joinrestrictinfo, b->joinrestrictinfo))
return false;
return true;
}
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.33 2000/01/27 18:11:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.34 2000/02/07 04:40:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -735,7 +735,7 @@ _freeRelOptInfo(RelOptInfo *node)
*/
freeObject(node->cheapestpath);
freeObject(node->restrictinfo);
freeObject(node->baserestrictinfo);
freeObject(node->joininfo);
freeObject(node->innerjoin);
......@@ -853,6 +853,10 @@ FreeJoinPathFields(JoinPath *node)
{
freeObject(node->outerjoinpath);
freeObject(node->innerjoinpath);
/* XXX probably wrong, since ordinarily a JoinPath would share its
* restrictinfo list with other paths made for the same join?
*/
freeObject(node->joinrestrictinfo);
}
/* ----------------
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.105 2000/01/27 18:11:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.106 2000/02/07 04:40:57 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
......@@ -915,10 +915,10 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
*/
appendStringInfo(str,
" :cheapestpath @ 0x%x :pruneable %s :restrictinfo ",
" :cheapestpath @ 0x%x :pruneable %s :baserestrictinfo ",
(int) node->cheapestpath,
node->pruneable ? "true" : "false");
_outNode(str, node->restrictinfo);
_outNode(str, node->baserestrictinfo);
appendStringInfo(str, " :joininfo ");
_outNode(str, node->joininfo);
......@@ -1035,16 +1035,12 @@ _outNestPath(StringInfo str, NestPath *node)
node->path.pathtype,
node->path.path_cost);
_outNode(str, node->path.pathkeys);
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->outerjoinpath,
(int) node->innerjoinpath);
appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->outerjoinpath);
appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->innerjoinpath);
appendStringInfo(str, " :joinrestrictinfo ");
_outNode(str, node->joinrestrictinfo);
}
/*
......@@ -1058,16 +1054,12 @@ _outMergePath(StringInfo str, MergePath *node)
node->jpath.path.pathtype,
node->jpath.path.path_cost);
_outNode(str, node->jpath.path.pathkeys);
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->jpath.outerjoinpath,
(int) node->jpath.innerjoinpath);
appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->jpath.outerjoinpath);
appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->jpath.innerjoinpath);
appendStringInfo(str, " :joinrestrictinfo ");
_outNode(str, node->jpath.joinrestrictinfo);
appendStringInfo(str, " :path_mergeclauses ");
_outNode(str, node->path_mergeclauses);
......@@ -1090,16 +1082,12 @@ _outHashPath(StringInfo str, HashPath *node)
node->jpath.path.pathtype,
node->jpath.path.path_cost);
_outNode(str, node->jpath.path.pathkeys);
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->jpath.outerjoinpath,
(int) node->jpath.innerjoinpath);
appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->jpath.outerjoinpath);
appendStringInfo(str, " :innerjoinpath ");
_outNode(str, node->jpath.innerjoinpath);
appendStringInfo(str, " :joinrestrictinfo ");
_outNode(str, node->jpath.joinrestrictinfo);
appendStringInfo(str, " :path_hashclauses ");
_outNode(str, node->path_hashclauses);
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.81 2000/01/27 18:11:28 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.82 2000/02/07 04:40:57 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
......@@ -1288,8 +1288,8 @@ _readRelOptInfo()
sscanf(token, "%x", (unsigned int *) &local_node->cheapestpath);
token = lsptok(NULL, &length); /* get :restrictinfo */
local_node->restrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :baserestrictinfo */
local_node->baserestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :joininfo */
local_node->joininfo = nodeRead(true); /* now read it */
......@@ -1518,25 +1518,14 @@ _readNestPath()
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*
* GJK: Since I am parsing this stuff, I'll just ignore the addresses,
* and initialize these pointers to NULL.
*/
token = lsptok(NULL, &length); /* get :outerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->outerjoinpath = NULL;
local_node->outerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :innerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->innerjoinpath = nodeRead(true); /* now read it */
local_node->innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->joinrestrictinfo = nodeRead(true); /* now read it */
return local_node;
}
......@@ -1569,25 +1558,14 @@ _readMergePath()
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*
* GJK: Since I am parsing this stuff, I'll just ignore the addresses,
* and initialize these pointers to NULL.
*/
token = lsptok(NULL, &length); /* get :outerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.outerjoinpath = NULL;
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :innerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
local_node->jpath.innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :path_mergeclauses */
local_node->path_mergeclauses = nodeRead(true); /* now read it */
......@@ -1629,25 +1607,14 @@ _readHashPath()
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
/*
* Not sure if these are nodes; they're declared as "struct path *".
* For now, i'll just print the addresses.
*
* GJK: Since I am parsing this stuff, I'll just ignore the addresses,
* and initialize these pointers to NULL.
*/
token = lsptok(NULL, &length); /* get :outerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.outerjoinpath = NULL;
local_node->jpath.outerjoinpath = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :innerjoinpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.innerjoinpath = nodeRead(true); /* now read it */
local_node->jpath.innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :joinrestrictinfo */
local_node->jpath.joinrestrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :path_hashclauses */
local_node->path_hashclauses = nodeRead(true); /* now read it */
......
This diff is collapsed.
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_eval.c,v 1.46 2000/01/26 05:56:33 momjian Exp $
* $Id: geqo_eval.c,v 1.47 2000/02/07 04:40:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -32,6 +32,7 @@
#include "optimizer/cost.h"
#include "optimizer/geqo.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "utils/portal.h"
......@@ -121,7 +122,6 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
{
RelOptInfo *inner_rel; /* current relation */
int base_rel_index;
List *new_rels;
RelOptInfo *new_rel;
if (rel_count < num_gene)
......@@ -130,7 +130,7 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
/* tour[0] = 3; tour[1] = 1; tour[2] = 2 */
base_rel_index = (int) tour[rel_count];
inner_rel = (RelOptInfo *) nth(base_rel_index - 1, root->base_rel_list);
inner_rel = (RelOptInfo *) nth(base_rel_index-1, root->base_rel_list);
if (rel_count == 0)
{ /* processing first join with
......@@ -140,54 +140,23 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
}
else
{ /* tree main part */
if (!(new_rels = make_rels_by_clause_joins(root, old_rel,
old_rel->joininfo,
inner_rel->relids)))
{
new_rels = make_rels_by_clauseless_joins(old_rel,
lcons(inner_rel, NIL));
/*
* we don't do bushy plans in geqo, do we? bjm 02/18/1999
* new_rels = append(new_rels,
* make_rels_by_clauseless_joins(old_rel,
* lcons(old_rel,NIL));
*/
}
/* process new_rel->pathlist */
update_rels_pathlist_for_joins(root, new_rels);
/* prune new_rels */
/* MAU: is this necessary? */
/*
* what's the matter if more than one new rel is left till
* now?
*/
List *acceptable_rels = lcons(inner_rel, NIL);
/*
* joinrels in newrels with different ordering of relids are
* not possible
*/
if (length(new_rels) > 1)
merge_rels_with_same_relids(new_rels);
if (length(new_rels) > 1)
{ /* should never be reached ... */
elog(DEBUG, "gimme_tree: still %d relations left", length(new_rels));
new_rel = make_rels_by_clause_joins(root, old_rel,
acceptable_rels);
if (! new_rel)
{
new_rel = make_rels_by_clauseless_joins(root, old_rel,
acceptable_rels);
if (! new_rel)
elog(ERROR, "gimme_tree: failed to construct join rel");
}
rels_set_cheapest(root, new_rels);
/* get essential new relation */
new_rel = (RelOptInfo *) lfirst(new_rels);
rel_count++;
Assert(length(new_rel->relids) == rel_count);
/* processing of other new_rel attributes */
set_rel_rows_width(root, new_rel);
root->join_rel_list = lcons(new_rel, NIL);
/* Find and save the cheapest path for this rel */
set_cheapest(new_rel, new_rel->pathlist);
return gimme_tree(root, tour, rel_count, num_gene, new_rel);
}
......
......@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_misc.c,v 1.26 2000/01/26 05:56:33 momjian Exp $
* $Id: geqo_misc.c,v 1.27 2000/02/07 04:40:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -188,7 +188,7 @@ geqo_print_path(Query *root, Path *path, int indent)
for (i = 0; i < indent + 1; i++)
printf("\t");
printf(" clauses=(");
geqo_print_joinclauses(root, path->parent->restrictinfo);
geqo_print_joinclauses(root, jp->joinrestrictinfo);
printf(")\n");
if (nodeTag(path) == T_MergePath)
......
......@@ -4,7 +4,7 @@
# Makefile for optimizer/path
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.12 1999/12/13 22:32:52 momjian Exp $
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.13 2000/02/07 04:40:59 tgl Exp $
#
#-------------------------------------------------------------------------
......@@ -14,8 +14,7 @@ include ../../../Makefile.global
CFLAGS += -I../..
OBJS = allpaths.o clausesel.o costsize.o indxpath.o \
joinpath.o joinrels.o orindxpath.o pathkeys.o prune.o \
tidpath.o
joinpath.o joinrels.o orindxpath.o pathkeys.o tidpath.o
all: SUBSYS.o
......
This diff is collapsed.
......@@ -19,7 +19,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.50 2000/01/26 05:56:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.51 2000/02/07 04:40:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -447,22 +447,26 @@ cost_hashjoin(Path *outer_path,
}
/*
* set_rel_rows_width
* Set the 'rows' and 'width' estimates for the given base relation.
* set_baserel_size_estimates
* Set the size estimates for the given base relation.
*
* 'rows' is the estimated number of output tuples (after applying
* restriction clauses).
* 'width' is the estimated average output tuple width in bytes.
* The rel's targetlist and restrictinfo list must have been constructed
* already.
*
* We set the following fields of the rel node:
* rows: the estimated number of output tuples (after applying
* restriction clauses).
* width: the estimated average output tuple width in bytes.
*/
void
set_rel_rows_width(Query *root, RelOptInfo *rel)
set_baserel_size_estimates(Query *root, RelOptInfo *rel)
{
/* Should only be applied to base relations */
Assert(length(rel->relids) == 1);
rel->rows = rel->tuples *
restrictlist_selectivity(root,
rel->restrictinfo,
rel->baserestrictinfo,
lfirsti(rel->relids));
Assert(rel->rows >= 0);
......@@ -470,28 +474,56 @@ set_rel_rows_width(Query *root, RelOptInfo *rel)
}
/*
* set_joinrel_rows_width
* Set the 'rows' and 'width' estimates for the given join relation.
* set_joinrel_size_estimates
* Set the size estimates for the given join relation.
*
* The rel's targetlist must have been constructed already, and a
* restriction clause list that matches the given component rels must
* be provided.
*
* Since there is more than one way to make a joinrel for more than two
* base relations, the results we get here could depend on which component
* rel pair is provided. In theory we should get the same answers no matter
* which pair is provided; in practice, since the selectivity estimation
* routines don't handle all cases equally well, we might not. But there's
* not much to be done about it. (Would it make sense to repeat the
* calculations for each pair of input rels that's encountered, and somehow
* average the results? Probably way more trouble than it's worth.)
*
* We set the same relnode fields as set_baserel_size_estimates() does.
*/
void
set_joinrel_rows_width(Query *root, RelOptInfo *rel,
JoinPath *joinpath)
set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
List *restrictlist)
{
double temp;
/* cartesian product */
temp = joinpath->outerjoinpath->parent->rows *
joinpath->innerjoinpath->parent->rows;
temp = outer_rel->rows * inner_rel->rows;
/* apply join restrictivity */
/*
* Apply join restrictivity. Note that we are only considering clauses
* that become restriction clauses at this join level; we are not
* double-counting them because they were not considered in estimating
* the sizes of the component rels.
*/
temp *= restrictlist_selectivity(root,
joinpath->path.parent->restrictinfo,
restrictlist,
0);
Assert(temp >= 0);
rel->rows = temp;
set_rel_width(root, rel);
/*
* We could apply set_rel_width() to compute the output tuple width
* from scratch, but at present it's always just the sum of the input
* widths, so why work harder than necessary? If relnode.c is ever
* taught to remove unneeded columns from join targetlists, go back
* to using set_rel_width here.
*/
rel->width = outer_rel->width + inner_rel->width;
}
/*
......@@ -516,6 +548,7 @@ set_rel_width(Query *root, RelOptInfo *rel)
*
* If a field is variable-length, we make a default assumption. Would be
* better if VACUUM recorded some stats about the average field width...
* also, we have access to the atttypmod, but fail to use it...
*/
static int
compute_attribute_width(TargetEntry *tlistentry)
......
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* prune.c
* Routines to prune redundant paths and relations
*
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.46 2000/02/06 03:27:32 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
static List *merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels);
/*
* merge_rels_with_same_relids
* Removes any redundant relation entries from a list of rel nodes
* 'rel_list', merging their pathlists into the first non-duplicate
* relation entry for each value of relids.
*
* Returns the resulting list.
*/
void
merge_rels_with_same_relids(List *rel_list)
{
List *i;
/*
* rel_list can shorten while running as duplicate relations are
* deleted. Obviously, the first relation can't be a duplicate,
* so the list head pointer won't change.
*/
foreach(i, rel_list)
{
lnext(i) = merge_rel_with_same_relids((RelOptInfo *) lfirst(i),
lnext(i));
}
}
/*
* merge_rel_with_same_relids
* Prunes those relations from 'unmerged_rels' that are redundant with
* 'rel'. A relation is redundant if it is built up of the same
* relations as 'rel'. Paths for the redundant relations are merged into
* the pathlist of 'rel'.
*
* Returns a list of non-redundant relations, and sets the pathlist field
* of 'rel' appropriately.
*
*/
static List *
merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels)
{
List *result = NIL;
List *i;
foreach(i, unmerged_rels)
{
RelOptInfo *unmerged_rel = (RelOptInfo *) lfirst(i);
if (sameseti(rel->relids, unmerged_rel->relids))
{
/*
* These rels are for the same set of base relations,
* so get the best of their pathlists. We assume it's
* ok to reassign a path to the other RelOptInfo without
* doing more than changing its parent pointer (cf. pathnode.c).
*/
rel->pathlist = add_pathlist(rel,
rel->pathlist,
unmerged_rel->pathlist);
}
else
result = lappend(result, unmerged_rel);
}
return result;
}
/*
* rels_set_cheapest
* For each relation entry in 'rel_list' (which should contain only join
* relations), set pointers to the cheapest path and compute rel size.
*/
void
rels_set_cheapest(Query *root, List *rel_list)
{
List *x;
foreach(x, rel_list)
{
RelOptInfo *rel = (RelOptInfo *) lfirst(x);
JoinPath *cheapest;
cheapest = (JoinPath *) set_cheapest(rel, rel->pathlist);
if (IsA_JoinPath(cheapest))
set_joinrel_rows_width(root, rel, cheapest);
else
elog(ERROR, "rels_set_cheapest: non JoinPath found");
}
}
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.3 2000/01/26 05:56:34 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.4 2000/02/07 04:40:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -37,7 +37,7 @@
#include "utils/lsyscache.h"
static List *create_tidscan_joinpaths(RelOptInfo *);
static List *TidqualFromRestrictinfo(List *relids, List * restrictinfo);
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
static bool isEvaluable(int varno, Node *node);
static Node *TidequalClause(int varno, Expr *node);
static List *TidqualFromExpr(int varno, Expr *expr);
......@@ -209,16 +209,17 @@ List *TidqualFromExpr(int varno, Expr *expr)
return rlst;
}
static
List *TidqualFromRestrictinfo(List *relids, List * restrictinfo)
static List *
TidqualFromRestrictinfo(List *relids, List *restrictinfo)
{
List *lst, *rlst = NIL;
int varno;
Node *node;
Expr *expr;
if (length(relids)>1) return NIL;
varno = (int)lfirst(relids);
if (length(relids) != 1)
return NIL;
varno = lfirsti(relids);
foreach (lst, restrictinfo)
{
node = lfirst(lst);
......@@ -226,9 +227,7 @@ List *TidqualFromRestrictinfo(List *relids, List * restrictinfo)
expr = ((RestrictInfo *)node)->clause;
rlst = TidqualFromExpr(varno, expr);
if (rlst)
{
break;
}
}
return rlst;
}
......@@ -281,8 +280,9 @@ List *
create_tidscan_paths(Query *root, RelOptInfo *rel)
{
List *rlst = NIL;
TidPath *pathnode = (TidPath *)0;
List *tideval = TidqualFromRestrictinfo(rel->relids, rel->restrictinfo);
TidPath *pathnode = (TidPath *) NULL;
List *tideval = TidqualFromRestrictinfo(rel->relids,
rel->baserestrictinfo);
if (tideval)
pathnode = create_tidscan_path(rel, tideval);
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.83 2000/02/03 06:12:18 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.84 2000/02/07 04:41:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -147,7 +147,7 @@ create_scan_node(Query *root, Path *best_path, List *tlist)
* Extract the relevant restriction clauses from the parent relation;
* the executor must apply all these restrictions during the scan.
*/
scan_clauses = get_actual_clauses(best_path->parent->restrictinfo);
scan_clauses = get_actual_clauses(best_path->parent->baserestrictinfo);
switch (best_path->pathtype)
{
......@@ -203,7 +203,7 @@ create_join_node(Query *root, JoinPath *best_path, List *tlist)
inner_node = create_plan(root, best_path->innerjoinpath);
inner_tlist = inner_node->targetlist;
clauses = get_actual_clauses(best_path->path.parent->restrictinfo);
clauses = get_actual_clauses(best_path->joinrestrictinfo);
switch (best_path->path.pathtype)
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.43 2000/01/26 05:56:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.44 2000/02/07 04:41:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -179,7 +179,8 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
*/
RelOptInfo *rel = get_base_rel(root, lfirsti(relids));
rel->restrictinfo = lcons(restrictinfo, rel->restrictinfo);
rel->baserestrictinfo = lcons(restrictinfo,
rel->baserestrictinfo);
}
else
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.50 2000/01/26 05:56:37 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.51 2000/02/07 04:41:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -219,7 +219,7 @@ subplanner(Query *root,
add_restrict_and_join_to_rels(root, qual);
add_missing_rels_to_query(root);
final_rel = make_one_rel(root, root->base_rel_list);
final_rel = make_one_rel(root);
if (! final_rel)
{
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.58 2000/01/26 05:56:40 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.59 2000/02/07 04:41:01 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -76,101 +76,104 @@ set_cheapest(RelOptInfo *parent_rel, List *pathlist)
/*
* add_pathlist
* Construct an output path list by adding to old_paths each path in
* new_paths that is worth considering --- that is, it has either a
* better sort order (better pathkeys) or cheaper cost than any of the
* existing old paths.
*
* Unless parent_rel->pruneable is false, we also remove from the output
* pathlist any old paths that are dominated by added path(s) --- that is,
* some new path is both cheaper and at least as well ordered.
* Consider each path given in new_paths, and add it to the parent rel's
* pathlist if it seems worthy.
*/
void
add_pathlist(RelOptInfo *parent_rel, List *new_paths)
{
List *p1;
foreach(p1, new_paths)
{
Path *new_path = (Path *) lfirst(p1);
add_path(parent_rel, new_path);
}
}
/*
* add_path
* Consider a potential implementation path for the specified parent rel,
* and add it to the rel's pathlist if it is worthy of consideration.
* A path is worthy if it has either a better sort order (better pathkeys)
* or cheaper cost than any of the existing old paths.
*
* Note: the list old_paths is destructively modified, and in fact is
* turned into the output list.
* Unless parent_rel->pruneable is false, we also remove from the rel's
* pathlist any old paths that are dominated by new_path --- that is,
* new_path is both cheaper and at least as well ordered.
*
* 'parent_rel' is the relation entry to which these paths correspond.
* 'old_paths' is the list of previously accepted paths for parent_rel.
* 'new_paths' is a list of potential new paths.
* 'parent_rel' is the relation entry to which the path corresponds.
* 'new_path' is a potential path for parent_rel.
*
* Returns the updated list of interesting pathnodes.
* Returns nothing, but modifies parent_rel->pathlist.
*/
List *
add_pathlist(RelOptInfo *parent_rel, List *old_paths, List *new_paths)
void
add_path(RelOptInfo *parent_rel, Path *new_path)
{
bool accept_new = true; /* unless we find a superior old path */
List *p1_prev = NIL;
List *p1;
foreach(p1, new_paths)
/*
* Loop to check proposed new path against old paths. Note it is
* possible for more than one old path to be tossed out because
* new_path dominates it.
*/
foreach(p1, parent_rel->pathlist)
{
Path *new_path = (Path *) lfirst(p1);
bool accept_new = true; /* unless we find a superior old path */
List *p2_prev = NIL;
List *p2;
Path *old_path = (Path *) lfirst(p1);
bool remove_old = false; /* unless new proves superior */
/*
* Loop to check proposed new path against old paths. Note it is
* possible for more than one old path to be tossed out because
* new_path dominates it.
*/
foreach(p2, old_paths)
switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
{
Path *old_path = (Path *) lfirst(p2);
bool remove_old = false; /* unless new proves superior */
switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
{
case PATHKEYS_EQUAL:
if (new_path->path_cost < old_path->path_cost)
remove_old = true; /* new dominates old */
else
accept_new = false; /* old equals or dominates new */
break;
case PATHKEYS_BETTER1:
if (new_path->path_cost <= old_path->path_cost)
remove_old = true; /* new dominates old */
break;
case PATHKEYS_BETTER2:
if (new_path->path_cost >= old_path->path_cost)
accept_new = false; /* old dominates new */
break;
case PATHKEYS_DIFFERENT:
/* keep both paths, since they have different ordering */
break;
}
/*
* Remove current element from old_list if dominated by new,
* unless xfunc told us not to remove any paths.
*/
if (remove_old && parent_rel->pruneable)
{
if (p2_prev)
lnext(p2_prev) = lnext(p2);
case PATHKEYS_EQUAL:
if (new_path->path_cost < old_path->path_cost)
remove_old = true; /* new dominates old */
else
old_paths = lnext(p2);
}
else
p2_prev = p2;
/*
* If we found an old path that dominates new_path, we can quit
* scanning old_paths; we will not add new_path, and we assume
* new_path cannot dominate any other elements of old_paths.
*/
if (! accept_new)
accept_new = false; /* old equals or dominates new */
break;
case PATHKEYS_BETTER1:
if (new_path->path_cost <= old_path->path_cost)
remove_old = true; /* new dominates old */
break;
case PATHKEYS_BETTER2:
if (new_path->path_cost >= old_path->path_cost)
accept_new = false; /* old dominates new */
break;
case PATHKEYS_DIFFERENT:
/* keep both paths, since they have different ordering */
break;
}
if (accept_new)
/*
* Remove current element from pathlist if dominated by new,
* unless xfunc told us not to remove any paths.
*/
if (remove_old && parent_rel->pruneable)
{
/* Accept the path. Note that it will now be eligible to be
* compared against the additional elements of new_paths...
*/
new_path->parent = parent_rel; /* not redundant, see prune.c */
old_paths = lcons(new_path, old_paths);
if (p1_prev)
lnext(p1_prev) = lnext(p1);
else
parent_rel->pathlist = lnext(p1);
}
else
p1_prev = p1;
/*
* If we found an old path that dominates new_path, we can quit
* scanning the pathlist; we will not add new_path, and we assume
* new_path cannot dominate any other elements of the pathlist.
*/
if (! accept_new)
break;
}
return old_paths;
if (accept_new)
{
/* Accept the path */
parent_rel->pathlist = lcons(new_path, parent_rel->pathlist);
}
}
......@@ -271,6 +274,7 @@ create_tidscan_path(RelOptInfo *rel, List *tideval)
* 'joinrel' is the join relation.
* 'outer_path' is the outer path
* 'inner_path' is the inner path
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
* 'pathkeys' are the path keys of the new join path
*
* Returns the resulting path node.
......@@ -280,6 +284,7 @@ NestPath *
create_nestloop_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys)
{
NestPath *pathnode = makeNode(NestPath);
......@@ -288,6 +293,7 @@ create_nestloop_path(RelOptInfo *joinrel,
pathnode->path.parent = joinrel;
pathnode->outerjoinpath = outer_path;
pathnode->innerjoinpath = inner_path;
pathnode->joinrestrictinfo = restrict_clauses;
pathnode->path.pathkeys = pathkeys;
pathnode->path.path_cost = cost_nestloop(outer_path,
......@@ -305,6 +311,7 @@ create_nestloop_path(RelOptInfo *joinrel,
* 'joinrel' is the join relation
* 'outer_path' is the outer path
* 'inner_path' is the inner path
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
* 'pathkeys' are the path keys of the new join path
* 'mergeclauses' are the applicable join/restriction clauses
* 'outersortkeys' are the sort varkeys for the outer relation
......@@ -315,6 +322,7 @@ MergePath *
create_mergejoin_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
List *outersortkeys,
......@@ -337,6 +345,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
pathnode->jpath.path.parent = joinrel;
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->path_mergeclauses = mergeclauses;
pathnode->outersortkeys = outersortkeys;
......@@ -356,6 +365,7 @@ create_mergejoin_path(RelOptInfo *joinrel,
* 'joinrel' is the join relation
* 'outer_path' is the cheapest outer path
* 'inner_path' is the cheapest inner path
* 'restrict_clauses' are the RestrictInfo nodes to apply at the join
* 'hashclauses' is a list of the hash join clause (always a 1-element list)
* 'innerdisbursion' is an estimate of the disbursion of the inner hash key
*
......@@ -364,6 +374,7 @@ HashPath *
create_hashjoin_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *hashclauses,
Selectivity innerdisbursion)
{
......@@ -373,6 +384,7 @@ create_hashjoin_path(RelOptInfo *joinrel,
pathnode->jpath.path.parent = joinrel;
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.joinrestrictinfo = restrict_clauses;
/* A hashjoin never has pathkeys, since its ordering is unpredictable */
pathnode->jpath.path.pathkeys = NIL;
pathnode->path_hashclauses = hashclauses;
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.42 2000/01/26 05:58:17 momjian Exp $
* $Id: relation.h,v 1.43 2000/02/07 04:41:02 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -54,13 +54,26 @@ typedef List *Relids;
* * The presence of the remaining fields depends on the restrictions
* and joins that the relation participates in:
*
* restrictinfo - List of RestrictInfo nodes, containing info about each
* qualification clause in which this relation participates
* baserestrictinfo - List of RestrictInfo nodes, containing info about
* each qualification clause in which this relation
* participates (only used for base rels)
* joininfo - List of JoinInfo nodes, containing info about each join
* clause in which this relation participates
* innerjoin - List of Path nodes that represent indices that may be used
* as inner paths of nestloop joins. This field is non-null
* only for base rels, since join rels have no indices.
*
* Note: Keeping a restrictinfo list in the RelOptInfo is useful only for
* base rels, because for a join rel the set of clauses that are treated as
* restrict clauses varies depending on which sub-relations we choose to join.
* (For example, in a 3-base-rel join, a clause relating rels 1 and 2 must be
* treated as a restrictclause if we join {1} and {2 3} to make {1 2 3}; but
* if we join {1 2} and {3} then that clause will be a restrictclause in {1 2}
* and should not be processed again at the level of {1 2 3}.) Therefore,
* the restrictinfo list in the join case appears in individual JoinPaths
* (field joinrestrictinfo), not in the parent relation. But it's OK for
* the RelOptInfo to store the joininfo lists, because those are the same
* for a given rel no matter how we form it.
*/
typedef struct RelOptInfo
......@@ -86,7 +99,7 @@ typedef struct RelOptInfo
double tuples;
/* used by various scans and joins: */
List *restrictinfo; /* RestrictInfo structures */
List *baserestrictinfo; /* RestrictInfo structures (if base rel) */
List *joininfo; /* JoinInfo structures */
List *innerjoin; /* potential indexscans for nestloop joins */
/* innerjoin indexscans are not in the main pathlist because they are
......@@ -235,6 +248,10 @@ typedef struct JoinPath
Path *outerjoinpath; /* path for the outer side of the join */
Path *innerjoinpath; /* path for the inner side of the join */
List *joinrestrictinfo; /* RestrictInfos to apply to join */
/* See the notes for RelOptInfo to understand why joinrestrictinfo is
* needed in JoinPath, and can't be merged into the parent RelOptInfo.
*/
} JoinPath;
/*
......@@ -289,18 +306,29 @@ typedef struct HashPath
* without having to evaluate the rest. The RestrictInfo node itself stores
* data used by the optimizer while choosing the best query plan.
*
* A restriction clause will appear in the restrictinfo list of a RelOptInfo
* that describes exactly the set of base relations referenced by the
* restriction clause. It is not possible to apply the clause at any lower
* nesting level, and there is little point in delaying its evaluation to
* higher nesting levels. (The "predicate migration" code was once intended
* to push restriction clauses up and down the plan tree, but it's dead code
* and is unlikely to be resurrected in the foreseeable future.)
* If a restriction clause references a single base relation, it will appear
* in the baserestrictinfo list of the RelOptInfo for that base rel.
*
* If a restriction clause references more than one base rel, it will also
* appear in the JoinInfo list of every RelOptInfo that describes a strict
* If a restriction clause references more than one base rel, it will
* appear in the JoinInfo lists of every RelOptInfo that describes a strict
* subset of the base rels mentioned in the clause. The JoinInfo lists are
* used to drive join tree building by selecting plausible join candidates.
* The clause cannot actually be applied until we have built a join rel
* containing all the base rels it references, however.
*
* When we construct a join rel that describes exactly the set of base rels
* referenced in a multi-relation restriction clause, we place that clause
* into the joinrestrictinfo lists of paths for the join rel. It will be
* applied at that join level, and will not propagate any further up the
* join tree. (Note: the "predicate migration" code was once intended to
* push restriction clauses up and down the plan tree based on evaluation
* costs, but it's dead code and is unlikely to be resurrected in the
* foreseeable future.)
*
* Note that in the presence of more than two rels, a multi-rel restriction
* might reach different heights in the join tree depending on the join
* sequence we use. So, these clauses cannot be associated directly with
* the join RelOptInfo, but must be kept track of on a per-join-path basis.
*
* In general, the referenced clause might be arbitrarily complex. The
* kinds of clauses we can handle as indexscan quals, mergejoin clauses,
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: cost.h,v 1.28 2000/01/26 05:58:20 momjian Exp $
* $Id: cost.h,v 1.29 2000/02/07 04:41:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -55,9 +55,11 @@ extern Cost cost_mergejoin(Path *outer_path, Path *inner_path,
List *outersortkeys, List *innersortkeys);
extern Cost cost_hashjoin(Path *outer_path, Path *inner_path,
Selectivity innerdisbursion);
extern void set_rel_rows_width(Query *root, RelOptInfo *rel);
extern void set_joinrel_rows_width(Query *root, RelOptInfo *rel,
JoinPath *joinpath);
extern void set_baserel_size_estimates(Query *root, RelOptInfo *rel);
extern void set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
List *restrictlist);
/*
* prototypes for clausesel.c
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: pathnode.h,v 1.24 2000/01/26 05:58:20 momjian Exp $
* $Id: pathnode.h,v 1.25 2000/02/07 04:41:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,8 +21,8 @@
*/
extern bool path_is_cheaper(Path *path1, Path *path2);
extern Path *set_cheapest(RelOptInfo *parent_rel, List *pathlist);
extern List *add_pathlist(RelOptInfo *parent_rel, List *old_paths,
List *new_paths);
extern void add_path(RelOptInfo *parent_rel, Path *new_path);
extern void add_pathlist(RelOptInfo *parent_rel, List *new_paths);
extern Path *create_seqscan_path(RelOptInfo *rel);
extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
......@@ -31,25 +31,34 @@ extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
extern TidPath *create_tidscan_path(RelOptInfo *rel, List *tideval);
extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
Path *outer_path, Path *inner_path,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys);
extern MergePath *create_mergejoin_path(RelOptInfo *joinrel, Path *outer_path,
Path *inner_path, List *pathkeys,
extern MergePath *create_mergejoin_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *pathkeys,
List *mergeclauses,
List *outersortkeys,
List *innersortkeys);
extern HashPath *create_hashjoin_path(RelOptInfo *joinrel, Path *outer_path,
Path *inner_path, List *hashclauses,
extern HashPath *create_hashjoin_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
List *hashclauses,
Selectivity innerdisbursion);
/*
* prototypes for rel.c
* prototypes for relnode.c
*/
extern RelOptInfo *rel_member(Relids relid, List *rels);
extern RelOptInfo *get_base_rel(Query *root, int relid);
extern RelOptInfo *get_join_rel(Query *root, Relids relid);
extern RelOptInfo *get_join_rel(Query *root, RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
List **restrictlist_ptr);
/*
* prototypes for indexnode.h
......
......@@ -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.41 2000/02/06 03:27:35 tgl Exp $
* $Id: paths.h,v 1.42 2000/02/07 04:41:04 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -27,15 +27,15 @@
extern bool enable_geqo;
extern int geqo_rels;
extern RelOptInfo *make_one_rel(Query *root, List *rels);
extern RelOptInfo *make_one_rel(Query *root);
/*
* indxpath.c
* routines to generate index paths
*/
extern List *create_index_paths(Query *root, RelOptInfo *rel, List *indices,
List *restrictinfo_list,
List *joininfo_list);
List *restrictinfo_list,
List *joininfo_list);
extern Oid indexable_operator(Expr *clause, Oid opclass, Oid relam,
bool indexkey_on_left);
extern List *extract_or_indexqual_conditions(RelOptInfo *rel,
......@@ -60,7 +60,22 @@ extern List *create_tidscan_paths(Query *root, RelOptInfo *rel);
* joinpath.c
* routines to create join paths
*/
extern void update_rels_pathlist_for_joins(Query *root, List *joinrels);
extern void add_paths_to_joinrel(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *restrictlist);
/*
* joinrels.c
* routines to determine which relations to join
*/
extern void make_rels_by_joins(Query *root, int level);
extern RelOptInfo *make_rels_by_clause_joins(Query *root,
RelOptInfo *old_rel,
List *other_rels);
extern RelOptInfo *make_rels_by_clauseless_joins(Query *root,
RelOptInfo *old_rel,
List *other_rels);
/*
* pathkeys.c
......@@ -90,22 +105,4 @@ extern List *find_mergeclauses_for_pathkeys(List *pathkeys,
extern List *make_pathkeys_for_mergeclauses(List *mergeclauses,
List *tlist);
/*
* joinrels.c
* routines to determine which relations to join
*/
extern List *make_rels_by_joins(Query *root, List *old_rels);
extern List *make_rels_by_clause_joins(Query *root, RelOptInfo *old_rel,
List *joininfo_list, Relids only_relids);
extern List *make_rels_by_clauseless_joins(RelOptInfo *old_rel,
List *inner_rels);
extern RelOptInfo *get_cheapest_complete_rel(List *join_rel_list);
/*
* prune.c
*/
extern void merge_rels_with_same_relids(List *rel_list);
extern void rels_set_cheapest(Query *root, List *rel_list);
extern List *del_rels_all_bushy_inactive(List *old_rels);
#endif /* PATHS_H */
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