mirror of
https://github.com/postgres/postgres.git
synced 2025-11-15 03:41:20 +03:00
Another round of planner/optimizer work. This is just restructuring and
code cleanup; no major improvements yet. However, EXPLAIN does produce more intuitive outputs for nested loops with indexscans now...
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.54 1999/11/23 20:06:54 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.55 2000/01/09 00:26:29 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -66,23 +66,16 @@ make_one_rel(Query *root, List *rels)
|
||||
|
||||
if (levels_needed <= 1)
|
||||
{
|
||||
|
||||
/*
|
||||
* Unsorted single relation, no more processing is required.
|
||||
* Single relation, no more processing is required.
|
||||
*/
|
||||
return lfirst(rels);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* This means that joins or sorts are required. Set selectivities
|
||||
* of any clauses not yet set. (I think that this is redundant;
|
||||
* set_base_rel_pathlist should have set them all already. But
|
||||
* a scan to check that they are all set doesn't cost much...)
|
||||
* Generate join tree.
|
||||
*/
|
||||
set_rest_relselec(root, rels);
|
||||
|
||||
return make_one_rel_by_joins(root, rels, levels_needed);
|
||||
}
|
||||
}
|
||||
@@ -115,7 +108,7 @@ set_base_rel_pathlist(Query *root, List *rels)
|
||||
tidscan_pathlist = create_tidscan_paths(root, rel);
|
||||
if (tidscan_pathlist)
|
||||
sequential_scan_list = nconc(sequential_scan_list,
|
||||
tidscan_pathlist);
|
||||
tidscan_pathlist);
|
||||
rel_index_scan_list = create_index_paths(root,
|
||||
rel,
|
||||
indices,
|
||||
@@ -126,7 +119,6 @@ set_base_rel_pathlist(Query *root, List *rels)
|
||||
* to have marked OR restriction clauses with relevant indices;
|
||||
* this is why it doesn't need to be given the full list of indices.
|
||||
*/
|
||||
|
||||
or_index_scan_list = create_or_index_paths(root, rel,
|
||||
rel->restrictinfo);
|
||||
|
||||
@@ -139,19 +131,10 @@ set_base_rel_pathlist(Query *root, List *rels)
|
||||
nconc(rel_index_scan_list,
|
||||
or_index_scan_list));
|
||||
|
||||
/* Now find the cheapest of the paths */
|
||||
set_cheapest(rel, rel->pathlist);
|
||||
|
||||
/* Set the selectivity estimates for any restriction clauses that
|
||||
* didn't get set as a byproduct of index-path selectivity estimation
|
||||
* (see create_index_path()).
|
||||
*/
|
||||
set_rest_selec(root, rel->restrictinfo);
|
||||
|
||||
/* Calculate the estimated size (post-restrictions) and tuple width
|
||||
* for this base rel. This uses the restriction clause selectivities.
|
||||
*/
|
||||
rel->size = compute_rel_size(rel);
|
||||
rel->width = compute_rel_width(rel);
|
||||
/* Mark rel with estimated output rows and width */
|
||||
set_rel_rows_width(root, rel);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,16 +200,12 @@ make_one_rel_by_joins(Query *root, List *rels, int levels_needed)
|
||||
xfunc_trypullup((RelOptInfo *) lfirst(x));
|
||||
#endif
|
||||
|
||||
rels_set_cheapest(joined_rels);
|
||||
rels_set_cheapest(root, joined_rels);
|
||||
|
||||
foreach(x, joined_rels)
|
||||
{
|
||||
rel = (RelOptInfo *) lfirst(x);
|
||||
|
||||
if (rel->size <= 0)
|
||||
rel->size = compute_rel_size(rel);
|
||||
rel->width = compute_rel_width(rel);
|
||||
|
||||
#ifdef OPTIMIZER_DEBUG
|
||||
printf("levels left: %d\n", levels_needed);
|
||||
debug_print_rel(root, rel);
|
||||
@@ -297,10 +276,9 @@ print_path(Query *root, Path *path, int indent)
|
||||
}
|
||||
if (join)
|
||||
{
|
||||
int size = path->parent->size;
|
||||
|
||||
jp = (JoinPath *) path;
|
||||
printf("%s size=%d cost=%f\n", ptype, size, path->path_cost);
|
||||
printf("%s rows=%.0f cost=%f\n",
|
||||
ptype, path->parent->rows, path->path_cost);
|
||||
switch (nodeTag(path))
|
||||
{
|
||||
case T_MergePath:
|
||||
@@ -308,7 +286,7 @@ print_path(Query *root, Path *path, int indent)
|
||||
for (i = 0; i < indent + 1; i++)
|
||||
printf("\t");
|
||||
printf(" clauses=(");
|
||||
print_joinclauses(root, ((JoinPath *) path)->pathinfo);
|
||||
print_joinclauses(root, jp->path.parent->restrictinfo);
|
||||
printf(")\n");
|
||||
|
||||
if (nodeTag(path) == T_MergePath)
|
||||
@@ -333,11 +311,10 @@ print_path(Query *root, Path *path, int indent)
|
||||
}
|
||||
else
|
||||
{
|
||||
int size = path->parent->size;
|
||||
int relid = lfirsti(path->parent->relids);
|
||||
|
||||
printf("%s(%d) size=%d cost=%f\n",
|
||||
ptype, relid, size, path->path_cost);
|
||||
printf("%s(%d) rows=%.0f cost=%f\n",
|
||||
ptype, relid, path->parent->rows, path->path_cost);
|
||||
|
||||
if (IsA(path, IndexPath))
|
||||
{
|
||||
@@ -355,7 +332,7 @@ debug_print_rel(Query *root, RelOptInfo *rel)
|
||||
printf("(");
|
||||
foreach(l, rel->relids)
|
||||
printf("%d ", lfirsti(l));
|
||||
printf("): size=%d width=%d\n", rel->size, rel->width);
|
||||
printf("): rows=%.0f width=%d\n", rel->rows, rel->width);
|
||||
|
||||
printf("\tpath list:\n");
|
||||
foreach(l, rel->pathlist)
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.26 1999/09/09 02:35:47 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/clausesel.c,v 1.27 2000/01/09 00:26:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -23,103 +23,59 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* ROUTINES TO SET CLAUSE SELECTIVITIES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* set_clause_selectivities -
|
||||
* Sets the selectivity field for each clause in 'restrictinfo-list'
|
||||
* to 'new-selectivity'. If the selectivity has already been set,
|
||||
* change it only if the new one is better.
|
||||
*/
|
||||
void
|
||||
set_clause_selectivities(List *restrictinfo_list, Cost new_selectivity)
|
||||
{
|
||||
List *rlist;
|
||||
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *clausenode = (RestrictInfo *) lfirst(rlist);
|
||||
Cost cost_clause = clausenode->selectivity;
|
||||
|
||||
if (cost_clause <= 0 || new_selectivity < cost_clause)
|
||||
clausenode->selectivity = new_selectivity;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* product_selec -
|
||||
* Multiplies the selectivities of each clause in 'restrictinfo-list'.
|
||||
*
|
||||
* Returns a flonum corresponding to the selectivity of 'restrictinfo-list'.
|
||||
*/
|
||||
Cost
|
||||
product_selec(List *restrictinfo_list)
|
||||
{
|
||||
Cost result = (Cost) 1.0;
|
||||
List *rlist;
|
||||
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
result *= ((RestrictInfo *) lfirst(rlist))->selectivity;
|
||||
}
|
||||
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.
|
||||
*
|
||||
* MODIFIES: selectivities of the various rel's restrictinfo slots.
|
||||
*/
|
||||
void
|
||||
set_rest_relselec(Query *root, List *rel_list)
|
||||
{
|
||||
List *x;
|
||||
|
||||
foreach(x, rel_list)
|
||||
{
|
||||
RelOptInfo *rel = (RelOptInfo *) lfirst(x);
|
||||
set_rest_selec(root, rel->restrictinfo);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* set_rest_selec -
|
||||
* Sets the selectivity fields for those clauses within a single
|
||||
* relation's 'restrictinfo-list' that haven't already been set.
|
||||
*/
|
||||
void
|
||||
set_rest_selec(Query *root, List *restrictinfo_list)
|
||||
{
|
||||
List *rlist;
|
||||
|
||||
foreach(rlist, restrictinfo_list)
|
||||
{
|
||||
RestrictInfo *clause = (RestrictInfo *) lfirst(rlist);
|
||||
|
||||
if (clause->selectivity <= 0)
|
||||
{
|
||||
clause->selectivity =
|
||||
compute_clause_selec(root, (Node *) clause->clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* ROUTINES TO COMPUTE SELECTIVITIES
|
||||
****************************************************************************/
|
||||
|
||||
/*
|
||||
* compute_clause_selec -
|
||||
* Computes the selectivity of a clause.
|
||||
* restrictlist_selec -
|
||||
* Compute the selectivity of an implicitly-ANDed list of RestrictInfo
|
||||
* clauses.
|
||||
*
|
||||
* This is the same as clauselist_selec except for the form of the input.
|
||||
*/
|
||||
Cost
|
||||
Selectivity
|
||||
restrictlist_selec(Query *root, List *restrictinfo_list)
|
||||
{
|
||||
List *clauselist = get_actual_clauses(restrictinfo_list);
|
||||
Selectivity result;
|
||||
|
||||
result = clauselist_selec(root, clauselist);
|
||||
freeList(clauselist);
|
||||
return result;
|
||||
}
|
||||
|
||||
/*
|
||||
* clauselist_selec -
|
||||
* Compute the selectivity of an implicitly-ANDed list of boolean
|
||||
* expression clauses.
|
||||
*/
|
||||
Selectivity
|
||||
clauselist_selec(Query *root, List *clauses)
|
||||
{
|
||||
Selectivity s1 = 1.0;
|
||||
List *clause;
|
||||
|
||||
/* Use the product of the selectivities of the subclauses.
|
||||
* XXX this is probably too optimistic, since the subclauses
|
||||
* are very likely not independent...
|
||||
*/
|
||||
foreach(clause, clauses)
|
||||
{
|
||||
Selectivity s2 = compute_clause_selec(root, (Node *) lfirst(clause));
|
||||
s1 = s1 * s2;
|
||||
}
|
||||
return s1;
|
||||
}
|
||||
|
||||
/*
|
||||
* compute_clause_selec -
|
||||
* Compute the selectivity of a general boolean expression clause.
|
||||
*/
|
||||
Selectivity
|
||||
compute_clause_selec(Query *root, Node *clause)
|
||||
{
|
||||
Cost s1 = 1.0; /* default for any unhandled clause type */
|
||||
Selectivity s1 = 1.0; /* default for any unhandled clause type */
|
||||
|
||||
if (clause == NULL)
|
||||
return s1;
|
||||
@@ -131,17 +87,12 @@ compute_clause_selec(Query *root, Node *clause)
|
||||
* 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.
|
||||
*
|
||||
* XXX why are we using varno and varoattno? Seems like it should
|
||||
* be varno/varattno or varnoold/varoattno, not mix & match...
|
||||
*/
|
||||
Oid relid = getrelid(((Var *) clause)->varno,
|
||||
root->rtable);
|
||||
|
||||
s1 = restriction_selectivity(F_EQSEL,
|
||||
BooleanEqualOperator,
|
||||
relid,
|
||||
((Var *) clause)->varoattno,
|
||||
getrelid(((Var *) clause)->varno,
|
||||
root->rtable),
|
||||
((Var *) clause)->varattno,
|
||||
Int8GetDatum(true),
|
||||
SEL_CONSTANT | SEL_RIGHT);
|
||||
}
|
||||
@@ -163,21 +114,12 @@ compute_clause_selec(Query *root, Node *clause)
|
||||
}
|
||||
else if (and_clause(clause))
|
||||
{
|
||||
/* Use the product of the selectivities of the subclauses.
|
||||
* XXX this is probably too optimistic, since the subclauses
|
||||
* are very likely not independent...
|
||||
*/
|
||||
List *arg;
|
||||
s1 = 1.0;
|
||||
foreach(arg, ((Expr *) clause)->args)
|
||||
{
|
||||
Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
|
||||
s1 = s1 * s2;
|
||||
}
|
||||
s1 = clauselist_selec(root, ((Expr *) clause)->args);
|
||||
}
|
||||
else if (or_clause(clause))
|
||||
{
|
||||
/* Selectivities for an 'or' clause are computed as s1+s2 - s1*s2
|
||||
/*
|
||||
* Selectivities for an 'or' clause are computed as s1+s2 - s1*s2
|
||||
* to account for the probable overlap of selected tuple sets.
|
||||
* XXX is this too conservative?
|
||||
*/
|
||||
@@ -185,31 +127,15 @@ compute_clause_selec(Query *root, Node *clause)
|
||||
s1 = 0.0;
|
||||
foreach(arg, ((Expr *) clause)->args)
|
||||
{
|
||||
Cost s2 = compute_clause_selec(root, (Node *) lfirst(arg));
|
||||
Selectivity s2 = compute_clause_selec(root, (Node *) lfirst(arg));
|
||||
s1 = s1 + s2 - s1 * s2;
|
||||
}
|
||||
}
|
||||
else if (is_funcclause(clause))
|
||||
{
|
||||
/*
|
||||
* 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 = (Cost) 0.3333333;
|
||||
}
|
||||
else if (is_subplan(clause))
|
||||
{
|
||||
/*
|
||||
* Just for the moment! FIX ME! - vadim 02/04/98
|
||||
*/
|
||||
s1 = 1.0;
|
||||
}
|
||||
else if (is_opclause(clause))
|
||||
{
|
||||
if (NumRelids(clause) == 1)
|
||||
{
|
||||
/* The clause is not a join clause, since there is only one
|
||||
/* The opclause 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.
|
||||
*/
|
||||
@@ -221,33 +147,20 @@ compute_clause_selec(Query *root, Node *clause)
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
if (!oprrest)
|
||||
s1 = (Cost) 0.5;
|
||||
s1 = (Selectivity) 0.5;
|
||||
else
|
||||
{
|
||||
int relidx;
|
||||
AttrNumber attno;
|
||||
Datum constval;
|
||||
int flag;
|
||||
Oid reloid;
|
||||
|
||||
get_relattval(clause, 0, &relidx, &attno, &constval, &flag);
|
||||
if (relidx && attno)
|
||||
s1 = (Cost) restriction_selectivity(oprrest,
|
||||
opno,
|
||||
getrelid(relidx,
|
||||
root->rtable),
|
||||
attno,
|
||||
constval,
|
||||
flag);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* attno can be 0 if the clause had a function in it,
|
||||
* i.e. WHERE myFunc(f) = 10
|
||||
*
|
||||
* XXX should be FIXED to use function selectivity
|
||||
*/
|
||||
s1 = (Cost) (0.5);
|
||||
}
|
||||
reloid = relidx ? getrelid(relidx, root->rtable) : InvalidOid;
|
||||
s1 = restriction_selectivity(oprrest, opno,
|
||||
reloid, attno,
|
||||
constval, flag);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -265,30 +178,41 @@ compute_clause_selec(Query *root, Node *clause)
|
||||
* selectivity of 0.5
|
||||
*/
|
||||
if (!oprjoin)
|
||||
s1 = (Cost) (0.5);
|
||||
s1 = (Selectivity) 0.5;
|
||||
else
|
||||
{
|
||||
int relid1,
|
||||
relid2;
|
||||
AttrNumber attno1,
|
||||
attno2;
|
||||
Oid reloid1,
|
||||
reloid2;
|
||||
|
||||
get_rels_atts(clause, &relid1, &attno1, &relid2, &attno2);
|
||||
if (relid1 && relid2 && attno1 && attno2)
|
||||
|
||||
s1 = (Cost) join_selectivity(oprjoin,
|
||||
opno,
|
||||
getrelid(relid1,
|
||||
root->rtable),
|
||||
attno1,
|
||||
getrelid(relid2,
|
||||
root->rtable),
|
||||
attno2);
|
||||
else /* XXX more code for function selectivity? */
|
||||
s1 = (Cost) (0.5);
|
||||
reloid1 = relid1 ? getrelid(relid1, root->rtable) : InvalidOid;
|
||||
reloid2 = relid2 ? getrelid(relid2, root->rtable) : InvalidOid;
|
||||
s1 = join_selectivity(oprjoin, opno,
|
||||
reloid1, attno1,
|
||||
reloid2, attno2);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (is_funcclause(clause))
|
||||
{
|
||||
/*
|
||||
* 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 = (Selectivity) 0.3333333;
|
||||
}
|
||||
else if (is_subplan(clause))
|
||||
{
|
||||
/*
|
||||
* Just for the moment! FIX ME! - vadim 02/04/98
|
||||
*/
|
||||
s1 = 1.0;
|
||||
}
|
||||
|
||||
return s1;
|
||||
}
|
||||
|
||||
@@ -18,15 +18,14 @@
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.46 1999/11/23 20:06:54 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.47 2000/01/09 00:26:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "postgres.h"
|
||||
|
||||
#include <math.h>
|
||||
#ifdef HAVE_LIMITS_H
|
||||
#include <limits.h>
|
||||
#ifndef MAXINT
|
||||
@@ -45,13 +44,17 @@
|
||||
#include "utils/lsyscache.h"
|
||||
|
||||
|
||||
static int compute_targetlist_width(List *targetlist);
|
||||
static void set_rel_width(Query *root, RelOptInfo *rel);
|
||||
static int compute_attribute_width(TargetEntry *tlistentry);
|
||||
static double relation_byte_size(int tuples, int width);
|
||||
static double relation_byte_size(double tuples, int width);
|
||||
static double page_size(double tuples, int width);
|
||||
static double base_log(double x, double b);
|
||||
|
||||
|
||||
int _disable_cost_ = 30000000;
|
||||
Cost _cpu_page_weight_ = _CPU_PAGE_WEIGHT_;
|
||||
Cost _cpu_index_page_weight_ = _CPU_INDEX_PAGE_WEIGHT_;
|
||||
|
||||
Cost _disable_cost_ = 100000000.0;
|
||||
|
||||
bool _enable_seqscan_ = true;
|
||||
bool _enable_indexscan_ = true;
|
||||
@@ -61,9 +64,6 @@ bool _enable_mergejoin_ = true;
|
||||
bool _enable_hashjoin_ = true;
|
||||
bool _enable_tidscan_ = true;
|
||||
|
||||
Cost _cpu_page_weight_ = _CPU_PAGE_WEIGHT_;
|
||||
Cost _cpu_index_page_weight_ = _CPU_INDEX_PAGE_WEIGHT_;
|
||||
|
||||
/*
|
||||
* cost_seqscan
|
||||
* Determines and returns the cost of scanning a relation sequentially.
|
||||
@@ -74,27 +74,21 @@ Cost _cpu_index_page_weight_ = _CPU_INDEX_PAGE_WEIGHT_;
|
||||
* 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.
|
||||
*
|
||||
* cpu = CPU-PAGE-WEIGHT * t
|
||||
*/
|
||||
Cost
|
||||
cost_seqscan(int relid, int relpages, int reltuples)
|
||||
cost_seqscan(RelOptInfo *baserel)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
/* Should only be applied to base relations */
|
||||
Assert(length(baserel->relids) == 1);
|
||||
|
||||
if (!_enable_seqscan_)
|
||||
temp += _disable_cost_;
|
||||
|
||||
if (relid < 0)
|
||||
if (lfirsti(baserel->relids) < 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* cost of sequentially scanning a materialized temporary relation
|
||||
*/
|
||||
@@ -102,9 +96,10 @@ cost_seqscan(int relid, int relpages, int reltuples)
|
||||
}
|
||||
else
|
||||
{
|
||||
temp += relpages;
|
||||
temp += _cpu_page_weight_ * reltuples;
|
||||
temp += baserel->pages;
|
||||
temp += _cpu_page_weight_ * baserel->tuples;
|
||||
}
|
||||
|
||||
Assert(temp >= 0);
|
||||
return temp;
|
||||
}
|
||||
@@ -115,31 +110,38 @@ cost_seqscan(int relid, int relpages, int reltuples)
|
||||
* 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)
|
||||
* cpu = CPU-INDEX-PAGE-WEIGHT * expected-index-tuples +
|
||||
* CPU-PAGE-WEIGHT * 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.
|
||||
* 'baserel' is the base relation the index is for
|
||||
* 'index' is the index to be used
|
||||
* 'expected_indexpages' is the estimated number of index pages that will
|
||||
* be touched in the scan (this is computed by index-type-specific code)
|
||||
* 'selec' is the selectivity of the index, ie, the fraction of base-relation
|
||||
* tuples that we will have to fetch and examine
|
||||
* 'is_injoin' is T if we are considering using the index scan as the inside
|
||||
* of a nestloop join.
|
||||
*
|
||||
* NOTE: 'selec' should be calculated on the basis of indexqual conditions
|
||||
* only. Any additional quals evaluated as qpquals may reduce the number
|
||||
* of returned tuples, but they won't reduce the number of tuples we have
|
||||
* to fetch from the table, so they don't reduce the scan cost.
|
||||
*/
|
||||
Cost
|
||||
cost_index(Oid indexid,
|
||||
int expected_indexpages,
|
||||
Cost selec,
|
||||
int relpages,
|
||||
int reltuples,
|
||||
int indexpages,
|
||||
int indextuples,
|
||||
cost_index(RelOptInfo *baserel,
|
||||
IndexOptInfo *index,
|
||||
long expected_indexpages,
|
||||
Selectivity selec,
|
||||
bool is_injoin)
|
||||
{
|
||||
Cost temp = 0;
|
||||
double reltuples = selec * baserel->tuples;
|
||||
double indextuples = selec * index->tuples;
|
||||
double relpages;
|
||||
|
||||
/* Should only be applied to base relations */
|
||||
Assert(IsA(baserel, RelOptInfo) && IsA(index, IndexOptInfo));
|
||||
Assert(length(baserel->relids) == 1);
|
||||
|
||||
if (!_enable_indexscan_ && !is_injoin)
|
||||
temp += _disable_cost_;
|
||||
@@ -151,25 +153,49 @@ cost_index(Oid indexid,
|
||||
*/
|
||||
if (expected_indexpages <= 0)
|
||||
expected_indexpages = 1;
|
||||
if (indextuples <= 0)
|
||||
indextuples = 1;
|
||||
if (indextuples <= 0.0)
|
||||
indextuples = 1.0;
|
||||
|
||||
/* expected index relation pages */
|
||||
temp += expected_indexpages;
|
||||
|
||||
/*
|
||||
* expected base relation pages XXX this isn't really right, since we
|
||||
* will access the table nonsequentially and might have to fetch the
|
||||
* same page more than once. This calculation assumes the buffer
|
||||
* cache will prevent that from happening...
|
||||
/*--------------------
|
||||
* expected base relation pages
|
||||
*
|
||||
* Worst case is that each tuple the index tells us to fetch comes
|
||||
* from a different base-rel page, in which case the I/O cost would be
|
||||
* 'reltuples' pages. In practice we can expect the number of page
|
||||
* fetches to be reduced by the buffer cache, because more than one
|
||||
* tuple can be retrieved per page fetched. Currently, we estimate
|
||||
* the number of pages to be retrieved as
|
||||
* MIN(reltuples, relpages)
|
||||
* This amounts to assuming that the buffer cache is perfectly efficient
|
||||
* and never ends up reading the same page twice within one scan, which
|
||||
* of course is too optimistic. On the other hand, we are assuming that
|
||||
* the target tuples are perfectly uniformly distributed across the
|
||||
* relation's pages, which is too pessimistic --- any nonuniformity of
|
||||
* distribution will reduce the number of pages we have to fetch.
|
||||
* So, we guess-and-hope that these sources of error will more or less
|
||||
* balance out.
|
||||
*
|
||||
* XXX if the relation has recently been "clustered" using this index,
|
||||
* then in fact the target tuples will be highly nonuniformly distributed,
|
||||
* and we will be seriously overestimating the scan cost! Currently we
|
||||
* have no way to know whether the relation has been clustered, nor how
|
||||
* much it's been modified since the last clustering, so we ignore this
|
||||
* effect. Would be nice to do better someday.
|
||||
*--------------------
|
||||
*/
|
||||
temp += ceil(((double) selec) * ((double) relpages));
|
||||
relpages = reltuples;
|
||||
if (baserel->pages > 0 && baserel->pages < relpages)
|
||||
relpages = baserel->pages;
|
||||
temp += relpages;
|
||||
|
||||
/* per index tuples */
|
||||
temp += _cpu_index_page_weight_ * selec * indextuples;
|
||||
temp += _cpu_index_page_weight_ * indextuples;
|
||||
|
||||
/* per heap tuples */
|
||||
temp += _cpu_page_weight_ * selec * reltuples;
|
||||
temp += _cpu_page_weight_ * reltuples;
|
||||
|
||||
Assert(temp >= 0);
|
||||
return temp;
|
||||
@@ -180,13 +206,10 @@ cost_index(Oid indexid,
|
||||
* Determines and returns the cost of scanning a relation using tid-s.
|
||||
*
|
||||
* disk = number of tids
|
||||
* cpu = *CPU-PAGE-WEIGHT* * number_of_tids
|
||||
*
|
||||
* Returns a flonum.
|
||||
*
|
||||
* cpu = CPU-PAGE-WEIGHT * number_of_tids
|
||||
*/
|
||||
Cost
|
||||
cost_tidscan(List *tideval)
|
||||
cost_tidscan(RelOptInfo *baserel, List *tideval)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
@@ -200,28 +223,39 @@ cost_tidscan(List *tideval)
|
||||
|
||||
/*
|
||||
* cost_sort
|
||||
* Determines and returns the cost of sorting a relation by considering
|
||||
* the cost of doing an external sort: XXX this is probably too low
|
||||
* disk = (p lg p)
|
||||
* cpu = *CPU-PAGE-WEIGHT* * (t lg t)
|
||||
* Determines and returns the cost of sorting a relation.
|
||||
*
|
||||
* If the total volume of data to sort is less than SortMem, we will do
|
||||
* an in-memory sort, which requires no I/O and about t*log2(t) tuple
|
||||
* comparisons for t tuples. We use _cpu_index_page_weight as the cost
|
||||
* of a tuple comparison (is this reasonable, or do we need another
|
||||
* basic parameter?).
|
||||
*
|
||||
* If the total volume exceeds SortMem, we switch to a tape-style merge
|
||||
* algorithm. There will still be about t*log2(t) tuple comparisons in
|
||||
* total, but we will also need to write and read each tuple once per
|
||||
* merge pass. We expect about ceil(log6(r)) merge passes where r is the
|
||||
* number of initial runs formed (log6 because tuplesort.c uses six-tape
|
||||
* merging). Since the average initial run should be about twice SortMem,
|
||||
* we have
|
||||
* disk = 2 * p * ceil(log6(p / (2*SortMem)))
|
||||
* cpu = CPU-INDEX-PAGE-WEIGHT * t * log2(t)
|
||||
*
|
||||
* 'pathkeys' is a list of sort keys
|
||||
* 'tuples' is the number of tuples in the relation
|
||||
* 'width' is the average tuple width in bytes
|
||||
*
|
||||
* NOTE: some callers currently pass NULL for pathkeys because they
|
||||
* NOTE: some callers currently pass NIL for pathkeys because they
|
||||
* can't conveniently supply the sort keys. Since this routine doesn't
|
||||
* currently do anything with pathkeys anyway, that doesn't matter...
|
||||
* but if it ever does, it should react gracefully to lack of key data.
|
||||
*
|
||||
* Returns a flonum.
|
||||
*/
|
||||
Cost
|
||||
cost_sort(List *pathkeys, int tuples, int width)
|
||||
cost_sort(List *pathkeys, double tuples, int width)
|
||||
{
|
||||
Cost temp = 0;
|
||||
int npages = page_size(tuples, width);
|
||||
double log_npages;
|
||||
double nbytes = relation_byte_size(tuples, width);
|
||||
long sortmembytes = SortMem * 1024L;
|
||||
|
||||
if (!_enable_sort_)
|
||||
temp += _disable_cost_;
|
||||
@@ -231,25 +265,23 @@ cost_sort(List *pathkeys, int tuples, int width)
|
||||
* even if passed-in tuple count is zero. Besides, mustn't do
|
||||
* log(0)...
|
||||
*/
|
||||
if (tuples <= 0)
|
||||
tuples = 1;
|
||||
if (npages <= 0)
|
||||
npages = 1;
|
||||
if (tuples < 2.0)
|
||||
tuples = 2.0;
|
||||
|
||||
log_npages = ceil(base_log((double) npages, 2.0));
|
||||
if (log_npages <= 0.0)
|
||||
log_npages = 1.0;
|
||||
temp += _cpu_index_page_weight_ * tuples * base_log(tuples, 2.0);
|
||||
|
||||
temp += npages * log_npages;
|
||||
if (nbytes > sortmembytes)
|
||||
{
|
||||
double npages = ceil(nbytes / BLCKSZ);
|
||||
double nruns = nbytes / (sortmembytes * 2);
|
||||
double log_runs = ceil(base_log(nruns, 6.0));
|
||||
|
||||
/*
|
||||
* could be base_log(tuples, NBuffers), but we are only doing 2-way
|
||||
* merges
|
||||
*/
|
||||
temp += _cpu_page_weight_ * tuples * base_log((double) tuples, 2.0);
|
||||
if (log_runs < 1.0)
|
||||
log_runs = 1.0;
|
||||
temp += 2 * npages * log_runs;
|
||||
}
|
||||
|
||||
Assert(temp > 0);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
@@ -258,18 +290,15 @@ cost_sort(List *pathkeys, int tuples, int width)
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
#ifdef NOT_USED
|
||||
Cost
|
||||
cost_result(int tuples, int width)
|
||||
cost_result(double tuples, int width)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
temp = temp + page_size(tuples, width);
|
||||
temp = temp + _cpu_page_weight_ * tuples;
|
||||
temp += page_size(tuples, width);
|
||||
temp += _cpu_page_weight_ * tuples;
|
||||
Assert(temp >= 0);
|
||||
return temp;
|
||||
}
|
||||
@@ -281,111 +310,106 @@ cost_result(int tuples, int width)
|
||||
* 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.
|
||||
*
|
||||
* 'outer_path' is the path for the outer relation
|
||||
* 'inner_path' is the path for the inner relation
|
||||
* 'is_indexjoin' is true if we are using an indexscan for the inner relation
|
||||
*/
|
||||
Cost
|
||||
cost_nestloop(Cost outercost,
|
||||
Cost innercost,
|
||||
int outertuples,
|
||||
int innertuples,
|
||||
int outerpages,
|
||||
cost_nestloop(Path *outer_path,
|
||||
Path *inner_path,
|
||||
bool is_indexjoin)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
if (!_enable_nestloop_)
|
||||
temp += _disable_cost_;
|
||||
temp += outercost;
|
||||
temp += outertuples * innercost;
|
||||
Assert(temp >= 0);
|
||||
|
||||
temp += outer_path->path_cost;
|
||||
temp += outer_path->parent->rows * inner_path->path_cost;
|
||||
|
||||
Assert(temp >= 0);
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_mergejoin
|
||||
* '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 (or NIL if no explicit
|
||||
* sort is needed because the source path is already ordered)
|
||||
* '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.
|
||||
* Determines and returns the cost of joining two relations using the
|
||||
* merge join algorithm.
|
||||
*
|
||||
* 'outer_path' is the path for the outer relation
|
||||
* 'inner_path' is the path for the inner relation
|
||||
* 'outersortkeys' and 'innersortkeys' are lists of the keys to be used
|
||||
* to sort the outer and inner relations, or NIL if no explicit
|
||||
* sort is needed because the source path is already ordered
|
||||
*/
|
||||
Cost
|
||||
cost_mergejoin(Cost outercost,
|
||||
Cost innercost,
|
||||
cost_mergejoin(Path *outer_path,
|
||||
Path *inner_path,
|
||||
List *outersortkeys,
|
||||
List *innersortkeys,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth)
|
||||
List *innersortkeys)
|
||||
{
|
||||
Cost temp = 0;
|
||||
|
||||
if (!_enable_mergejoin_)
|
||||
temp += _disable_cost_;
|
||||
|
||||
temp += outercost;
|
||||
temp += innercost;
|
||||
/* cost of source data */
|
||||
temp += outer_path->path_cost + inner_path->path_cost;
|
||||
|
||||
if (outersortkeys) /* do we need to sort? */
|
||||
temp += cost_sort(outersortkeys, outersize, outerwidth);
|
||||
temp += cost_sort(outersortkeys,
|
||||
outer_path->parent->rows,
|
||||
outer_path->parent->width);
|
||||
|
||||
if (innersortkeys) /* do we need to sort? */
|
||||
temp += cost_sort(innersortkeys, innersize, innerwidth);
|
||||
temp += _cpu_page_weight_ * (outersize + innersize);
|
||||
temp += cost_sort(innersortkeys,
|
||||
inner_path->parent->rows,
|
||||
inner_path->parent->width);
|
||||
|
||||
/*
|
||||
* Estimate the number of tuples to be processed in the mergejoin itself
|
||||
* as one per tuple in the two source relations. This could be a drastic
|
||||
* underestimate if there are many equal-keyed tuples in either relation,
|
||||
* but we have no good way of estimating that...
|
||||
*/
|
||||
temp += _cpu_page_weight_ * (outer_path->parent->rows +
|
||||
inner_path->parent->rows);
|
||||
|
||||
Assert(temp >= 0);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/*
|
||||
* cost_hashjoin
|
||||
* Determines and returns the cost of joining two relations using the
|
||||
* hash join algorithm.
|
||||
*
|
||||
* 'outercost' and 'innercost' are the (disk+cpu) costs of scanning 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
|
||||
* 'innerdisbursion' is an estimate of the disbursion statistic
|
||||
* 'outer_path' is the path for the outer relation
|
||||
* 'inner_path' is the path for the inner relation
|
||||
* 'innerdisbursion' is an estimate of the disbursion statistic
|
||||
* for the inner hash key.
|
||||
*
|
||||
* Returns a flonum.
|
||||
*/
|
||||
Cost
|
||||
cost_hashjoin(Cost outercost,
|
||||
Cost innercost,
|
||||
int outersize,
|
||||
int innersize,
|
||||
int outerwidth,
|
||||
int innerwidth,
|
||||
Cost innerdisbursion)
|
||||
cost_hashjoin(Path *outer_path,
|
||||
Path *inner_path,
|
||||
Selectivity innerdisbursion)
|
||||
{
|
||||
Cost temp = 0;
|
||||
double outerbytes = relation_byte_size(outersize, outerwidth);
|
||||
double innerbytes = relation_byte_size(innersize, innerwidth);
|
||||
double outerbytes = relation_byte_size(outer_path->parent->rows,
|
||||
outer_path->parent->width);
|
||||
double innerbytes = relation_byte_size(inner_path->parent->rows,
|
||||
inner_path->parent->width);
|
||||
long hashtablebytes = SortMem * 1024L;
|
||||
|
||||
if (!_enable_hashjoin_)
|
||||
temp += _disable_cost_;
|
||||
|
||||
/* cost of source data */
|
||||
temp += outercost + innercost;
|
||||
temp += outer_path->path_cost + inner_path->path_cost;
|
||||
|
||||
/* cost of computing hash function: must do it once per tuple */
|
||||
temp += _cpu_page_weight_ * (outersize + innersize);
|
||||
temp += _cpu_page_weight_ * (outer_path->parent->rows +
|
||||
inner_path->parent->rows);
|
||||
|
||||
/* the number of tuple comparisons needed is the number of outer
|
||||
* tuples times the typical hash bucket size, which we estimate
|
||||
@@ -393,8 +417,8 @@ cost_hashjoin(Cost outercost,
|
||||
* count. The cost per comparison is set at _cpu_index_page_weight_;
|
||||
* is that reasonable, or do we need another basic parameter?
|
||||
*/
|
||||
temp += _cpu_index_page_weight_ * outersize *
|
||||
(innersize * innerdisbursion);
|
||||
temp += _cpu_index_page_weight_ * outer_path->parent->rows *
|
||||
(inner_path->parent->rows * innerdisbursion);
|
||||
|
||||
/*
|
||||
* if inner relation is too big then we will need to "batch" the join,
|
||||
@@ -402,8 +426,10 @@ cost_hashjoin(Cost outercost,
|
||||
* extra time. Charge one cost unit per page of I/O.
|
||||
*/
|
||||
if (innerbytes > hashtablebytes)
|
||||
temp += 2 * (page_size(outersize, outerwidth) +
|
||||
page_size(innersize, innerwidth));
|
||||
temp += 2 * (page_size(outer_path->parent->rows,
|
||||
outer_path->parent->width) +
|
||||
page_size(inner_path->parent->rows,
|
||||
inner_path->parent->width));
|
||||
|
||||
/*
|
||||
* Bias against putting larger relation on inside. We don't want
|
||||
@@ -415,76 +441,74 @@ cost_hashjoin(Cost outercost,
|
||||
temp *= 1.1; /* is this an OK fudge factor? */
|
||||
|
||||
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.
|
||||
* set_rel_rows_width
|
||||
* Set the 'rows' and 'width' estimates for the given base relation.
|
||||
*
|
||||
* Sets the 'size' field for each relation entry with this computed size.
|
||||
*
|
||||
* Returns the size.
|
||||
* 'rows' is the estimated number of output tuples (after applying
|
||||
* restriction clauses).
|
||||
* 'width' is the estimated average output tuple width in bytes.
|
||||
*/
|
||||
int
|
||||
compute_rel_size(RelOptInfo *rel)
|
||||
void
|
||||
set_rel_rows_width(Query *root, RelOptInfo *rel)
|
||||
{
|
||||
Cost temp;
|
||||
int temp1;
|
||||
/* Should only be applied to base relations */
|
||||
Assert(length(rel->relids) == 1);
|
||||
|
||||
rel->rows = rel->tuples * restrictlist_selec(root, rel->restrictinfo);
|
||||
Assert(rel->rows >= 0);
|
||||
|
||||
set_rel_width(root, rel);
|
||||
}
|
||||
|
||||
/*
|
||||
* set_joinrel_rows_width
|
||||
* Set the 'rows' and 'width' estimates for the given join relation.
|
||||
*/
|
||||
void
|
||||
set_joinrel_rows_width(Query *root, RelOptInfo *rel,
|
||||
JoinPath *joinpath)
|
||||
{
|
||||
double temp;
|
||||
|
||||
/* cartesian product */
|
||||
temp = joinpath->outerjoinpath->parent->rows *
|
||||
joinpath->innerjoinpath->parent->rows;
|
||||
|
||||
/* apply restrictivity */
|
||||
temp *= restrictlist_selec(root, joinpath->path.parent->restrictinfo);
|
||||
|
||||
temp = rel->tuples * product_selec(rel->restrictinfo);
|
||||
Assert(temp >= 0);
|
||||
if (temp >= (MAXINT - 1))
|
||||
temp1 = MAXINT;
|
||||
else
|
||||
temp1 = ceil((double) temp);
|
||||
Assert(temp1 >= 0);
|
||||
Assert(temp1 <= MAXINT);
|
||||
return temp1;
|
||||
rel->rows = temp;
|
||||
|
||||
set_rel_width(root, rel);
|
||||
}
|
||||
|
||||
/*
|
||||
* compute_rel_width
|
||||
* Computes the width in bytes of a tuple from 'rel'.
|
||||
*
|
||||
* Returns the width of the tuple as a fixnum.
|
||||
* set_rel_width
|
||||
* Set the estimated output width of the relation.
|
||||
*/
|
||||
int
|
||||
compute_rel_width(RelOptInfo *rel)
|
||||
static void
|
||||
set_rel_width(Query *root, RelOptInfo *rel)
|
||||
{
|
||||
return compute_targetlist_width(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.
|
||||
*/
|
||||
static int
|
||||
compute_targetlist_width(List *targetlist)
|
||||
{
|
||||
List *temp_tl;
|
||||
int tuple_width = 0;
|
||||
List *tle;
|
||||
|
||||
foreach(temp_tl, targetlist)
|
||||
{
|
||||
tuple_width += compute_attribute_width(lfirst(temp_tl));
|
||||
}
|
||||
return tuple_width;
|
||||
foreach(tle, rel->targetlist)
|
||||
tuple_width += compute_attribute_width((TargetEntry *) lfirst(tle));
|
||||
Assert(tuple_width >= 0);
|
||||
rel->width = 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.
|
||||
* If a field is variable-length, we make a default assumption. Would be
|
||||
* better if VACUUM recorded some stats about the average field width...
|
||||
*/
|
||||
static int
|
||||
compute_attribute_width(TargetEntry *tlistentry)
|
||||
@@ -497,47 +521,15 @@ compute_attribute_width(TargetEntry *tlistentry)
|
||||
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;
|
||||
|
||||
/* cartesian product */
|
||||
temp *= ((Path *) joinpath->outerjoinpath)->parent->size;
|
||||
temp *= ((Path *) joinpath->innerjoinpath)->parent->size;
|
||||
|
||||
temp = temp * product_selec(joinpath->pathinfo);
|
||||
if (temp >= (MAXINT - 1) / 2)
|
||||
{
|
||||
/* if we exceed (MAXINT-1)/2, we switch to log scale */
|
||||
/* +1 prevents log(0) */
|
||||
temp1 = ceil(log(temp + 1 - (MAXINT - 1) / 2) + (MAXINT - 1) / 2);
|
||||
}
|
||||
else
|
||||
temp1 = ceil((double) temp);
|
||||
Assert(temp1 >= 0);
|
||||
|
||||
return temp1;
|
||||
}
|
||||
|
||||
/*
|
||||
* relation_byte_size
|
||||
* Estimate the storage space in bytes for a given number of tuples
|
||||
* of a given width (size in bytes).
|
||||
* To avoid overflow with big relations, result is a double.
|
||||
*/
|
||||
|
||||
static double
|
||||
relation_byte_size(int tuples, int width)
|
||||
relation_byte_size(double tuples, int width)
|
||||
{
|
||||
return ((double) tuples) * ((double) (width + sizeof(HeapTupleData)));
|
||||
return tuples * ((double) (width + sizeof(HeapTupleData)));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -545,14 +537,10 @@ relation_byte_size(int tuples, int width)
|
||||
* 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)
|
||||
static double
|
||||
page_size(double tuples, int width)
|
||||
{
|
||||
int temp;
|
||||
|
||||
temp = (int) ceil(relation_byte_size(tuples, width) / BLCKSZ);
|
||||
Assert(temp >= 0);
|
||||
return temp;
|
||||
return ceil(relation_byte_size(tuples, width) / BLCKSZ);
|
||||
}
|
||||
|
||||
static double
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.75 1999/12/31 05:38:25 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.76 2000/01/09 00:26:31 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -41,51 +41,55 @@
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
#define is_indexable_operator(clause,opclass,relam,indexkey_on_left) \
|
||||
(indexable_operator(clause,opclass,relam,indexkey_on_left) != InvalidOid)
|
||||
|
||||
typedef enum {
|
||||
Prefix_None, Prefix_Partial, Prefix_Exact
|
||||
} Prefix_Status;
|
||||
|
||||
static void match_index_orclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
static void match_index_orclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int indexkey, Oid opclass,
|
||||
List *restrictinfo_list);
|
||||
static List *match_index_orclause(RelOptInfo *rel, RelOptInfo *index,
|
||||
static List *match_index_orclause(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int indexkey, Oid opclass,
|
||||
List *or_clauses,
|
||||
List *other_matching_indices);
|
||||
static bool match_or_subclause_to_indexkey(RelOptInfo *rel, RelOptInfo *index,
|
||||
static bool match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
int indexkey, Oid opclass,
|
||||
Expr *clause);
|
||||
static List *group_clauses_by_indexkey(RelOptInfo *rel, RelOptInfo *index,
|
||||
static List *group_clauses_by_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int *indexkeys, Oid *classes,
|
||||
List *restrictinfo_list);
|
||||
static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel, RelOptInfo *index,
|
||||
static List *group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
IndexOptInfo *index,
|
||||
int *indexkeys, Oid *classes,
|
||||
List *join_cinfo_list,
|
||||
List *restr_cinfo_list);
|
||||
static bool match_clause_to_indexkey(RelOptInfo *rel, RelOptInfo *index,
|
||||
static bool match_clause_to_indexkey(RelOptInfo *rel, IndexOptInfo *index,
|
||||
int indexkey, Oid opclass,
|
||||
Expr *clause, bool join);
|
||||
static bool indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
bool indexkey_on_left);
|
||||
static bool pred_test(List *predicate_list, List *restrictinfo_list,
|
||||
List *joininfo_list);
|
||||
static bool one_pred_test(Expr *predicate, List *restrictinfo_list);
|
||||
static bool one_pred_clause_expr_test(Expr *predicate, Node *clause);
|
||||
static bool one_pred_clause_test(Expr *predicate, Node *clause);
|
||||
static bool clause_pred_clause_test(Expr *predicate, Node *clause);
|
||||
static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
static void indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *joininfo_list, List *restrictinfo_list,
|
||||
List **clausegroups, List **outerrelids);
|
||||
static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *clausegroup_list, List *outerrelids_list);
|
||||
static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index,
|
||||
static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *joininfo_list);
|
||||
static bool useful_for_ordering(Query *root, RelOptInfo *rel,
|
||||
RelOptInfo *index);
|
||||
IndexOptInfo *index);
|
||||
static bool match_index_to_operand(int indexkey, Var *operand,
|
||||
RelOptInfo *rel, RelOptInfo *index);
|
||||
RelOptInfo *rel, IndexOptInfo *index);
|
||||
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
|
||||
RelOptInfo *index);
|
||||
IndexOptInfo *index);
|
||||
static bool match_special_index_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
bool indexkey_on_left);
|
||||
static Prefix_Status like_fixed_prefix(char *patt, char **prefix);
|
||||
@@ -145,7 +149,7 @@ create_index_paths(Query *root,
|
||||
|
||||
foreach(ilist, indices)
|
||||
{
|
||||
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
||||
List *restrictclauses;
|
||||
List *joinclausegroups;
|
||||
List *joinouterrelids;
|
||||
@@ -268,7 +272,7 @@ create_index_paths(Query *root,
|
||||
*/
|
||||
static void
|
||||
match_index_orclauses(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int indexkey,
|
||||
Oid opclass,
|
||||
List *restrictinfo_list)
|
||||
@@ -317,7 +321,7 @@ match_index_orclauses(RelOptInfo *rel,
|
||||
*/
|
||||
static List *
|
||||
match_index_orclause(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int indexkey,
|
||||
Oid opclass,
|
||||
List *or_clauses,
|
||||
@@ -368,7 +372,7 @@ match_index_orclause(RelOptInfo *rel,
|
||||
*/
|
||||
static bool
|
||||
match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int indexkey,
|
||||
Oid opclass,
|
||||
Expr *clause)
|
||||
@@ -435,7 +439,7 @@ match_or_subclause_to_indexkey(RelOptInfo *rel,
|
||||
*/
|
||||
static List *
|
||||
group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int *indexkeys,
|
||||
Oid *classes,
|
||||
List *restrictinfo_list)
|
||||
@@ -497,7 +501,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
|
||||
*/
|
||||
static List *
|
||||
group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int *indexkeys,
|
||||
Oid *classes,
|
||||
List *join_cinfo_list,
|
||||
@@ -614,7 +618,7 @@ group_clauses_by_ikey_for_joins(RelOptInfo *rel,
|
||||
*/
|
||||
static bool
|
||||
match_clause_to_indexkey(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
int indexkey,
|
||||
Oid opclass,
|
||||
Expr *clause,
|
||||
@@ -642,7 +646,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
if ((IsA(rightop, Const) || IsA(rightop, Param)) &&
|
||||
match_index_to_operand(indexkey, leftop, rel, index))
|
||||
{
|
||||
if (indexable_operator(clause, opclass, index->relam, true))
|
||||
if (is_indexable_operator(clause, opclass, index->relam, true))
|
||||
return true;
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass,
|
||||
@@ -656,7 +660,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
if ((IsA(leftop, Const) || IsA(leftop, Param)) &&
|
||||
match_index_to_operand(indexkey, rightop, rel, index))
|
||||
{
|
||||
if (indexable_operator(clause, opclass, index->relam, false))
|
||||
if (is_indexable_operator(clause, opclass, index->relam, false))
|
||||
return true;
|
||||
/*
|
||||
* If we didn't find a member of the index's opclass,
|
||||
@@ -683,7 +687,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
|
||||
freeList(othervarnos);
|
||||
if (isIndexable &&
|
||||
indexable_operator(clause, opclass, index->relam, true))
|
||||
is_indexable_operator(clause, opclass, index->relam, true))
|
||||
return true;
|
||||
}
|
||||
else if (match_index_to_operand(indexkey, rightop, rel, index))
|
||||
@@ -694,7 +698,7 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
isIndexable = ! intMember(lfirsti(rel->relids), othervarnos);
|
||||
freeList(othervarnos);
|
||||
if (isIndexable &&
|
||||
indexable_operator(clause, opclass, index->relam, false))
|
||||
is_indexable_operator(clause, opclass, index->relam, false))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -707,9 +711,9 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
* Does a binary opclause contain an operator matching the index's
|
||||
* access method?
|
||||
*
|
||||
* If the indexkey is on the right, what we actually want to know
|
||||
* is whether the operator has a commutator operator that matches
|
||||
* the index's access method.
|
||||
* If the indexkey is on the right, what we actually want to know
|
||||
* is whether the operator has a commutator operator that matches
|
||||
* the index's access method.
|
||||
*
|
||||
* We try both the straightforward match and matches that rely on
|
||||
* recognizing binary-compatible datatypes. For example, if we have
|
||||
@@ -717,12 +721,13 @@ match_clause_to_indexkey(RelOptInfo *rel,
|
||||
* which we need to replace with oideq in order to recognize it as
|
||||
* matching an oid_ops index on the oid field.
|
||||
*
|
||||
* NOTE: if a binary-compatible match is made, we destructively modify
|
||||
* the given clause to use the binary-compatible substitute operator!
|
||||
* This should be safe even if we don't end up using the index, but it's
|
||||
* a tad ugly...
|
||||
* Returns the OID of the matching operator, or InvalidOid if no match.
|
||||
* Note that the returned OID will be different from the one in the given
|
||||
* expression if we used a binary-compatible substitution. Also note that
|
||||
* if indexkey_on_left is FALSE (meaning we need to commute), the returned
|
||||
* OID is *not* commuted; it can be plugged directly into the given clause.
|
||||
*/
|
||||
static bool
|
||||
Oid
|
||||
indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
bool indexkey_on_left)
|
||||
{
|
||||
@@ -737,11 +742,11 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
else
|
||||
commuted_op = get_commutator(expr_op);
|
||||
if (commuted_op == InvalidOid)
|
||||
return false;
|
||||
return InvalidOid;
|
||||
|
||||
/* Done if the (commuted) operator is a member of the index's AM */
|
||||
if (op_class(commuted_op, opclass, relam))
|
||||
return true;
|
||||
return expr_op;
|
||||
|
||||
/*
|
||||
* Maybe the index uses a binary-compatible operator set.
|
||||
@@ -758,7 +763,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
Operator newop;
|
||||
|
||||
if (opname == NULL)
|
||||
return false; /* probably shouldn't happen */
|
||||
return InvalidOid; /* probably shouldn't happen */
|
||||
|
||||
/* Use the datatype of the index key */
|
||||
if (indexkey_on_left)
|
||||
@@ -781,22 +786,15 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
else
|
||||
commuted_op = get_commutator(new_expr_op);
|
||||
if (commuted_op == InvalidOid)
|
||||
return false;
|
||||
return InvalidOid;
|
||||
|
||||
if (op_class(commuted_op, opclass, relam))
|
||||
{
|
||||
/*
|
||||
* Success! Change the opclause to use the
|
||||
* binary-compatible operator.
|
||||
*/
|
||||
((Oper *) clause->oper)->opno = new_expr_op;
|
||||
return true;
|
||||
}
|
||||
return new_expr_op;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return InvalidOid;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -816,7 +814,7 @@ indexable_operator(Expr *clause, Oid opclass, Oid relam,
|
||||
*/
|
||||
static bool
|
||||
useful_for_mergejoin(RelOptInfo *rel,
|
||||
RelOptInfo *index,
|
||||
IndexOptInfo *index,
|
||||
List *joininfo_list)
|
||||
{
|
||||
int *indexkeys = index->indexkeys;
|
||||
@@ -867,7 +865,7 @@ useful_for_mergejoin(RelOptInfo *rel,
|
||||
static bool
|
||||
useful_for_ordering(Query *root,
|
||||
RelOptInfo *rel,
|
||||
RelOptInfo *index)
|
||||
IndexOptInfo *index)
|
||||
{
|
||||
List *index_pathkeys;
|
||||
|
||||
@@ -1335,7 +1333,7 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
|
||||
* '*outerrelids' receives a list of relid lists
|
||||
*/
|
||||
static void
|
||||
indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
indexable_joinclauses(RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *joininfo_list, List *restrictinfo_list,
|
||||
List **clausegroups, List **outerrelids)
|
||||
{
|
||||
@@ -1384,7 +1382,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
|
||||
* Returns a list of index pathnodes.
|
||||
*/
|
||||
static List *
|
||||
index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
|
||||
List *clausegroup_list, List *outerrelids_list)
|
||||
{
|
||||
List *path_list = NIL;
|
||||
@@ -1395,16 +1393,16 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
List *clausegroup = lfirst(i);
|
||||
IndexPath *pathnode = makeNode(IndexPath);
|
||||
List *indexquals;
|
||||
float npages;
|
||||
float selec;
|
||||
long npages;
|
||||
Selectivity selec;
|
||||
|
||||
indexquals = get_actual_clauses(clausegroup);
|
||||
/* expand special operators to indexquals the executor can handle */
|
||||
indexquals = expand_indexqual_conditions(indexquals);
|
||||
|
||||
index_selectivity(root,
|
||||
lfirsti(rel->relids),
|
||||
lfirsti(index->relids),
|
||||
rel,
|
||||
index,
|
||||
indexquals,
|
||||
&npages,
|
||||
&selec);
|
||||
@@ -1419,20 +1417,14 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
|
||||
* therefore, both indexid and indexqual should be single-element
|
||||
* lists.
|
||||
*/
|
||||
Assert(length(index->relids) == 1);
|
||||
pathnode->indexid = index->relids;
|
||||
pathnode->indexid = lconsi(index->indexoid, NIL);
|
||||
pathnode->indexqual = lcons(indexquals, NIL);
|
||||
|
||||
/* joinrelids saves the rels needed on the outer side of the join */
|
||||
pathnode->joinrelids = lfirst(outerrelids_list);
|
||||
|
||||
pathnode->path.path_cost = cost_index((Oid) lfirsti(index->relids),
|
||||
(int) npages,
|
||||
selec,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
pathnode->path.path_cost = cost_index(rel, index,
|
||||
npages, selec,
|
||||
true);
|
||||
|
||||
path_list = lappend(path_list, pathnode);
|
||||
@@ -1455,7 +1447,7 @@ static bool
|
||||
match_index_to_operand(int indexkey,
|
||||
Var *operand,
|
||||
RelOptInfo *rel,
|
||||
RelOptInfo *index)
|
||||
IndexOptInfo *index)
|
||||
{
|
||||
if (index->indproc == InvalidOid)
|
||||
{
|
||||
@@ -1477,7 +1469,7 @@ match_index_to_operand(int indexkey,
|
||||
}
|
||||
|
||||
static bool
|
||||
function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
|
||||
function_index_operand(Expr *funcOpnd, RelOptInfo *rel, IndexOptInfo *index)
|
||||
{
|
||||
int relvarno = lfirsti(rel->relids);
|
||||
Func *function;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.46 1999/08/21 03:49:00 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.47 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -40,7 +40,7 @@ static List *match_unsorted_inner(RelOptInfo *joinrel, RelOptInfo *outerrel,
|
||||
List *mergeclause_list);
|
||||
static List *hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
|
||||
RelOptInfo *outerrel, RelOptInfo *innerrel);
|
||||
static Cost estimate_disbursion(Query *root, Var *var);
|
||||
static Selectivity estimate_disbursion(Query *root, Var *var);
|
||||
static List *select_mergejoin_clauses(List *restrictinfo_list);
|
||||
|
||||
/*
|
||||
@@ -258,12 +258,8 @@ sort_inner_and_outer(RelOptInfo *joinrel,
|
||||
curclause_list);
|
||||
/* And now we can make the path. */
|
||||
path_node = create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path *) outerrel->cheapestpath,
|
||||
(Path *) innerrel->cheapestpath,
|
||||
outerrel->cheapestpath,
|
||||
innerrel->cheapestpath,
|
||||
merge_pathkeys,
|
||||
get_actual_clauses(curclause_list),
|
||||
outerkeys,
|
||||
@@ -359,7 +355,6 @@ match_unsorted_outer(RelOptInfo *joinrel,
|
||||
/* Always consider a nestloop join with this outer and best inner. */
|
||||
path_list = lappend(path_list,
|
||||
create_nestloop_path(joinrel,
|
||||
outerrel,
|
||||
outerpath,
|
||||
nestinnerpath,
|
||||
merge_pathkeys));
|
||||
@@ -393,7 +388,7 @@ match_unsorted_outer(RelOptInfo *joinrel,
|
||||
int clausecount;
|
||||
|
||||
cheapest_cost = cheapest_inner->path_cost +
|
||||
cost_sort(innersortkeys, innerrel->size, innerrel->width);
|
||||
cost_sort(innersortkeys, innerrel->rows, innerrel->width);
|
||||
|
||||
for (clausecount = mergeclausecount;
|
||||
clausecount > 0;
|
||||
@@ -427,10 +422,6 @@ match_unsorted_outer(RelOptInfo *joinrel,
|
||||
get_actual_clauses(mergeclauses));
|
||||
path_list = lappend(path_list,
|
||||
create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
outerpath,
|
||||
mergeinnerpath,
|
||||
merge_pathkeys,
|
||||
@@ -496,7 +487,7 @@ match_unsorted_inner(RelOptInfo *joinrel,
|
||||
if (mergeouterpath != NULL &&
|
||||
mergeouterpath->path_cost <=
|
||||
(outerrel->cheapestpath->path_cost +
|
||||
cost_sort(outersortkeys, outerrel->size, outerrel->width)))
|
||||
cost_sort(outersortkeys, outerrel->rows, outerrel->width)))
|
||||
{
|
||||
/* Use mergeouterpath */
|
||||
outersortkeys = NIL; /* no explicit sort step */
|
||||
@@ -516,10 +507,6 @@ match_unsorted_inner(RelOptInfo *joinrel,
|
||||
mergeclauses = get_actual_clauses(mergeclauses);
|
||||
path_list = lappend(path_list,
|
||||
create_mergejoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
mergeouterpath,
|
||||
innerpath,
|
||||
merge_pathkeys,
|
||||
@@ -563,7 +550,7 @@ hash_inner_and_outer(Query *root,
|
||||
Var *leftop = get_leftop(clause);
|
||||
Var *rightop = get_rightop(clause);
|
||||
Var *innerop;
|
||||
Cost innerdisbursion;
|
||||
Selectivity innerdisbursion;
|
||||
HashPath *hash_path;
|
||||
|
||||
/* find the inner var and estimate its disbursion */
|
||||
@@ -574,12 +561,8 @@ hash_inner_and_outer(Query *root,
|
||||
innerdisbursion = estimate_disbursion(root, innerop);
|
||||
|
||||
hash_path = create_hashjoin_path(joinrel,
|
||||
outerrel->size,
|
||||
innerrel->size,
|
||||
outerrel->width,
|
||||
innerrel->width,
|
||||
(Path *) outerrel->cheapestpath,
|
||||
(Path *) innerrel->cheapestpath,
|
||||
outerrel->cheapestpath,
|
||||
innerrel->cheapestpath,
|
||||
lcons(clause, NIL),
|
||||
innerdisbursion);
|
||||
hpath_list = lappend(hpath_list, hash_path);
|
||||
@@ -598,7 +581,7 @@ hash_inner_and_outer(Query *root,
|
||||
* we know that the inner rel is well-dispersed (or the alternatives
|
||||
* seem much worse).
|
||||
*/
|
||||
static Cost
|
||||
static Selectivity
|
||||
estimate_disbursion(Query *root, Var *var)
|
||||
{
|
||||
Oid relid;
|
||||
@@ -608,7 +591,7 @@ estimate_disbursion(Query *root, Var *var)
|
||||
|
||||
relid = getrelid(var->varno, root->rtable);
|
||||
|
||||
return (Cost) get_attdisbursion(relid, var->varattno, 0.1);
|
||||
return (Selectivity) get_attdisbursion(relid, var->varattno, 0.1);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.39 1999/08/16 02:17:51 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.40 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -35,8 +35,6 @@ static List *new_join_tlist(List *tlist, int first_resdomno);
|
||||
static void build_joinrel_restrict_and_join(RelOptInfo *joinrel,
|
||||
List *joininfo_list,
|
||||
Relids join_relids);
|
||||
static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel);
|
||||
|
||||
/*
|
||||
* make_rels_by_joins
|
||||
@@ -207,19 +205,15 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel)
|
||||
* The list will be flattened out in update_rels_pathlist_for_joins().
|
||||
*/
|
||||
joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL));
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->size = 0;
|
||||
joinrel->rows = 0;
|
||||
joinrel->width = 0;
|
||||
/* joinrel->targetlist = NIL;*/
|
||||
joinrel->targetlist = NIL;
|
||||
joinrel->pathlist = NIL;
|
||||
joinrel->cheapestpath = (Path *) NULL;
|
||||
joinrel->pruneable = true;
|
||||
joinrel->classlist = NULL;
|
||||
joinrel->indexkeys = NULL;
|
||||
joinrel->ordering = NULL;
|
||||
joinrel->relam = InvalidOid;
|
||||
joinrel->indexed = false;
|
||||
joinrel->pages = 0;
|
||||
joinrel->tuples = 0;
|
||||
joinrel->restrictinfo = NIL;
|
||||
joinrel->joininfo = NIL;
|
||||
joinrel->innerjoin = NIL;
|
||||
@@ -236,21 +230,22 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel)
|
||||
|
||||
/*
|
||||
* Construct restrict and join clause lists for the new joinrel.
|
||||
*
|
||||
* nconc(listCopy(x), y) is an idiom for making a new list without
|
||||
* changing either input list.
|
||||
*/
|
||||
build_joinrel_restrict_and_join(joinrel,
|
||||
nconc(copyObject(outer_rel->joininfo),
|
||||
copyObject(inner_rel->joininfo)),
|
||||
nconc(listCopy(outer_rel->joininfo),
|
||||
inner_rel->joininfo),
|
||||
nconc(listCopy(outer_rel->relids),
|
||||
listCopy(inner_rel->relids)));
|
||||
|
||||
set_joinrel_size(joinrel, outer_rel, inner_rel);
|
||||
inner_rel->relids));
|
||||
|
||||
return joinrel;
|
||||
}
|
||||
|
||||
/*
|
||||
* new_join_tlist
|
||||
* Builds a join relations's target list by keeping those elements that
|
||||
* Builds a join relation'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
|
||||
@@ -311,18 +306,16 @@ new_join_tlist(List *tlist,
|
||||
* 'joininfo_list' is a list of joininfo nodes from the relations being joined
|
||||
* 'join_relids' is a list of all base relids in the new join relation
|
||||
*
|
||||
* NB: the elements of joininfo_list have all been COPIED and so can safely
|
||||
* be destructively modified and/or inserted in the new joinrel's lists.
|
||||
* The amount of copying going on here is probably vastly excessive,
|
||||
* since we copied the underlying clauses as well...
|
||||
* NB: Formerly, we made deep(!) copies of each input RestrictInfo to pass
|
||||
* up to the join relation. I believe this is no longer necessary, because
|
||||
* RestrictInfo nodes are no longer context-dependent. Instead, just add
|
||||
* the original nodes to the lists belonging to the join relation.
|
||||
*/
|
||||
static void
|
||||
build_joinrel_restrict_and_join(RelOptInfo *joinrel,
|
||||
List *joininfo_list,
|
||||
Relids join_relids)
|
||||
{
|
||||
List *output_restrictinfo_list = NIL;
|
||||
List *output_joininfo_list = NIL;
|
||||
List *xjoininfo;
|
||||
|
||||
foreach(xjoininfo, joininfo_list)
|
||||
@@ -341,38 +334,25 @@ build_joinrel_restrict_and_join(RelOptInfo *joinrel,
|
||||
* Be careful to eliminate duplicates, since we will see the
|
||||
* same clauses arriving from both input relations...
|
||||
*/
|
||||
output_restrictinfo_list =
|
||||
LispUnion(output_restrictinfo_list,
|
||||
joinrel->restrictinfo =
|
||||
LispUnion(joinrel->restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
JoinInfo *old_joininfo;
|
||||
|
||||
/*
|
||||
* There might already be a JoinInfo with the same set of
|
||||
* unjoined relids in output_joininfo_list; don't make a
|
||||
* redundant entry.
|
||||
* These clauses are still join clauses at this level,
|
||||
* so find or make the appropriate JoinInfo item for the joinrel,
|
||||
* and add the clauses to it (eliminating duplicates).
|
||||
*/
|
||||
old_joininfo = joininfo_member(new_unjoined_relids,
|
||||
output_joininfo_list);
|
||||
if (old_joininfo)
|
||||
{
|
||||
old_joininfo->jinfo_restrictinfo =
|
||||
LispUnion(old_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
joininfo->unjoined_relids = new_unjoined_relids;
|
||||
output_joininfo_list = lcons(joininfo,
|
||||
output_joininfo_list);
|
||||
}
|
||||
JoinInfo *new_joininfo;
|
||||
|
||||
new_joininfo = find_joininfo_node(joinrel, new_unjoined_relids);
|
||||
new_joininfo->jinfo_restrictinfo =
|
||||
LispUnion(new_joininfo->jinfo_restrictinfo,
|
||||
joininfo->jinfo_restrictinfo);
|
||||
}
|
||||
}
|
||||
|
||||
joinrel->restrictinfo = output_restrictinfo_list;
|
||||
joinrel->joininfo = output_joininfo_list;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -424,36 +404,6 @@ get_cheapest_complete_rel(List *join_rel_list)
|
||||
return final_rel;
|
||||
}
|
||||
|
||||
static void
|
||||
set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
|
||||
RelOptInfo *inner_rel)
|
||||
{
|
||||
double dtuples;
|
||||
int ntuples;
|
||||
|
||||
/* avoid overflow ... probably, tuple estimates in RelOptInfo
|
||||
* just ought to be double ...
|
||||
*/
|
||||
dtuples = (double) outer_rel->tuples * (double) inner_rel->tuples;
|
||||
|
||||
if (joinrel->restrictinfo != NULL)
|
||||
dtuples *= product_selec(joinrel->restrictinfo);
|
||||
|
||||
if (dtuples >= MAXINT) /* avoid overflow */
|
||||
ntuples = MAXINT;
|
||||
else
|
||||
ntuples = (int) dtuples;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* Subset-inclusion tests on integer lists.
|
||||
*
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.32 1999/08/16 02:17:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.33 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -30,11 +30,11 @@ static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
|
||||
List *subclauses, List *indices,
|
||||
List **indexquals,
|
||||
List **indexids,
|
||||
Cost *cost, Cost *selec);
|
||||
Cost *cost);
|
||||
static void best_or_subclause_index(Query *root, RelOptInfo *rel,
|
||||
List *indexqual, List *indices,
|
||||
int *retIndexid,
|
||||
Cost *retCost, Cost *retSelec);
|
||||
Oid *retIndexid,
|
||||
Cost *retCost);
|
||||
|
||||
|
||||
/*
|
||||
@@ -89,7 +89,6 @@ create_or_index_paths(Query *root,
|
||||
List *indexquals;
|
||||
List *indexids;
|
||||
Cost cost;
|
||||
Cost selec;
|
||||
|
||||
best_or_subclause_indices(root,
|
||||
rel,
|
||||
@@ -97,8 +96,7 @@ create_or_index_paths(Query *root,
|
||||
clausenode->subclauseindices,
|
||||
&indexquals,
|
||||
&indexids,
|
||||
&cost,
|
||||
&selec);
|
||||
&cost);
|
||||
|
||||
pathnode->path.pathtype = T_IndexScan;
|
||||
pathnode->path.parent = rel;
|
||||
@@ -114,7 +112,6 @@ create_or_index_paths(Query *root,
|
||||
pathnode->indexqual = indexquals;
|
||||
pathnode->joinrelids = NIL; /* no join clauses here */
|
||||
pathnode->path.path_cost = cost;
|
||||
clausenode->selectivity = (Cost) selec;
|
||||
|
||||
path_list = lappend(path_list, pathnode);
|
||||
}
|
||||
@@ -141,13 +138,12 @@ create_or_index_paths(Query *root,
|
||||
*
|
||||
* 'rel' is the node of the relation on which the indexes are defined
|
||||
* 'subclauses' are the subclauses of the 'or' clause
|
||||
* 'indices' is a list of sublists of the index nodes that matched each
|
||||
* subclause of the 'or' clause
|
||||
* 'indices' is a list of sublists of the IndexOptInfo nodes that matched
|
||||
* each subclause of the 'or' clause
|
||||
* '*indexquals' gets the constructed indexquals for the path (a list
|
||||
* of sublists of clauses, one sublist per scan of the base rel)
|
||||
* '*indexids' gets a list of the index IDs for each scan of the rel
|
||||
* '*indexids' gets a list of the index OIDs for each scan of the rel
|
||||
* '*cost' gets the total cost of the path
|
||||
* '*selec' gets the total selectivity of the path.
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_indices(Query *root,
|
||||
@@ -156,23 +152,20 @@ best_or_subclause_indices(Query *root,
|
||||
List *indices,
|
||||
List **indexquals, /* return value */
|
||||
List **indexids, /* return value */
|
||||
Cost *cost, /* return value */
|
||||
Cost *selec) /* return value */
|
||||
Cost *cost) /* return value */
|
||||
{
|
||||
List *slist;
|
||||
|
||||
*indexquals = NIL;
|
||||
*indexids = NIL;
|
||||
*cost = (Cost) 0.0;
|
||||
*selec = (Cost) 0.0;
|
||||
|
||||
foreach(slist, subclauses)
|
||||
{
|
||||
Expr *subclause = lfirst(slist);
|
||||
List *indexqual;
|
||||
int best_indexid;
|
||||
Oid best_indexid;
|
||||
Cost best_cost;
|
||||
Cost best_selec;
|
||||
|
||||
/* Convert this 'or' subclause to an indexqual list */
|
||||
indexqual = make_ands_implicit(subclause);
|
||||
@@ -180,18 +173,13 @@ best_or_subclause_indices(Query *root,
|
||||
indexqual = expand_indexqual_conditions(indexqual);
|
||||
|
||||
best_or_subclause_index(root, rel, indexqual, lfirst(indices),
|
||||
&best_indexid, &best_cost, &best_selec);
|
||||
&best_indexid, &best_cost);
|
||||
|
||||
Assert(best_indexid != InvalidOid);
|
||||
|
||||
*indexquals = lappend(*indexquals, indexqual);
|
||||
*indexids = lappendi(*indexids, best_indexid);
|
||||
*cost += best_cost;
|
||||
/* We approximate the selectivity as the sum of the clause
|
||||
* selectivities (but not more than 1).
|
||||
* XXX This is too pessimistic, isn't it?
|
||||
*/
|
||||
*selec += best_selec;
|
||||
if (*selec > (Cost) 1.0)
|
||||
*selec = (Cost) 1.0;
|
||||
|
||||
indices = lnext(indices);
|
||||
}
|
||||
@@ -205,59 +193,50 @@ best_or_subclause_indices(Query *root,
|
||||
*
|
||||
* 'rel' is the node of the relation on which the index is defined
|
||||
* 'indexqual' is the indexqual list derived from the subclause
|
||||
* 'indices' is a list of index nodes that match the subclause
|
||||
* '*retIndexid' gets the ID of the best index
|
||||
* 'indices' is a list of IndexOptInfo nodes that match the subclause
|
||||
* '*retIndexid' gets the OID of the best index
|
||||
* '*retCost' gets the cost of a scan with that index
|
||||
* '*retSelec' gets the selectivity of that scan
|
||||
*/
|
||||
static void
|
||||
best_or_subclause_index(Query *root,
|
||||
RelOptInfo *rel,
|
||||
List *indexqual,
|
||||
List *indices,
|
||||
int *retIndexid, /* return value */
|
||||
Cost *retCost, /* return value */
|
||||
Cost *retSelec) /* return value */
|
||||
Oid *retIndexid, /* return value */
|
||||
Cost *retCost) /* return value */
|
||||
{
|
||||
bool first_run = true;
|
||||
List *ilist;
|
||||
|
||||
/* if we don't match anything, return zeros */
|
||||
*retIndexid = 0;
|
||||
*retCost = (Cost) 0.0;
|
||||
*retSelec = (Cost) 0.0;
|
||||
*retIndexid = InvalidOid;
|
||||
*retCost = 0.0;
|
||||
|
||||
foreach(ilist, indices)
|
||||
{
|
||||
RelOptInfo *index = (RelOptInfo *) lfirst(ilist);
|
||||
Oid indexid = (Oid) lfirsti(index->relids);
|
||||
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
|
||||
long npages;
|
||||
Selectivity selec;
|
||||
Cost subcost;
|
||||
float npages;
|
||||
float selec;
|
||||
|
||||
Assert(IsA(index, IndexOptInfo));
|
||||
|
||||
index_selectivity(root,
|
||||
lfirsti(rel->relids),
|
||||
indexid,
|
||||
rel,
|
||||
index,
|
||||
indexqual,
|
||||
&npages,
|
||||
&selec);
|
||||
|
||||
subcost = cost_index(indexid,
|
||||
(int) npages,
|
||||
(Cost) selec,
|
||||
rel->pages,
|
||||
rel->tuples,
|
||||
index->pages,
|
||||
index->tuples,
|
||||
subcost = cost_index(rel, index,
|
||||
npages, selec,
|
||||
false);
|
||||
|
||||
if (first_run || subcost < *retCost)
|
||||
{
|
||||
*retIndexid = indexid;
|
||||
*retIndexid = index->indexoid;
|
||||
*retCost = subcost;
|
||||
*retSelec = selec;
|
||||
first_run = false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.16 1999/08/22 20:14:42 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.17 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -261,7 +261,7 @@ get_cheapest_path_for_pathkeys(List *paths, List *pathkeys,
|
||||
* ordering of that key is not interesting.
|
||||
*/
|
||||
List *
|
||||
build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
|
||||
build_index_pathkeys(Query *root, RelOptInfo *rel, IndexOptInfo *index)
|
||||
{
|
||||
List *retval = NIL;
|
||||
int *indexkeys = index->indexkeys;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.43 1999/08/16 02:17:52 tgl Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.44 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -90,7 +90,7 @@ merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels)
|
||||
* relations), set pointers to the cheapest path and compute rel size.
|
||||
*/
|
||||
void
|
||||
rels_set_cheapest(List *rel_list)
|
||||
rels_set_cheapest(Query *root, List *rel_list)
|
||||
{
|
||||
List *x;
|
||||
|
||||
@@ -101,7 +101,7 @@ rels_set_cheapest(List *rel_list)
|
||||
|
||||
cheapest = (JoinPath *) set_cheapest(rel, rel->pathlist);
|
||||
if (IsA_JoinPath(cheapest))
|
||||
rel->size = compute_joinrel_size(cheapest);
|
||||
set_joinrel_rows_width(root, rel, cheapest);
|
||||
else
|
||||
elog(ERROR, "rels_set_cheapest: non JoinPath found");
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.1 1999/11/23 20:06:55 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.2 2000/01/09 00:26:33 tgl Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@@ -241,30 +241,27 @@ List *TidqualFromRestrictinfo(List *relids, List * restrictinfo)
|
||||
List *
|
||||
create_tidscan_joinpaths(RelOptInfo *rel)
|
||||
{
|
||||
List *rlst = NIL, *lst;
|
||||
TidPath *pathnode = (TidPath *)0;
|
||||
List *restinfo, *tideval;
|
||||
List *rlst = NIL,
|
||||
*lst;
|
||||
TidPath *pathnode = (TidPath *) NULL;
|
||||
List *restinfo,
|
||||
*tideval;
|
||||
|
||||
foreach (lst, rel->joininfo)
|
||||
{
|
||||
JoinInfo *joininfo = (JoinInfo *)lfirst(lst);
|
||||
JoinInfo *joininfo = (JoinInfo *)lfirst(lst);
|
||||
|
||||
restinfo = joininfo->jinfo_restrictinfo;
|
||||
tideval = TidqualFromRestrictinfo(rel->relids, restinfo);
|
||||
if (tideval && length(tideval) == 1)
|
||||
if (length(tideval) == 1)
|
||||
{
|
||||
pathnode = makeNode(TidPath);
|
||||
|
||||
pathnode->path.pathtype = T_TidScan;
|
||||
pathnode->path.parent = rel;
|
||||
pathnode->path.path_cost = 0.0;
|
||||
pathnode->path.pathkeys = NIL;
|
||||
|
||||
pathnode->path.path_cost = cost_tidscan(tideval);
|
||||
pathnode->path.path_cost = cost_tidscan(rel, tideval);
|
||||
pathnode->tideval = tideval;
|
||||
/*
|
||||
pathnode->tideval = copyObject(tideval);
|
||||
freeList(tideval);
|
||||
*/
|
||||
pathnode->unjoined_relids = joininfo->unjoined_relids;
|
||||
rlst = lappend(rlst, pathnode);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user