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:
21
src/backend/optimizer/path/Makefile.inc
Normal file
21
src/backend/optimizer/path/Makefile.inc
Normal 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
|
||||
|
||||
|
||||
|
||||
351
src/backend/optimizer/path/allpaths.c
Normal file
351
src/backend/optimizer/path/allpaths.c
Normal 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 */
|
||||
331
src/backend/optimizer/path/clausesel.c
Normal file
331
src/backend/optimizer/path/clausesel.c
Normal 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);
|
||||
}
|
||||
}
|
||||
|
||||
456
src/backend/optimizer/path/costsize.c
Normal file
456
src/backend/optimizer/path/costsize.c
Normal 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));
|
||||
}
|
||||
120
src/backend/optimizer/path/hashutils.c
Normal file
120
src/backend/optimizer/path/hashutils.c
Normal 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);
|
||||
}
|
||||
1206
src/backend/optimizer/path/indxpath.c
Normal file
1206
src/backend/optimizer/path/indxpath.c
Normal file
File diff suppressed because it is too large
Load Diff
623
src/backend/optimizer/path/joinpath.c
Normal file
623
src/backend/optimizer/path/joinpath.c
Normal 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);
|
||||
}
|
||||
|
||||
528
src/backend/optimizer/path/joinrels.c
Normal file
528
src/backend/optimizer/path/joinrels.c
Normal 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;
|
||||
}
|
||||
432
src/backend/optimizer/path/joinutils.c
Normal file
432
src/backend/optimizer/path/joinutils.c
Normal 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);
|
||||
}
|
||||
122
src/backend/optimizer/path/mergeutils.c
Normal file
122
src/backend/optimizer/path/mergeutils.c
Normal 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);
|
||||
}
|
||||
271
src/backend/optimizer/path/orindxpath.c
Normal file
271
src/backend/optimizer/path/orindxpath.c
Normal 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;
|
||||
}
|
||||
773
src/backend/optimizer/path/predmig.c
Normal file
773
src/backend/optimizer/path/predmig.c
Normal 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);
|
||||
}
|
||||
203
src/backend/optimizer/path/prune.c
Normal file
203
src/backend/optimizer/path/prune.c
Normal 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)));
|
||||
}
|
||||
1360
src/backend/optimizer/path/xfunc.c
Normal file
1360
src/backend/optimizer/path/xfunc.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user