Commit b1577a7c authored by Tom Lane's avatar Tom Lane

New cost model for planning, incorporating a penalty for random page

accesses versus sequential accesses, a (very crude) estimate of the
effects of caching on random page accesses, and cost to evaluate WHERE-
clause expressions.  Export critical parameters for this model as SET
variables.  Also, create SET variables for the planner's enable flags
(enable_seqscan, enable_indexscan, etc) so that these can be controlled
more conveniently than via PGOPTIONS.

Planner now estimates both startup cost (cost before retrieving
first tuple) and total cost of each path, so it can optimize queries
with LIMIT on a reasonable basis by interpolating between these costs.
Same facility is a win for EXISTS(...) subqueries and some other cases.

Redesign pathkey representation to achieve a major speedup in planning
(I saw as much as 5X on a 10-way join); also minor changes in planner
to reduce memory consumption by recycling discarded Path nodes and
not constructing unnecessary lists.

Minor cleanups to display more-plausible costs in some cases in
EXPLAIN output.

Initdb forced by change in interface to index cost estimation
functions.
parent 553b5da6
...@@ -164,24 +164,6 @@ ...@@ -164,24 +164,6 @@
sets the default mode for the genetic optimizer. sets the default mode for the genetic optimizer.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<envar>PGRPLANS</envar>
sets the default mode to allow or disable right-sided plans in the optimizer.
</para>
</listitem>
<listitem>
<para>
<envar>PGCOSTHEAP</envar>
sets the default cost for heap searches for the optimizer.
</para>
</listitem>
<listitem>
<para>
<envar>PGCOSTINDEX</envar>
sets the default cost for indexed searches for the optimizer.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
......
...@@ -1900,24 +1900,6 @@ behavior for every Postgres session: ...@@ -1900,24 +1900,6 @@ behavior for every Postgres session:
sets the default mode for the genetic optimizer. sets the default mode for the genetic optimizer.
</para> </para>
</listitem> </listitem>
<listitem>
<para>
<envar>PGRPLANS</envar>
sets the default mode to allow or disable right-sided plans in the optimizer.
</para>
</listitem>
<listitem>
<para>
<envar>PGCOSTHEAP</envar>
sets the default cost for heap searches for the optimizer.
</para>
</listitem>
<listitem>
<para>
<envar>PGCOSTINDEX</envar>
sets the default cost for indexed searches for the optimizer.
</para>
</listitem>
</itemizedlist> </itemizedlist>
</para> </para>
......
This diff is collapsed.
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/show.sgml,v 1.7 1999/07/22 15:09:15 thomas Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/ref/show.sgml,v 1.8 2000/02/15 20:49:07 tgl Exp $
Postgres documentation Postgres documentation
--> -->
...@@ -106,14 +106,14 @@ SHOW VARIABLE ...@@ -106,14 +106,14 @@ SHOW VARIABLE
Description Description
</title> </title>
<para> <para>
<command>SHOW</command> will display the current <command>SHOW</command> will display the current setting of a
configuration parameters for run-time parameter during a session.
variable during a session.
</para> </para>
<para> <para>
The session can be configured using <command>SET</command> statement, These variables can be set using the <command>SET</command> statement,
and values and
can be restored to the defaults using <command>RESET</command> statement. can be restored to the default values using the <command>RESET</command>
statement.
Parameters and values are case-insensitive. Parameters and values are case-insensitive.
</para> </para>
...@@ -125,13 +125,12 @@ SHOW VARIABLE ...@@ -125,13 +125,12 @@ SHOW VARIABLE
Notes Notes
</title> </title>
<para> <para>
The <command>SHOW</command> is a <productname>Postgres</productname> <command>SHOW</command> is a <productname>Postgres</productname>
language extension. language extension.
</para> </para>
<para> <para>
Refer to <command>SET</command>/<command>RESET</command> Refer to <command>SET</command>/<command>RESET</command>
to set/reset variable values. to set/reset variable values.
See also <command>SET TIME ZONE</command>.
</para> </para>
</refsect2> </refsect2>
</refsect1> </refsect1>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994-5, Regents of the University of California * Portions Copyright (c) 1994-5, Regents of the University of California
* *
* $Id: explain.c,v 1.53 2000/02/15 03:36:39 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.54 2000/02/15 20:49:08 tgl Exp $
* *
*/ */
...@@ -217,39 +217,24 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -217,39 +217,24 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
{ {
relation = RelationIdGetRelation(lfirsti(l)); relation = RelationIdGetRelation(lfirsti(l));
Assert(relation); Assert(relation);
if (++i > 1) appendStringInfo(str, "%s%s",
appendStringInfo(str, ", "); (++i > 1) ? ", " : "",
appendStringInfo(str,
stringStringInfo(RelationGetRelationName(relation))); stringStringInfo(RelationGetRelationName(relation)));
/* drop relcache refcount from RelationIdGetRelation */ /* drop relcache refcount from RelationIdGetRelation */
RelationDecrementReferenceCount(relation); RelationDecrementReferenceCount(relation);
} }
/* FALL THRU */
case T_SeqScan: case T_SeqScan:
case T_TidScan:
if (((Scan *) plan)->scanrelid > 0) if (((Scan *) plan)->scanrelid > 0)
{ {
RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable); RangeTblEntry *rte = nth(((Scan *) plan)->scanrelid - 1, es->rtable);
appendStringInfo(str, " on "); appendStringInfo(str, " on %s",
if (strcmp(rte->ref->relname, rte->relname) != 0) stringStringInfo(rte->relname));
{ if (rte->ref && strcmp(rte->ref->relname, rte->relname) != 0)
appendStringInfo(str, "%s ", appendStringInfo(str, " %s",
stringStringInfo(rte->relname)); stringStringInfo(rte->ref->relname));
}
appendStringInfo(str, stringStringInfo(rte->ref->relname));
}
break;
case T_TidScan:
if (((TidScan *) plan)->scan.scanrelid > 0)
{
RangeTblEntry *rte = nth(((TidScan *) plan)->scan.scanrelid - 1, es->rtable);
appendStringInfo(str, " on ");
if (strcmp(rte->ref->relname, rte->relname) != 0)
{
appendStringInfo(str, "%s ",
stringStringInfo(rte->relname));
}
appendStringInfo(str, stringStringInfo(rte->ref->relname));
} }
break; break;
default: default:
...@@ -257,8 +242,9 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es) ...@@ -257,8 +242,9 @@ explain_outNode(StringInfo str, Plan *plan, int indent, ExplainState *es)
} }
if (es->printCost) if (es->printCost)
{ {
appendStringInfo(str, " (cost=%.2f rows=%.0f width=%d)", appendStringInfo(str, " (cost=%.2f..%.2f rows=%.0f width=%d)",
plan->cost, plan->plan_rows, plan->plan_width); plan->startup_cost, plan->total_cost,
plan->plan_rows, plan->plan_width);
} }
appendStringInfo(str, "\n"); appendStringInfo(str, "\n");
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.105 2000/02/15 03:37:08 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.106 2000/02/15 20:49:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -75,7 +75,8 @@ listCopy(List *list) ...@@ -75,7 +75,8 @@ listCopy(List *list)
static void static void
CopyPlanFields(Plan *from, Plan *newnode) CopyPlanFields(Plan *from, Plan *newnode)
{ {
newnode->cost = from->cost; newnode->startup_cost = from->startup_cost;
newnode->total_cost = from->total_cost;
newnode->plan_rows = from->plan_rows; newnode->plan_rows = from->plan_rows;
newnode->plan_width = from->plan_width; newnode->plan_width = from->plan_width;
/* state is NOT copied */ /* state is NOT copied */
...@@ -981,8 +982,9 @@ _copyRelOptInfo(RelOptInfo *from) ...@@ -981,8 +982,9 @@ _copyRelOptInfo(RelOptInfo *from)
Node_Copy(from, newnode, targetlist); Node_Copy(from, newnode, targetlist);
Node_Copy(from, newnode, pathlist); Node_Copy(from, newnode, pathlist);
/* XXX cheapestpath should point to a member of pathlist? */ /* XXX cheapest-path fields should point to members of pathlist? */
Node_Copy(from, newnode, cheapestpath); Node_Copy(from, newnode, cheapest_startup_path);
Node_Copy(from, newnode, cheapest_total_path);
newnode->pruneable = from->pruneable; newnode->pruneable = from->pruneable;
newnode->indexed = from->indexed; newnode->indexed = from->indexed;
...@@ -990,6 +992,7 @@ _copyRelOptInfo(RelOptInfo *from) ...@@ -990,6 +992,7 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->tuples = from->tuples; newnode->tuples = from->tuples;
Node_Copy(from, newnode, baserestrictinfo); Node_Copy(from, newnode, baserestrictinfo);
newnode->baserestrictcost = from->baserestrictcost;
Node_Copy(from, newnode, joininfo); Node_Copy(from, newnode, joininfo);
Node_Copy(from, newnode, innerjoin); Node_Copy(from, newnode, innerjoin);
...@@ -1045,6 +1048,7 @@ _copyIndexOptInfo(IndexOptInfo *from) ...@@ -1045,6 +1048,7 @@ _copyIndexOptInfo(IndexOptInfo *from)
newnode->amcostestimate = from->amcostestimate; newnode->amcostestimate = from->amcostestimate;
newnode->indproc = from->indproc; newnode->indproc = from->indproc;
Node_Copy(from, newnode, indpred); Node_Copy(from, newnode, indpred);
newnode->lossy = from->lossy;
return newnode; return newnode;
} }
...@@ -1066,7 +1070,8 @@ CopyPathFields(Path *from, Path *newnode) ...@@ -1066,7 +1070,8 @@ CopyPathFields(Path *from, Path *newnode)
*/ */
newnode->parent = from->parent; newnode->parent = from->parent;
newnode->path_cost = from->path_cost; newnode->startup_cost = from->startup_cost;
newnode->total_cost = from->total_cost;
newnode->pathtype = from->pathtype; newnode->pathtype = from->pathtype;
...@@ -1108,6 +1113,7 @@ _copyIndexPath(IndexPath *from) ...@@ -1108,6 +1113,7 @@ _copyIndexPath(IndexPath *from)
*/ */
newnode->indexid = listCopy(from->indexid); newnode->indexid = listCopy(from->indexid);
Node_Copy(from, newnode, indexqual); Node_Copy(from, newnode, indexqual);
newnode->indexscandir = from->indexscandir;
newnode->joinrelids = listCopy(from->joinrelids); newnode->joinrelids = listCopy(from->joinrelids);
return newnode; return newnode;
...@@ -1339,8 +1345,7 @@ _copyRangeTblEntry(RangeTblEntry *from) ...@@ -1339,8 +1345,7 @@ _copyRangeTblEntry(RangeTblEntry *from)
if (from->relname) if (from->relname)
newnode->relname = pstrdup(from->relname); newnode->relname = pstrdup(from->relname);
if (from->ref) Node_Copy(from, newnode, ref);
Node_Copy(from, newnode, ref);
newnode->relid = from->relid; newnode->relid = from->relid;
newnode->inh = from->inh; newnode->inh = from->inh;
newnode->inFromCl = from->inFromCl; newnode->inFromCl = from->inFromCl;
...@@ -1449,8 +1454,10 @@ _copyQuery(Query *from) ...@@ -1449,8 +1454,10 @@ _copyQuery(Query *from)
Node_Copy(from, newnode, limitOffset); Node_Copy(from, newnode, limitOffset);
Node_Copy(from, newnode, limitCount); Node_Copy(from, newnode, limitCount);
/* we do not copy the planner internal fields: base_rel_list, /*
* join_rel_list, query_pathkeys. Not entirely clear if this is right? * We do not copy the planner internal fields: base_rel_list,
* join_rel_list, equi_key_list, query_pathkeys.
* Not entirely clear if this is right?
*/ */
return newnode; return newnode;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.60 2000/02/15 03:37:08 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.61 2000/02/15 20:49:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -100,10 +100,10 @@ _equalAttr(Attr *a, Attr *b) ...@@ -100,10 +100,10 @@ _equalAttr(Attr *a, Attr *b)
{ {
if (!strcmp(a->relname, b->relname)) if (!strcmp(a->relname, b->relname))
return false; return false;
if (length(a->attrs) != length(b->attrs)) if (!equal(a->attrs, b->attrs))
return false; return false;
return equal(a->attrs, b->attrs); return true;
} }
static bool static bool
...@@ -342,8 +342,8 @@ _equalPath(Path *a, Path *b) ...@@ -342,8 +342,8 @@ _equalPath(Path *a, Path *b)
return false; return false;
if (!equal(a->parent, b->parent)) if (!equal(a->parent, b->parent))
return false; return false;
/* do not check path_cost, since it may not be set yet, and being /* do not check path costs, since they may not be set yet, and being
* a float there are roundoff error issues anyway... * float values there are roundoff error issues anyway...
*/ */
if (!equal(a->pathkeys, b->pathkeys)) if (!equal(a->pathkeys, b->pathkeys))
return false; return false;
...@@ -359,6 +359,8 @@ _equalIndexPath(IndexPath *a, IndexPath *b) ...@@ -359,6 +359,8 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
return false; return false;
if (!equal(a->indexqual, b->indexqual)) if (!equal(a->indexqual, b->indexqual))
return false; return false;
if (a->indexscandir != b->indexscandir)
return false;
if (!equali(a->joinrelids, b->joinrelids)) if (!equali(a->joinrelids, b->joinrelids))
return false; return false;
return true; return true;
...@@ -625,8 +627,9 @@ _equalQuery(Query *a, Query *b) ...@@ -625,8 +627,9 @@ _equalQuery(Query *a, Query *b)
/* /*
* We do not check the internal-to-the-planner fields: base_rel_list, * We do not check the internal-to-the-planner fields: base_rel_list,
* join_rel_list, query_pathkeys. They might not be set yet, and * join_rel_list, equi_key_list, query_pathkeys.
* in any case they should be derivable from the other fields. * They might not be set yet, and in any case they should be derivable
* from the other fields.
*/ */
return true; return true;
} }
...@@ -644,16 +647,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b) ...@@ -644,16 +647,8 @@ _equalRangeTblEntry(RangeTblEntry *a, RangeTblEntry *b)
if (a->relname != b->relname) if (a->relname != b->relname)
return false; return false;
} }
if (a->ref && b->ref) if (!equal(a->ref, b->ref))
{ return false;
if (! equal(a->ref, b->ref))
return false;
}
else
{
if (a->ref != b->ref)
return false;
}
if (a->relid != b->relid) if (a->relid != b->relid)
return false; return false;
if (a->inh != b->inh) if (a->inh != b->inh)
...@@ -784,6 +779,9 @@ equal(void *a, void *b) ...@@ -784,6 +779,9 @@ equal(void *a, void *b)
case T_Stream: case T_Stream:
retval = _equalStream(a, b); retval = _equalStream(a, b);
break; break;
case T_Attr:
retval = _equalAttr(a, b);
break;
case T_Var: case T_Var:
retval = _equalVar(a, b); retval = _equalVar(a, b);
break; break;
...@@ -856,9 +854,6 @@ equal(void *a, void *b) ...@@ -856,9 +854,6 @@ equal(void *a, void *b)
case T_EState: case T_EState:
retval = _equalEState(a, b); retval = _equalEState(a, b);
break; break;
case T_Attr:
retval = _equalAttr(a, b);
break;
case T_Integer: case T_Integer:
case T_String: case T_String:
case T_Float: case T_Float:
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.35 2000/02/15 03:37:08 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.36 2000/02/15 20:49:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -730,10 +730,11 @@ _freeRelOptInfo(RelOptInfo *node) ...@@ -730,10 +730,11 @@ _freeRelOptInfo(RelOptInfo *node)
freeObject(node->targetlist); freeObject(node->targetlist);
freeObject(node->pathlist); freeObject(node->pathlist);
/* XXX is this right? cheapestpath will typically be a pointer into /* XXX is this right? cheapest-path fields will typically be pointers
* pathlist, won't it? * into pathlist, not separate structs...
*/ */
freeObject(node->cheapestpath); freeObject(node->cheapest_startup_path);
freeObject(node->cheapest_total_path);
freeObject(node->baserestrictinfo); freeObject(node->baserestrictinfo);
freeObject(node->joininfo); freeObject(node->joininfo);
...@@ -1013,8 +1014,7 @@ _freeRangeTblEntry(RangeTblEntry *node) ...@@ -1013,8 +1014,7 @@ _freeRangeTblEntry(RangeTblEntry *node)
{ {
if (node->relname) if (node->relname)
pfree(node->relname); pfree(node->relname);
if (node->ref) freeObject(node->ref);
freeObject(node->ref);
pfree(node); pfree(node);
} }
...@@ -1024,8 +1024,7 @@ _freeAttr(Attr *node) ...@@ -1024,8 +1024,7 @@ _freeAttr(Attr *node)
{ {
if (node->relname) if (node->relname)
pfree(node->relname); pfree(node->relname);
if (node->attrs) freeObject(node->attrs);
freeObject(node->attrs);
pfree(node); pfree(node);
} }
...@@ -1346,10 +1345,3 @@ freeObject(void *node) ...@@ -1346,10 +1345,3 @@ freeObject(void *node)
break; break;
} }
} }
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.107 2000/02/15 03:37:09 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.108 2000/02/15 20:49:09 tgl Exp $
* *
* NOTES * NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which * Every (plan) node in POSTGRES has an associated "out" routine which
...@@ -321,8 +321,9 @@ static void ...@@ -321,8 +321,9 @@ static void
_outPlanInfo(StringInfo str, Plan *node) _outPlanInfo(StringInfo str, Plan *node)
{ {
appendStringInfo(str, appendStringInfo(str,
":cost %g :rows %.0f :width %d :state %s :qptargetlist ", ":startup_cost %.2f :total_cost %.2f :rows %.0f :width %d :state %s :qptargetlist ",
node->cost, node->startup_cost,
node->total_cost,
node->plan_rows, node->plan_rows,
node->plan_width, node->plan_width,
node->state ? "not-NULL" : "<>"); node->state ? "not-NULL" : "<>");
...@@ -908,15 +909,13 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node) ...@@ -908,15 +909,13 @@ _outRelOptInfo(StringInfo str, RelOptInfo *node)
appendStringInfo(str, " :pathlist "); appendStringInfo(str, " :pathlist ");
_outNode(str, node->pathlist); _outNode(str, node->pathlist);
/* appendStringInfo(str, " :cheapest_startup_path ");
* Not sure if these are nodes or not. They're declared as struct _outNode(str, node->cheapest_startup_path);
* Path *. Since i don't know, i'll just print the addresses for now. appendStringInfo(str, " :cheapest_total_path ");
* This can be changed later, if necessary. _outNode(str, node->cheapest_total_path);
*/
appendStringInfo(str, appendStringInfo(str,
" :cheapestpath @ 0x%x :pruneable %s :baserestrictinfo ", " :pruneable %s :baserestrictinfo ",
(int) node->cheapestpath,
node->pruneable ? "true" : "false"); node->pruneable ? "true" : "false");
_outNode(str, node->baserestrictinfo); _outNode(str, node->baserestrictinfo);
...@@ -977,9 +976,11 @@ _outRowMark(StringInfo str, RowMark *node) ...@@ -977,9 +976,11 @@ _outRowMark(StringInfo str, RowMark *node)
static void static void
_outPath(StringInfo str, Path *node) _outPath(StringInfo str, Path *node)
{ {
appendStringInfo(str, " PATH :pathtype %d :cost %.2f :pathkeys ", appendStringInfo(str,
" PATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->pathtype, node->pathtype,
node->path_cost); node->startup_cost,
node->total_cost);
_outNode(str, node->pathkeys); _outNode(str, node->pathkeys);
} }
...@@ -990,9 +991,10 @@ static void ...@@ -990,9 +991,10 @@ static void
_outIndexPath(StringInfo str, IndexPath *node) _outIndexPath(StringInfo str, IndexPath *node)
{ {
appendStringInfo(str, appendStringInfo(str,
" INDEXPATH :pathtype %d :cost %.2f :pathkeys ", " INDEXPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->path.pathtype, node->path.pathtype,
node->path.path_cost); node->path.startup_cost,
node->path.total_cost);
_outNode(str, node->path.pathkeys); _outNode(str, node->path.pathkeys);
appendStringInfo(str, " :indexid "); appendStringInfo(str, " :indexid ");
...@@ -1001,7 +1003,8 @@ _outIndexPath(StringInfo str, IndexPath *node) ...@@ -1001,7 +1003,8 @@ _outIndexPath(StringInfo str, IndexPath *node)
appendStringInfo(str, " :indexqual "); appendStringInfo(str, " :indexqual ");
_outNode(str, node->indexqual); _outNode(str, node->indexqual);
appendStringInfo(str, " :joinrelids "); appendStringInfo(str, " :indexscandir %d :joinrelids ",
(int) node->indexscandir);
_outIntList(str, node->joinrelids); _outIntList(str, node->joinrelids);
} }
...@@ -1012,9 +1015,10 @@ static void ...@@ -1012,9 +1015,10 @@ static void
_outTidPath(StringInfo str, TidPath *node) _outTidPath(StringInfo str, TidPath *node)
{ {
appendStringInfo(str, appendStringInfo(str,
" TIDPATH :pathtype %d :cost %.2f :pathkeys ", " TIDPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->path.pathtype, node->path.pathtype,
node->path.path_cost); node->path.startup_cost,
node->path.total_cost);
_outNode(str, node->path.pathkeys); _outNode(str, node->path.pathkeys);
appendStringInfo(str, " :tideval "); appendStringInfo(str, " :tideval ");
...@@ -1031,9 +1035,10 @@ static void ...@@ -1031,9 +1035,10 @@ static void
_outNestPath(StringInfo str, NestPath *node) _outNestPath(StringInfo str, NestPath *node)
{ {
appendStringInfo(str, appendStringInfo(str,
" NESTPATH :pathtype %d :cost %.2f :pathkeys ", " NESTPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->path.pathtype, node->path.pathtype,
node->path.path_cost); node->path.startup_cost,
node->path.total_cost);
_outNode(str, node->path.pathkeys); _outNode(str, node->path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->outerjoinpath); _outNode(str, node->outerjoinpath);
...@@ -1050,9 +1055,10 @@ static void ...@@ -1050,9 +1055,10 @@ static void
_outMergePath(StringInfo str, MergePath *node) _outMergePath(StringInfo str, MergePath *node)
{ {
appendStringInfo(str, appendStringInfo(str,
" MERGEPATH :pathtype %d :cost %.2f :pathkeys ", " MERGEPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->jpath.path.pathtype, node->jpath.path.pathtype,
node->jpath.path.path_cost); node->jpath.path.startup_cost,
node->jpath.path.total_cost);
_outNode(str, node->jpath.path.pathkeys); _outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->jpath.outerjoinpath); _outNode(str, node->jpath.outerjoinpath);
...@@ -1078,9 +1084,10 @@ static void ...@@ -1078,9 +1084,10 @@ static void
_outHashPath(StringInfo str, HashPath *node) _outHashPath(StringInfo str, HashPath *node)
{ {
appendStringInfo(str, appendStringInfo(str,
" HASHPATH :pathtype %d :cost %.2f :pathkeys ", " HASHPATH :pathtype %d :startup_cost %.2f :total_cost %.2f :pathkeys ",
node->jpath.path.pathtype, node->jpath.path.pathtype,
node->jpath.path.path_cost); node->jpath.path.startup_cost,
node->jpath.path.total_cost);
_outNode(str, node->jpath.path.pathkeys); _outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :outerjoinpath "); appendStringInfo(str, " :outerjoinpath ");
_outNode(str, node->jpath.outerjoinpath); _outNode(str, node->jpath.outerjoinpath);
...@@ -1364,7 +1371,7 @@ _outNode(StringInfo str, void *obj) ...@@ -1364,7 +1371,7 @@ _outNode(StringInfo str, void *obj)
return; return;
} }
if (nodeTag(obj) == T_List) if (IsA(obj, List))
{ {
List *l; List *l;
...@@ -1377,6 +1384,11 @@ _outNode(StringInfo str, void *obj) ...@@ -1377,6 +1384,11 @@ _outNode(StringInfo str, void *obj)
} }
appendStringInfoChar(str, ')'); appendStringInfoChar(str, ')');
} }
else if (IsA_Value(obj))
{
/* nodeRead does not want to see { } around these! */
_outValue(str, obj);
}
else else
{ {
appendStringInfoChar(str, '{'); appendStringInfoChar(str, '{');
...@@ -1550,11 +1562,6 @@ _outNode(StringInfo str, void *obj) ...@@ -1550,11 +1562,6 @@ _outNode(StringInfo str, void *obj)
case T_Stream: case T_Stream:
_outStream(str, obj); _outStream(str, obj);
break; break;
case T_Integer:
case T_String:
case T_Float:
_outValue(str, obj);
break;
case T_A_Expr: case T_A_Expr:
_outAExpr(str, obj); _outAExpr(str, obj);
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.36 2000/02/15 03:37:09 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.37 2000/02/15 20:49:12 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -175,9 +175,8 @@ print_expr(Node *expr, List *rtable) ...@@ -175,9 +175,8 @@ print_expr(Node *expr, List *rtable)
{ {
rt = rt_fetch(var->varno, rtable); rt = rt_fetch(var->varno, rtable);
relname = rt->relname; relname = rt->relname;
if (rt->ref) if (rt->ref && rt->ref->relname)
if (rt->ref->relname) relname = rt->ref->relname; /* table renamed */
relname = rt->relname; /* table renamed */
attname = get_attname(rt->relid, var->varattno); attname = get_attname(rt->relid, var->varattno);
} }
break; break;
...@@ -366,8 +365,9 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label) ...@@ -366,8 +365,9 @@ print_plan_recursive(Plan *p, Query *parsetree, int indentLevel, char *label)
return; return;
for (i = 0; i < indentLevel; i++) for (i = 0; i < indentLevel; i++)
printf(" "); printf(" ");
printf("%s%s :c=%.4f :r=%.0f :w=%d ", label, plannode_type(p), printf("%s%s :c=%.2f..%.2f :r=%.0f :w=%d ", label, plannode_type(p),
p->cost, p->plan_rows, p->plan_width); p->startup_cost, p->total_cost,
p->plan_rows, p->plan_width);
if (IsA(p, Scan) ||IsA(p, SeqScan)) if (IsA(p, Scan) ||IsA(p, SeqScan))
{ {
RangeTblEntry *rte; RangeTblEntry *rte;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.83 2000/02/15 03:37:09 thomas Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.84 2000/02/15 20:49:12 tgl Exp $
* *
* NOTES * NOTES
* Most of the read functions for plan nodes are tested. (In fact, they * Most of the read functions for plan nodes are tested. (In fact, they
...@@ -217,9 +217,13 @@ _getPlan(Plan *node) ...@@ -217,9 +217,13 @@ _getPlan(Plan *node)
char *token; char *token;
int length; int length;
token = lsptok(NULL, &length); /* first token is :cost */ token = lsptok(NULL, &length); /* first token is :startup_cost */
token = lsptok(NULL, &length); /* next is the actual cost */ token = lsptok(NULL, &length); /* next is the actual cost */
node->cost = (Cost) atof(token); node->startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* skip the :total_cost */
token = lsptok(NULL, &length); /* next is the actual cost */
node->total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* skip the :rows */ token = lsptok(NULL, &length); /* skip the :rows */
token = lsptok(NULL, &length); /* get the plan_rows */ token = lsptok(NULL, &length); /* get the plan_rows */
...@@ -520,7 +524,6 @@ _readIndexScan() ...@@ -520,7 +524,6 @@ _readIndexScan()
token = lsptok(NULL, &length); /* eat :indxorderdir */ token = lsptok(NULL, &length); /* eat :indxorderdir */
token = lsptok(NULL, &length); /* get indxorderdir */ token = lsptok(NULL, &length); /* get indxorderdir */
local_node->indxorderdir = atoi(token); local_node->indxorderdir = atoi(token);
return local_node; return local_node;
...@@ -1275,18 +1278,15 @@ _readRelOptInfo() ...@@ -1275,18 +1278,15 @@ _readRelOptInfo()
token = lsptok(NULL, &length); /* get :pathlist */ token = lsptok(NULL, &length); /* get :pathlist */
local_node->pathlist = nodeRead(true); /* now read it */ local_node->pathlist = nodeRead(true); /* now read it */
/* token = lsptok(NULL, &length); /* get :cheapest_startup_path */
* Not sure if these are nodes or not. They're declared as struct local_node->cheapest_startup_path = nodeRead(true); /* now read it */
* Path *. Since i don't know, i'll just print the addresses for now.
* This can be changed later, if necessary.
*/
token = lsptok(NULL, &length); /* get :cheapestpath */
token = lsptok(NULL, &length); /* get @ */
token = lsptok(NULL, &length); /* now read it */
sscanf(token, "%x", (unsigned int *) &local_node->cheapestpath); token = lsptok(NULL, &length); /* get :cheapest_total_path */
local_node->cheapest_total_path = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :pruneable */
token = lsptok(NULL, &length); /* get :pruneable */
local_node->pruneable = (token[0] == 't') ? true : false;
token = lsptok(NULL, &length); /* get :baserestrictinfo */ token = lsptok(NULL, &length); /* get :baserestrictinfo */
local_node->baserestrictinfo = nodeRead(true); /* now read it */ local_node->baserestrictinfo = nodeRead(true); /* now read it */
...@@ -1322,29 +1322,6 @@ _readTargetEntry() ...@@ -1322,29 +1322,6 @@ _readTargetEntry()
return local_node; return local_node;
} }
static List *
_readList()
{
List *local_node = NULL;
char *token;
int length;
token = lsptok(NULL, &length); /* eat "(" */
token = lsptok(NULL, &length); /* get "{" */
while (strncmp(token, "{", length) == 0)
{
nconc(local_node, nodeRead(true));
token = lsptok(NULL, &length); /* eat ")" */
if (strncmp(token, "}", length) != 0)
elog(ERROR, "badly formatted attribute list"
" in planstring \"%.10s\"...\n", token);
token = lsptok(NULL, &length); /* "{" or ")" */
}
return local_node;
}
static Attr * static Attr *
_readAttr() _readAttr()
{ {
...@@ -1356,13 +1333,10 @@ _readAttr() ...@@ -1356,13 +1333,10 @@ _readAttr()
token = lsptok(NULL, &length); /* eat :relname */ token = lsptok(NULL, &length); /* eat :relname */
token = lsptok(NULL, &length); /* get relname */ token = lsptok(NULL, &length); /* get relname */
if (length == 0) local_node->relname = debackslash(token, length);
local_node->relname = pstrdup("");
else
local_node->relname = debackslash(token, length);
token = lsptok(NULL, &length); /* eat :attrs */ token = lsptok(NULL, &length); /* eat :attrs */
local_node->attrs = _readList(); local_node->attrs = nodeRead(true); /* now read it */
return local_node; return local_node;
} }
...@@ -1388,7 +1362,7 @@ _readRangeTblEntry() ...@@ -1388,7 +1362,7 @@ _readRangeTblEntry()
local_node->relname = debackslash(token, length); local_node->relname = debackslash(token, length);
token = lsptok(NULL, &length); /* eat :ref */ token = lsptok(NULL, &length); /* eat :ref */
local_node->ref = nodeRead(true); local_node->ref = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* eat :relid */ token = lsptok(NULL, &length); /* eat :relid */
token = lsptok(NULL, &length); /* get :relid */ token = lsptok(NULL, &length); /* get :relid */
...@@ -1450,9 +1424,13 @@ _readPath() ...@@ -1450,9 +1424,13 @@ _readPath()
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->pathtype = atol(token); local_node->pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path_cost = (Cost) atof(token); local_node->total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->pathkeys = nodeRead(true); /* now read it */ local_node->pathkeys = nodeRead(true); /* now read it */
...@@ -1479,9 +1457,13 @@ _readIndexPath() ...@@ -1479,9 +1457,13 @@ _readIndexPath()
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.pathtype = atol(token); local_node->path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.path_cost = (Cost) atof(token); local_node->path.startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */ local_node->path.pathkeys = nodeRead(true); /* now read it */
...@@ -1492,6 +1474,10 @@ _readIndexPath() ...@@ -1492,6 +1474,10 @@ _readIndexPath()
token = lsptok(NULL, &length); /* get :indexqual */ token = lsptok(NULL, &length); /* get :indexqual */
local_node->indexqual = nodeRead(true); /* now read it */ local_node->indexqual = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :indexscandir */
token = lsptok(NULL, &length); /* now read it */
local_node->indexscandir = (ScanDirection) atoi(token);
token = lsptok(NULL, &length); /* get :joinrelids */ token = lsptok(NULL, &length); /* get :joinrelids */
local_node->joinrelids = toIntList(nodeRead(true)); local_node->joinrelids = toIntList(nodeRead(true));
...@@ -1517,9 +1503,13 @@ _readTidPath() ...@@ -1517,9 +1503,13 @@ _readTidPath()
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.pathtype = atol(token); local_node->path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.path_cost = (Cost) atof(token); local_node->path.startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */ local_node->path.pathkeys = nodeRead(true); /* now read it */
...@@ -1552,9 +1542,13 @@ _readNestPath() ...@@ -1552,9 +1542,13 @@ _readNestPath()
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.pathtype = atol(token); local_node->path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.startup_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->path.path_cost = (Cost) atof(token); local_node->path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */ local_node->path.pathkeys = nodeRead(true); /* now read it */
...@@ -1588,13 +1582,15 @@ _readMergePath() ...@@ -1588,13 +1582,15 @@ _readMergePath()
token = lsptok(NULL, &length); /* get :pathtype */ token = lsptok(NULL, &length); /* get :pathtype */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.pathtype = atol(token); local_node->jpath.path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.startup_cost = (Cost) atof(token);
local_node->jpath.path.path_cost = (Cost) atof(token); token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */ local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
...@@ -1637,13 +1633,15 @@ _readHashPath() ...@@ -1637,13 +1633,15 @@ _readHashPath()
token = lsptok(NULL, &length); /* get :pathtype */ token = lsptok(NULL, &length); /* get :pathtype */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.pathtype = atol(token); local_node->jpath.path.pathtype = atol(token);
token = lsptok(NULL, &length); /* get :cost */ token = lsptok(NULL, &length); /* get :startup_cost */
token = lsptok(NULL, &length); /* now read it */ token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.startup_cost = (Cost) atof(token);
local_node->jpath.path.path_cost = (Cost) atof(token); token = lsptok(NULL, &length); /* get :total_cost */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.total_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathkeys */ token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */ local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
...@@ -1886,14 +1884,6 @@ parsePlanString(void) ...@@ -1886,14 +1884,6 @@ parsePlanString(void)
return_value = _readCaseWhen(); return_value = _readCaseWhen();
else if (length == 7 && strncmp(token, "ROWMARK", length) == 0) else if (length == 7 && strncmp(token, "ROWMARK", length) == 0)
return_value = _readRowMark(); return_value = _readRowMark();
#if 0
else if (length == 1 && strncmp(token, "{", length) == 0)
{
/* raw list (of strings?) found in Attr structure - thomas 2000-02-09 */
return_value = nodeRead(true);
token = lsptok(NULL, &length); /* eat trailing brace */
}
#endif
else else
elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token); elog(ERROR, "badly formatted planstring \"%.10s\"...\n", token);
......
...@@ -122,7 +122,7 @@ among other choices. Although the jointree scanning code produces these ...@@ -122,7 +122,7 @@ among other choices. Although the jointree scanning code produces these
potential join combinations one at a time, all the ways to produce the potential join combinations one at a time, all the ways to produce the
same set of joined base rels will share the same RelOptInfo, so the paths same set of joined base rels will share the same RelOptInfo, so the paths
produced from different join combinations that produce equivalent joinrels produced from different join combinations that produce equivalent joinrels
will compete in add_pathlist. will compete in add_path.
Once we have built the final join rel, we use either the cheapest path Once we have built the final join rel, we use either the cheapest path
for it or the cheapest path with the desired ordering (if that's cheaper for it or the cheapest path with the desired ordering (if that's cheaper
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: geqo_eval.c,v 1.47 2000/02/07 04:40:58 tgl Exp $ * $Id: geqo_eval.c,v 1.48 2000/02/15 20:49:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -96,8 +96,13 @@ geqo_eval(Query *root, Gene *tour, int num_gene) ...@@ -96,8 +96,13 @@ geqo_eval(Query *root, Gene *tour, int num_gene)
/* construct the best path for the given combination of relations */ /* construct the best path for the given combination of relations */
joinrel = gimme_tree(root, tour, 0, num_gene, NULL); joinrel = gimme_tree(root, tour, 0, num_gene, NULL);
/* compute fitness */ /*
fitness = joinrel->cheapestpath->path_cost; * compute fitness
*
* XXX geqo does not currently support optimization for partial
* result retrieval --- how to fix?
*/
fitness = joinrel->cheapest_total_path->total_cost;
/* restore join_rel_list */ /* restore join_rel_list */
root->join_rel_list = savelist; root->join_rel_list = savelist;
...@@ -155,8 +160,8 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old ...@@ -155,8 +160,8 @@ gimme_tree(Query *root, Gene *tour, int rel_count, int num_gene, RelOptInfo *old
rel_count++; rel_count++;
Assert(length(new_rel->relids) == rel_count); Assert(length(new_rel->relids) == rel_count);
/* Find and save the cheapest path for this rel */ /* Find and save the cheapest paths for this rel */
set_cheapest(new_rel, new_rel->pathlist); set_cheapest(new_rel);
return gimme_tree(root, tour, rel_count, num_gene, new_rel); return gimme_tree(root, tour, rel_count, num_gene, new_rel);
} }
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: geqo_misc.c,v 1.27 2000/02/07 04:40:58 tgl Exp $ * $Id: geqo_misc.c,v 1.28 2000/02/15 20:49:14 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -179,8 +179,9 @@ geqo_print_path(Query *root, Path *path, int indent) ...@@ -179,8 +179,9 @@ geqo_print_path(Query *root, Path *path, int indent)
if (join) if (join)
{ {
jp = (JoinPath *) path; jp = (JoinPath *) path;
printf("%s rows=%.0f cost=%f\n", printf("%s rows=%.0f cost=%.2f..%.2f\n",
ptype, path->parent->rows, path->path_cost); ptype, path->parent->rows,
path->startup_cost, path->total_cost);
switch (nodeTag(path)) switch (nodeTag(path))
{ {
case T_MergePath: case T_MergePath:
...@@ -215,8 +216,9 @@ geqo_print_path(Query *root, Path *path, int indent) ...@@ -215,8 +216,9 @@ geqo_print_path(Query *root, Path *path, int indent)
{ {
int relid = lfirsti(path->parent->relids); int relid = lfirsti(path->parent->relids);
printf("%s(%d) rows=%.0f cost=%f\n", printf("%s(%d) rows=%.0f cost=%.2f..%.2f\n",
ptype, relid, path->parent->rows, path->path_cost); ptype, relid, path->parent->rows,
path->startup_cost, path->total_cost);
if (IsA(path, IndexPath)) if (IsA(path, IndexPath))
{ {
...@@ -241,6 +243,9 @@ geqo_print_rel(Query *root, RelOptInfo *rel) ...@@ -241,6 +243,9 @@ geqo_print_rel(Query *root, RelOptInfo *rel)
foreach(l, rel->pathlist) foreach(l, rel->pathlist)
geqo_print_path(root, lfirst(l), 1); geqo_print_path(root, lfirst(l), 1);
printf("\tcheapest path:\n"); printf("\tcheapest startup path:\n");
geqo_print_path(root, rel->cheapestpath, 1); geqo_print_path(root, rel->cheapest_startup_path, 1);
printf("\tcheapest total path:\n");
geqo_print_path(root, rel->cheapest_total_path, 1);
} }
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.58 2000/02/07 04:40:59 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.59 2000/02/15 20:49:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -100,7 +100,7 @@ set_base_rel_pathlist(Query *root) ...@@ -100,7 +100,7 @@ set_base_rel_pathlist(Query *root)
/* /*
* Generate paths and add them to the rel's pathlist. * Generate paths and add them to the rel's pathlist.
* *
* add_path/add_pathlist will discard any paths that are dominated * Note: add_path() will discard any paths that are dominated
* by another available path, keeping only those paths that are * by another available path, keeping only those paths that are
* superior along at least one dimension of cost or sortedness. * superior along at least one dimension of cost or sortedness.
*/ */
...@@ -109,24 +109,21 @@ set_base_rel_pathlist(Query *root) ...@@ -109,24 +109,21 @@ set_base_rel_pathlist(Query *root)
add_path(rel, create_seqscan_path(rel)); add_path(rel, create_seqscan_path(rel));
/* Consider TID scans */ /* Consider TID scans */
add_pathlist(rel, create_tidscan_paths(root, rel)); create_tidscan_paths(root, rel);
/* Consider index paths for both simple and OR index clauses */ /* Consider index paths for both simple and OR index clauses */
add_pathlist(rel, create_index_paths(root, create_index_paths(root, rel, indices,
rel, rel->baserestrictinfo,
indices, rel->joininfo);
rel->baserestrictinfo,
rel->joininfo));
/* Note: create_or_index_paths depends on create_index_paths /* Note: create_or_index_paths depends on create_index_paths
* to have marked OR restriction clauses with relevant indices; * to have marked OR restriction clauses with relevant indices;
* this is why it doesn't need to be given the full list of indices. * this is why it doesn't need to be given the list of indices.
*/ */
add_pathlist(rel, create_or_index_paths(root, rel, create_or_index_paths(root, rel, rel->baserestrictinfo);
rel->baserestrictinfo));
/* Now find the cheapest of the paths for this rel */ /* Now find the cheapest of the paths for this rel */
set_cheapest(rel, rel->pathlist); set_cheapest(rel);
} }
} }
...@@ -196,8 +193,8 @@ make_one_rel_by_joins(Query *root, int levels_needed) ...@@ -196,8 +193,8 @@ make_one_rel_by_joins(Query *root, int levels_needed)
xfunc_trypullup(rel); xfunc_trypullup(rel);
#endif #endif
/* Find and save the cheapest path for this rel */ /* Find and save the cheapest paths for this rel */
set_cheapest(rel, rel->pathlist); set_cheapest(rel);
#ifdef OPTIMIZER_DEBUG #ifdef OPTIMIZER_DEBUG
debug_print_rel(root, rel); debug_print_rel(root, rel);
...@@ -279,15 +276,26 @@ print_path(Query *root, Path *path, int indent) ...@@ -279,15 +276,26 @@ print_path(Query *root, Path *path, int indent)
if (join) if (join)
{ {
jp = (JoinPath *) path; jp = (JoinPath *) path;
printf("%s rows=%.0f cost=%f\n",
ptype, path->parent->rows, path->path_cost); printf("%s rows=%.0f cost=%.2f..%.2f\n",
ptype, path->parent->rows,
path->startup_cost, path->total_cost);
if (path->pathkeys)
{
for (i = 0; i < indent; i++)
printf("\t");
printf(" pathkeys=");
print_pathkeys(path->pathkeys, root->rtable);
}
switch (nodeTag(path)) switch (nodeTag(path))
{ {
case T_MergePath: case T_MergePath:
case T_HashPath: case T_HashPath:
for (i = 0; i < indent + 1; i++) for (i = 0; i < indent; i++)
printf("\t"); printf("\t");
printf(" clauses=("); printf(" clauses=(");
print_joinclauses(root, jp->joinrestrictinfo); print_joinclauses(root, jp->joinrestrictinfo);
printf(")\n"); printf(")\n");
...@@ -297,9 +305,9 @@ print_path(Query *root, Path *path, int indent) ...@@ -297,9 +305,9 @@ print_path(Query *root, Path *path, int indent)
if (mp->outersortkeys || mp->innersortkeys) if (mp->outersortkeys || mp->innersortkeys)
{ {
for (i = 0; i < indent + 1; i++) for (i = 0; i < indent; i++)
printf("\t"); printf("\t");
printf(" sortouter=%d sortinner=%d\n", printf(" sortouter=%d sortinner=%d\n",
((mp->outersortkeys) ? 1 : 0), ((mp->outersortkeys) ? 1 : 0),
((mp->innersortkeys) ? 1 : 0)); ((mp->innersortkeys) ? 1 : 0));
} }
...@@ -315,11 +323,14 @@ print_path(Query *root, Path *path, int indent) ...@@ -315,11 +323,14 @@ print_path(Query *root, Path *path, int indent)
{ {
int relid = lfirsti(path->parent->relids); int relid = lfirsti(path->parent->relids);
printf("%s(%d) rows=%.0f cost=%f\n", printf("%s(%d) rows=%.0f cost=%.2f..%.2f\n",
ptype, relid, path->parent->rows, path->path_cost); ptype, relid, path->parent->rows,
path->startup_cost, path->total_cost);
if (IsA(path, IndexPath)) if (path->pathkeys)
{ {
for (i = 0; i < indent; i++)
printf("\t");
printf(" pathkeys="); printf(" pathkeys=");
print_pathkeys(path->pathkeys, root->rtable); print_pathkeys(path->pathkeys, root->rtable);
} }
...@@ -339,8 +350,10 @@ debug_print_rel(Query *root, RelOptInfo *rel) ...@@ -339,8 +350,10 @@ debug_print_rel(Query *root, RelOptInfo *rel)
printf("\tpath list:\n"); printf("\tpath list:\n");
foreach(l, rel->pathlist) foreach(l, rel->pathlist)
print_path(root, lfirst(l), 1); print_path(root, lfirst(l), 1);
printf("\tcheapest path:\n"); printf("\tcheapest startup path:\n");
print_path(root, rel->cheapestpath, 1); print_path(root, rel->cheapest_startup_path, 1);
printf("\tcheapest total path:\n");
print_path(root, rel->cheapest_total_path, 1);
} }
#endif /* OPTIMIZER_DEBUG */ #endif /* OPTIMIZER_DEBUG */
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.79 2000/02/05 18:26:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.80 2000/02/15 20:49:16 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -83,7 +83,8 @@ static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -83,7 +83,8 @@ static List *index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index, static bool useful_for_mergejoin(RelOptInfo *rel, IndexOptInfo *index,
List *joininfo_list); List *joininfo_list);
static bool useful_for_ordering(Query *root, RelOptInfo *rel, static bool useful_for_ordering(Query *root, RelOptInfo *rel,
IndexOptInfo *index); IndexOptInfo *index,
ScanDirection scandir);
static bool match_index_to_operand(int indexkey, Var *operand, static bool match_index_to_operand(int indexkey, Var *operand,
RelOptInfo *rel, IndexOptInfo *index); RelOptInfo *rel, IndexOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel,
...@@ -106,6 +107,8 @@ static bool string_lessthan(const char * str1, const char * str2, ...@@ -106,6 +107,8 @@ static bool string_lessthan(const char * str1, const char * str2,
/* /*
* create_index_paths() * create_index_paths()
* Generate all interesting index paths for the given relation. * Generate all interesting index paths for the given relation.
* Candidate paths are added to the rel's pathlist (using add_path).
* Additional IndexPath nodes may also be added to rel's innerjoin list.
* *
* To be considered for an index scan, an index must match one or more * To be considered for an index scan, an index must match one or more
* restriction clauses or join clauses from the query's qual condition, * restriction clauses or join clauses from the query's qual condition,
...@@ -120,29 +123,26 @@ static bool string_lessthan(const char * str1, const char * str2, ...@@ -120,29 +123,26 @@ static bool string_lessthan(const char * str1, const char * str2,
* in its join clauses. In that context, values for the other rels' * in its join clauses. In that context, values for the other rels'
* attributes are available and fixed during any one scan of the indexpath. * attributes are available and fixed during any one scan of the indexpath.
* *
* This routine's return value is a list of plain IndexPaths for each * An IndexPath is generated and submitted to add_path() for each index
* index the routine deems potentially interesting for the current query * this routine deems potentially interesting for the current query
* (at most one IndexPath per index on the given relation). An innerjoin * (at most one IndexPath per index on the given relation). An innerjoin
* path is also generated for each interesting combination of outer join * path is also generated for each interesting combination of outer join
* relations. The innerjoin paths are *not* in the return list, but are * relations. The innerjoin paths are *not* passed to add_path(), but are
* appended to the "innerjoin" list of the relation itself. * appended to the "innerjoin" list of the relation for later consideration
* in nested-loop joins.
* *
* 'rel' is the relation for which we want to generate index paths * 'rel' is the relation for which we want to generate index paths
* 'indices' is a list of available indexes for 'rel' * 'indices' is a list of available indexes for 'rel'
* 'restrictinfo_list' is a list of restrictinfo nodes for 'rel' * 'restrictinfo_list' is a list of restrictinfo nodes for 'rel'
* 'joininfo_list' is a list of joininfo nodes for 'rel' * 'joininfo_list' is a list of joininfo nodes for 'rel'
*
* Returns a list of IndexPath access path descriptors. Additional
* IndexPath nodes may also be added to the rel->innerjoin list.
*/ */
List * void
create_index_paths(Query *root, create_index_paths(Query *root,
RelOptInfo *rel, RelOptInfo *rel,
List *indices, List *indices,
List *restrictinfo_list, List *restrictinfo_list,
List *joininfo_list) List *joininfo_list)
{ {
List *retval = NIL;
List *ilist; List *ilist;
foreach(ilist, indices) foreach(ilist, indices)
...@@ -189,9 +189,9 @@ create_index_paths(Query *root, ...@@ -189,9 +189,9 @@ create_index_paths(Query *root,
restrictinfo_list); restrictinfo_list);
if (restrictclauses != NIL) if (restrictclauses != NIL)
retval = lappend(retval, add_path(rel, (Path *) create_index_path(root, rel, index,
create_index_path(root, rel, index, restrictclauses,
restrictclauses)); NoMovementScanDirection));
/* /*
* 3. If this index can be used for a mergejoin, then create an * 3. If this index can be used for a mergejoin, then create an
...@@ -205,10 +205,22 @@ create_index_paths(Query *root, ...@@ -205,10 +205,22 @@ create_index_paths(Query *root,
if (restrictclauses == NIL) if (restrictclauses == NIL)
{ {
if (useful_for_mergejoin(rel, index, joininfo_list) || if (useful_for_mergejoin(rel, index, joininfo_list) ||
useful_for_ordering(root, rel, index)) useful_for_ordering(root, rel, index, ForwardScanDirection))
retval = lappend(retval, add_path(rel, (Path *)
create_index_path(root, rel, index, NIL)); create_index_path(root, rel, index,
NIL,
ForwardScanDirection));
} }
/*
* Currently, backwards scan is never considered except for the case
* of matching a query result ordering. Possibly should consider
* it in other places?
*/
if (useful_for_ordering(root, rel, index, BackwardScanDirection))
add_path(rel, (Path *)
create_index_path(root, rel, index,
NIL,
BackwardScanDirection));
/* /*
* 4. Create an innerjoin index path for each combination of * 4. Create an innerjoin index path for each combination of
...@@ -231,8 +243,6 @@ create_index_paths(Query *root, ...@@ -231,8 +243,6 @@ create_index_paths(Query *root,
joinouterrelids)); joinouterrelids));
} }
} }
return retval;
} }
...@@ -892,39 +902,26 @@ useful_for_mergejoin(RelOptInfo *rel, ...@@ -892,39 +902,26 @@ useful_for_mergejoin(RelOptInfo *rel,
* Determine whether the given index can produce an ordering matching * Determine whether the given index can produce an ordering matching
* the order that is wanted for the query result. * the order that is wanted for the query result.
* *
* We check to see whether either forward or backward scan direction can
* match the specified pathkeys.
*
* 'rel' is the relation for which 'index' is defined * 'rel' is the relation for which 'index' is defined
* 'scandir' is the contemplated scan direction
*/ */
static bool static bool
useful_for_ordering(Query *root, useful_for_ordering(Query *root,
RelOptInfo *rel, RelOptInfo *rel,
IndexOptInfo *index) IndexOptInfo *index,
ScanDirection scandir)
{ {
List *index_pathkeys; List *index_pathkeys;
if (root->query_pathkeys == NIL) if (root->query_pathkeys == NIL)
return false; /* no special ordering requested */ return false; /* no special ordering requested */
index_pathkeys = build_index_pathkeys(root, rel, index); index_pathkeys = build_index_pathkeys(root, rel, index, scandir);
if (index_pathkeys == NIL) if (index_pathkeys == NIL)
return false; /* unordered index */ return false; /* unordered index */
if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys)) return pathkeys_contained_in(root->query_pathkeys, index_pathkeys);
return true;
/* caution: commute_pathkeys destructively modifies its argument;
* safe because we just built the index_pathkeys for local use here.
*/
if (commute_pathkeys(index_pathkeys))
{
if (pathkeys_contained_in(root->query_pathkeys, index_pathkeys))
return true; /* useful as a reverse-order path */
}
return false;
} }
/**************************************************************************** /****************************************************************************
...@@ -1433,7 +1430,12 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -1433,7 +1430,12 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
pathnode->path.pathtype = T_IndexScan; pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->path.pathkeys = build_index_pathkeys(root, rel, index); /*
* There's no point in marking the path with any pathkeys, since
* it will only ever be used as the inner path of a nestloop,
* and so its ordering does not matter.
*/
pathnode->path.pathkeys = NIL;
indexquals = get_actual_clauses(clausegroup); indexquals = get_actual_clauses(clausegroup);
/* expand special operators to indexquals the executor can handle */ /* expand special operators to indexquals the executor can handle */
...@@ -1446,11 +1448,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index, ...@@ -1446,11 +1448,13 @@ index_innerjoin(Query *root, RelOptInfo *rel, IndexOptInfo *index,
pathnode->indexid = lconsi(index->indexoid, NIL); pathnode->indexid = lconsi(index->indexoid, NIL);
pathnode->indexqual = lcons(indexquals, NIL); pathnode->indexqual = lcons(indexquals, NIL);
/* We don't actually care what order the index scans in ... */
pathnode->indexscandir = NoMovementScanDirection;
/* joinrelids saves the rels needed on the outer side of the join */ /* joinrelids saves the rels needed on the outer side of the join */
pathnode->joinrelids = lfirst(outerrelids_list); pathnode->joinrelids = lfirst(outerrelids_list);
pathnode->path.path_cost = cost_index(root, rel, index, indexquals, cost_index(&pathnode->path, root, rel, index, indexquals, true);
true);
path_list = lappend(path_list, pathnode); path_list = lappend(path_list, pathnode);
outerrelids_list = lnext(outerrelids_list); outerrelids_list = lnext(outerrelids_list);
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.36 2000/02/05 18:26:09 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.37 2000/02/15 20:49:17 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/internal.h" #include "optimizer/internal.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/plancat.h" #include "optimizer/plancat.h"
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
...@@ -27,14 +28,13 @@ ...@@ -27,14 +28,13 @@
static void best_or_subclause_indices(Query *root, RelOptInfo *rel, static void best_or_subclause_indices(Query *root, RelOptInfo *rel,
List *subclauses, List *indices, List *subclauses, List *indices,
List **indexquals, IndexPath *pathnode);
List **indexids,
Cost *cost);
static void best_or_subclause_index(Query *root, RelOptInfo *rel, static void best_or_subclause_index(Query *root, RelOptInfo *rel,
Expr *subclause, List *indices, Expr *subclause, List *indices,
List **retIndexQual, List **retIndexQual,
Oid *retIndexid, Oid *retIndexid,
Cost *retCost); Cost *retStartupCost,
Cost *retTotalCost);
/* /*
...@@ -45,14 +45,13 @@ static void best_or_subclause_index(Query *root, RelOptInfo *rel, ...@@ -45,14 +45,13 @@ static void best_or_subclause_index(Query *root, RelOptInfo *rel,
* 'rel' is the relation entry for which the paths are to be created * 'rel' is the relation entry for which the paths are to be created
* 'clauses' is the list of available restriction clause nodes * 'clauses' is the list of available restriction clause nodes
* *
* Returns a list of index path nodes. * Returns nothing, but adds paths to rel->pathlist via add_path().
*
*/ */
List * void
create_or_index_paths(Query *root, create_or_index_paths(Query *root,
RelOptInfo *rel, List *clauses) RelOptInfo *rel,
List *clauses)
{ {
List *path_list = NIL;
List *clist; List *clist;
foreach(clist, clauses) foreach(clist, clauses)
...@@ -86,17 +85,6 @@ create_or_index_paths(Query *root, ...@@ -86,17 +85,6 @@ create_or_index_paths(Query *root,
* best available index for each subclause. * best available index for each subclause.
*/ */
IndexPath *pathnode = makeNode(IndexPath); IndexPath *pathnode = makeNode(IndexPath);
List *indexquals;
List *indexids;
Cost cost;
best_or_subclause_indices(root,
rel,
clausenode->clause->args,
clausenode->subclauseindices,
&indexquals,
&indexids,
&cost);
pathnode->path.pathtype = T_IndexScan; pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
...@@ -108,17 +96,21 @@ create_or_index_paths(Query *root, ...@@ -108,17 +96,21 @@ create_or_index_paths(Query *root,
*/ */
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
pathnode->indexid = indexids; /* We don't actually care what order the index scans in ... */
pathnode->indexqual = indexquals; pathnode->indexscandir = NoMovementScanDirection;
pathnode->joinrelids = NIL; /* no join clauses here */ pathnode->joinrelids = NIL; /* no join clauses here */
pathnode->path.path_cost = cost;
path_list = lappend(path_list, pathnode); best_or_subclause_indices(root,
rel,
clausenode->clause->args,
clausenode->subclauseindices,
pathnode);
add_path(rel, (Path *) pathnode);
} }
} }
} }
return path_list;
} }
/* /*
...@@ -128,53 +120,68 @@ create_or_index_paths(Query *root, ...@@ -128,53 +120,68 @@ create_or_index_paths(Query *root,
* indices. The cost is the sum of the individual index costs, since * indices. The cost is the sum of the individual index costs, since
* the executor will perform a scan for each subclause of the 'or'. * the executor will perform a scan for each subclause of the 'or'.
* *
* This routine also creates the indexquals and indexids lists that will * This routine also creates the indexqual and indexid lists that will
* be needed by the executor. The indexquals list has one entry for each * be needed by the executor. The indexqual list has one entry for each
* scan of the base rel, which is a sublist of indexqual conditions to * scan of the base rel, which is a sublist of indexqual conditions to
* apply in that scan. The implicit semantics are AND across each sublist * apply in that scan. The implicit semantics are AND across each sublist
* of quals, and OR across the toplevel list (note that the executor * of quals, and OR across the toplevel list (note that the executor
* takes care not to return any single tuple more than once). The indexids * takes care not to return any single tuple more than once). The indexid
* list gives the index to be used in each scan. * list gives the OID of the index to be used in each scan.
* *
* 'rel' is the node of the relation on which the indexes are defined * 'rel' is the node of the relation on which the indexes are defined
* 'subclauses' are the subclauses of the 'or' clause * 'subclauses' are the subclauses of the 'or' clause
* 'indices' is a list of sublists of the IndexOptInfo nodes that matched * 'indices' is a list of sublists of the IndexOptInfo nodes that matched
* each subclause of the 'or' clause * each subclause of the 'or' clause
* '*indexquals' gets the constructed indexquals for the path (a list * 'pathnode' is the IndexPath node being built.
*
* Results are returned by setting these fields of the passed pathnode:
* 'indexqual' gets the constructed indexquals for the path (a list
* of sublists of clauses, one sublist per scan of the base rel) * of sublists of clauses, one sublist per scan of the base rel)
* '*indexids' gets a list of the index OIDs for each scan of the rel * 'indexid' gets a list of the index OIDs for each scan of the rel
* '*cost' gets the total cost of the path * 'startup_cost' and 'total_cost' get the complete path costs.
*
* 'startup_cost' is the startup cost for the first index scan only;
* startup costs for later scans will be paid later on, so they just
* get reflected in total_cost.
*
* NOTE: we choose each scan on the basis of its total cost, ignoring startup
* cost. This is reasonable as long as all index types have zero or small
* startup cost, but we might have to work harder if any index types with
* nontrivial startup cost are ever invented.
*/ */
static void static void
best_or_subclause_indices(Query *root, best_or_subclause_indices(Query *root,
RelOptInfo *rel, RelOptInfo *rel,
List *subclauses, List *subclauses,
List *indices, List *indices,
List **indexquals, /* return value */ IndexPath *pathnode)
List **indexids, /* return value */
Cost *cost) /* return value */
{ {
List *slist; List *slist;
*indexquals = NIL; pathnode->indexqual = NIL;
*indexids = NIL; pathnode->indexid = NIL;
*cost = (Cost) 0.0; pathnode->path.startup_cost = 0;
pathnode->path.total_cost = 0;
foreach(slist, subclauses) foreach(slist, subclauses)
{ {
Expr *subclause = lfirst(slist); Expr *subclause = lfirst(slist);
List *best_indexqual; List *best_indexqual;
Oid best_indexid; Oid best_indexid;
Cost best_cost; Cost best_startup_cost;
Cost best_total_cost;
best_or_subclause_index(root, rel, subclause, lfirst(indices), best_or_subclause_index(root, rel, subclause, lfirst(indices),
&best_indexqual, &best_indexid, &best_cost); &best_indexqual, &best_indexid,
&best_startup_cost, &best_total_cost);
Assert(best_indexid != InvalidOid); Assert(best_indexid != InvalidOid);
*indexquals = lappend(*indexquals, best_indexqual); pathnode->indexqual = lappend(pathnode->indexqual, best_indexqual);
*indexids = lappendi(*indexids, best_indexid); pathnode->indexid = lappendi(pathnode->indexid, best_indexid);
*cost += best_cost; if (slist == subclauses) /* first scan? */
pathnode->path.startup_cost = best_startup_cost;
pathnode->path.total_cost += best_total_cost;
indices = lnext(indices); indices = lnext(indices);
} }
...@@ -182,16 +189,17 @@ best_or_subclause_indices(Query *root, ...@@ -182,16 +189,17 @@ best_or_subclause_indices(Query *root,
/* /*
* best_or_subclause_index * best_or_subclause_index
* Determines which is the best index to be used with a subclause of * Determines which is the best index to be used with a subclause of an
* an 'or' clause by estimating the cost of using each index and selecting * 'or' clause by estimating the cost of using each index and selecting
* the least expensive. * the least expensive (considering total cost only, for now).
* *
* 'rel' is the node of the relation on which the index is defined * 'rel' is the node of the relation on which the index is defined
* 'subclause' is the OR subclause being considered * 'subclause' is the OR subclause being considered
* 'indices' is a list of IndexOptInfo nodes that match the subclause * 'indices' is a list of IndexOptInfo nodes that match the subclause
* '*retIndexQual' gets a list of the indexqual conditions for the best index * '*retIndexQual' gets a list of the indexqual conditions for the best index
* '*retIndexid' gets the OID of the best index * '*retIndexid' gets the OID of the best index
* '*retCost' gets the cost of a scan with that index * '*retStartupCost' gets the startup cost of a scan with that index
* '*retTotalCost' gets the total cost of a scan with that index
*/ */
static void static void
best_or_subclause_index(Query *root, best_or_subclause_index(Query *root,
...@@ -200,7 +208,8 @@ best_or_subclause_index(Query *root, ...@@ -200,7 +208,8 @@ best_or_subclause_index(Query *root,
List *indices, List *indices,
List **retIndexQual, /* return value */ List **retIndexQual, /* return value */
Oid *retIndexid, /* return value */ Oid *retIndexid, /* return value */
Cost *retCost) /* return value */ Cost *retStartupCost, /* return value */
Cost *retTotalCost) /* return value */
{ {
bool first_time = true; bool first_time = true;
List *ilist; List *ilist;
...@@ -208,27 +217,28 @@ best_or_subclause_index(Query *root, ...@@ -208,27 +217,28 @@ best_or_subclause_index(Query *root,
/* if we don't match anything, return zeros */ /* if we don't match anything, return zeros */
*retIndexQual = NIL; *retIndexQual = NIL;
*retIndexid = InvalidOid; *retIndexid = InvalidOid;
*retCost = 0.0; *retStartupCost = 0;
*retTotalCost = 0;
foreach(ilist, indices) foreach(ilist, indices)
{ {
IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist); IndexOptInfo *index = (IndexOptInfo *) lfirst(ilist);
List *indexqual; List *indexqual;
Cost subcost; Path subclause_path;
Assert(IsA(index, IndexOptInfo)); Assert(IsA(index, IndexOptInfo));
/* Convert this 'or' subclause to an indexqual list */ /* Convert this 'or' subclause to an indexqual list */
indexqual = extract_or_indexqual_conditions(rel, index, subclause); indexqual = extract_or_indexqual_conditions(rel, index, subclause);
subcost = cost_index(root, rel, index, indexqual, cost_index(&subclause_path, root, rel, index, indexqual, false);
false);
if (first_time || subcost < *retCost) if (first_time || subclause_path.total_cost < *retTotalCost)
{ {
*retIndexQual = indexqual; *retIndexQual = indexqual;
*retIndexid = index->indexoid; *retIndexid = index->indexoid;
*retCost = subcost; *retStartupCost = subclause_path.startup_cost;
*retTotalCost = subclause_path.total_cost;
first_time = false; first_time = false;
} }
} }
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.4 2000/02/07 04:40:59 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/tidpath.c,v 1.5 2000/02/15 20:49:17 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -36,7 +36,7 @@ ...@@ -36,7 +36,7 @@
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
static List *create_tidscan_joinpaths(RelOptInfo *); static void create_tidscan_joinpaths(RelOptInfo *rel);
static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo); static List *TidqualFromRestrictinfo(List *relids, List *restrictinfo);
static bool isEvaluable(int varno, Node *node); static bool isEvaluable(int varno, Node *node);
static Node *TidequalClause(int varno, Expr *node); static Node *TidequalClause(int varno, Expr *node);
...@@ -234,61 +234,54 @@ TidqualFromRestrictinfo(List *relids, List *restrictinfo) ...@@ -234,61 +234,54 @@ TidqualFromRestrictinfo(List *relids, List *restrictinfo)
/* /*
* create_tidscan_joinpaths * create_tidscan_joinpaths
* Creates a path corresponding to a tid_direct scan, returning the * Create innerjoin paths if there are suitable joinclauses.
* pathnode.
* *
* XXX does this actually work?
*/ */
List * static void
create_tidscan_joinpaths(RelOptInfo *rel) create_tidscan_joinpaths(RelOptInfo *rel)
{ {
List *rlst = NIL, List *rlst = NIL,
*lst; *lst;
TidPath *pathnode = (TidPath *) NULL;
List *restinfo,
*tideval;
foreach (lst, rel->joininfo) foreach (lst, rel->joininfo)
{ {
JoinInfo *joininfo = (JoinInfo *)lfirst(lst); JoinInfo *joininfo = (JoinInfo *) lfirst(lst);
List *restinfo,
*tideval;
restinfo = joininfo->jinfo_restrictinfo; restinfo = joininfo->jinfo_restrictinfo;
tideval = TidqualFromRestrictinfo(rel->relids, restinfo); tideval = TidqualFromRestrictinfo(rel->relids, restinfo);
if (length(tideval) == 1) if (length(tideval) == 1)
{ {
pathnode = makeNode(TidPath); TidPath *pathnode = makeNode(TidPath);
pathnode->path.pathtype = T_TidScan; pathnode->path.pathtype = T_TidScan;
pathnode->path.parent = rel; pathnode->path.parent = rel;
pathnode->path.pathkeys = NIL; pathnode->path.pathkeys = NIL;
pathnode->path.path_cost = cost_tidscan(rel, tideval);
pathnode->tideval = tideval; pathnode->tideval = tideval;
pathnode->unjoined_relids = joininfo->unjoined_relids; pathnode->unjoined_relids = joininfo->unjoined_relids;
cost_tidscan(&pathnode->path, rel, tideval);
rlst = lappend(rlst, pathnode); rlst = lappend(rlst, pathnode);
} }
} }
rel->innerjoin = nconc(rel->innerjoin, rlst); rel->innerjoin = nconc(rel->innerjoin, rlst);
return rlst;
} }
/* /*
* create_tidscan_paths * create_tidscan_paths
* Creates a path corresponding to a tid direct scan, returning the * Creates paths corresponding to tid direct scans of the given rel.
* pathnode List. * Candidate paths are added to the rel's pathlist (using add_path).
*
*/ */
List * void
create_tidscan_paths(Query *root, RelOptInfo *rel) create_tidscan_paths(Query *root, RelOptInfo *rel)
{ {
List *rlst = NIL;
TidPath *pathnode = (TidPath *) NULL;
List *tideval = TidqualFromRestrictinfo(rel->relids, List *tideval = TidqualFromRestrictinfo(rel->relids,
rel->baserestrictinfo); rel->baserestrictinfo);
if (tideval) if (tideval)
pathnode = create_tidscan_path(rel, tideval); add_path(rel, (Path *) create_tidscan_path(rel, tideval));
if (pathnode)
rlst = lcons(pathnode, rlst);
create_tidscan_joinpaths(rel); create_tidscan_joinpaths(rel);
return rlst;
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.84 2000/02/07 04:41:00 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.85 2000/02/15 20:49:18 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -57,7 +57,9 @@ static Node *fix_indxqual_operand(Node *node, int baserelid, ...@@ -57,7 +57,9 @@ static Node *fix_indxqual_operand(Node *node, int baserelid,
Form_pg_index index, Form_pg_index index,
Oid *opclass); Oid *opclass);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid, static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual, List *indxqualorig); List *indxid, List *indxqual,
List *indxqualorig,
ScanDirection indexscandir);
static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid, static TidScan *make_tidscan(List *qptlist, List *qpqual, Index scanrelid,
List *tideval); List *tideval);
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree, static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
...@@ -427,9 +429,13 @@ create_indexscan_node(Query *root, ...@@ -427,9 +429,13 @@ create_indexscan_node(Query *root,
baserelid, baserelid,
best_path->indexid, best_path->indexid,
fixed_indxqual, fixed_indxqual,
indxqual); indxqual,
best_path->indexscandir);
copy_path_costsize(&scan_node->scan.plan, &best_path->path); copy_path_costsize(&scan_node->scan.plan, &best_path->path);
/* set up rows estimate (just to make EXPLAIN output reasonable) */
if (plan_rows < 1.0)
plan_rows = 1.0;
scan_node->scan.plan.plan_rows = plan_rows; scan_node->scan.plan.plan_rows = plan_rows;
return scan_node; return scan_node;
...@@ -437,16 +443,14 @@ create_indexscan_node(Query *root, ...@@ -437,16 +443,14 @@ create_indexscan_node(Query *root,
static TidScan * static TidScan *
make_tidscan(List *qptlist, make_tidscan(List *qptlist,
List *qpqual, List *qpqual,
Index scanrelid, Index scanrelid,
List *tideval) List *tideval)
{ {
TidScan *node = makeNode(TidScan); TidScan *node = makeNode(TidScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
plan->cost = 0; /* cost should be inserted by caller */
plan->plan_rows = 0;
plan->plan_width = 0;
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = qptlist; plan->targetlist = qptlist;
plan->qual = qpqual; plan->qual = qpqual;
...@@ -1038,13 +1042,15 @@ copy_path_costsize(Plan *dest, Path *src) ...@@ -1038,13 +1042,15 @@ copy_path_costsize(Plan *dest, Path *src)
{ {
if (src) if (src)
{ {
dest->cost = src->path_cost; dest->startup_cost = src->startup_cost;
dest->total_cost = src->total_cost;
dest->plan_rows = src->parent->rows; dest->plan_rows = src->parent->rows;
dest->plan_width = src->parent->width; dest->plan_width = src->parent->width;
} }
else else
{ {
dest->cost = 0; dest->startup_cost = 0;
dest->total_cost = 0;
dest->plan_rows = 0; dest->plan_rows = 0;
dest->plan_width = 0; dest->plan_width = 0;
} }
...@@ -1061,13 +1067,15 @@ copy_plan_costsize(Plan *dest, Plan *src) ...@@ -1061,13 +1067,15 @@ copy_plan_costsize(Plan *dest, Plan *src)
{ {
if (src) if (src)
{ {
dest->cost = src->cost; dest->startup_cost = src->startup_cost;
dest->total_cost = src->total_cost;
dest->plan_rows = src->plan_rows; dest->plan_rows = src->plan_rows;
dest->plan_width = src->plan_width; dest->plan_width = src->plan_width;
} }
else else
{ {
dest->cost = 0; dest->startup_cost = 0;
dest->total_cost = 0;
dest->plan_rows = 0; dest->plan_rows = 0;
dest->plan_width = 0; dest->plan_width = 0;
} }
...@@ -1130,7 +1138,7 @@ make_seqscan(List *qptlist, ...@@ -1130,7 +1138,7 @@ make_seqscan(List *qptlist,
SeqScan *node = makeNode(SeqScan); SeqScan *node = makeNode(SeqScan);
Plan *plan = &node->plan; Plan *plan = &node->plan;
copy_plan_costsize(plan, NULL); /* cost should be inserted by caller */
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = qptlist; plan->targetlist = qptlist;
plan->qual = qpqual; plan->qual = qpqual;
...@@ -1148,12 +1156,13 @@ make_indexscan(List *qptlist, ...@@ -1148,12 +1156,13 @@ make_indexscan(List *qptlist,
Index scanrelid, Index scanrelid,
List *indxid, List *indxid,
List *indxqual, List *indxqual,
List *indxqualorig) List *indxqualorig,
ScanDirection indexscandir)
{ {
IndexScan *node = makeNode(IndexScan); IndexScan *node = makeNode(IndexScan);
Plan *plan = &node->scan.plan; Plan *plan = &node->scan.plan;
copy_plan_costsize(plan, NULL); /* cost should be inserted by caller */
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = qptlist; plan->targetlist = qptlist;
plan->qual = qpqual; plan->qual = qpqual;
...@@ -1163,7 +1172,7 @@ make_indexscan(List *qptlist, ...@@ -1163,7 +1172,7 @@ make_indexscan(List *qptlist,
node->indxid = indxid; node->indxid = indxid;
node->indxqual = indxqual; node->indxqual = indxqual;
node->indxqualorig = indxqualorig; node->indxqualorig = indxqualorig;
node->indxorderdir = NoMovementScanDirection; node->indxorderdir = indexscandir;
node->scan.scanstate = (CommonScanState *) NULL; node->scan.scanstate = (CommonScanState *) NULL;
return node; return node;
...@@ -1219,6 +1228,10 @@ make_hash(List *tlist, Var *hashkey, Plan *lefttree) ...@@ -1219,6 +1228,10 @@ make_hash(List *tlist, Var *hashkey, Plan *lefttree)
Plan *plan = &node->plan; Plan *plan = &node->plan;
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree);
/* For plausibility, make startup & total costs equal total cost of
* input plan; this only affects EXPLAIN display not decisions.
*/
plan->startup_cost = plan->total_cost;
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = tlist; plan->targetlist = tlist;
plan->qual = NULL; plan->qual = NULL;
...@@ -1255,9 +1268,12 @@ make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount) ...@@ -1255,9 +1268,12 @@ make_sort(List *tlist, Oid nonameid, Plan *lefttree, int keycount)
{ {
Sort *node = makeNode(Sort); Sort *node = makeNode(Sort);
Plan *plan = &node->plan; Plan *plan = &node->plan;
Path sort_path; /* dummy for result of cost_sort */
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree); /* only care about copying size */
plan->cost += cost_sort(NIL, plan->plan_rows, plan->plan_width); cost_sort(&sort_path, NIL, lefttree->plan_rows, lefttree->plan_width);
plan->startup_cost = sort_path.startup_cost + lefttree->total_cost;
plan->total_cost = sort_path.total_cost + lefttree->total_cost;
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = tlist; plan->targetlist = tlist;
plan->qual = NIL; plan->qual = NIL;
...@@ -1279,7 +1295,11 @@ make_material(List *tlist, ...@@ -1279,7 +1295,11 @@ make_material(List *tlist,
Plan *plan = &node->plan; Plan *plan = &node->plan;
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree);
/* XXX shouldn't we charge some additional cost for materialization? */ /* For plausibility, make startup & total costs equal total cost of
* input plan; this only affects EXPLAIN display not decisions.
* XXX shouldn't we charge some additional cost for materialization?
*/
plan->startup_cost = plan->total_cost;
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = tlist; plan->targetlist = tlist;
plan->qual = NIL; plan->qual = NIL;
...@@ -1292,30 +1312,38 @@ make_material(List *tlist, ...@@ -1292,30 +1312,38 @@ make_material(List *tlist,
} }
Agg * Agg *
make_agg(List *tlist, Plan *lefttree) make_agg(List *tlist, List *qual, Plan *lefttree)
{ {
Agg *node = makeNode(Agg); Agg *node = makeNode(Agg);
Plan *plan = &node->plan;
copy_plan_costsize(&node->plan, lefttree); copy_plan_costsize(plan, lefttree);
/*
* Charge one cpu_operator_cost per aggregate function per input tuple.
*/
plan->total_cost += cpu_operator_cost * plan->plan_rows *
(length(pull_agg_clause((Node *) tlist)) +
length(pull_agg_clause((Node *) qual)));
/* /*
* The tuple width from the input node is OK, as is the cost (we are * We will produce a single output tuple if the input is not a Group,
* ignoring the cost of computing the aggregate; is there any value * and a tuple per group otherwise. For now, estimate the number of
* in accounting for it?). But the tuple count is bogus. We will * groups as 10% of the number of tuples --- bogus, but how to do better?
* produce a single tuple if the input is not a Group, and a tuple
* per group otherwise. For now, estimate the number of groups as
* 10% of the number of tuples --- bogus, but how to do better?
* (Note we assume the input Group node is in "tuplePerGroup" mode, * (Note we assume the input Group node is in "tuplePerGroup" mode,
* so it didn't reduce its row count already.) * so it didn't reduce its row count already.)
*/ */
if (IsA(lefttree, Group)) if (IsA(lefttree, Group))
node->plan.plan_rows *= 0.1; plan->plan_rows *= 0.1;
else else
node->plan.plan_rows = 1; {
node->plan.state = (EState *) NULL; plan->plan_rows = 1;
node->plan.qual = NULL; plan->startup_cost = plan->total_cost;
node->plan.targetlist = tlist; }
node->plan.lefttree = lefttree;
node->plan.righttree = (Plan *) NULL; plan->state = (EState *) NULL;
plan->qual = qual;
plan->targetlist = tlist;
plan->lefttree = lefttree;
plan->righttree = (Plan *) NULL;
return node; return node;
} }
...@@ -1328,8 +1356,14 @@ make_group(List *tlist, ...@@ -1328,8 +1356,14 @@ make_group(List *tlist,
Plan *lefttree) Plan *lefttree)
{ {
Group *node = makeNode(Group); Group *node = makeNode(Group);
Plan *plan = &node->plan;
copy_plan_costsize(&node->plan, lefttree); copy_plan_costsize(plan, lefttree);
/*
* Charge one cpu_operator_cost per comparison per input tuple.
* We assume all columns get compared at most of the tuples.
*/
plan->total_cost += cpu_operator_cost * plan->plan_rows * ngrp;
/* /*
* If tuplePerGroup (which is named exactly backwards) is true, * If tuplePerGroup (which is named exactly backwards) is true,
* we will return all the input tuples, so the input node's row count * we will return all the input tuples, so the input node's row count
...@@ -1338,12 +1372,13 @@ make_group(List *tlist, ...@@ -1338,12 +1372,13 @@ make_group(List *tlist,
* tuples --- bogus, but how to do better? * tuples --- bogus, but how to do better?
*/ */
if (! tuplePerGroup) if (! tuplePerGroup)
node->plan.plan_rows *= 0.1; plan->plan_rows *= 0.1;
node->plan.state = (EState *) NULL;
node->plan.qual = NULL; plan->state = (EState *) NULL;
node->plan.targetlist = tlist; plan->qual = NULL;
node->plan.lefttree = lefttree; plan->targetlist = tlist;
node->plan.righttree = (Plan *) NULL; plan->lefttree = lefttree;
plan->righttree = (Plan *) NULL;
node->tuplePerGroup = tuplePerGroup; node->tuplePerGroup = tuplePerGroup;
node->numCols = ngrp; node->numCols = ngrp;
node->grpColIdx = grpColIdx; node->grpColIdx = grpColIdx;
...@@ -1367,11 +1402,17 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList) ...@@ -1367,11 +1402,17 @@ make_unique(List *tlist, Plan *lefttree, List *distinctList)
List *slitem; List *slitem;
copy_plan_costsize(plan, lefttree); copy_plan_costsize(plan, lefttree);
/*
* Charge one cpu_operator_cost per comparison per input tuple.
* We assume all columns get compared at most of the tuples.
*/
plan->total_cost += cpu_operator_cost * plan->plan_rows * numCols;
/* /*
* As for Group, we make the unsupported assumption that there will be * As for Group, we make the unsupported assumption that there will be
* 10% as many tuples out as in. * 10% as many tuples out as in.
*/ */
plan->plan_rows *= 0.1; plan->plan_rows *= 0.1;
plan->state = (EState *) NULL; plan->state = (EState *) NULL;
plan->targetlist = tlist; plan->targetlist = tlist;
plan->qual = NIL; plan->qual = NIL;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.44 2000/02/07 04:41:00 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.45 2000/02/15 20:49:18 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "optimizer/cost.h" #include "optimizer/cost.h"
#include "optimizer/joininfo.h" #include "optimizer/joininfo.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "optimizer/var.h" #include "optimizer/var.h"
...@@ -31,7 +32,6 @@ static void add_restrict_and_join_to_rel(Query *root, Node *clause); ...@@ -31,7 +32,6 @@ static void add_restrict_and_join_to_rel(Query *root, Node *clause);
static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, static void add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
Relids join_relids); Relids join_relids);
static void add_vars_to_targetlist(Query *root, List *vars); static void add_vars_to_targetlist(Query *root, List *vars);
static void set_restrictinfo_joininfo(RestrictInfo *restrictinfo);
static void check_mergejoinable(RestrictInfo *restrictinfo); static void check_mergejoinable(RestrictInfo *restrictinfo);
static void check_hashjoinable(RestrictInfo *restrictinfo); static void check_hashjoinable(RestrictInfo *restrictinfo);
...@@ -150,7 +150,9 @@ add_restrict_and_join_to_rels(Query *root, List *clauses) ...@@ -150,7 +150,9 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field * Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
* (depending on whether the clause is a join) of each base relation * (depending on whether the clause is a join) of each base relation
* mentioned in the clause. A RestrictInfo node is created and added to * mentioned in the clause. A RestrictInfo node is created and added to
* the appropriate list for each rel. * the appropriate list for each rel. Also, if the clause uses a
* mergejoinable operator, enter the left- and right-side expressions
* into the query's lists of equijoined vars.
*/ */
static void static void
add_restrict_and_join_to_rel(Query *root, Node *clause) add_restrict_and_join_to_rel(Query *root, Node *clause)
...@@ -181,14 +183,29 @@ add_restrict_and_join_to_rel(Query *root, Node *clause) ...@@ -181,14 +183,29 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
rel->baserestrictinfo = lcons(restrictinfo, rel->baserestrictinfo = lcons(restrictinfo,
rel->baserestrictinfo); rel->baserestrictinfo);
/*
* Check for a "mergejoinable" clause even though it's not a join
* clause. This is so that we can recognize that "a.x = a.y" makes
* x and y eligible to be considered equal, even when they belong
* to the same rel. Without this, we would not recognize that
* "a.x = a.y AND a.x = b.z AND a.y = c.q" allows us to consider
* z and q equal after their rels are joined.
*/
check_mergejoinable(restrictinfo);
} }
else else
{ {
/* /*
* 'clause' is a join clause, since there is more than one atom in * 'clause' is a join clause, since there is more than one atom in
* the relid list. Set additional RestrictInfo fields for joining. * the relid list. Set additional RestrictInfo fields for joining.
*
* We need the merge info whether or not mergejoin is enabled (for
* constructing equijoined-var lists), but we don't bother setting
* hash info if hashjoin is disabled.
*/ */
set_restrictinfo_joininfo(restrictinfo); check_mergejoinable(restrictinfo);
if (enable_hashjoin)
check_hashjoinable(restrictinfo);
/* /*
* Add clause to the join lists of all the relevant * Add clause to the join lists of all the relevant
* relations. (If, perchance, 'clause' contains NO vars, then * relations. (If, perchance, 'clause' contains NO vars, then
...@@ -202,6 +219,15 @@ add_restrict_and_join_to_rel(Query *root, Node *clause) ...@@ -202,6 +219,15 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
*/ */
add_vars_to_targetlist(root, vars); add_vars_to_targetlist(root, vars);
} }
/*
* If the clause has a mergejoinable operator, then the two sides
* represent equivalent PathKeyItems for path keys: any path that is
* sorted by one side will also be sorted by the other (after joining,
* that is). Record the key equivalence for future use.
*/
if (restrictinfo->mergejoinoperator != InvalidOid)
add_equijoined_keys(root, restrictinfo);
} }
/* /*
...@@ -247,24 +273,10 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, ...@@ -247,24 +273,10 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
/***************************************************************************** /*****************************************************************************
* *
* JOININFO * CHECKS FOR MERGEJOINABLE AND HASHJOINABLE CLAUSES
* *
*****************************************************************************/ *****************************************************************************/
/*
* set_restrictinfo_joininfo
* Examine a RestrictInfo that has been determined to be a join clause,
* and set the merge and hash info fields if it can be merge/hash joined.
*/
static void
set_restrictinfo_joininfo(RestrictInfo *restrictinfo)
{
if (enable_mergejoin)
check_mergejoinable(restrictinfo);
if (enable_hashjoin)
check_hashjoinable(restrictinfo);
}
/* /*
* check_mergejoinable * check_mergejoinable
* If the restrictinfo's clause is mergejoinable, set the mergejoin * If the restrictinfo's clause is mergejoinable, set the mergejoin
...@@ -272,10 +284,7 @@ set_restrictinfo_joininfo(RestrictInfo *restrictinfo) ...@@ -272,10 +284,7 @@ set_restrictinfo_joininfo(RestrictInfo *restrictinfo)
* *
* Currently, we support mergejoin for binary opclauses where * Currently, we support mergejoin for binary opclauses where
* both operands are simple Vars and the operator is a mergejoinable * both operands are simple Vars and the operator is a mergejoinable
* operator. (Note: since we are only examining clauses that were * operator.
* classified as joins, it is certain that the two Vars belong to
* different relations... if we accepted more general clause structures
* we might need to check that the two sides refer to different rels...)
*/ */
static void static void
check_mergejoinable(RestrictInfo *restrictinfo) check_mergejoinable(RestrictInfo *restrictinfo)
...@@ -320,10 +329,7 @@ check_mergejoinable(RestrictInfo *restrictinfo) ...@@ -320,10 +329,7 @@ check_mergejoinable(RestrictInfo *restrictinfo)
* *
* Currently, we support hashjoin for binary opclauses where * Currently, we support hashjoin for binary opclauses where
* both operands are simple Vars and the operator is a hashjoinable * both operands are simple Vars and the operator is a hashjoinable
* operator. (Note: since we are only examining clauses that were * operator.
* classified as joins, it is certain that the two Vars belong to
* different relations... if we accepted more general clause structures
* we might need to check that the two sides refer to different rels...)
*/ */
static void static void
check_hashjoinable(RestrictInfo *restrictinfo) check_hashjoinable(RestrictInfo *restrictinfo)
......
This diff is collapsed.
This diff is collapsed.
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.27 2000/01/26 05:56:38 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.28 2000/02/15 20:49:18 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -123,6 +123,7 @@ static Node * ...@@ -123,6 +123,7 @@ static Node *
make_subplan(SubLink *slink) make_subplan(SubLink *slink)
{ {
SubPlan *node = makeNode(SubPlan); SubPlan *node = makeNode(SubPlan);
double tuple_fraction;
Plan *plan; Plan *plan;
List *lst; List *lst;
Node *result; Node *result;
...@@ -132,7 +133,26 @@ make_subplan(SubLink *slink) ...@@ -132,7 +133,26 @@ make_subplan(SubLink *slink)
PlannerQueryLevel++; /* we becomes child */ PlannerQueryLevel++; /* we becomes child */
node->plan = plan = union_planner((Query *) slink->subselect); /*
* For an EXISTS subplan, tell lower-level planner to expect that
* only the first tuple will be retrieved. For ALL, ANY, and MULTIEXPR
* subplans, we will be able to stop evaluating if the test condition
* fails, so very often not all the tuples will be retrieved; for lack
* of a better idea, specify 50% retrieval. For EXPR_SUBLINK use default
* behavior.
*
* NOTE: if you change these numbers, also change cost_qual_eval_walker
* in costsize.c.
*/
if (slink->subLinkType == EXISTS_SUBLINK)
tuple_fraction = 1.0; /* just like a LIMIT 1 */
else if (slink->subLinkType == EXPR_SUBLINK)
tuple_fraction = -1.0; /* default behavior */
else
tuple_fraction = 0.5; /* 50% */
node->plan = plan = union_planner((Query *) slink->subselect,
tuple_fraction);
/* /*
* Assign subPlan, extParam and locParam to plan nodes. At the moment, * Assign subPlan, extParam and locParam to plan nodes. At the moment,
......
This diff is collapsed.
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.46 2000/01/26 05:56:40 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/plancat.c,v 1.47 2000/02/15 20:49:20 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -118,6 +118,7 @@ find_secondary_indexes(Query *root, Index relid) ...@@ -118,6 +118,7 @@ find_secondary_indexes(Query *root, Index relid)
} }
else else
info->indpred = NIL; info->indpred = NIL;
info->lossy = index->indislossy;
for (i = 0; i < INDEX_MAX_KEYS; i++) for (i = 0; i < INDEX_MAX_KEYS; i++)
info->indexkeys[i] = index->indkey[i]; info->indexkeys[i] = index->indkey[i];
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.23 2000/02/07 04:41:02 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.24 2000/02/15 20:49:21 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,12 +62,14 @@ get_base_rel(Query *root, int relid) ...@@ -62,12 +62,14 @@ get_base_rel(Query *root, int relid)
rel->width = 0; rel->width = 0;
rel->targetlist = NIL; rel->targetlist = NIL;
rel->pathlist = NIL; rel->pathlist = NIL;
rel->cheapestpath = (Path *) NULL; rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL;
rel->pruneable = true; rel->pruneable = true;
rel->indexed = false; rel->indexed = false;
rel->pages = 0; rel->pages = 0;
rel->tuples = 0; rel->tuples = 0;
rel->baserestrictinfo = NIL; rel->baserestrictinfo = NIL;
rel->baserestrictcost = 0;
rel->joininfo = NIL; rel->joininfo = NIL;
rel->innerjoin = NIL; rel->innerjoin = NIL;
...@@ -180,12 +182,14 @@ get_join_rel(Query *root, ...@@ -180,12 +182,14 @@ get_join_rel(Query *root,
joinrel->width = 0; joinrel->width = 0;
joinrel->targetlist = NIL; joinrel->targetlist = NIL;
joinrel->pathlist = NIL; joinrel->pathlist = NIL;
joinrel->cheapestpath = (Path *) NULL; joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL;
joinrel->pruneable = true; joinrel->pruneable = true;
joinrel->indexed = false; joinrel->indexed = false;
joinrel->pages = 0; joinrel->pages = 0;
joinrel->tuples = 0; joinrel->tuples = 0;
joinrel->baserestrictinfo = NIL; joinrel->baserestrictinfo = NIL;
joinrel->baserestrictcost = 0;
joinrel->joininfo = NIL; joinrel->joininfo = NIL;
joinrel->innerjoin = NIL; joinrel->innerjoin = NIL;
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment