1
0
mirror of https://github.com/postgres/postgres.git synced 2025-11-15 03:41:20 +03:00

Postgres95 1.01 Distribution - Virgin Sources

This commit is contained in:
Marc G. Fournier
1996-07-09 06:22:35 +00:00
commit d31084e9d1
868 changed files with 242656 additions and 0 deletions

View File

@@ -0,0 +1,21 @@
#-------------------------------------------------------------------------
#
# Makefile.inc--
# Makefile for optimizer/path
#
# Copyright (c) 1994, Regents of the University of California
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/Makefile.inc,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
#
#-------------------------------------------------------------------------
SUBSRCS= allpaths.c clausesel.c costsize.c hashutils.c indxpath.c \
joinpath.c joinrels.c joinutils.c mergeutils.c orindxpath.c \
prune.c
# not ready yet: predmig.c xfunc.c

View File

@@ -0,0 +1,351 @@
/*-------------------------------------------------------------------------
*
* allpaths.c--
* Routines to find possible search paths for processing a query
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <string.h>
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/pathnode.h"
#include "optimizer/clauses.h"
#include "optimizer/xfunc.h"
#include "optimizer/cost.h"
#include "commands/creatinh.h"
static void find_rel_paths(Query *root, List *rels);
static List *find_join_paths(Query *root, List *outer_rels, int levels_left);
/*
* find-paths--
* Finds all possible access paths for executing a query, returning the
* top level list of relation entries.
*
* 'rels' is the list of single relation entries appearing in the query
*/
List *
find_paths(Query *root, List *rels)
{
int levels_left;
/*
* Set the number of join (not nesting) levels yet to be processed.
*/
levels_left = length(rels);
if (levels_left <= 0)
return NIL;
/*
* Find the base relation paths.
*/
find_rel_paths(root, rels);
if (levels_left <= 1) {
/*
* Unsorted single relation, no more processing is required.
*/
return (rels);
}else {
/*
* this means that joins or sorts are required.
* set selectivities of clauses that have not been set
* by an index.
*/
set_rest_relselec(root, rels);
return(find_join_paths(root, rels, levels_left-1));
}
}
/*
* find-rel-paths--
* Finds all paths available for scanning each relation entry in
* 'rels'. Sequential scan and any available indices are considered
* if possible(indices are not considered for lower nesting levels).
* All unique paths are attached to the relation's 'pathlist' field.
*
* MODIFIES: rels
*/
static void
find_rel_paths(Query *root, List *rels)
{
List *temp;
Rel *rel;
List *lastpath;
foreach(temp, rels) {
List *sequential_scan_list;
List *rel_index_scan_list;
List *or_index_scan_list;
rel = (Rel *)lfirst(temp);
sequential_scan_list = lcons(create_seqscan_path(rel),
NIL);
rel_index_scan_list =
find_index_paths(root,
rel,
find_relation_indices(root,rel),
rel->clauseinfo,
rel->joininfo);
or_index_scan_list =
create_or_index_paths(root, rel, rel->clauseinfo);
rel->pathlist = add_pathlist(rel,
sequential_scan_list,
append(rel_index_scan_list,
or_index_scan_list));
/* The unordered path is always the last in the list.
* If it is not the cheapest path, prune it.
*/
lastpath = rel->pathlist;
while(lnext(lastpath)!=NIL)
lastpath=lnext(lastpath);
prune_rel_path(rel, (Path*)lfirst(lastpath));
/*
* if there is a qualification of sequential scan the selec.
* value is not set -- so set it explicitly -- Sunita
*/
set_rest_selec(root, rel->clauseinfo);
rel->size = compute_rel_size(rel);
rel->width = compute_rel_width(rel);
}
return;
}
/*
* find-join-paths--
* Find all possible joinpaths for a query by successively finding ways
* to join single relations into join relations.
*
* if BushyPlanFlag is set, bushy tree plans will be generated:
* Find all possible joinpaths(bushy trees) for a query by systematically
* finding ways to join relations(both original and derived) together.
*
* 'outer-rels' is the current list of relations for which join paths
* are to be found, i.e., he current list of relations that
* have already been derived.
* 'levels-left' is the current join level being processed, where '1' is
* the "last" level
*
* Returns the final level of join relations, i.e., the relation that is
* the result of joining all the original relations togehter.
*/
static List *
find_join_paths(Query *root, List *outer_rels, int levels_left)
{
List *x;
List *new_rels;
Rel *rel;
/*
* Determine all possible pairs of relations to be joined at this level.
* Determine paths for joining these relation pairs and modify 'new-rels'
* accordingly, then eliminate redundant join relations.
*/
new_rels = find_join_rels(root, outer_rels);
find_all_join_paths(root, new_rels);
new_rels = prune_joinrels(new_rels);
#if 0
/*
** for each expensive predicate in each path in each distinct rel,
** consider doing pullup -- JMH
*/
if (XfuncMode != XFUNC_NOPULL && XfuncMode != XFUNC_OFF)
foreach(x, new_rels)
xfunc_trypullup((Rel*)lfirst(x));
#endif
prune_rel_paths(new_rels);
if(BushyPlanFlag) {
/*
* In case of bushy trees
* if there is still a join between a join relation and another
* relation, add a new joininfo that involves the join relation
* to the joininfo list of the other relation
*/
add_new_joininfos(root, new_rels,outer_rels);
}
foreach(x, new_rels) {
rel = (Rel*)lfirst(x);
rel->size = compute_rel_size(rel);
rel->width = compute_rel_width(rel);
/*#define OPTIMIZER_DEBUG*/
#ifdef OPTIMIZER_DEBUG
printf("levels left: %d\n", levels_left);
debug_print_rel(root, rel);
#endif
}
if(BushyPlanFlag) {
/*
* prune rels that have been completely incorporated into
* new join rels
*/
outer_rels = prune_oldrels(outer_rels);
/*
* merge join rels if then contain the same list of base rels
*/
outer_rels = merge_joinrels(new_rels,outer_rels);
root->join_relation_list_ = outer_rels;
}
else {
root->join_relation_list_ = new_rels;
}
if(levels_left == 1) {
if(BushyPlanFlag)
return(final_join_rels(outer_rels));
else
return(new_rels);
} else {
if(BushyPlanFlag)
return(find_join_paths(root, outer_rels, levels_left - 1));
else
return(find_join_paths(root, new_rels, levels_left - 1));
}
}
/*****************************************************************************
*
*****************************************************************************/
static void
print_joinclauses(Query *root, List *clauses)
{
List *l;
extern void print_expr(Node *expr, List *rtable); /* in print.c */
foreach(l, clauses) {
CInfo *c = lfirst(l);
print_expr((Node*)c->clause, root->rtable);
if (lnext(l)) printf(" ");
}
}
void
print_path(Query *root, Path *path, int indent)
{
char *ptype = NULL;
JoinPath *jp;
bool join;
int i;
for(i=0; i < indent; i++)
printf("\t");
switch(nodeTag(path)) {
case T_Path:
ptype = "SeqScan"; join=false; break;
case T_IndexPath:
ptype = "IdxScan"; join=false; break;
case T_JoinPath:
ptype = "Nestloop"; join=true; break;
case T_MergePath:
ptype = "MergeJoin"; join=true; break;
case T_HashPath:
ptype = "HashJoin"; join=true; break;
default:
break;
}
if (join) {
int size = path->parent->size;
jp = (JoinPath*)path;
printf("%s size=%d cost=%f\n", ptype, size, path->path_cost);
switch(nodeTag(path)) {
case T_MergePath:
case T_HashPath:
for(i=0; i < indent+1; i++)
printf("\t");
printf(" clauses=(");
print_joinclauses(root,
((JoinPath*)path)->pathclauseinfo);
printf(")\n");
if (nodeTag(path)==T_MergePath) {
MergePath *mp = (MergePath*)path;
if (mp->outersortkeys || mp->innersortkeys) {
for(i=0; i < indent+1; 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->innerjoinpath, indent+1);
} else {
int size = path->parent->size;
int relid = lfirsti(path->parent->relids);
printf("%s(%d) size=%d cost=%f",
ptype, relid, size, path->path_cost);
if (nodeTag(path)==T_IndexPath) {
List *k, *l;
printf(" keys=");
foreach (k, path->keys) {
printf("(");
foreach (l, lfirst(k)) {
Var *var = lfirst(l);
printf("%d.%d", var->varnoold, var->varoattno);
if (lnext(l)) printf(", ");
}
printf(")");
if (lnext(k)) printf(", ");
}
}
printf("\n");
}
}
#ifdef OPTIMIZER_DEBUG
static void
debug_print_rel(Query *root, Rel *rel)
{
List *l;
printf("(");
foreach(l, rel->relids) {
printf("%d ", lfirsti(l));
}
printf("): size=%d width=%d\n", rel->size, rel->width);
printf("\tpath list:\n");
foreach (l, rel->pathlist) {
print_path(root, lfirst(l), 1);
}
printf("\tcheapest path:\n");
print_path(root, rel->cheapestpath, 1);
}
#endif /* OPTIMIZER_DEBUG */

View File

@@ -0,0 +1,331 @@
/*-------------------------------------------------------------------------
*
* clausesel.c--
* Routines to compute and set clause selectivities
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/cost.h"
#include "optimizer/plancat.h"
#include "parser/parsetree.h" /* for getrelid() */
#include "catalog/pg_proc.h"
#include "catalog/pg_operator.h"
#include "utils/elog.h"
#include "utils/lsyscache.h"
static Cost compute_selec(Query *root, List *clauses, List *or_selectivities);
/****************************************************************************
* ROUTINES TO SET CLAUSE SELECTIVITIES
****************************************************************************/
/*
* set_clause_selectivities -
* Sets the selectivity field for each of clause in 'clauseinfo-list'
* to 'new-selectivity'. If the selectivity has already been set, reset
* it only if the new one is better.
*
* Returns nothing of interest.
*
*/
void
set_clause_selectivities(List *clauseinfo_list, Cost new_selectivity)
{
List *temp;
CInfo *clausenode;
Cost cost_clause;
foreach (temp,clauseinfo_list) {
clausenode = (CInfo*)lfirst(temp);
cost_clause = clausenode->selectivity;
if ( FLOAT_IS_ZERO(cost_clause) || new_selectivity < cost_clause) {
clausenode->selectivity = new_selectivity;
}
}
}
/*
* product_selec -
* Multiplies the selectivities of each clause in 'clauseinfo-list'.
*
* Returns a flonum corresponding to the selectivity of 'clauseinfo-list'.
*/
Cost
product_selec(List *clauseinfo_list)
{
Cost result = 1.0;
if (clauseinfo_list!=NIL) {
List *xclausenode = NIL;
Cost temp;
foreach(xclausenode,clauseinfo_list) {
temp = ((CInfo *)lfirst(xclausenode))->selectivity;
result = result * temp;
}
}
return(result);
}
/*
* set_rest_relselec -
* Scans through clauses on each relation and assigns a selectivity to
* those clauses that haven't been assigned a selectivity by an index.
*
* Returns nothing of interest.
* MODIFIES: selectivities of the various rel's clauseinfo
* slots.
*/
void
set_rest_relselec(Query *root, List *rel_list)
{
Rel *rel;
List *x;
foreach (x,rel_list) {
rel = (Rel*)lfirst(x);
set_rest_selec(root, rel->clauseinfo);
}
}
/*
* set_rest_selec -
* Sets the selectivity fields for those clauses within a single
* relation's 'clauseinfo-list' that haven't already been set.
*
* Returns nothing of interest.
*
*/
void
set_rest_selec(Query *root, List *clauseinfo_list)
{
List *temp = NIL;
CInfo *clausenode = (CInfo*)NULL;
Cost cost_clause;
foreach (temp,clauseinfo_list) {
clausenode = (CInfo*)lfirst(temp);
cost_clause = clausenode->selectivity;
/*
* Check to see if the selectivity of this clause or any 'or'
* subclauses (if any) haven't been set yet.
*/
if (valid_or_clause(clausenode) || FLOAT_IS_ZERO(cost_clause)) {
clausenode->selectivity =
compute_clause_selec(root,
(Node*)clausenode->clause,
lcons(makeFloat(cost_clause), NIL));
}
}
}
/****************************************************************************
* ROUTINES TO COMPUTE SELECTIVITIES
****************************************************************************/
/*
* compute_clause_selec -
* Given a clause, this routine will compute the selectivity of the
* clause by calling 'compute_selec' with the appropriate parameters
* and possibly use that return value to compute the real selectivity
* of a clause.
*
* 'or-selectivities' are selectivities that have already been assigned
* to subclauses of an 'or' clause.
*
* Returns a flonum corresponding to the clause selectivity.
*
*/
Cost
compute_clause_selec(Query *root, Node *clause, List *or_selectivities)
{
if (!is_opclause (clause)) {
/* if it's not an operator clause, then it is a boolean clause -jolly*/
/*
* Boolean variables get a selectivity of 1/2.
*/
return(0.1);
} else if (not_clause (clause)) {
/*
* 'not' gets "1.0 - selectivity-of-inner-clause".
*/
return (1.000000 - compute_selec(root,
lcons(get_notclausearg((Expr*)clause),
NIL),
or_selectivities));
} else if (or_clause(clause)) {
/*
* Both 'or' and 'and' clauses are evaluated as described in
* (compute_selec).
*/
return (compute_selec(root,
((Expr*)clause)->args, or_selectivities));
} else {
return(compute_selec(root,
lcons(clause,NIL),or_selectivities));
}
}
/*
* compute_selec -
* Computes the selectivity of a clause.
*
* If there is more than one clause in the argument 'clauses', then the
* desired selectivity is that of an 'or' clause. Selectivities for an
* 'or' clause such as (OR a b) are computed by finding the selectivity
* of a (s1) and b (s2) and computing s1+s2 - s1*s2.
*
* In addition, if the clause is an 'or' clause, individual selectivities
* may have already been assigned by indices to subclauses. These values
* are contained in the list 'or-selectivities'.
*
* Returns the clause selectivity as a flonum.
*
*/
static Cost
compute_selec(Query *root, List *clauses, List *or_selectivities)
{
Cost s1 = 0;
List *clause = lfirst(clauses);
if (clauses==NULL) {
s1 = 1.0;
} else if (IsA(clause,Param)) {
/* XXX How're we handling this before?? -ay */
s1 = 1.0;
} else if (IsA(clause,Const)) {
s1 = ((bool) ((Const*) clause)->constvalue) ? 1.0 : 0.0;
} else if (IsA(clause,Var)) {
Oid relid = getrelid(((Var*)clause)->varno,
root->rtable);
/*
* we have a bool Var. This is exactly equivalent to the clause:
* reln.attribute = 't'
* so we compute the selectivity as if that is what we have. The
* magic #define constants are a hack. I didn't want to have to
* do system cache look ups to find out all of that info.
*/
s1 = restriction_selectivity(EqualSelectivityProcedure,
BooleanEqualOperator,
relid,
((Var*)clause)->varoattno,
"t",
_SELEC_CONSTANT_RIGHT_);
} else if (or_selectivities) {
/* If s1 has already been assigned by an index, use that value. */
List *this_sel = lfirst(or_selectivities);
s1 = floatVal(this_sel);
} else if (is_funcclause((Node*)clause)) {
/* this isn't an Oper, it's a Func!! */
/*
** This is not an operator, so we guess at the selectivity.
** THIS IS A HACK TO GET V4 OUT THE DOOR. FUNCS SHOULD BE
** ABLE TO HAVE SELECTIVITIES THEMSELVES.
** -- JMH 7/9/92
*/
s1 = 0.1;
} else if (NumRelids((Node*) clause) == 1) {
/* ...otherwise, calculate s1 from 'clauses'.
* The clause is not a join clause, since there is
* only one relid in the clause. The clause
* selectivity will be based on the operator
* selectivity and operand values.
*/
Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
RegProcedure oprrest = get_oprrest(opno);
Oid relid;
int relidx;
AttrNumber attno;
Datum constval;
int flag;
get_relattval((Node*)clause, &relidx, &attno, &constval, &flag);
relid = getrelid(relidx, root->rtable);
/* if the oprrest procedure is missing for whatever reason,
use a selectivity of 0.5*/
if (!oprrest)
s1 = (Cost) (0.5);
else
if (attno == InvalidAttrNumber) {
/* attno can be Invalid if the clause had a function in it,
i.e. WHERE myFunc(f) = 10 */
/* this should be FIXED somehow to use function selectivity */
s1 = (Cost) (0.5);
} else
s1 = (Cost) restriction_selectivity(oprrest,
opno,
relid,
attno,
(char *)constval,
flag);
} else {
/* The clause must be a join clause. The clause
* selectivity will be based on the relations to be
* scanned and the attributes they are to be joined
* on.
*/
Oid opno = ((Oper*)((Expr*)clause)->oper)->opno;
RegProcedure oprjoin = get_oprjoin (opno);
int relid1, relid2;
AttrNumber attno1, attno2;
get_rels_atts((Node*)clause, &relid1, &attno1, &relid2, &attno2);
relid1 = getrelid(relid1, root->rtable);
relid2 = getrelid(relid2, root->rtable);
/* if the oprjoin procedure is missing for whatever reason,
use a selectivity of 0.5*/
if (!oprjoin)
s1 = (Cost) (0.5);
else
s1 = (Cost) join_selectivity(oprjoin,
opno,
relid1,
attno1,
relid2,
attno2);
}
/* A null clause list eliminates no tuples, so return a selectivity
* of 1.0. If there is only one clause, the selectivity is not
* that of an 'or' clause, but rather that of the single clause.
*/
if (length (clauses) < 2) {
return(s1);
} else {
/* Compute selectivity of the 'or'ed subclauses. */
/* Added check for taking lnext(NIL). -- JMH 3/9/92 */
Cost s2;
if (or_selectivities != NIL)
s2 = compute_selec(root, lnext(clauses), lnext(or_selectivities));
else
s2 = compute_selec(root, lnext(clauses), NIL);
return(s1 + s2 - s1 * s2);
}
}

View File

@@ -0,0 +1,456 @@
/*-------------------------------------------------------------------------
*
* costsize.c--
* Routines to compute (and set) relation sizes and path costs
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
#ifdef WIN32
#include <float.h>
#include <limits.h>
#define MAXINT INT_MAX
#else
# if defined(PORTNAME_BSD44_derived) || defined(PORTNAME_bsdi)
# include <machine/limits.h>
# define MAXINT INT_MAX
# else
# include <values.h>
# endif /* !PORTNAME_BSD44_derived */
#endif /* WIN32 */
#include "postgres.h"
#include "nodes/relation.h"
#include "optimizer/cost.h"
#include "optimizer/internal.h"
#include "optimizer/keys.h"
#include "optimizer/tlist.h"
#include "storage/bufmgr.h" /* for BLCKSZ */
static int compute_attribute_width(TargetEntry *tlistentry);
static double base_log(double x, double b);
int _disable_cost_ = 30000000;
bool _enable_seqscan_ = true;
bool _enable_indexscan_ = true;
bool _enable_sort_ = true;
bool _enable_hash_ = true;
bool _enable_nestloop_ = true;
bool _enable_mergesort_ = true;
bool _enable_hashjoin_ = true;
/*
* cost_seqscan--
* Determines and returns the cost of scanning a relation sequentially.
* If the relation is a temporary to be materialized from a query
* embedded within a data field (determined by 'relid' containing an
* attribute reference), then a predetermined constant is returned (we
* have NO IDEA how big the result of a POSTQUEL procedure is going to
* be).
*
* disk = p
* cpu = *CPU-PAGE-WEIGHT* * t
*
* 'relid' is the relid of the relation to be scanned
* 'relpages' is the number of pages in the relation to be scanned
* (as determined from the system catalogs)
* 'reltuples' is the number of tuples in the relation to be scanned
*
* Returns a flonum.
*
*/
Cost
cost_seqscan(int relid, int relpages, int reltuples)
{
Cost temp = 0;
if ( !_enable_seqscan_ )
temp += _disable_cost_;
if (relid < 0) {
/*
* cost of sequentially scanning a materialized temporary relation
*/
temp += _TEMP_SCAN_COST_;
} else {
temp += relpages;
temp += _CPU_PAGE_WEIGHT_ * reltuples;
}
Assert(temp >= 0);
return(temp);
}
/*
* cost_index--
* Determines and returns the cost of scanning a relation using an index.
*
* disk = expected-index-pages + expected-data-pages
* cpu = *CPU-PAGE-WEIGHT* *
* (expected-index-tuples + expected-data-tuples)
*
* 'indexid' is the index OID
* 'expected-indexpages' is the number of index pages examined in the scan
* 'selec' is the selectivity of the index
* 'relpages' is the number of pages in the main relation
* 'reltuples' is the number of tuples in the main relation
* 'indexpages' is the number of pages in the index relation
* 'indextuples' is the number of tuples in the index relation
*
* Returns a flonum.
*
*/
Cost
cost_index(Oid indexid,
int expected_indexpages,
Cost selec,
int relpages,
int reltuples,
int indexpages,
int indextuples,
bool is_injoin)
{
Cost temp;
Cost temp2;
temp = temp2 = (Cost) 0;
if (!_enable_indexscan_ && !is_injoin)
temp += _disable_cost_;
/* expected index relation pages */
temp += expected_indexpages;
/* about one base relation page */
temp += Min(relpages,(int)ceil((double)selec*indextuples));
/*
* per index tuple
*/
temp2 += selec * indextuples;
temp2 += selec * reltuples;
temp = temp + (_CPU_PAGE_WEIGHT_ * temp2);
Assert(temp >= 0);
return(temp);
}
/*
* cost_sort--
* Determines and returns the cost of sorting a relation by considering
* 1. the cost of doing an external sort: XXX this is probably too low
* disk = (p lg p)
* cpu = *CPU-PAGE-WEIGHT* * (t lg t)
* 2. the cost of reading the sort result into memory (another seqscan)
* unless 'noread' is set
*
* 'keys' is a list of sort keys
* 'tuples' is the number of tuples in the relation
* 'width' is the average tuple width in bytes
* 'noread' is a flag indicating that the sort result can remain on disk
* (i.e., the sort result is the result relation)
*
* Returns a flonum.
*
*/
Cost
cost_sort(List *keys, int tuples, int width, bool noread)
{
Cost temp = 0;
int npages = page_size (tuples,width);
Cost pages = (Cost)npages;
Cost numTuples = tuples;
if ( !_enable_sort_ )
temp += _disable_cost_ ;
if (tuples == 0 || keys==NULL)
{
Assert(temp >= 0);
return(temp);
}
temp += pages * base_log((double)pages, (double)2.0);
/*
* could be base_log(pages, NBuffers), but we are only doing 2-way merges
*/
temp += _CPU_PAGE_WEIGHT_ *
numTuples * base_log((double)pages,(double)2.0);
if( !noread )
temp = temp + cost_seqscan(_TEMP_RELATION_ID_, npages, tuples);
Assert(temp >= 0);
return(temp);
}
/*
* cost_result--
* Determines and returns the cost of writing a relation of 'tuples'
* tuples of 'width' bytes out to a result relation.
*
* Returns a flonum.
*
*/
Cost
cost_result(int tuples, int width)
{
Cost temp =0;
temp = temp + page_size(tuples,width);
temp = temp + _CPU_PAGE_WEIGHT_ * tuples;
Assert(temp >= 0);
return(temp);
}
/*
* cost_nestloop--
* Determines and returns the cost of joining two relations using the
* nested loop algorithm.
*
* 'outercost' is the (disk+cpu) cost of scanning the outer relation
* 'innercost' is the (disk+cpu) cost of scanning the inner relation
* 'outertuples' is the number of tuples in the outer relation
*
* Returns a flonum.
*
*/
Cost
cost_nestloop(Cost outercost,
Cost innercost,
int outertuples,
int innertuples,
int outerpages,
bool is_indexjoin)
{
Cost temp =0;
if ( !_enable_nestloop_ )
temp += _disable_cost_;
temp += outercost;
temp += outertuples * innercost;
Assert(temp >= 0);
return(temp);
}
/*
* cost_mergesort--
* 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
* outer and inner relations
* 'outersortkeys' and 'innersortkeys' are lists of the keys to be used
* to sort the outer and inner relations
* 'outertuples' and 'innertuples' are the number of tuples in the outer
* and inner relations
* 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
* of the tuples of the outer and inner relations
*
* Returns a flonum.
*
*/
Cost
cost_mergesort(Cost outercost,
Cost innercost,
List *outersortkeys,
List *innersortkeys,
int outersize,
int innersize,
int outerwidth,
int innerwidth)
{
Cost temp = 0;
if ( !_enable_mergesort_ )
temp += _disable_cost_;
temp += outercost;
temp += innercost;
temp += cost_sort(outersortkeys,outersize,outerwidth,false);
temp += cost_sort(innersortkeys,innersize,innerwidth,false);
temp += _CPU_PAGE_WEIGHT_ * (outersize + innersize);
Assert(temp >= 0);
return(temp);
}
/*
* cost_hashjoin-- XXX HASH
* 'outercost' and 'innercost' are the (disk+cpu) costs of scanning the
* outer and inner relations
* 'outerkeys' and 'innerkeys' are lists of the keys to be used
* to hash the outer and inner relations
* 'outersize' and 'innersize' are the number of tuples in the outer
* and inner relations
* 'outerwidth' and 'innerwidth' are the (typical) widths (in bytes)
* of the tuples of the outer and inner relations
*
* Returns a flonum.
*/
Cost
cost_hashjoin(Cost outercost,
Cost innercost,
List *outerkeys,
List *innerkeys,
int outersize,
int innersize,
int outerwidth,
int innerwidth)
{
Cost temp = 0;
int outerpages = page_size (outersize,outerwidth);
int innerpages = page_size (innersize,innerwidth);
int nrun = ceil((double)outerpages/(double)NBuffers);
if (outerpages < innerpages)
return _disable_cost_;
if ( !_enable_hashjoin_ )
temp += _disable_cost_;
/* temp += outercost + (nrun + 1) * innercost; */
/*
the innercost shouldn't be used it. Instead the
cost of hashing the innerpath should be used
ASSUME innercost is 1 for now -- a horrible hack
- jolly
*/
temp += outercost + (nrun + 1);
temp += _CPU_PAGE_WEIGHT_ * (outersize + nrun * innersize);
Assert(temp >= 0);
return(temp);
}
/*
* compute-rel-size--
* Computes the size of each relation in 'rel-list' (after applying
* restrictions), by multiplying the selectivity of each restriction
* by the original size of the relation.
*
* Sets the 'size' field for each relation entry with this computed size.
*
* Returns the size.
*/
int compute_rel_size(Rel *rel)
{
Cost temp;
int temp1;
temp = rel->tuples * product_selec(rel->clauseinfo);
Assert(temp >= 0);
if (temp >= (MAXINT - 1)) {
temp1 = MAXINT;
} else {
temp1 = ceil((double) temp);
}
Assert(temp1 >= 0);
Assert(temp1 <= MAXINT);
return(temp1);
}
/*
* compute-rel-width--
* Computes the width in bytes of a tuple from 'rel'.
*
* Returns the width of the tuple as a fixnum.
*/
int
compute_rel_width(Rel *rel)
{
return (compute_targetlist_width(get_actual_tlist(rel->targetlist)));
}
/*
* compute-targetlist-width--
* Computes the width in bytes of a tuple made from 'targetlist'.
*
* Returns the width of the tuple as a fixnum.
*/
int
compute_targetlist_width(List *targetlist)
{
List *temp_tl;
int tuple_width = 0;
foreach (temp_tl, targetlist) {
tuple_width = tuple_width +
compute_attribute_width(lfirst(temp_tl));
}
return(tuple_width);
}
/*
* compute-attribute-width--
* Given a target list entry, find the size in bytes of the attribute.
*
* If a field is variable-length, it is assumed to be at least the size
* of a TID field.
*
* Returns the width of the attribute as a fixnum.
*/
static int
compute_attribute_width(TargetEntry *tlistentry)
{
int width = get_typlen(tlistentry->resdom->restype);
if (width < 0)
return(_DEFAULT_ATTRIBUTE_WIDTH_);
else
return(width);
}
/*
* compute-joinrel-size--
* Computes the size of the join relation 'joinrel'.
*
* Returns a fixnum.
*/
int
compute_joinrel_size(JoinPath *joinpath)
{
Cost temp = 1.0;
int temp1 = 0;
temp *= ((Path*)joinpath->outerjoinpath)->parent->size;
temp *= ((Path*)joinpath->innerjoinpath)->parent->size;
temp = temp * product_selec(joinpath->pathclauseinfo);
if (temp >= (MAXINT -1)) {
temp1 = MAXINT;
} else {
/* should be ceil here, we don't want joinrel size's of one, do we? */
temp1 = ceil((double)temp);
}
Assert(temp1 >= 0);
return(temp1);
}
/*
* page-size--
* Returns an estimate of the number of pages covered by a given
* number of tuples of a given width (size in bytes).
*/
int page_size(int tuples, int width)
{
int temp =0;
temp = ceil((double)(tuples * (width + sizeof(HeapTupleData)))
/ BLCKSZ);
Assert(temp >= 0);
return(temp);
}
static double
base_log(double x, double b)
{
return(log(x)/log(b));
}

View File

@@ -0,0 +1,120 @@
/*-------------------------------------------------------------------------
*
* hashutils.c--
* Utilities for finding applicable merge clauses and pathkeys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/clauses.h"
static HInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list);
/*
* group-clauses-by-hashop--
* If a join clause node in 'clauseinfo-list' is hashjoinable, store
* it within a hashinfo node containing other clause nodes with the same
* hash operator.
*
* 'clauseinfo-list' is the list of clauseinfo nodes
* 'inner-relid' is the relid of the inner join relation
*
* Returns the new list of hashinfo nodes.
*
*/
List *
group_clauses_by_hashop(List *clauseinfo_list,
int inner_relid)
{
List *hashinfo_list = NIL;
CInfo *clauseinfo = (CInfo*)NULL;
List *i = NIL;
Oid hashjoinop = 0;
foreach (i,clauseinfo_list) {
clauseinfo = (CInfo*)lfirst(i);
hashjoinop = clauseinfo->hashjoinoperator;
/*
* Create a new hashinfo node and add it to 'hashinfo-list' if one
* does not yet exist for this hash operator.
*/
if (hashjoinop ) {
HInfo *xhashinfo = (HInfo*)NULL;
Expr *clause = clauseinfo->clause;
Var *leftop = get_leftop(clause);
Var *rightop = get_rightop(clause);
JoinKey *keys = (JoinKey*)NULL;
xhashinfo =
match_hashop_hashinfo(hashjoinop,hashinfo_list);
if (inner_relid == leftop->varno){
keys = makeNode(JoinKey);
keys->outer = rightop;
keys->inner = leftop;
} else {
keys = makeNode(JoinKey);
keys->outer = leftop;
keys->inner = rightop;
}
if (xhashinfo==NULL) {
xhashinfo = makeNode(HInfo);
xhashinfo->hashop = hashjoinop;
xhashinfo->jmethod.jmkeys = NIL;
xhashinfo->jmethod.clauses = NIL;
/* XXX was push */
hashinfo_list = lappend(hashinfo_list,xhashinfo);
hashinfo_list = nreverse(hashinfo_list);
}
xhashinfo->jmethod.clauses =
lcons(clause, xhashinfo->jmethod.clauses);
xhashinfo->jmethod.jmkeys =
lcons(keys, xhashinfo->jmethod.jmkeys);
}
}
return(hashinfo_list);
}
/*
* match-hashop-hashinfo--
* Searches the list 'hashinfo-list' for a hashinfo node whose hash op
* field equals 'hashop'.
*
* Returns the node if it exists.
*
*/
static HInfo *
match_hashop_hashinfo(Oid hashop, List *hashinfo_list)
{
Oid key = 0;
HInfo *xhashinfo = (HInfo*)NULL;
List *i = NIL;
foreach( i, hashinfo_list) {
xhashinfo = (HInfo*)lfirst(i);
key = xhashinfo->hashop;
if (hashop == key) { /* found */
return(xhashinfo); /* should be a hashinfo node ! */
}
}
return((HInfo*)NIL);
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,623 @@
/*-------------------------------------------------------------------------
*
* joinpath.c--
* Routines to find all possible paths for processing a set of joins
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.1.1.1 1996/07/09 06:21:35 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include <math.h>
#include "storage/buf_internals.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/plannodes.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/pathnode.h"
#include "optimizer/keys.h"
#include "optimizer/cost.h" /* for _enable_{hashjoin, _enable_mergesort} */
static Path *best_innerjoin(List *join_paths, List *outer_relid);
static List *sort_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
List *mergeinfo_list);
static List *match_unsorted_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin,
List *mergeinfo_list);
static List *match_unsorted_inner(Rel *joinrel, Rel *outerrel, Rel *innerrel,
List *innerpath_list, List *mergeinfo_list);
static bool EnoughMemoryForHashjoin(Rel *hashrel);
static List *hash_inner_and_outer(Rel *joinrel, Rel *outerrel, Rel *innerrel,
List *hashinfo_list);
/*
* find-all-join-paths--
* Creates all possible ways to process joins for each of the join
* relations in the list 'joinrels.' Each unique path will be included
* in the join relation's 'pathlist' field.
*
* In postgres, n-way joins are handled left-only(permuting clauseless
* joins doesn't usually win much).
*
* if BushyPlanFlag is true, bushy tree plans will be generated
*
* 'joinrels' is the list of relation entries to be joined
*
* Modifies the pathlist field of the appropriate rel node to contain
* the unique join paths.
* If bushy trees are considered, may modify the relid field of the
* join rel nodes to flatten the lists.
*
* Returns nothing of interest. (?)
* It does a destructive modification.
*/
void
find_all_join_paths(Query *root, List *joinrels)
{
List *mergeinfo_list = NIL;
List *hashinfo_list = NIL;
List *temp_list = NIL;
List *path = NIL;
while (joinrels != NIL) {
Rel *joinrel = (Rel *)lfirst(joinrels);
List *innerrelids;
List *outerrelids;
Rel *innerrel;
Rel *outerrel;
Path *bestinnerjoin;
List *pathlist = NIL;
innerrelids = lsecond(joinrel->relids);
outerrelids = lfirst(joinrel->relids);
/*
* base relation id is an integer and join relation relid is a
* list of integers.
*/
innerrel = (length(innerrelids)==1)?
get_base_rel(root, lfirsti(innerrelids)) : get_join_rel(root,innerrelids);
outerrel = (length(outerrelids)==1)?
get_base_rel(root, lfirsti(outerrelids)) : get_join_rel(root, outerrelids);
bestinnerjoin = best_innerjoin(innerrel->innerjoin,
outerrel->relids);
if( _enable_mergesort_ ) {
mergeinfo_list =
group_clauses_by_order(joinrel->clauseinfo,
lfirsti(innerrel->relids));
}
if( _enable_hashjoin_ ) {
hashinfo_list =
group_clauses_by_hashop(joinrel->clauseinfo,
lfirsti(innerrel->relids));
}
/* need to flatten the relids list */
joinrel->relids = intAppend(outerrelids, innerrelids);
/*
* 1. Consider mergesort paths where both relations must be
* explicitly sorted.
*/
pathlist = sort_inner_and_outer(joinrel,outerrel,
innerrel,mergeinfo_list);
/*
* 2. Consider paths where the outer relation need not be explicitly
* sorted. This may include either nestloops and mergesorts where
* the outer path is already ordered.
*/
pathlist =
add_pathlist(joinrel, pathlist,
match_unsorted_outer(joinrel,
outerrel,
innerrel,
outerrel->pathlist,
(Path*)innerrel->cheapestpath,
bestinnerjoin,
mergeinfo_list));
/*
* 3. Consider paths where the inner relation need not be explicitly
* sorted. This may include nestloops and mergesorts the actual
* nestloop nodes were constructed in (match-unsorted-outer).
*/
pathlist =
add_pathlist(joinrel,pathlist,
match_unsorted_inner(joinrel,outerrel,
innerrel,
innerrel->pathlist,
mergeinfo_list));
/*
* 4. Consider paths where both outer and inner relations must be
* hashed before being joined.
*/
pathlist =
add_pathlist(joinrel, pathlist,
hash_inner_and_outer(joinrel,outerrel,
innerrel,hashinfo_list));
joinrel->pathlist = pathlist;
/*
* 'OuterJoinCost is only valid when calling (match-unsorted-inner)
* with the same arguments as the previous invokation of
* (match-unsorted-outer), so clear the field before going on.
*/
temp_list = innerrel->pathlist;
foreach(path, temp_list) {
/*
* XXX
*
* This gross hack is to get around an apparent optimizer bug on
* Sparc (or maybe it is a bug of ours?) that causes really wierd
* behavior.
*/
if (IsA_JoinPath(path)) {
((Path*)lfirst(path))->outerjoincost = (Cost) 0;
}
/* do it iff it is a join path, which is not always
true, esp since the base level */
}
joinrels = lnext(joinrels);
}
}
/*
* best-innerjoin--
* Find the cheapest index path that has already been identified by
* (indexable_joinclauses) as being a possible inner path for the given
* outer relation in a nestloop join.
*
* 'join-paths' is a list of join nodes
* 'outer-relid' is the relid of the outer join relation
*
* Returns the pathnode of the selected path.
*/
static Path *
best_innerjoin(List *join_paths, List *outer_relids)
{
Path *cheapest = (Path*)NULL;
List *join_path;
foreach(join_path, join_paths) {
Path *path = (Path *)lfirst(join_path);
if (intMember(lfirsti(path->joinid), outer_relids)
&& ((cheapest==NULL ||
path_is_cheaper((Path*)lfirst(join_path),cheapest)))) {
cheapest = (Path*)lfirst(join_path);
}
}
return(cheapest);
}
/*
* sort-inner-and-outer--
* Create mergesort join paths by explicitly sorting both the outer and
* inner join relations on each available merge ordering.
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'mergeinfo-list' is a list of nodes containing info on(mergesortable)
* clauses for joining the relations
*
* Returns a list of mergesort paths.
*/
static List *
sort_inner_and_outer(Rel *joinrel,
Rel *outerrel,
Rel *innerrel,
List *mergeinfo_list)
{
List *ms_list = NIL;
MInfo *xmergeinfo = (MInfo*)NULL;
MergePath *temp_node = (MergePath*)NULL;
List *i;
List *outerkeys = NIL;
List *innerkeys = NIL;
List *merge_pathkeys = NIL;
foreach(i, mergeinfo_list) {
xmergeinfo = (MInfo *)lfirst(i);
outerkeys =
extract_path_keys(xmergeinfo->jmethod.jmkeys,
outerrel->targetlist,
OUTER);
innerkeys =
extract_path_keys(xmergeinfo->jmethod.jmkeys,
innerrel->targetlist,
INNER);
merge_pathkeys =
new_join_pathkeys(outerkeys, joinrel->targetlist,
xmergeinfo->jmethod.clauses);
temp_node =
create_mergesort_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
(Path*)outerrel->cheapestpath,
(Path*)innerrel->cheapestpath,
merge_pathkeys,
xmergeinfo->m_ordering,
xmergeinfo->jmethod.clauses,
outerkeys,
innerkeys);
ms_list = lappend(ms_list, temp_node);
}
return(ms_list);
}
/*
* match-unsorted-outer--
* Creates possible join paths for processing a single join relation
* 'joinrel' by employing either iterative substitution or
* mergesorting on each of its possible outer paths(assuming that the
* outer relation need not be explicitly sorted).
*
* 1. The inner path is the cheapest available inner path.
* 2. Mergesort wherever possible. Mergesorts are considered if there
* are mergesortable join clauses between the outer and inner join
* relations such that the outer path is keyed on the variables
* appearing in the clauses. The corresponding inner merge path is
* either a path whose keys match those of the outer path(if such a
* path is available) or an explicit sort on the appropriate inner
* join keys, whichever is cheaper.
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'outerpath-list' is the list of possible outer paths
* 'cheapest-inner' is the cheapest inner path
* 'best-innerjoin' is the best inner index path(if any)
* 'mergeinfo-list' is a list of nodes containing info on mergesortable
* clauses
*
* Returns a list of possible join path nodes.
*/
static List *
match_unsorted_outer(Rel *joinrel,
Rel *outerrel,
Rel *innerrel,
List *outerpath_list,
Path *cheapest_inner,
Path *best_innerjoin,
List *mergeinfo_list)
{
Path *outerpath = (Path*)NULL;
List *jp_list = NIL;
List *temp_node = NIL;
List *merge_pathkeys = NIL;
Path *nestinnerpath =(Path*)NULL;
List *paths = NIL;
List *i = NIL;
PathOrder *outerpath_ordering = NULL;
foreach(i,outerpath_list) {
List *clauses = NIL;
List *matchedJoinKeys = NIL;
List *matchedJoinClauses = NIL;
MInfo *xmergeinfo = (MInfo*)NULL;
outerpath = (Path*)lfirst(i);
outerpath_ordering = &outerpath->p_ordering;
if (outerpath_ordering) {
xmergeinfo =
match_order_mergeinfo(outerpath_ordering,
mergeinfo_list);
}
if (xmergeinfo) {
clauses = xmergeinfo->jmethod.clauses;
}
if (clauses) {
List *keys = xmergeinfo->jmethod.jmkeys;
List *clauses = xmergeinfo->jmethod.clauses;
matchedJoinKeys =
match_pathkeys_joinkeys(outerpath->keys,
keys,
clauses,
OUTER,
&matchedJoinClauses);
merge_pathkeys =
new_join_pathkeys(outerpath->keys,
joinrel->targetlist, clauses);
} else {
merge_pathkeys = outerpath->keys;
}
if(best_innerjoin &&
path_is_cheaper(best_innerjoin, cheapest_inner)) {
nestinnerpath = best_innerjoin;
} else {
nestinnerpath = cheapest_inner;
}
paths = lcons(create_nestloop_path(joinrel,
outerrel,
outerpath,
nestinnerpath,
merge_pathkeys),
NIL);
if (clauses && matchedJoinKeys) {
bool path_is_cheaper_than_sort;
List *varkeys = NIL;
Path *mergeinnerpath =
match_paths_joinkeys(matchedJoinKeys,
outerpath_ordering,
innerrel->pathlist,
INNER);
path_is_cheaper_than_sort =
(bool) (mergeinnerpath &&
(mergeinnerpath->path_cost <
(cheapest_inner->path_cost +
cost_sort(matchedJoinKeys,
innerrel->size,
innerrel->width,
false))));
if(!path_is_cheaper_than_sort) {
varkeys =
extract_path_keys(matchedJoinKeys,
innerrel->targetlist,
INNER);
}
/*
* Keep track of the cost of the outer path used with
* this ordered inner path for later processing in
* (match-unsorted-inner), since it isn't a sort and
* thus wouldn't otherwise be considered.
*/
if (path_is_cheaper_than_sort) {
mergeinnerpath->outerjoincost = outerpath->path_cost;
} else {
mergeinnerpath = cheapest_inner;
}
temp_node =
lcons(create_mergesort_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
outerpath,
mergeinnerpath,
merge_pathkeys,
xmergeinfo->m_ordering,
matchedJoinClauses,
NIL,
varkeys),
paths);
} else {
temp_node = paths;
}
jp_list = nconc(jp_list, temp_node);
}
return(jp_list);
}
/*
* match-unsorted-inner --
* Find the cheapest ordered join path for a given(ordered, unsorted)
* inner join path.
*
* Scans through each path available on an inner join relation and tries
* matching its ordering keys against those of mergejoin clauses.
* If 1. an appropriately-ordered inner path and matching mergeclause are
* found, and
* 2. sorting the cheapest outer path is cheaper than using an ordered
* but unsorted outer path(as was considered in
* (match-unsorted-outer)),
* then this merge path is considered.
*
* 'joinrel' is the join result relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'innerpath-list' is the list of possible inner join paths
* 'mergeinfo-list' is a list of nodes containing info on mergesortable
* clauses
*
* Returns a list of possible merge paths.
*/
static List *
match_unsorted_inner(Rel *joinrel,
Rel *outerrel,
Rel *innerrel,
List *innerpath_list,
List *mergeinfo_list)
{
Path *innerpath = (Path*)NULL;
List *mp_list = NIL;
List *temp_node = NIL;
PathOrder *innerpath_ordering = NULL;
Cost temp1 = 0.0;
bool temp2 = false;
List *i = NIL;
foreach (i, innerpath_list) {
MInfo *xmergeinfo = (MInfo*)NULL;
List *clauses = NIL;
List *matchedJoinKeys = NIL;
List *matchedJoinClauses = NIL;
innerpath = (Path*)lfirst(i);
innerpath_ordering = &innerpath->p_ordering;
if (innerpath_ordering) {
xmergeinfo =
match_order_mergeinfo(innerpath_ordering,
mergeinfo_list);
}
if (xmergeinfo) {
clauses = ((JoinMethod*)xmergeinfo)->clauses;
}
if (clauses) {
List *keys = xmergeinfo->jmethod.jmkeys;
List *cls = xmergeinfo->jmethod.clauses;
matchedJoinKeys =
match_pathkeys_joinkeys(innerpath->keys,
keys,
cls,
INNER,
&matchedJoinClauses);
}
/*
* (match-unsorted-outer) if it is applicable.
* 'OuterJoinCost was set above in
*/
if (clauses && matchedJoinKeys) {
temp1 = outerrel->cheapestpath->path_cost +
cost_sort(matchedJoinKeys, outerrel->size, outerrel->width,
false);
temp2 = (bool) (FLOAT_IS_ZERO(innerpath->outerjoincost)
|| (innerpath->outerjoincost > temp1));
if(temp2) {
List *outerkeys =
extract_path_keys(matchedJoinKeys,
outerrel->targetlist,
OUTER);
List *merge_pathkeys =
new_join_pathkeys(outerkeys,
joinrel->targetlist,
clauses);
temp_node =
lcons(create_mergesort_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
(Path*)outerrel->cheapestpath,
innerpath,
merge_pathkeys,
xmergeinfo->m_ordering,
matchedJoinClauses,
outerkeys,
NIL),
NIL);
mp_list = nconc(mp_list,temp_node);
}
}
}
return(mp_list);
}
static bool
EnoughMemoryForHashjoin(Rel *hashrel)
{
int ntuples;
int tupsize;
int pages;
ntuples = hashrel->size;
if (ntuples == 0) ntuples = 1000;
tupsize = hashrel->width + sizeof(HeapTupleData);
pages = page_size(ntuples, tupsize);
/*
* if amount of buffer space below hashjoin threshold,
* return false
*/
if (ceil(sqrt((double)pages)) > NBuffers)
return false;
return true;
}
/*
* hash-inner-and-outer-- XXX HASH
* Create hashjoin join paths by explicitly hashing both the outer and
* inner join relations on each available hash op.
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'hashinfo-list' is a list of nodes containing info on(hashjoinable)
* clauses for joining the relations
*
* Returns a list of hashjoin paths.
*/
static List *
hash_inner_and_outer(Rel *joinrel,
Rel *outerrel,
Rel *innerrel,
List *hashinfo_list)
{
HInfo *xhashinfo = (HInfo*)NULL;
List *hjoin_list = NIL;
HashPath *temp_node = (HashPath*)NULL;
List *i = NIL;
List *outerkeys = NIL;
List *innerkeys = NIL;
List *hash_pathkeys = NIL;
foreach (i, hashinfo_list) {
xhashinfo = (HInfo*)lfirst(i);
outerkeys =
extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
outerrel->targetlist,
OUTER);
innerkeys =
extract_path_keys(((JoinMethod*)xhashinfo)->jmkeys,
innerrel->targetlist,
INNER);
hash_pathkeys =
new_join_pathkeys(outerkeys,
joinrel->targetlist,
((JoinMethod*)xhashinfo)->clauses);
if (EnoughMemoryForHashjoin(innerrel)) {
temp_node = create_hashjoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
(Path*)outerrel->cheapestpath,
(Path*)innerrel->cheapestpath,
hash_pathkeys,
xhashinfo->hashop,
((JoinMethod*)xhashinfo)->clauses,
outerkeys,
innerkeys);
hjoin_list = lappend(hjoin_list, temp_node);
}
}
return(hjoin_list);
}

View File

@@ -0,0 +1,528 @@
/*-------------------------------------------------------------------------
*
* joinrels.c--
* Routines to determine which relations should be joined
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "optimizer/internal.h"
#include "optimizer/cost.h"
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
#include "optimizer/joininfo.h"
#include "optimizer/pathnode.h"
static List *find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list);
static List *find_clauseless_joins(Rel *outer_rel, List *inner_rels);
static Rel *init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo);
static List *new_join_tlist(List *tlist, List *other_relids,
int first_resdomno);
static List *new_joininfo_list(List *joininfo_list, List *join_relids);
static void add_superrels(Rel *rel, Rel *super_rel);
static bool nonoverlap_rels(Rel *rel1, Rel *rel2);
static bool nonoverlap_sets(List *s1, List *s2);
static void set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel,
JInfo *jinfo);
/*
* find-join-rels--
* Find all possible joins for each of the outer join relations in
* 'outer-rels'. A rel node is created for each possible join relation,
* and the resulting list of nodes is returned. If at all possible, only
* those relations for which join clauses exist are considered. If none
* of these exist for a given relation, all remaining possibilities are
* considered.
*
* 'outer-rels' is the list of rel nodes
*
* Returns a list of rel nodes corresponding to the new join relations.
*/
List *
find_join_rels(Query *root, List *outer_rels)
{
List *joins = NIL;
List *join_list = NIL;
List *r = NIL;
foreach(r, outer_rels) {
Rel *outer_rel = (Rel *)lfirst(r);
if(!(joins = find_clause_joins(root, outer_rel,outer_rel->joininfo)))
if (BushyPlanFlag)
joins = find_clauseless_joins(outer_rel,outer_rels);
else
joins = find_clauseless_joins(outer_rel,root->base_relation_list_);
join_list = nconc(join_list, joins);
}
return(join_list);
}
/*
* find-clause-joins--
* Determines whether joins can be performed between an outer relation
* 'outer-rel' and those relations within 'outer-rel's joininfo nodes
* (i.e., relations that participate in join clauses that 'outer-rel'
* participates in). This is possible if all but one of the relations
* contained within the join clauses of the joininfo node are already
* contained within 'outer-rel'.
*
* 'outer-rel' is the relation entry for the outer relation
* 'joininfo-list' is a list of join clauses which 'outer-rel'
* participates in
*
* Returns a list of new join relations.
*/
static List *
find_clause_joins(Query *root, Rel *outer_rel, List *joininfo_list)
{
List *join_list = NIL;
List *i = NIL;
foreach (i, joininfo_list) {
JInfo *joininfo = (JInfo*)lfirst(i);
Rel *rel;
if(!joininfo->inactive) {
List *other_rels = joininfo->otherrels;
if(other_rels != NIL) {
if(length(other_rels) == 1) {
rel = init_join_rel(outer_rel,
get_base_rel(root, lfirsti(other_rels)),
joininfo);
} else if (BushyPlanFlag) {
rel = init_join_rel(outer_rel,
get_join_rel(root, other_rels),
joininfo);
} else {
rel = NULL;
}
if (rel != NULL)
join_list = lappend(join_list, rel);
}
}
}
return(join_list);
}
/*
* find-clauseless-joins--
* Given an outer relation 'outer-rel' and a list of inner relations
* 'inner-rels', create a join relation between 'outer-rel' and each
* member of 'inner-rels' that isn't already included in 'outer-rel'.
*
* Returns a list of new join relations.
*/
static List *
find_clauseless_joins(Rel *outer_rel, List *inner_rels)
{
Rel *inner_rel;
List *t_list = NIL;
List *temp_node = NIL;
List *i = NIL;
foreach (i, inner_rels) {
inner_rel = (Rel *)lfirst(i);
if(nonoverlap_rels(inner_rel, outer_rel)) {
temp_node = lcons(init_join_rel(outer_rel,
inner_rel,
(JInfo*)NULL),
NIL);
t_list = nconc(t_list,temp_node);
}
}
return(t_list);
}
/*
* init-join-rel--
* Creates and initializes a new join relation.
*
* 'outer-rel' and 'inner-rel' are relation nodes for the relations to be
* joined
* 'joininfo' is the joininfo node(join clause) containing both
* 'outer-rel' and 'inner-rel', if any exists
*
* Returns the new join relation node.
*/
static Rel *
init_join_rel(Rel *outer_rel, Rel *inner_rel, JInfo *joininfo)
{
Rel *joinrel = makeNode(Rel);
List *joinrel_joininfo_list = NIL;
List *new_outer_tlist;
List *new_inner_tlist;
/*
* Create a new tlist by removing irrelevant elements from both
* tlists of the outer and inner join relations and then merging
* the results together.
*/
new_outer_tlist =
new_join_tlist(outer_rel->targetlist, /* XXX 1-based attnos */
inner_rel->relids, 1);
new_inner_tlist =
new_join_tlist(inner_rel->targetlist, /* XXX 1-based attnos */
outer_rel->relids,
length(new_outer_tlist) + 1);
joinrel->relids = NIL;
joinrel->indexed = false;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->width = 0;
/* joinrel->targetlist = NIL;*/
joinrel->pathlist = NIL;
joinrel->unorderedpath = (Path *)NULL;
joinrel->cheapestpath = (Path *)NULL;
joinrel->pruneable = true;
joinrel->classlist = NULL;
joinrel->relam = InvalidOid;
joinrel->ordering = NULL;
joinrel->clauseinfo = NIL;
joinrel->joininfo = NULL;
joinrel->innerjoin = NIL;
joinrel->superrels = NIL;
joinrel->relids = lcons(outer_rel->relids, /* ??? aren't they lists? -ay */
lcons(inner_rel->relids, NIL));
new_outer_tlist = nconc(new_outer_tlist,new_inner_tlist);
joinrel->targetlist = new_outer_tlist;
if (joininfo) {
joinrel->clauseinfo = joininfo->jinfoclauseinfo;
if (BushyPlanFlag)
joininfo->inactive = true;
}
joinrel_joininfo_list =
new_joininfo_list(append(outer_rel->joininfo, inner_rel->joininfo),
intAppend(outer_rel->relids, inner_rel->relids));
joinrel->joininfo = joinrel_joininfo_list;
set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo);
return(joinrel);
}
/*
* new-join-tlist--
* Builds a join relations's target list by keeping those elements that
* will be in the final target list and any other elements that are still
* needed for future joins. For a target list entry to still be needed
* for future joins, its 'joinlist' field must not be empty after removal
* of all relids in 'other-relids'.
*
* 'tlist' is the target list of one of the join relations
* 'other-relids' is a list of relids contained within the other
* join relation
* 'first-resdomno' is the resdom number to use for the first created
* target list entry
*
* Returns the new target list.
*/
static List *
new_join_tlist(List *tlist,
List *other_relids,
int first_resdomno)
{
int resdomno = first_resdomno - 1;
TargetEntry *xtl = NULL;
List *temp_node = NIL;
List *t_list = NIL;
List *i = NIL;
List *join_list = NIL;
bool in_final_tlist =false;
foreach(i,tlist) {
xtl= lfirst(i);
in_final_tlist = (join_list==NIL);
if( in_final_tlist) {
resdomno += 1;
temp_node =
lcons(create_tl_element(get_expr(xtl),
resdomno),
NIL);
t_list = nconc(t_list,temp_node);
}
}
return(t_list);
}
/*
* new-joininfo-list--
* Builds a join relation's joininfo list by checking for join clauses
* which still need to used in future joins involving this relation. A
* join clause is still needed if there are still relations in the clause
* not contained in the list of relations comprising this join relation.
* New joininfo nodes are only created and added to
* 'current-joininfo-list' if a node for a particular join hasn't already
* been created.
*
* 'current-joininfo-list' contains a list of those joininfo nodes that
* have already been built
* 'joininfo-list' is the list of join clauses involving this relation
* 'join-relids' is a list of relids corresponding to the relations
* currently being joined
*
* Returns a list of joininfo nodes, new and old.
*/
static List *
new_joininfo_list(List *joininfo_list, List *join_relids)
{
List *current_joininfo_list = NIL;
List *new_otherrels = NIL;
JInfo *other_joininfo = (JInfo*)NULL;
List *xjoininfo = NIL;
foreach (xjoininfo, joininfo_list) {
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
new_otherrels = joininfo->otherrels;
if (nonoverlap_sets(new_otherrels,join_relids)) {
other_joininfo = joininfo_member(new_otherrels,
current_joininfo_list);
if(other_joininfo) {
other_joininfo->jinfoclauseinfo =
(List*)LispUnion(joininfo->jinfoclauseinfo,
other_joininfo->jinfoclauseinfo);
}else {
other_joininfo = makeNode(JInfo);
other_joininfo->otherrels =
joininfo->otherrels;
other_joininfo->jinfoclauseinfo =
joininfo->jinfoclauseinfo;
other_joininfo->mergesortable =
joininfo->mergesortable;
other_joininfo->hashjoinable =
joininfo->hashjoinable;
other_joininfo->inactive = false;
current_joininfo_list = lcons(other_joininfo,
current_joininfo_list);
}
}
}
return(current_joininfo_list);
}
/*
* add-new-joininfos--
* For each new join relation, create new joininfos that
* use the join relation as inner relation, and add
* the new joininfos to those rel nodes that still
* have joins with the join relation.
*
* 'joinrels' is a list of join relations.
*
* Modifies the joininfo field of appropriate rel nodes.
*/
void
add_new_joininfos(Query *root, List *joinrels, List *outerrels)
{
List *xjoinrel = NIL;
List *xrelid = NIL;
List *xrel = NIL;
List *xjoininfo = NIL;
foreach(xjoinrel, joinrels) {
Rel *joinrel = (Rel *)lfirst(xjoinrel);
foreach(xrelid, joinrel->relids) {
Relid relid = (Relid)lfirst(xrelid);
Rel *rel = get_join_rel(root, relid);
add_superrels(rel,joinrel);
}
}
foreach(xjoinrel, joinrels) {
Rel *joinrel = (Rel *)lfirst(xjoinrel);
foreach(xjoininfo, joinrel->joininfo) {
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
List *other_rels = joininfo->otherrels;
List *clause_info = joininfo->jinfoclauseinfo;
bool mergesortable = joininfo->mergesortable;
bool hashjoinable = joininfo->hashjoinable;
foreach(xrelid, other_rels) {
Relid relid = (Relid)lfirst(xrelid);
Rel *rel = get_join_rel(root, relid);
List *super_rels = rel->superrels;
List *xsuper_rel = NIL;
JInfo *new_joininfo = makeNode(JInfo);
new_joininfo->otherrels = joinrel->relids;
new_joininfo->jinfoclauseinfo = clause_info;
new_joininfo->mergesortable = mergesortable;
new_joininfo->hashjoinable = hashjoinable;
new_joininfo->inactive = false;
rel->joininfo =
lappend(rel->joininfo, new_joininfo);
foreach(xsuper_rel, super_rels) {
Rel *super_rel = (Rel *)lfirst(xsuper_rel);
if( nonoverlap_rels(super_rel,joinrel) ) {
List *new_relids = super_rel->relids;
JInfo *other_joininfo =
joininfo_member(new_relids,
joinrel->joininfo);
if (other_joininfo) {
other_joininfo->jinfoclauseinfo =
(List*)LispUnion(clause_info,
other_joininfo->jinfoclauseinfo);
} else {
JInfo *new_joininfo = makeNode(JInfo);
new_joininfo->otherrels = new_relids;
new_joininfo->jinfoclauseinfo = clause_info;
new_joininfo->mergesortable = mergesortable;
new_joininfo->hashjoinable = hashjoinable;
new_joininfo->inactive = false;
joinrel->joininfo =
lappend(joinrel->joininfo,
new_joininfo);
}
}
}
}
}
}
foreach(xrel, outerrels) {
Rel *rel = (Rel *)lfirst(xrel);
rel->superrels = NIL;
}
}
/*
* final-join-rels--
* Find the join relation that includes all the original
* relations, i.e. the final join result.
*
* 'join-rel-list' is a list of join relations.
*
* Returns the list of final join relations.
*/
List *
final_join_rels(List *join_rel_list)
{
List *xrel = NIL;
List *temp = NIL;
List *t_list = NIL;
/*
* find the relations that has no further joins,
* i.e., its joininfos all have otherrels nil.
*/
foreach(xrel,join_rel_list) {
Rel *rel = (Rel *)lfirst(xrel);
List *xjoininfo = NIL;
bool final = true;
foreach (xjoininfo, rel->joininfo) {
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
if (joininfo->otherrels != NIL) {
final = false;
break;
}
}
if (final) {
temp = lcons(rel, NIL);
t_list = nconc(t_list, temp);
}
}
return(t_list);
}
/*
* add_superrels--
* add rel to the temporary property list superrels.
*
* 'rel' a rel node
* 'super-rel' rel node of a join relation that includes rel
*
* Modifies the superrels field of rel
*/
static void
add_superrels(Rel *rel, Rel *super_rel)
{
rel->superrels = lappend(rel->superrels, super_rel);
}
/*
* nonoverlap-rels--
* test if two join relations overlap, i.e., includes the same
* relation.
*
* 'rel1' and 'rel2' are two join relations
*
* Returns non-nil if rel1 and rel2 do not overlap.
*/
static bool
nonoverlap_rels(Rel *rel1, Rel *rel2)
{
return(nonoverlap_sets(rel1->relids, rel2->relids));
}
static bool
nonoverlap_sets(List *s1, List *s2)
{
List *x = NIL;
foreach(x,s1) {
int e = lfirsti(x);
if(intMember(e,s2))
return(false);
}
return(true);
}
static void
set_joinrel_size(Rel *joinrel, Rel *outer_rel, Rel *inner_rel, JInfo *jinfo)
{
int ntuples;
float selec;
/* voodoo magic. but better than a size of 0. I have no idea why
we didn't set the size before. -ay 2/95 */
if (jinfo==NULL) {
/* worst case: the cartesian product */
ntuples = outer_rel->tuples * inner_rel->tuples;
} else {
selec = product_selec(jinfo->jinfoclauseinfo);
/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */
ntuples = outer_rel->tuples * inner_rel->tuples * selec;
}
/* I bet sizes less than 1 will screw up optimization so
make the best case 1 instead of 0 - jolly*/
if (ntuples < 1)
ntuples = 1;
joinrel->tuples = ntuples;
}

View File

@@ -0,0 +1,432 @@
/*-------------------------------------------------------------------------
*
* joinutils.c--
* Utilities for matching and building join and path keys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/joinutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/plannodes.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/var.h"
#include "optimizer/keys.h"
#include "optimizer/tlist.h"
#include "optimizer/joininfo.h"
#include "optimizer/ordering.h"
static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
int which_subkey);
static bool every_func(List *joinkeys, List *pathkey,
int which_subkey);
static List *new_join_pathkey(List *subkeys,
List *considered_subkeys, List *join_rel_tlist,
List *joinclauses);
static List *new_matching_subkeys(Var *subkey, List *considered_subkeys,
List *join_rel_tlist, List *joinclauses);
/****************************************************************************
* KEY COMPARISONS
****************************************************************************/
/*
* match-pathkeys-joinkeys--
* Attempts to match the keys of a path against the keys of join clauses.
* This is done by looking for a matching join key in 'joinkeys' for
* every path key in the list 'pathkeys'. If there is a matching join key
* (not necessarily unique) for every path key, then the list of
* corresponding join keys and join clauses are returned in the order in
* which the keys matched the path keys.
*
* 'pathkeys' is a list of path keys:
* ( ( (var) (var) ... ) ( (var) ... ) )
* 'joinkeys' is a list of join keys:
* ( (outer inner) (outer inner) ... )
* 'joinclauses' is a list of clauses corresponding to the join keys in
* 'joinkeys'
* 'which-subkey' is a flag that selects the desired subkey of a join key
* in 'joinkeys'
*
* Returns the join keys and corresponding join clauses in a list if all
* of the path keys were matched:
* (
* ( (outerkey0 innerkey0) ... (outerkeyN innerkeyN) )
* ( clause0 ... clauseN )
* )
* and nil otherwise.
*
* Returns a list of matched join keys and a list of matched join clauses
* in matchedJoinClausesPtr. - ay 11/94
*/
List *
match_pathkeys_joinkeys(List *pathkeys,
List *joinkeys,
List *joinclauses,
int which_subkey,
List **matchedJoinClausesPtr)
{
List *matched_joinkeys = NIL;
List *matched_joinclauses = NIL;
List *pathkey = NIL;
List *i = NIL;
int matched_joinkey_index = -1;
foreach(i, pathkeys) {
pathkey = lfirst(i);
matched_joinkey_index =
match_pathkey_joinkeys(pathkey, joinkeys, which_subkey);
if (matched_joinkey_index != -1 ) {
List *xjoinkey = nth(matched_joinkey_index,joinkeys);
List *joinclause = nth(matched_joinkey_index,joinclauses);
/* XXX was "push" function */
matched_joinkeys = lappend(matched_joinkeys,xjoinkey);
matched_joinkeys = nreverse(matched_joinkeys);
matched_joinclauses = lappend(matched_joinclauses,joinclause);
matched_joinclauses = nreverse(matched_joinclauses);
joinkeys = LispRemove(xjoinkey,joinkeys);
} else {
return(NIL);
}
}
if(matched_joinkeys==NULL ||
length(matched_joinkeys) != length(pathkeys)) {
return NIL;
}
*matchedJoinClausesPtr = nreverse(matched_joinclauses);
return (nreverse(matched_joinkeys));
}
/*
* match-pathkey-joinkeys--
* Returns the 0-based index into 'joinkeys' of the first joinkey whose
* outer or inner subkey matches any subkey of 'pathkey'.
*/
static int
match_pathkey_joinkeys(List *pathkey,
List *joinkeys,
int which_subkey)
{
Var *path_subkey;
int pos;
List *i = NIL;
List *x = NIL;
JoinKey *jk;
foreach(i, pathkey) {
path_subkey = (Var *)lfirst(i);
pos = 0;
foreach(x, joinkeys) {
jk = (JoinKey*)lfirst(x);
if(var_equal(path_subkey,
extract_subkey(jk, which_subkey)))
return(pos);
pos++;
}
}
return(-1); /* no index found */
}
/*
* match-paths-joinkeys--
* Attempts to find a path in 'paths' whose keys match a set of join
* keys 'joinkeys'. To match,
* 1. the path node ordering must equal 'ordering'.
* 2. each subkey of a given path must match(i.e., be(var_equal) to) the
* appropriate subkey of the corresponding join key in 'joinkeys',
* i.e., the Nth path key must match its subkeys against the subkey of
* the Nth join key in 'joinkeys'.
*
* 'joinkeys' is the list of key pairs to which the path keys must be
* matched
* 'ordering' is the ordering of the(outer) path to which 'joinkeys'
* must correspond
* 'paths' is a list of(inner) paths which are to be matched against
* each join key in 'joinkeys'
* 'which-subkey' is a flag that selects the desired subkey of a join key
* in 'joinkeys'
*
* Returns the matching path node if one exists, nil otherwise.
*/
static bool
every_func(List *joinkeys, List *pathkey, int which_subkey)
{
JoinKey *xjoinkey;
Var *temp;
Var *tempkey = NULL;
bool found = false;
List *i = NIL;
List *j = NIL;
foreach(i,joinkeys) {
xjoinkey = (JoinKey*)lfirst(i);
found = false;
foreach(j,pathkey) {
temp = (Var*)lfirst((List*)lfirst(j));
if(temp == NULL) continue;
tempkey = extract_subkey(xjoinkey,which_subkey);
if(var_equal(tempkey, temp)) {
found = true;
break;
}
}
if(found == false)
return(false);
}
return(found);
}
/*
* match_paths_joinkeys -
* find the cheapest path that matches the join keys
*/
Path *
match_paths_joinkeys(List *joinkeys,
PathOrder *ordering,
List *paths,
int which_subkey)
{
Path *matched_path = NULL ;
bool key_match = false;
List *i = NIL;
foreach(i,paths) {
Path *path = (Path*)lfirst(i);
key_match = every_func(joinkeys, path->keys, which_subkey);
if (equal_path_path_ordering(ordering,
&path->p_ordering) &&
length(joinkeys) == length(path->keys) &&
key_match) {
if (matched_path) {
if (path->path_cost < matched_path->path_cost)
matched_path = path;
} else {
matched_path = path;
}
}
}
return matched_path;
}
/*
* extract-path-keys--
* Builds a subkey list for a path by pulling one of the subkeys from
* a list of join keys 'joinkeys' and then finding the var node in the
* target list 'tlist' that corresponds to that subkey.
*
* 'joinkeys' is a list of join key pairs
* 'tlist' is a relation target list
* 'which-subkey' is a flag that selects the desired subkey of a join key
* in 'joinkeys'
*
* Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
* [I've no idea why they have to be list of lists. Should be fixed. -ay 12/94]
*/
List *
extract_path_keys(List *joinkeys,
List *tlist,
int which_subkey)
{
List *pathkeys = NIL;
List *jk;
foreach(jk, joinkeys) {
JoinKey *jkey = (JoinKey*)lfirst(jk);
Var *var, *key;
List *p;
/*
* find the right Var in the target list for this key
*/
var = (Var*)extract_subkey(jkey, which_subkey);
key = (Var*)matching_tlvar(var, tlist);
/*
* include it in the pathkeys list if we haven't already done so
*/
foreach(p, pathkeys) {
Var *pkey = lfirst((List*)lfirst(p)); /* XXX fix me */
if (key == pkey)
break;
}
if (p!=NIL)
continue; /* key already in pathkeys */
pathkeys =
lappend(pathkeys, lcons(key,NIL));
}
return(pathkeys);
}
/****************************************************************************
* NEW PATHKEY FORMATION
****************************************************************************/
/*
* new-join-pathkeys--
* Find the path keys for a join relation by finding all vars in the list
* of join clauses 'joinclauses' such that:
* (1) the var corresponding to the outer join relation is a
* key on the outer path
* (2) the var appears in the target list of the join relation
* In other words, add to each outer path key the inner path keys that
* are required for qualification.
*
* 'outer-pathkeys' is the list of the outer path's path keys
* 'join-rel-tlist' is the target list of the join relation
* 'joinclauses' is the list of restricting join clauses
*
* Returns the list of new path keys.
*
*/
List *
new_join_pathkeys(List *outer_pathkeys,
List *join_rel_tlist,
List *joinclauses)
{
List *outer_pathkey = NIL;
List *t_list = NIL;
List *x;
List *i = NIL;
foreach(i, outer_pathkeys) {
outer_pathkey = lfirst(i);
x = new_join_pathkey(outer_pathkey, NIL,
join_rel_tlist,joinclauses);
if (x!=NIL) {
t_list = lappend(t_list, x);
}
}
return(t_list);
}
/*
* new-join-pathkey--
* Finds new vars that become subkeys due to qualification clauses that
* contain any previously considered subkeys. These new subkeys plus the
* subkeys from 'subkeys' form a new pathkey for the join relation.
*
* Note that each returned subkey is the var node found in
* 'join-rel-tlist' rather than the joinclause var node.
*
* 'subkeys' is a list of subkeys for which matching subkeys are to be
* found
* 'considered-subkeys' is the current list of all subkeys corresponding
* to a given pathkey
*
* Returns a new pathkey(list of subkeys).
*
*/
static List *
new_join_pathkey(List *subkeys,
List *considered_subkeys,
List *join_rel_tlist,
List *joinclauses)
{
List *t_list = NIL;
Var *subkey;
List *i = NIL;
List *matched_subkeys = NIL;
Expr *tlist_key = (Expr*)NULL;
List *newly_considered_subkeys = NIL;
foreach (i, subkeys) {
subkey = (Var *)lfirst(i);
if(subkey == NULL)
break; /* XXX something is wrong */
matched_subkeys =
new_matching_subkeys(subkey,considered_subkeys,
join_rel_tlist,joinclauses);
tlist_key = matching_tlvar(subkey,join_rel_tlist);
newly_considered_subkeys = NIL;
if (tlist_key) {
if(!member(tlist_key, matched_subkeys))
newly_considered_subkeys = lcons(tlist_key,
matched_subkeys);
}
else {
newly_considered_subkeys = matched_subkeys;
}
considered_subkeys =
append(considered_subkeys, newly_considered_subkeys);
t_list = nconc(t_list,newly_considered_subkeys);
}
return(t_list);
}
/*
* new-matching-subkeys--
* Returns a list of new subkeys:
* (1) which are not listed in 'considered-subkeys'
* (2) for which the "other" variable in some clause in 'joinclauses' is
* 'subkey'
* (3) which are mentioned in 'join-rel-tlist'
*
* Note that each returned subkey is the var node found in
* 'join-rel-tlist' rather than the joinclause var node.
*
* 'subkey' is the var node for which we are trying to find matching
* clauses
*
* Returns a list of new subkeys.
*
*/
static List *
new_matching_subkeys(Var *subkey,
List *considered_subkeys,
List *join_rel_tlist,
List *joinclauses)
{
Expr *joinclause = NULL;
List *t_list = NIL;
List *temp = NIL;
List *i = NIL;
Expr *tlist_other_var = (Expr *)NULL;
foreach(i,joinclauses) {
joinclause = lfirst(i);
tlist_other_var =
matching_tlvar(other_join_clause_var(subkey,joinclause),
join_rel_tlist);
if(tlist_other_var &&
!(member(tlist_other_var,considered_subkeys))) {
/* XXX was "push" function */
considered_subkeys = lappend(considered_subkeys,
tlist_other_var);
/* considered_subkeys = nreverse(considered_subkeys);
XXX -- I am not sure of this. */
temp = lcons(tlist_other_var, NIL);
t_list = nconc(t_list,temp);
}
}
return(t_list);
}

View File

@@ -0,0 +1,122 @@
/*-------------------------------------------------------------------------
*
* mergeutils.c--
* Utilities for finding applicable merge clauses and pathkeys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "optimizer/internal.h"
#include "optimizer/paths.h"
#include "optimizer/clauses.h"
#include "optimizer/ordering.h"
/*
* group-clauses-by-order--
* If a join clause node in 'clauseinfo-list' is mergesortable, store
* it within a mergeinfo node containing other clause nodes with the same
* mergesort ordering.
*
* 'clauseinfo-list' is the list of clauseinfo nodes
* 'inner-relid' is the relid of the inner join relation
*
* Returns the new list of mergeinfo nodes.
*
*/
List *
group_clauses_by_order(List *clauseinfo_list,
int inner_relid)
{
List *mergeinfo_list = NIL;
List *xclauseinfo = NIL;
foreach (xclauseinfo, clauseinfo_list) {
CInfo *clauseinfo = (CInfo *)lfirst(xclauseinfo);
MergeOrder *merge_ordering = clauseinfo->mergesortorder;
if (merge_ordering) {
/*
* Create a new mergeinfo node and add it to
* 'mergeinfo-list' if one does not yet exist for this
* merge ordering.
*/
PathOrder p_ordering;
MInfo *xmergeinfo;
Expr *clause = clauseinfo->clause;
Var *leftop = get_leftop (clause);
Var *rightop = get_rightop (clause);
JoinKey *keys;
p_ordering.ordtype = MERGE_ORDER;
p_ordering.ord.merge = merge_ordering;
xmergeinfo =
match_order_mergeinfo(&p_ordering, mergeinfo_list);
if (inner_relid == leftop->varno) {
keys = makeNode(JoinKey);
keys->outer = rightop;
keys->inner = leftop;
} else {
keys = makeNode(JoinKey);
keys->outer = leftop;
keys->inner = rightop;
}
if (xmergeinfo==NULL) {
xmergeinfo = makeNode(MInfo);
xmergeinfo->m_ordering = merge_ordering;
mergeinfo_list = lcons(xmergeinfo,
mergeinfo_list);
}
((JoinMethod *)xmergeinfo)->clauses =
lcons(clause,
((JoinMethod *)xmergeinfo)->clauses);
((JoinMethod *)xmergeinfo)->jmkeys =
lcons(keys,
((JoinMethod *)xmergeinfo)->jmkeys);
}
}
return(mergeinfo_list);
}
/*
* match-order-mergeinfo--
* Searches the list 'mergeinfo-list' for a mergeinfo node whose order
* field equals 'ordering'.
*
* Returns the node if it exists.
*
*/
MInfo *
match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list)
{
MergeOrder *xmergeorder;
List *xmergeinfo = NIL;
foreach(xmergeinfo, mergeinfo_list) {
MInfo *mergeinfo = (MInfo*)lfirst(xmergeinfo);
xmergeorder = mergeinfo->m_ordering;
if ((ordering->ordtype==MERGE_ORDER &&
equal_merge_merge_ordering(ordering->ord.merge, xmergeorder)) ||
(ordering->ordtype==SORTOP_ORDER &&
equal_path_merge_ordering(ordering->ord.sortop, xmergeorder))) {
return (mergeinfo);
}
}
return((MInfo*) NIL);
}

View File

@@ -0,0 +1,271 @@
/*-------------------------------------------------------------------------
*
* orindxpath.c--
* Routines to find index paths that match a set of 'or' clauses
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/internal.h"
#include "optimizer/clauses.h"
#include "optimizer/clauseinfo.h"
#include "optimizer/paths.h"
#include "optimizer/cost.h"
#include "optimizer/plancat.h"
#include "optimizer/xfunc.h"
#include "parser/parsetree.h"
static void best_or_subclause_indices(Query *root, Rel *rel, List *subclauses,
List *indices, List *examined_indexids, Cost subcost, List *selectivities,
List **indexids, Cost *cost, List **selecs);
static void best_or_subclause_index(Query *root, Rel *rel, Expr *subclause,
List *indices, int *indexid, Cost *cost, Cost *selec);
/*
* create-or-index-paths--
* Creates index paths for indices that match 'or' clauses.
*
* 'rel' is the relation entry for which the paths are to be defined on
* 'clauses' is the list of available restriction clause nodes
*
* Returns a list of these index path nodes.
*
*/
List *
create_or_index_paths(Query *root,
Rel *rel, List *clauses)
{
List *t_list = NIL;
if (clauses != NIL) {
CInfo *clausenode = (CInfo *) (lfirst (clauses));
/* Check to see if this clause is an 'or' clause, and, if so,
* whether or not each of the subclauses within the 'or' clause has
* been matched by an index (the 'Index field was set in
* (match_or) if no index matches a given subclause, one of the
* lists of index nodes returned by (get_index) will be 'nil').
*/
if (valid_or_clause(clausenode) &&
clausenode->indexids) {
List *temp = NIL;
List *index_list = NIL;
bool index_flag = true;
index_list = clausenode->indexids;
foreach(temp,index_list) {
if (!temp)
index_flag = false;
}
if (index_flag) { /* used to be a lisp every function */
IndexPath *pathnode = makeNode(IndexPath);
List *indexids;
Cost cost;
List *selecs;
best_or_subclause_indices(root,
rel,
clausenode->clause->args,
clausenode->indexids,
NIL,
(Cost)0,
NIL,
&indexids,
&cost,
&selecs);
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
pathnode->indexqual =
lcons(clausenode,NIL);
pathnode->indexid = indexids;
pathnode->path.path_cost = cost;
/* copy clauseinfo list into path for expensive
function processing -- JMH, 7/7/92 */
pathnode->path.locclauseinfo =
set_difference(clauses,
copyObject((Node*)
rel->clauseinfo));
#if 0 /* fix xfunc */
/* add in cost for expensive functions! -- JMH, 7/7/92 */
if (XfuncMode != XFUNC_OFF) {
((Path*)pathnode)->path_cost +=
xfunc_get_path_cost((Path)pathnode);
}
#endif
clausenode->selectivity = (Cost)floatVal(selecs);
t_list =
lcons(pathnode,
create_or_index_paths(root, rel,lnext(clauses)));
} else {
t_list = create_or_index_paths(root, rel,lnext(clauses));
}
}
}
return(t_list);
}
/*
* best-or-subclause-indices--
* Determines the best index to be used in conjunction with each subclause
* of an 'or' clause and the cost of scanning a relation using these
* indices. The cost is the sum of the individual index costs.
*
* 'rel' is the node of the relation on which the index is defined
* 'subclauses' are the subclauses of the 'or' clause
* 'indices' are those index nodes that matched subclauses of the 'or'
* clause
* 'examined-indexids' is a list of those index ids to be used with
* subclauses that have already been examined
* 'subcost' is the cost of using the indices in 'examined-indexids'
* 'selectivities' is a list of the selectivities of subclauses that
* have already been examined
*
* Returns a list of the indexids, cost, and selectivities of each
* subclause, e.g., ((i1 i2 i3) cost (s1 s2 s3)), where 'i' is an OID,
* 'cost' is a flonum, and 's' is a flonum.
*/
static void
best_or_subclause_indices(Query *root,
Rel *rel,
List *subclauses,
List *indices,
List *examined_indexids,
Cost subcost,
List *selectivities,
List **indexids, /* return value */
Cost *cost, /* return value */
List **selecs) /* return value */
{
if (subclauses==NIL) {
*indexids = nreverse(examined_indexids);
*cost = subcost;
*selecs = nreverse(selectivities);
} else {
int best_indexid;
Cost best_cost;
Cost best_selec;
best_or_subclause_index(root, rel, lfirst(subclauses), lfirst(indices),
&best_indexid, &best_cost, &best_selec);
best_or_subclause_indices(root,
rel,
lnext(subclauses),
lnext(indices),
lconsi(best_indexid, examined_indexids),
subcost + best_cost,
lcons(makeFloat(best_selec), selectivities),
indexids,
cost,
selecs);
}
return;
}
/*
* best-or-subclause-index--
* Determines which is the best index to be used with a subclause of
* an 'or' clause by estimating the cost of using each index and selecting
* the least expensive.
*
* 'rel' is the node of the relation on which the index is defined
* 'subclause' is the subclause
* 'indices' is a list of index nodes that match the subclause
*
* Returns a list (index-id index-subcost index-selectivity)
* (a fixnum, a fixnum, and a flonum respectively).
*
*/
static void
best_or_subclause_index(Query *root,
Rel *rel,
Expr *subclause,
List *indices,
int *retIndexid, /* return value */
Cost *retCost, /* return value */
Cost *retSelec) /* return value */
{
if (indices != NIL) {
Datum value;
int flag = 0;
Cost subcost;
Rel *index = (Rel *)lfirst (indices);
AttrNumber attno = (get_leftop (subclause))->varattno ;
Oid opno = ((Oper*)subclause->oper)->opno;
bool constant_on_right = non_null((Expr*)get_rightop(subclause));
float npages, selec;
int subclause_indexid;
Cost subclause_cost;
Cost subclause_selec;
if(constant_on_right) {
value = ((Const*)get_rightop (subclause))->constvalue;
} else {
value = NameGetDatum("");
}
if(constant_on_right) {
flag = (_SELEC_IS_CONSTANT_ ||_SELEC_CONSTANT_RIGHT_);
} else {
flag = _SELEC_CONSTANT_RIGHT_;
}
index_selectivity(lfirsti(index->relids),
index->classlist,
lconsi(opno,NIL),
getrelid(lfirsti(rel->relids),
root->rtable),
lconsi(attno,NIL),
lconsi(value,NIL),
lconsi(flag,NIL),
1,
&npages,
&selec);
subcost = cost_index((Oid) lfirsti(index->relids),
(int)npages,
(Cost)selec,
rel->pages,
rel->tuples,
index->pages,
index->tuples,
false);
best_or_subclause_index(root,
rel,
subclause,
lnext(indices),
&subclause_indexid,
&subclause_cost,
&subclause_selec);
if (subclause_indexid==0 || subcost < subclause_cost) {
*retIndexid = lfirsti(index->relids);
*retCost = subcost;
*retSelec = selec;
} else {
*retIndexid = 0;
*retCost = 0.0;
*retSelec = 0.0;
}
}
return;
}

View File

@@ -0,0 +1,773 @@
/*-------------------------------------------------------------------------
*
* predmig.c--
*
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/predmig.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
/*
** DESCRIPTION
** Main Routines to handle Predicate Migration (i.e. correct optimization
** of queries with expensive functions.)
**
** The reasoning behind some of these algorithms is rather detailed.
** Have a look at Sequoia Tech Report 92/13 for more info. Also
** see Monma and Sidney's paper "Sequencing with Series-Parallel
** Precedence Constraints", in "Mathematics of Operations Research",
** volume 4 (1979), pp. 215-224.
**
** The main thing that this code does that wasn't handled in xfunc.c is
** it considers the possibility that two joins in a stream may not
** be ordered by ascending rank -- in such a scenario, it may be optimal
** to pullup more restrictions than we did via xfunc_try_pullup.
**
** This code in some sense generalizes xfunc_try_pullup; if you
** run postgres -x noprune, you'll turn off xfunc_try_pullup, and this
** code will do everything that xfunc_try_pullup would have, and maybe
** more. However, this results in no pruning, which may slow down the
** optimizer and/or cause the system to run out of memory.
** -- JMH, 11/13/92
*/
#include "nodes/pg_list.h"
#include "nodes/nodes.h"
#include "nodes/primnodes.h"
#include "nodes/relation.h"
#include "utils/palloc.h"
#include "utils/elog.h"
#include "planner/xfunc.h"
#include "planner/pathnode.h"
#include "planner/internal.h"
#include "planner/cost.h"
#include "planner/keys.h"
#include "planner/tlist.h"
#include "lib/qsort.h"
#define is_clause(node) (get_cinfo(node)) /* a stream node represents a
clause (not a join) iff it
has a non-NULL cinfo field */
static void xfunc_predmig(JoinPath pathnode, Stream streamroot,
Stream laststream, bool *progressp);
static bool xfunc_series_llel(Stream stream);
static bool xfunc_llel_chains(Stream root, Stream bottom);
static Stream xfunc_complete_stream(Stream stream);
static bool xfunc_prdmig_pullup(Stream origstream, Stream pullme,
JoinPath joinpath);
static void xfunc_form_groups(Stream root, Stream bottom);
static void xfunc_free_stream(Stream root);
static Stream xfunc_add_clauses(Stream current);
static void xfunc_setup_group(Stream node, Stream bottom);
static Stream xfunc_streaminsert(CInfo clauseinfo, Stream current,
int clausetype);
static int xfunc_num_relids(Stream node);
static StreamPtr xfunc_get_downjoin(Stream node);
static StreamPtr xfunc_get_upjoin(Stream node);
static Stream xfunc_stream_qsort(Stream root, Stream bottom);
static int xfunc_stream_compare(void *arg1, void *arg2);
static bool xfunc_check_stream(Stream node);
static bool xfunc_in_stream(Stream node, Stream stream);
/* ----------------- MAIN FUNCTIONS ------------------------ */
/*
** xfunc_do_predmig
** wrapper for Predicate Migration. It calls xfunc_predmig until no
** more progress is made.
** return value says if any changes were ever made.
*/
bool xfunc_do_predmig(Path root)
{
bool progress, changed = false;
if (is_join(root))
do
{
progress = false;
Assert(IsA(root,JoinPath));
xfunc_predmig((JoinPath)root, (Stream)NULL, (Stream)NULL,
&progress);
if (changed && progress)
elog(DEBUG, "Needed to do a second round of predmig!\n");
if (progress) changed = true;
} while (progress);
return(changed);
}
/*
** xfunc_predmig
** The main routine for Predicate Migration. It traverses a join tree,
** and for each root-to-leaf path in the plan tree it constructs a
** "Stream", which it passes to xfunc_series_llel for optimization.
** Destructively modifies the join tree (via predicate pullup).
*/
static void
xfunc_predmig(JoinPath pathnode, /* root of the join tree */
Stream streamroot,
Stream laststream, /* for recursive calls -- these are
the root of the stream under
construction, and the lowest node
created so far */
bool *progressp)
{
Stream newstream;
/*
** traverse the join tree dfs-style, constructing a stream as you go.
** When you hit a scan node, pass the stream off to xfunc_series_llel.
*/
/* sanity check */
if ((!streamroot && laststream) ||
(streamroot && !laststream))
elog(WARN, "called xfunc_predmig with bad inputs");
if (streamroot) Assert(xfunc_check_stream(streamroot));
/* add path node to stream */
newstream = RMakeStream();
if (!streamroot)
streamroot = newstream;
set_upstream(newstream, (StreamPtr)laststream);
if (laststream)
set_downstream(laststream, (StreamPtr)newstream);
set_downstream(newstream, (StreamPtr)NULL);
set_pathptr(newstream, (pathPtr)pathnode);
set_cinfo(newstream, (CInfo)NULL);
set_clausetype(newstream, XFUNC_UNKNOWN);
/* base case: we're at a leaf, call xfunc_series_llel */
if (!is_join(pathnode))
{
/* form a fleshed-out copy of the stream */
Stream fullstream = xfunc_complete_stream(streamroot);
/* sort it via series-llel */
if (xfunc_series_llel(fullstream))
*progressp = true;
/* free up the copy */
xfunc_free_stream(fullstream);
}
else
{
/* visit left child */
xfunc_predmig((JoinPath)get_outerjoinpath(pathnode),
streamroot, newstream, progressp);
/* visit right child */
xfunc_predmig((JoinPath)get_innerjoinpath(pathnode),
streamroot, newstream, progressp);
}
/* remove this node */
if (get_upstream(newstream))
set_downstream((Stream)get_upstream(newstream), (StreamPtr)NULL);
pfree(newstream);
}
/*
** xfunc_series_llel
** A flavor of Monma and Sidney's Series-Parallel algorithm.
** Traverse stream downwards. When you find a node with restrictions on it,
** call xfunc_llel_chains on the substream from root to that node.
*/
static bool xfunc_series_llel(Stream stream)
{
Stream temp, next;
bool progress = false;
for (temp = stream; temp != (Stream)NULL; temp = next)
{
next = (Stream)xfunc_get_downjoin(temp);
/*
** if there are restrictions/secondary join clauses above this
** node, call xfunc_llel_chains
*/
if (get_upstream(temp) && is_clause((Stream)get_upstream(temp)))
if (xfunc_llel_chains(stream, temp))
progress = true;
}
return(progress);
}
/*
** xfunc_llel_chains
** A flavor of Monma and Sidney's Parallel Chains algorithm.
** Given a stream which has been well-ordered except for its lowermost
** restrictions/2-ary joins, pull up the restrictions/2-arys as appropriate.
** What that means here is to form groups in the chain above the lowest
** join node above bottom inclusive, and then take all the restrictions
** following bottom, and try to pull them up as far as possible.
*/
static bool xfunc_llel_chains(Stream root, Stream bottom)
{
bool progress = false;
Stream origstream;
Stream tmpstream, pathstream;
Stream rootcopy = root;
Assert(xfunc_check_stream(root));
/* xfunc_prdmig_pullup will need an unmodified copy of the stream */
origstream = (Stream)copyObject((Node)root);
/* form groups among ill-ordered nodes */
xfunc_form_groups(root, bottom);
/* sort chain by rank */
Assert(xfunc_in_stream(bottom, root));
rootcopy = xfunc_stream_qsort(root, bottom);
/*
** traverse sorted stream -- if any restriction has moved above a join,
** we must pull it up in the plan. That is, make plan tree
** reflect order of sorted stream.
*/
for (tmpstream = rootcopy,
pathstream = (Stream)xfunc_get_downjoin(rootcopy);
tmpstream != (Stream)NULL && pathstream != (Stream)NULL;
tmpstream = (Stream)get_downstream(tmpstream))
{
if (is_clause(tmpstream)
&& get_pathptr(pathstream) != get_pathptr(tmpstream))
{
/*
** If restriction moved above a Join after sort, we pull it
** up in the join plan.
** If restriction moved down, we ignore it.
** This is because Joey's Sequoia paper proves that
** restrictions should never move down. If this
** one were moved down, it would violate "semantic correctness",
** i.e. it would be lower than the attributes it references.
*/
Assert(xfunc_num_relids(pathstream)>xfunc_num_relids(tmpstream));
progress =
xfunc_prdmig_pullup(origstream, tmpstream,
(JoinPath)get_pathptr(pathstream));
}
if (get_downstream(tmpstream))
pathstream =
(Stream)xfunc_get_downjoin((Stream)get_downstream(tmpstream));
}
/* free up origstream */
xfunc_free_stream(origstream);
return(progress);
}
/*
** xfunc_complete_stream --
** Given a stream composed of join nodes only, make a copy containing the
** join nodes along with the associated restriction nodes.
*/
static Stream xfunc_complete_stream(Stream stream)
{
Stream tmpstream, copystream, curstream = (Stream)NULL;
copystream = (Stream)copyObject((Node)stream);
Assert(xfunc_check_stream(copystream));
curstream = copystream;
Assert(!is_clause(curstream));
/* curstream = (Stream)xfunc_get_downjoin(curstream); */
while(curstream != (Stream)NULL)
{
xfunc_add_clauses(curstream);
curstream = (Stream)xfunc_get_downjoin(curstream);
}
/* find top of stream and return it */
for (tmpstream = copystream; get_upstream(tmpstream) != (StreamPtr)NULL;
tmpstream = (Stream)get_upstream(tmpstream))
/* no body in for loop */;
return(tmpstream);
}
/*
** xfunc_prdmig_pullup
** pullup a clause in a path above joinpath. Since the JoinPath tree
** doesn't have upward pointers, it's difficult to deal with. Thus we
** require the original stream, which maintains pointers to all the path
** nodes. We use the original stream to find out what joins are
** above the clause.
*/
static bool
xfunc_prdmig_pullup(Stream origstream, Stream pullme, JoinPath joinpath)
{
CInfo clauseinfo = get_cinfo(pullme);
bool progress = false;
Stream upjoin, orignode, temp;
int whichchild;
/* find node in origstream that contains clause */
for (orignode = origstream;
orignode != (Stream) NULL
&& get_cinfo(orignode) != clauseinfo;
orignode = (Stream)get_downstream(orignode))
/* empty body in for loop */ ;
if (!orignode)
elog(WARN, "Didn't find matching node in original stream");
/* pull up this node as far as it should go */
for (upjoin = (Stream)xfunc_get_upjoin(orignode);
upjoin != (Stream)NULL
&& (JoinPath)get_pathptr((Stream)xfunc_get_downjoin(upjoin))
!= joinpath;
upjoin = (Stream)xfunc_get_upjoin(upjoin))
{
#ifdef DEBUG
elog(DEBUG, "pulling up in xfunc_predmig_pullup!");
#endif
/* move clause up in path */
if (get_pathptr((Stream)get_downstream(upjoin))
== (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)))
whichchild = OUTER;
else whichchild = INNER;
clauseinfo = xfunc_pullup((Path)get_pathptr((Stream)get_downstream(upjoin)),
(JoinPath)get_pathptr(upjoin),
clauseinfo,
whichchild,
get_clausetype(orignode));
set_pathptr(pullme, get_pathptr(upjoin));
/* pullme has been moved into locclauseinfo */
set_clausetype(pullme, XFUNC_LOCPRD);
/*
** xfunc_pullup makes new path nodes for children of
** get_pathptr(current). We must modify the stream nodes to point
** to these path nodes
*/
if (whichchild == OUTER)
{
for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
temp = (Stream)get_downstream(temp))
set_pathptr
(temp, (pathPtr)
get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
set_pathptr
(temp,
(pathPtr)get_outerjoinpath((JoinPath)get_pathptr(upjoin)));
}
else
{
for(temp = (Stream)get_downstream(upjoin); is_clause(temp);
temp = (Stream)get_downstream(temp))
set_pathptr
(temp, (pathPtr)
get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
set_pathptr
(temp, (pathPtr)
get_innerjoinpath((JoinPath)get_pathptr(upjoin)));
}
progress = true;
}
if (!progress)
elog(DEBUG, "didn't succeed in pulling up in xfunc_prdmig_pullup");
return(progress);
}
/*
** xfunc_form_groups --
** A group is a pair of stream nodes a,b such that a is constrained to
** precede b (for instance if a and b are both joins), but rank(a) > rank(b).
** In such a situation, Monma and Sidney prove that no clauses should end
** up between a and b, and therefore we may treat them as a group, with
** selectivity equal to the product of their selectivities, and cost
** equal to the cost of the first plus the selectivity of the first times the
** cost of the second. We define each node to be in a group by itself,
** and then repeatedly find adjacent groups which are ordered by descending
** rank, and make larger groups. You know that two adjacent nodes are in a
** group together if the lower has groupup set to true. They will both have
** the same groupcost and groupsel (since they're in the same group!)
*/
static void xfunc_form_groups(Query* queryInfo, Stream root, Stream bottom)
{
Stream temp, parent;
int lowest = xfunc_num_relids((Stream)xfunc_get_upjoin(bottom));
bool progress;
LispValue primjoin;
int whichchild;
if (!lowest) return; /* no joins in stream, so no groups */
/* initialize groups to be single nodes */
for (temp = root;
temp != (Stream)NULL && temp != bottom;
temp = (Stream)get_downstream(temp))
{
/* if a Join node */
if (!is_clause(temp))
{
if (get_pathptr((Stream)get_downstream(temp))
== (pathPtr)get_outerjoinpath((JoinPath)get_pathptr(temp)))
whichchild = OUTER;
else whichchild = INNER;
set_groupcost(temp,
xfunc_join_expense((JoinPath)get_pathptr(temp),
whichchild));
if (primjoin = xfunc_primary_join((JoinPath)get_pathptr(temp)))
{
set_groupsel(temp,
compute_clause_selec(queryInfo,
primjoin, NIL));
}
else
{
set_groupsel(temp,1.0);
}
}
else /* a restriction, or 2-ary join pred */
{
set_groupcost(temp,
xfunc_expense(queryInfo,
get_clause(get_cinfo(temp))));
set_groupsel(temp,
compute_clause_selec(queryInfo,
get_clause(get_cinfo(temp)),
NIL));
}
set_groupup(temp,false);
}
/* make passes upwards, forming groups */
do
{
progress = false;
for (temp = (Stream)get_upstream(bottom);
temp != (Stream)NULL;
temp = (Stream)get_upstream(temp))
{
/* check for grouping with node upstream */
if (!get_groupup(temp) && /* not already grouped */
(parent = (Stream)get_upstream(temp)) != (Stream)NULL &&
/* temp is a join or temp is the top of a group */
(is_join((Path)get_pathptr(temp)) ||
get_downstream(temp) &&
get_groupup((Stream)get_downstream(temp))) &&
get_grouprank(parent) < get_grouprank(temp))
{
progress = true; /* we formed a new group */
set_groupup(temp,true);
set_groupcost(temp,
get_groupcost(temp) +
get_groupsel(temp) * get_groupcost(parent));
set_groupsel(temp,get_groupsel(temp) * get_groupsel(parent));
/* fix costs and sels of all members of group */
xfunc_setup_group(temp, bottom);
}
}
} while(progress);
}
/* ------------------- UTILITY FUNCTIONS ------------------------- */
/*
** xfunc_free_stream --
** walk down a stream and pfree it
*/
static void xfunc_free_stream(Stream root)
{
Stream cur, next;
Assert(xfunc_check_stream(root));
if (root != (Stream)NULL)
for (cur = root; cur != (Stream)NULL; cur = next)
{
next = (Stream)get_downstream(cur);
pfree(cur);
}
}
/*
** xfunc_add<_clauses
** find any clauses above current, and insert them into stream as
** appropriate. Return uppermost clause inserted, or current if none.
*/
static Stream xfunc_add_clauses(Stream current)
{
Stream topnode = current;
LispValue temp;
LispValue primjoin;
/* first add in the local clauses */
foreach(temp, get_locclauseinfo((Path)get_pathptr(current)))
{
topnode =
xfunc_streaminsert((CInfo)lfirst(temp), topnode,
XFUNC_LOCPRD);
}
/* and add in the join clauses */
if (IsA(get_pathptr(current),JoinPath))
{
primjoin = xfunc_primary_join((JoinPath)get_pathptr(current));
foreach(temp, get_pathclauseinfo((JoinPath)get_pathptr(current)))
{
if (!equal(get_clause((CInfo)lfirst(temp)), primjoin))
topnode =
xfunc_streaminsert((CInfo)lfirst(temp), topnode,
XFUNC_JOINPRD);
}
}
return(topnode);
}
/*
** xfunc_setup_group
** find all elements of stream that are grouped with node and are above
** bottom, and set their groupcost and groupsel to be the same as node's.
*/
static void xfunc_setup_group(Stream node, Stream bottom)
{
Stream temp;
if (node != bottom)
/* traverse downwards */
for (temp = (Stream)get_downstream(node);
temp != (Stream)NULL && temp != bottom;
temp = (Stream)get_downstream(temp))
{
if (!get_groupup(temp)) break;
else
{
set_groupcost(temp, get_groupcost(node));
set_groupsel(temp, get_groupsel(node));
}
}
/* traverse upwards */
for (temp = (Stream)get_upstream(node); temp != (Stream)NULL;
temp = (Stream)get_upstream(temp))
{
if (!get_groupup((Stream)get_downstream(temp))) break;
else
{
set_groupcost(temp, get_groupcost(node));
set_groupsel(temp, get_groupsel(node));
}
}
}
/*
** xfunc_streaminsert
** Make a new Stream node to hold clause, and insert it above current.
** Return new node.
*/
static Stream
xfunc_streaminsert(CInfo clauseinfo,
Stream current,
int clausetype) /* XFUNC_LOCPRD or XFUNC_JOINPRD */
{
Stream newstream = RMakeStream();
set_upstream(newstream, get_upstream(current));
if (get_upstream(current))
set_downstream((Stream)(get_upstream(current)), (StreamPtr)newstream);
set_upstream(current, (StreamPtr)newstream);
set_downstream(newstream, (StreamPtr)current);
set_pathptr(newstream, get_pathptr(current));
set_cinfo(newstream, clauseinfo);
set_clausetype(newstream, clausetype);
return(newstream);
}
/*
** Given a Stream node, find the number of relids referenced in the pathnode
** associated with the stream node. The number of relids gives a unique
** ordering on the joins in a stream, which we use to compare the height of
** join nodes.
*/
static int xfunc_num_relids(Stream node)
{
if (!node || !IsA(get_pathptr(node),JoinPath))
return(0);
else return(length
(get_relids(get_parent((JoinPath)get_pathptr(node)))));
}
/*
** xfunc_get_downjoin --
** Given a stream node, find the next lowest node which points to a
** join predicate or a scan node.
*/
static StreamPtr xfunc_get_downjoin(Stream node)
{
Stream temp;
if (!is_clause(node)) /* if this is a join */
node = (Stream)get_downstream(node);
for (temp = node; temp && is_clause(temp);
temp = (Stream)get_downstream(temp))
/* empty body in for loop */ ;
return((StreamPtr)temp);
}
/*
** xfunc_get_upjoin --
** same as above, but upwards.
*/
static StreamPtr xfunc_get_upjoin(Stream node)
{
Stream temp;
if (!is_clause(node)) /* if this is a join */
node = (Stream)get_upstream(node);
for (temp = node; temp && is_clause(temp);
temp = (Stream)get_upstream(temp))
/* empty body in for loop */ ;
return((StreamPtr)temp);
}
/*
** xfunc_stream_qsort --
** Given a stream, sort by group rank the elements in the stream from the
** node "bottom" up. DESTRUCTIVELY MODIFIES STREAM! Returns new root.
*/
static Stream xfunc_stream_qsort(Stream root, Stream bottom)
{
int i;
size_t num;
Stream *nodearray, output;
Stream tmp;
/* find size of list */
for (num = 0, tmp = root; tmp != bottom;
tmp = (Stream)get_downstream(tmp))
num ++;
if (num <= 1) return (root);
/* copy elements of the list into an array */
nodearray = (Stream *) palloc(num * sizeof(Stream));
for (tmp = root, i = 0; tmp != bottom;
tmp = (Stream)get_downstream(tmp), i++)
nodearray[i] = tmp;
/* sort the array */
pg_qsort(nodearray, num, sizeof(LispValue), xfunc_stream_compare);
/* paste together the array elements */
output = nodearray[num - 1];
set_upstream(output, (StreamPtr)NULL);
for (i = num - 2; i >= 0; i--)
{
set_downstream(nodearray[i+1], (StreamPtr)nodearray[i]);
set_upstream(nodearray[i], (StreamPtr)nodearray[i+1]);
}
set_downstream(nodearray[0], (StreamPtr)bottom);
if (bottom)
set_upstream(bottom, (StreamPtr)nodearray[0]);
Assert(xfunc_check_stream(output));
return(output);
}
/*
** xfunc_stream_compare
** comparison function for xfunc_stream_qsort.
** Compare nodes by group rank. If group ranks are equal, ensure that
** join nodes appear in same order as in plan tree.
*/
static int xfunc_stream_compare(void *arg1, void *arg2)
{
Stream stream1 = *(Stream *) arg1;
Stream stream2 = *(Stream *) arg2;
Cost rank1, rank2;
rank1 = get_grouprank(stream1);
rank2 = get_grouprank(stream2);
if (rank1 > rank2) return(1);
else if (rank1 < rank2) return(-1);
else
{
if (is_clause(stream1) && is_clause(stream2))
return(0); /* doesn't matter what order if both are restrictions */
else if (!is_clause(stream1) && !is_clause(stream2))
{
if (xfunc_num_relids(stream1) < xfunc_num_relids(stream2))
return(-1);
else return(1);
}
else if (is_clause(stream1) && !is_clause(stream2))
{
if (xfunc_num_relids(stream1) == xfunc_num_relids(stream2))
/* stream1 is a restriction over stream2 */
return(1);
else return(-1);
}
else if (!is_clause(stream1) && is_clause(stream2))
{
/* stream2 is a restriction over stream1: never push down */
return(-1);
}
}
}
/* ------------------ DEBUGGING ROUTINES ---------------------------- */
/*
** Make sure all pointers in stream make sense. Make sure no joins are
** out of order.
*/
static bool xfunc_check_stream(Stream node)
{
Stream temp;
int numrelids, tmp;
/* set numrelids higher than max */
if (!is_clause(node))
numrelids = xfunc_num_relids(node) + 1;
else if (xfunc_get_downjoin(node))
numrelids = xfunc_num_relids((Stream)xfunc_get_downjoin(node)) + 1;
else numrelids = 1;
for (temp = node; get_downstream(temp); temp = (Stream)get_downstream(temp))
{
if ((Stream)get_upstream((Stream)get_downstream(temp)) != temp)
{
elog(WARN, "bad pointers in stream");
return(false);
}
if (!is_clause(temp))
{
if ((tmp = xfunc_num_relids(temp)) >= numrelids)
{
elog(WARN, "Joins got reordered!");
return(false);
}
numrelids = tmp;
}
}
return(true);
}
/*
** xfunc_in_stream
** check if node is in stream
*/
static bool xfunc_in_stream(Stream node, Stream stream)
{
Stream temp;
for (temp = stream; temp; temp = (Stream)get_downstream(temp))
if (temp == node) return(1);
return(0);
}

View File

@@ -0,0 +1,203 @@
/*-------------------------------------------------------------------------
*
* prune.c--
* Routines to prune redundant paths and relations
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.1.1.1 1996/07/09 06:21:36 scrappy Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/pg_list.h"
#include "nodes/relation.h"
#include "optimizer/internal.h"
#include "optimizer/cost.h"
#include "optimizer/paths.h"
#include "optimizer/pathnode.h"
#include "utils/elog.h"
static List *prune_joinrel(Rel *rel, List *other_rels);
/*
* prune-joinrels--
* Removes any redundant relation entries from a list of rel nodes
* 'rel-list'.
*
* Returns the resulting list.
*
*/
List *prune_joinrels(List *rel_list)
{
List *temp_list = NIL;
if (rel_list != NIL) {
temp_list = lcons(lfirst(rel_list),
prune_joinrels(prune_joinrel((Rel*)lfirst(rel_list),
lnext(rel_list))));
}
return(temp_list);
}
/*
* prune-joinrel--
* Prunes those relations from 'other-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 relation are merged into
* the pathlist of 'rel'.
*
* Returns a list of non-redundant relations, and sets the pathlist field
* of 'rel' appropriately.
*
*/
static List *
prune_joinrel(Rel *rel, List *other_rels)
{
List *i = NIL;
List *t_list = NIL;
List *temp_node = NIL;
Rel *other_rel = (Rel *)NULL;
foreach(i, other_rels) {
other_rel = (Rel*)lfirst(i);
if(same(rel->relids, other_rel->relids)) {
rel->pathlist = add_pathlist(rel,
rel->pathlist,
other_rel->pathlist);
t_list = nconc(t_list, NIL); /* XXX is this right ? */
} else {
temp_node = lcons(other_rel, NIL);
t_list = nconc(t_list,temp_node);
}
}
return(t_list);
}
/*
* prune-rel-paths--
* For each relation entry in 'rel-list' (which corresponds to a join
* relation), set pointers to the unordered path and cheapest paths
* (if the unordered path isn't the cheapest, it is pruned), and
* reset the relation's size field to reflect the join.
*
* Returns nothing of interest.
*
*/
void
prune_rel_paths(List *rel_list)
{
List *x = NIL;
List *y = NIL;
Path *path;
Rel *rel = (Rel*)NULL;
JoinPath *cheapest = (JoinPath*)NULL;
foreach(x, rel_list) {
rel = (Rel*)lfirst(x);
foreach(y, rel->pathlist) {
path = (Path*)lfirst(y);
if(!path->p_ordering.ord.sortop) {
break;
}
}
cheapest = (JoinPath*)prune_rel_path(rel, path);
if (IsA_JoinPath(cheapest))
{
rel->size = compute_joinrel_size(cheapest);
}
else
elog(WARN, "non JoinPath called");
}
}
/*
* prune-rel-path--
* Compares the unordered path for a relation with the cheapest path. If
* the unordered path is not cheapest, it is pruned.
*
* Resets the pointers in 'rel' for unordered and cheapest paths.
*
* Returns the cheapest path.
*
*/
Path *
prune_rel_path(Rel *rel, Path *unorderedpath)
{
Path *cheapest = set_cheapest(rel, rel->pathlist);
/* don't prune if not pruneable -- JMH, 11/23/92 */
if(unorderedpath != cheapest
&& rel->pruneable) {
rel->unorderedpath = (Path *)NULL;
rel->pathlist = lremove(unorderedpath, rel->pathlist);
} else {
rel->unorderedpath = (Path *)unorderedpath;
}
return(cheapest);
}
/*
* merge-joinrels--
* Given two lists of rel nodes that are already
* pruned, merge them into one pruned rel node list
*
* 'rel-list1' and
* 'rel-list2' are the rel node lists
*
* Returns one pruned rel node list
*/
List *
merge_joinrels(List *rel_list1, List *rel_list2)
{
List *xrel = NIL;
foreach(xrel,rel_list1) {
Rel *rel = (Rel*)lfirst(xrel);
rel_list2 = prune_joinrel(rel,rel_list2);
}
return(append(rel_list1, rel_list2));
}
/*
* prune_oldrels--
* If all the joininfo's in a rel node are inactive,
* that means that this node has been joined into
* other nodes in all possible ways, therefore
* this node can be discarded. If not, it will cause
* extra complexity of the optimizer.
*
* old_rels is a list of rel nodes
*
* Returns a new list of rel nodes
*/
List *prune_oldrels(List *old_rels)
{
Rel *rel;
List *joininfo_list, *xjoininfo;
if(old_rels == NIL)
return(NIL);
rel = (Rel*)lfirst(old_rels);
joininfo_list = rel->joininfo;
if(joininfo_list == NIL)
return (lcons(rel, prune_oldrels(lnext(old_rels))));
foreach(xjoininfo, joininfo_list) {
JInfo *joininfo = (JInfo*)lfirst(xjoininfo);
if(!joininfo->inactive)
return (lcons(rel, prune_oldrels(lnext(old_rels))));
}
return(prune_oldrels(lnext(old_rels)));
}

File diff suppressed because it is too large Load Diff