Major planner/optimizer revision: get rid of PathOrder node type,

store all ordering information in pathkeys lists (which are now lists of
lists of PathKeyItem nodes, not just lists of lists of vars).  This was
a big win --- the code is smaller and IMHO more understandable than it
was, even though it handles more cases.  I believe the node changes will
not force an initdb for anyone; planner nodes don't show up in stored
rules.
This commit is contained in:
Tom Lane 1999-08-16 02:17:58 +00:00
parent 08320bfb22
commit e6381966c1
43 changed files with 1937 additions and 3270 deletions

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.90 1999/08/09 06:20:23 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.91 1999/08/16 02:17:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -1030,10 +1030,6 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->indexkeys[len] = 0;
}
newnode->relam = from->relam;
newnode->indproc = from->indproc;
Node_Copy(from, newnode, indpred);
if (from->ordering)
{
for (len = 0; from->ordering[len] != 0; len++)
@ -1044,6 +1040,10 @@ _copyRelOptInfo(RelOptInfo *from)
newnode->ordering[len] = 0;
}
newnode->relam = from->relam;
newnode->indproc = from->indproc;
Node_Copy(from, newnode, indpred);
Node_Copy(from, newnode, restrictinfo);
Node_Copy(from, newnode, joininfo);
Node_Copy(from, newnode, innerjoin);
@ -1061,8 +1061,6 @@ _copyRelOptInfo(RelOptInfo *from)
static void
CopyPathFields(Path *from, Path *newnode)
{
newnode->pathtype = from->pathtype;
/*
* Modify the next line, since it causes the copying to cycle (i.e.
* the parent points right back here! -- JMH, 7/7/92. Old version:
@ -1072,32 +1070,9 @@ CopyPathFields(Path *from, Path *newnode)
newnode->path_cost = from->path_cost;
newnode->pathorder = makeNode(PathOrder);
newnode->pathorder->ordtype = from->pathorder->ordtype;
if (from->pathorder->ordtype == SORTOP_ORDER)
{
int len,
i;
Oid *ordering = from->pathorder->ord.sortop;
if (ordering)
{
for (len = 0; ordering[len] != 0; len++)
;
newnode->pathorder->ord.sortop = (Oid *) palloc(sizeof(Oid) * (len + 1));
for (i = 0; i < len; i++)
newnode->pathorder->ord.sortop[i] = ordering[i];
newnode->pathorder->ord.sortop[len] = 0;
}
}
else
Node_Copy(from, newnode, pathorder->ord.merge);
newnode->pathtype = from->pathtype;
Node_Copy(from, newnode, pathkeys);
newnode->outerjoincost = from->outerjoincost;
newnode->joinid = listCopy(from->joinid);
}
/* ----------------
@ -1135,32 +1110,20 @@ _copyIndexPath(IndexPath *from)
*/
newnode->indexid = listCopy(from->indexid);
Node_Copy(from, newnode, indexqual);
if (from->indexkeys)
{
int i,
len;
for (len = 0; from->indexkeys[len] != 0; len++)
;
newnode->indexkeys = (int *) palloc(sizeof(int) * (len + 1));
for (i = 0; i < len; i++)
newnode->indexkeys[i] = from->indexkeys[i];
newnode->indexkeys[len] = 0;
}
newnode->joinrelids = listCopy(from->joinrelids);
return newnode;
}
/* ----------------
* CopyNestPathFields
* CopyJoinPathFields
*
* This function copies the fields of the NestPath node. It is used by
* all the copy functions for classes which inherit from NestPath.
* This function copies the fields of the JoinPath node. It is used by
* all the copy functions for classes which inherit from JoinPath.
* ----------------
*/
static void
CopyNestPathFields(NestPath *from, NestPath *newnode)
CopyJoinPathFields(JoinPath *from, JoinPath *newnode)
{
Node_Copy(from, newnode, pathinfo);
Node_Copy(from, newnode, outerjoinpath);
@ -1181,7 +1144,7 @@ _copyNestPath(NestPath *from)
* ----------------
*/
CopyPathFields((Path *) from, (Path *) newnode);
CopyNestPathFields(from, newnode);
CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
return newnode;
}
@ -1200,7 +1163,7 @@ _copyMergePath(MergePath *from)
* ----------------
*/
CopyPathFields((Path *) from, (Path *) newnode);
CopyNestPathFields((NestPath *) from, (NestPath *) newnode);
CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
/* ----------------
* copy the remainder of the node
@ -1227,76 +1190,32 @@ _copyHashPath(HashPath *from)
* ----------------
*/
CopyPathFields((Path *) from, (Path *) newnode);
CopyNestPathFields((NestPath *) from, (NestPath *) newnode);
CopyJoinPathFields((JoinPath *) from, (JoinPath *) newnode);
/* ----------------
* copy remainder of node
* ----------------
*/
Node_Copy(from, newnode, path_hashclauses);
Node_Copy(from, newnode, outerhashkeys);
Node_Copy(from, newnode, innerhashkeys);
return newnode;
}
/* ----------------
* _copyOrderKey
* _copyPathKeyItem
* ----------------
*/
static OrderKey *
_copyOrderKey(OrderKey *from)
static PathKeyItem *
_copyPathKeyItem(PathKeyItem *from)
{
OrderKey *newnode = makeNode(OrderKey);
PathKeyItem *newnode = makeNode(PathKeyItem);
/* ----------------
* copy remainder of node
* ----------------
*/
newnode->attribute_number = from->attribute_number;
newnode->array_index = from->array_index;
return newnode;
}
/* ----------------
* _copyJoinKey
* ----------------
*/
static JoinKey *
_copyJoinKey(JoinKey *from)
{
JoinKey *newnode = makeNode(JoinKey);
/* ----------------
* copy remainder of node
* ----------------
*/
Node_Copy(from, newnode, outer);
Node_Copy(from, newnode, inner);
return newnode;
}
/* ----------------
* _copyMergeOrder
* ----------------
*/
static MergeOrder *
_copyMergeOrder(MergeOrder *from)
{
MergeOrder *newnode = makeNode(MergeOrder);
/* ----------------
* copy remainder of node
* ----------------
*/
newnode->join_operator = from->join_operator;
newnode->left_operator = from->left_operator;
newnode->right_operator = from->right_operator;
newnode->left_type = from->left_type;
newnode->right_type = from->right_type;
Node_Copy(from, newnode, key);
newnode->sortop = from->sortop;
return newnode;
}
@ -1315,83 +1234,16 @@ _copyRestrictInfo(RestrictInfo *from)
* ----------------
*/
Node_Copy(from, newnode, clause);
newnode->selectivity = from->selectivity;
Node_Copy(from, newnode, indexids);
Node_Copy(from, newnode, mergejoinorder);
Node_Copy(from, newnode, subclauseindices);
newnode->mergejoinoperator = from->mergejoinoperator;
newnode->left_sortop = from->left_sortop;
newnode->right_sortop = from->right_sortop;
newnode->hashjoinoperator = from->hashjoinoperator;
return newnode;
}
/* ----------------
* CopyJoinMethodFields
*
* This function copies the fields of the JoinMethod node. It is used by
* all the copy functions for classes which inherit from JoinMethod.
* ----------------
*/
static void
CopyJoinMethodFields(JoinMethod *from, JoinMethod *newnode)
{
Node_Copy(from, newnode, jmkeys);
Node_Copy(from, newnode, clauses);
return;
}
/* ----------------
* _copyJoinMethod
* ----------------
*/
static JoinMethod *
_copyJoinMethod(JoinMethod *from)
{
JoinMethod *newnode = makeNode(JoinMethod);
CopyJoinMethodFields(from, newnode);
return newnode;
}
/* ----------------
* _copyHashInfo
* ----------------
*/
static HashInfo *
_copyHashInfo(HashInfo *from)
{
HashInfo *newnode = makeNode(HashInfo);
/* ----------------
* copy remainder of node
* ----------------
*/
CopyJoinMethodFields((JoinMethod *) from, (JoinMethod *) newnode);
newnode->hashop = from->hashop;
return newnode;
}
/* ----------------
* _copyMergeInfo
* ----------------
*/
static MergeInfo *
_copyMergeInfo(MergeInfo *from)
{
MergeInfo *newnode = makeNode(MergeInfo);
/* ----------------
* copy remainder of node
* ----------------
*/
CopyJoinMethodFields((JoinMethod *) from, (JoinMethod *) newnode);
Node_Copy(from, newnode, m_ordering);
return newnode;
}
/* ----------------
* _copyJoinInfo
* ----------------
@ -1408,9 +1260,6 @@ _copyJoinInfo(JoinInfo *from)
newnode->unjoined_relids = listCopy(from->unjoined_relids);
Node_Copy(from, newnode, jinfo_restrictinfo);
newnode->mergejoinable = from->mergejoinable;
newnode->hashjoinable = from->hashjoinable;
return newnode;
}
@ -1756,27 +1605,12 @@ copyObject(void *from)
case T_HashPath:
retval = _copyHashPath(from);
break;
case T_OrderKey:
retval = _copyOrderKey(from);
break;
case T_JoinKey:
retval = _copyJoinKey(from);
break;
case T_MergeOrder:
retval = _copyMergeOrder(from);
case T_PathKeyItem:
retval = _copyPathKeyItem(from);
break;
case T_RestrictInfo:
retval = _copyRestrictInfo(from);
break;
case T_JoinMethod:
retval = _copyJoinMethod(from);
break;
case T_HashInfo:
retval = _copyHashInfo(from);
break;
case T_MergeInfo:
retval = _copyMergeInfo(from);
break;
case T_JoinInfo:
retval = _copyJoinInfo(from);
break;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.46 1999/08/09 06:20:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.47 1999/08/16 02:17:41 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -264,15 +264,15 @@ _equalRelOptInfo(RelOptInfo *a, RelOptInfo *b)
/* We treat RelOptInfos as equal if they refer to the same base rels
* joined in the same order. Is this sufficient?
*/
return equal(a->relids, b->relids);
return equali(a->relids, b->relids);
}
static bool
_equalJoinMethod(JoinMethod *a, JoinMethod *b)
_equalPathKeyItem(PathKeyItem *a, PathKeyItem *b)
{
if (!equal(a->jmkeys, b->jmkeys))
if (a->sortop != b->sortop)
return false;
if (!equal(a->clauses, b->clauses))
if (!equal(a->key, b->key))
return false;
return true;
}
@ -282,48 +282,13 @@ _equalPath(Path *a, Path *b)
{
if (a->pathtype != b->pathtype)
return false;
if (a->parent != b->parent) /* should this use equal() ? */
if (!equal(a->parent, b->parent))
return false;
/* do not check path_cost, since it may not be set yet, and being
* a float there are roundoff error issues anyway...
*/
/* XXX this should probably be in an _equalPathOrder function... */
if (a->pathorder->ordtype != b->pathorder->ordtype)
return false;
if (a->pathorder->ordtype == SORTOP_ORDER)
{
if (a->pathorder->ord.sortop == NULL ||
b->pathorder->ord.sortop == NULL)
{
if (a->pathorder->ord.sortop != b->pathorder->ord.sortop)
return false;
}
else
{
int i = 0;
while (a->pathorder->ord.sortop[i] != 0)
{
if (a->pathorder->ord.sortop[i] != b->pathorder->ord.sortop[i])
return false;
i++;
}
if (b->pathorder->ord.sortop[i] != 0)
return false;
}
}
else
{
if (!equal(a->pathorder->ord.merge, b->pathorder->ord.merge))
return false;
}
if (!equal(a->pathkeys, b->pathkeys))
return false;
/* do not check outerjoincost either */
if (!equali(a->joinid, b->joinid))
return false;
return true;
}
@ -336,12 +301,13 @@ _equalIndexPath(IndexPath *a, IndexPath *b)
return false;
if (!equal(a->indexqual, b->indexqual))
return false;
/* We do not need to check indexkeys */
if (!equali(a->joinrelids, b->joinrelids))
return false;
return true;
}
static bool
_equalNestPath(NestPath *a, NestPath *b)
_equalJoinPath(JoinPath *a, JoinPath *b)
{
if (!_equalPath((Path *) a, (Path *) b))
return false;
@ -354,10 +320,18 @@ _equalNestPath(NestPath *a, NestPath *b)
return true;
}
static bool
_equalNestPath(NestPath *a, NestPath *b)
{
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
return true;
}
static bool
_equalMergePath(MergePath *a, MergePath *b)
{
if (!_equalNestPath((NestPath *) a, (NestPath *) b))
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
if (!equal(a->path_mergeclauses, b->path_mergeclauses))
return false;
@ -371,50 +345,10 @@ _equalMergePath(MergePath *a, MergePath *b)
static bool
_equalHashPath(HashPath *a, HashPath *b)
{
if (!_equalNestPath((NestPath *) a, (NestPath *) b))
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
if (!equal(a->path_hashclauses, b->path_hashclauses))
return false;
if (!equal(a->outerhashkeys, b->outerhashkeys))
return false;
if (!equal(a->innerhashkeys, b->innerhashkeys))
return false;
return true;
}
static bool
_equalJoinKey(JoinKey *a, JoinKey *b)
{
if (!equal(a->outer, b->outer))
return false;
if (!equal(a->inner, b->inner))
return false;
return true;
}
static bool
_equalMergeOrder(MergeOrder *a, MergeOrder *b)
{
if (a->join_operator != b->join_operator)
return false;
if (a->left_operator != b->left_operator)
return false;
if (a->right_operator != b->right_operator)
return false;
if (a->left_type != b->left_type)
return false;
if (a->right_type != b->right_type)
return false;
return true;
}
static bool
_equalHashInfo(HashInfo *a, HashInfo *b)
{
if (!_equalJoinMethod((JoinMethod *) a, (JoinMethod *) b))
return false;
if (a->hashop != b->hashop)
return false;
return true;
}
@ -457,31 +391,33 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
return true;
}
static bool
_equalJoinInfo(JoinInfo *a, JoinInfo *b)
{
if (!equal(a->unjoined_relids, b->unjoined_relids))
return false;
if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo))
return false;
if (a->mergejoinable != b->mergejoinable)
return false;
if (a->hashjoinable != b->hashjoinable)
return false;
return true;
}
static bool
_equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
{
if (!equal(a->clause, b->clause))
return false;
/* do not check selectivity because of roundoff error worries */
if (!equal(a->mergejoinorder, b->mergejoinorder))
if (!equal(a->subclauseindices, b->subclauseindices))
return false;
if (a->mergejoinoperator != b->mergejoinoperator)
return false;
if (a->left_sortop != b->left_sortop)
return false;
if (a->right_sortop != b->right_sortop)
return false;
if (a->hashjoinoperator != b->hashjoinoperator)
return false;
return equal(a->indexids, b->indexids);
return true;
}
static bool
_equalJoinInfo(JoinInfo *a, JoinInfo *b)
{
if (!equali(a->unjoined_relids, b->unjoined_relids))
return false;
if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo))
return false;
return true;
}
static bool
@ -778,8 +714,8 @@ equal(void *a, void *b)
case T_RelOptInfo:
retval = _equalRelOptInfo(a, b);
break;
case T_JoinMethod:
retval = _equalJoinMethod(a, b);
case T_PathKeyItem:
retval = _equalPathKeyItem(a, b);
break;
case T_Path:
retval = _equalPath(a, b);
@ -796,15 +732,6 @@ equal(void *a, void *b)
case T_HashPath:
retval = _equalHashPath(a, b);
break;
case T_JoinKey:
retval = _equalJoinKey(a, b);
break;
case T_MergeOrder:
retval = _equalMergeOrder(a, b);
break;
case T_HashInfo:
retval = _equalHashInfo(a, b);
break;
case T_IndexScan:
retval = _equalIndexScan(a, b);
break;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.24 1999/07/27 03:51:08 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/Attic/freefuncs.c,v 1.25 1999/08/16 02:17:42 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -706,6 +706,9 @@ _freeRelOptInfo(RelOptInfo *node)
freeObject(node->targetlist);
freeObject(node->pathlist);
/* is this right? cheapestpath will typically be a pointer into
* pathlist, won't it?
*/
freeObject(node->cheapestpath);
if (node->classlist)
@ -714,11 +717,11 @@ _freeRelOptInfo(RelOptInfo *node)
if (node->indexkeys)
pfree(node->indexkeys);
freeObject(node->indpred);
if (node->ordering)
pfree(node->ordering);
freeObject(node->indpred);
freeObject(node->restrictinfo);
freeObject(node->joininfo);
freeObject(node->innerjoin);
@ -736,20 +739,9 @@ _freeRelOptInfo(RelOptInfo *node)
static void
FreePathFields(Path *node)
{
if (node->pathorder->ordtype == SORTOP_ORDER)
{
if (node->pathorder->ord.sortop)
pfree(node->pathorder->ord.sortop);
}
else
freeObject(node->pathorder->ord.merge);
pfree(node->pathorder); /* is it an object, but we don't have
* separate free for it */
/* we do NOT free the parent; it doesn't belong to the Path */
freeObject(node->pathkeys);
freeList(node->joinid);
}
/* ----------------
@ -783,22 +775,20 @@ _freeIndexPath(IndexPath *node)
*/
freeList(node->indexid);
freeObject(node->indexqual);
if (node->indexkeys)
pfree(node->indexkeys);
freeList(node->joinrelids);
pfree(node);
}
/* ----------------
* FreeNestPathFields
* FreeJoinPathFields
*
* This function frees the fields of the NestPath node. It is used by
* all the free functions for classes which inherit node NestPath.
* This function frees the fields of the JoinPath node. It is used by
* all the free functions for classes which inherit node JoinPath.
* ----------------
*/
static void
FreeNestPathFields(NestPath *node)
FreeJoinPathFields(JoinPath *node)
{
freeObject(node->pathinfo);
freeObject(node->outerjoinpath);
@ -817,7 +807,7 @@ _freeNestPath(NestPath *node)
* ----------------
*/
FreePathFields((Path *) node);
FreeNestPathFields(node);
FreeJoinPathFields((JoinPath *) node);
pfree(node);
}
@ -834,7 +824,7 @@ _freeMergePath(MergePath *node)
* ----------------
*/
FreePathFields((Path *) node);
FreeNestPathFields((NestPath *) node);
FreeJoinPathFields((JoinPath *) node);
/* ----------------
* free the remainder of the node
@ -859,60 +849,33 @@ _freeHashPath(HashPath *node)
* ----------------
*/
FreePathFields((Path *) node);
FreeNestPathFields((NestPath *) node);
FreeJoinPathFields((JoinPath *) node);
/* ----------------
* free remainder of node
* ----------------
*/
freeObject(node->path_hashclauses);
freeObject(node->outerhashkeys);
freeObject(node->innerhashkeys);
pfree(node);
}
/* ----------------
* _freeOrderKey
* _freePathKeyItem
* ----------------
*/
static void
_freeOrderKey(OrderKey *node)
{
pfree(node);
}
/* ----------------
* _freeJoinKey
* ----------------
*/
static void
_freeJoinKey(JoinKey *node)
_freePathKeyItem(PathKeyItem *node)
{
/* ----------------
* free remainder of node
* ----------------
*/
freeObject(node->outer);
freeObject(node->inner);
freeObject(node->key);
pfree(node);
}
/* ----------------
* _freeMergeOrder
* ----------------
*/
static void
_freeMergeOrder(MergeOrder *node)
{
/* ----------------
* free remainder of node
* ----------------
*/
pfree(node);
}
/* ----------------
* _freeRestrictInfo
@ -926,68 +889,10 @@ _freeRestrictInfo(RestrictInfo *node)
* ----------------
*/
freeObject(node->clause);
freeObject(node->indexids);
freeObject(node->mergejoinorder);
pfree(node);
}
/* ----------------
* FreeJoinMethodFields
*
* This function frees the fields of the JoinMethod node. It is used by
* all the free functions for classes which inherit node JoinMethod.
* ----------------
*/
static void
FreeJoinMethodFields(JoinMethod *node)
{
freeObject(node->jmkeys);
freeObject(node->clauses);
return;
}
/* ----------------
* _freeJoinMethod
* ----------------
*/
static void
_freeJoinMethod(JoinMethod *node)
{
FreeJoinMethodFields(node);
pfree(node);
}
/* ----------------
* _freeHInfo
* ----------------
*/
static void
_freeHashInfo(HashInfo *node)
{
/* ----------------
* free remainder of node
* ----------------
/* this is certainly wrong? index RelOptInfos don't belong to
* RestrictInfo...
*/
FreeJoinMethodFields((JoinMethod *) node);
pfree(node);
}
/* ----------------
* _freeMInfo
* ----------------
*/
static void
_freeMergeInfo(MergeInfo *node)
{
/* ----------------
* free remainder of node
* ----------------
*/
FreeJoinMethodFields((JoinMethod *) node);
freeObject(node->m_ordering);
freeObject(node->subclauseindices);
pfree(node);
}
@ -1279,27 +1184,12 @@ freeObject(void *node)
case T_HashPath:
_freeHashPath(node);
break;
case T_OrderKey:
_freeOrderKey(node);
break;
case T_JoinKey:
_freeJoinKey(node);
break;
case T_MergeOrder:
_freeMergeOrder(node);
case T_PathKeyItem:
_freePathKeyItem(node);
break;
case T_RestrictInfo:
_freeRestrictInfo(node);
break;
case T_JoinMethod:
_freeJoinMethod(node);
break;
case T_HashInfo:
_freeHashInfo(node);
break;
case T_MergeInfo:
_freeMergeInfo(node);
break;
case T_JoinInfo:
_freeJoinInfo(node);
break;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.26 1999/08/14 19:29:35 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.27 1999/08/16 02:17:42 tgl Exp $
*
* NOTES
* XXX a few of the following functions are duplicated to handle
@ -447,6 +447,31 @@ intLispRemove(int elem, List *list)
#endif
/*
* ltruncate
* Truncate a list to n elements.
* Does nothing if n >= length(list).
* NB: the list is modified in-place!
*/
List *
ltruncate(int n, List *list)
{
List *ptr;
if (n <= 0)
return NIL; /* truncate to zero length */
foreach(ptr, list)
{
if (--n == 0)
{
lnext(ptr) = NIL;
break;
}
}
return list;
}
/*
* set_difference
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/nodes.c,v 1.10 1999/07/17 20:17:07 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/nodes.c,v 1.11 1999/08/16 02:17:42 tgl Exp $
*
* HISTORY
* Andrew Yu Oct 20, 1994 file creation
@ -32,7 +32,7 @@ newNode(Size size, NodeTag tag)
{
Node *newNode;
Assert(size >= 4); /* need the tag, at least */
Assert(size >= sizeof(Node)); /* need the tag, at least */
newNode = (Node *) palloc(size);
MemSet((char *) newNode, 0, size);

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: outfuncs.c,v 1.92 1999/08/09 06:20:24 momjian Exp $
* $Id: outfuncs.c,v 1.93 1999/08/16 02:17:42 tgl Exp $
*
* NOTES
* Every (plan) node in POSTGRES has an associated "out" routine which
@ -880,35 +880,6 @@ _outRowMark(StringInfo str, RowMark *node)
appendStringInfo(str, " ROWMARK :rti %u :info %u", node->rti, node->info);
}
/*
* Path is a subclass of Node.
*/
static void
_outPathOrder(StringInfo str, PathOrder *node)
{
appendStringInfo(str, " PATHORDER :ordtype %d ",
node->ordtype);
if (node->ordtype == SORTOP_ORDER)
{
int i;
appendStringInfo(str, " :sortop ");
if (node->ord.sortop == NULL)
appendStringInfo(str, "<>");
else
{
for (i = 0; node->ord.sortop[i] != 0; i++)
appendStringInfo(str, " %d ", node->ord.sortop[i]);
appendStringInfo(str, " %d ", 0);
}
}
else
{
appendStringInfo(str, " :merge ");
_outNode(str, node->ord.merge);
}
}
/*
* Path is a subclass of Node.
*/
@ -919,9 +890,6 @@ _outPath(StringInfo str, Path *node)
node->pathtype,
node->path_cost);
_outNode(str, node->pathkeys);
appendStringInfo(str, " :pathorder ");
_outNode(str, node->pathorder);
}
/*
@ -936,14 +904,14 @@ _outIndexPath(StringInfo str, IndexPath *node)
node->path.path_cost);
_outNode(str, node->path.pathkeys);
appendStringInfo(str, " :pathorder ");
_outNode(str, node->path.pathorder);
appendStringInfo(str, " :indexid ");
_outIntList(str, node->indexid);
appendStringInfo(str, " :indexqual ");
_outNode(str, node->indexqual);
appendStringInfo(str, " :joinrelids ");
_outIntList(str, node->joinrelids);
}
/*
@ -958,9 +926,6 @@ _outNestPath(StringInfo str, NestPath *node)
node->path.path_cost);
_outNode(str, node->path.pathkeys);
appendStringInfo(str, " :pathorder ");
_outNode(str, node->path.pathorder);
appendStringInfo(str, " :pathinfo ");
_outNode(str, node->pathinfo);
@ -970,11 +935,9 @@ _outNestPath(StringInfo str, NestPath *node)
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x :outjoincost %f :joinid ",
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->outerjoinpath,
(int) node->innerjoinpath,
node->path.outerjoincost);
_outIntList(str, node->path.joinid);
(int) node->innerjoinpath);
}
/*
@ -989,9 +952,6 @@ _outMergePath(StringInfo str, MergePath *node)
node->jpath.path.path_cost);
_outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :pathorder ");
_outNode(str, node->jpath.path.pathorder);
appendStringInfo(str, " :pathinfo ");
_outNode(str, node->jpath.pathinfo);
@ -1001,11 +961,9 @@ _outMergePath(StringInfo str, MergePath *node)
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x :outerjoincost %f :joinid ",
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->jpath.outerjoinpath,
(int) node->jpath.innerjoinpath,
(int) node->jpath.path.outerjoincost);
_outIntList(str, node->jpath.path.joinid);
(int) node->jpath.innerjoinpath);
appendStringInfo(str, " :path_mergeclauses ");
_outNode(str, node->path_mergeclauses);
@ -1029,9 +987,6 @@ _outHashPath(StringInfo str, HashPath *node)
node->jpath.path.path_cost);
_outNode(str, node->jpath.path.pathkeys);
appendStringInfo(str, " :pathorder ");
_outNode(str, node->jpath.path.pathorder);
appendStringInfo(str, " :pathinfo ");
_outNode(str, node->jpath.pathinfo);
@ -1041,64 +996,23 @@ _outHashPath(StringInfo str, HashPath *node)
*/
appendStringInfo(str,
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x :outerjoincost %f :joinid ",
" :outerjoinpath @ 0x%x :innerjoinpath @ 0x%x ",
(int) node->jpath.outerjoinpath,
(int) node->jpath.innerjoinpath,
node->jpath.path.outerjoincost);
_outIntList(str, node->jpath.path.joinid);
(int) node->jpath.innerjoinpath);
appendStringInfo(str, " :path_hashclauses ");
_outNode(str, node->path_hashclauses);
appendStringInfo(str, " :outerhashkeys ");
_outNode(str, node->outerhashkeys);
appendStringInfo(str, " :innerhashkeys ");
_outNode(str, node->innerhashkeys);
}
/*
* OrderKey is a subclass of Node.
* PathKeyItem is a subclass of Node.
*/
static void
_outOrderKey(StringInfo str, OrderKey *node)
_outPathKeyItem(StringInfo str, PathKeyItem *node)
{
appendStringInfo(str,
" ORDERKEY :attribute_number %d :array_index %d ",
node->attribute_number,
node->array_index);
}
/*
* JoinKey is a subclass of Node.
*/
static void
_outJoinKey(StringInfo str, JoinKey *node)
{
appendStringInfo(str, " JOINKEY :outer ");
_outNode(str, node->outer);
appendStringInfo(str, " :inner ");
_outNode(str, node->inner);
}
/*
* MergeOrder is a subclass of Node.
*/
static void
_outMergeOrder(StringInfo str, MergeOrder *node)
{
appendStringInfo(str,
" MERGEORDER :join_operator %u :left_operator %u :right_operator %u ",
node->join_operator,
node->left_operator,
node->right_operator);
appendStringInfo(str,
" :left_type %u :right_type %u ",
node->left_type,
node->right_type);
appendStringInfo(str, " PATHKEYITEM :sortop %u :key ",
node->sortop);
_outNode(str, node->key);
}
/*
@ -1111,41 +1025,14 @@ _outRestrictInfo(StringInfo str, RestrictInfo *node)
_outNode(str, node->clause);
appendStringInfo(str,
" :selectivity %f :indexids ",
" :selectivity %f :subclauseindices ",
node->selectivity);
_outNode(str, node->indexids);
appendStringInfo(str, " :mergejoinorder ");
_outNode(str, node->mergejoinorder);
_outNode(str, node->subclauseindices);
appendStringInfo(str, " :mergejoinoperator %u ", node->mergejoinoperator);
appendStringInfo(str, " :left_sortop %u ", node->left_sortop);
appendStringInfo(str, " :right_sortop %u ", node->right_sortop);
appendStringInfo(str, " :hashjoinoperator %u ", node->hashjoinoperator);
}
/*
* JoinMethod is a subclass of Node.
*/
static void
_outJoinMethod(StringInfo str, JoinMethod *node)
{
appendStringInfo(str, " JOINMETHOD :jmkeys ");
_outNode(str, node->jmkeys);
appendStringInfo(str, " :clauses ");
_outNode(str, node->clauses);
}
/*
* HashInfo is a subclass of JoinMethod.
*/
static void
_outHashInfo(StringInfo str, HashInfo *node)
{
appendStringInfo(str, " HASHINFO :hashop %u :jmkeys ", node->hashop);
_outNode(str, node->jmethod.jmkeys);
appendStringInfo(str, " :clauses ");
_outNode(str, node->jmethod.clauses);
}
/*
@ -1159,10 +1046,6 @@ _outJoinInfo(StringInfo str, JoinInfo *node)
appendStringInfo(str, " :jinfo_restrictinfo ");
_outNode(str, node->jinfo_restrictinfo);
appendStringInfo(str, " :mergejoinable %s :hashjoinable %s ",
node->mergejoinable ? "true" : "false",
node->hashjoinable ? "true" : "false");
}
/*
@ -1541,9 +1424,6 @@ _outNode(StringInfo str, void *obj)
case T_RowMark:
_outRowMark(str, obj);
break;
case T_PathOrder:
_outPathOrder(str, obj);
break;
case T_Path:
_outPath(str, obj);
break;
@ -1559,24 +1439,12 @@ _outNode(StringInfo str, void *obj)
case T_HashPath:
_outHashPath(str, obj);
break;
case T_OrderKey:
_outOrderKey(str, obj);
break;
case T_JoinKey:
_outJoinKey(str, obj);
break;
case T_MergeOrder:
_outMergeOrder(str, obj);
case T_PathKeyItem:
_outPathKeyItem(str, obj);
break;
case T_RestrictInfo:
_outRestrictInfo(str, obj);
break;
case T_JoinMethod:
_outJoinMethod(str, obj);
break;
case T_HashInfo:
_outHashInfo(str, obj);
break;
case T_JoinInfo:
_outJoinInfo(str, obj);
break;

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.31 1999/07/17 20:17:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.32 1999/08/16 02:17:43 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -204,7 +204,7 @@ print_expr(Node *expr, List *rtable)
/*
* print_pathkeys -
* pathkeys list of list of Var's
* pathkeys list of list of PathKeyItems
*/
void
print_pathkeys(List *pathkeys, List *rtable)
@ -220,9 +220,9 @@ print_pathkeys(List *pathkeys, List *rtable)
printf("(");
foreach(k, pathkey)
{
Node *var = lfirst(k);
PathKeyItem *item = lfirst(k);
print_expr(var, rtable);
print_expr(item->key, rtable);
if (lnext(k))
printf(", ");
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.71 1999/08/09 06:20:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/nodes/readfuncs.c,v 1.72 1999/08/16 02:17:43 tgl Exp $
*
* NOTES
* Most of the read functions for plan nodes are tested. (In fact, they
@ -1413,55 +1413,6 @@ _readRowMark()
return local_node;
}
/* ----------------
* _readPathOrder
*
* PathOrder is part of Path and it's subclasses.
* ----------------
*/
static PathOrder *
_readPathOrder()
{
PathOrder *local_node;
char *token;
int length;
local_node = makeNode(PathOrder);
token = lsptok(NULL, &length); /* get :ordtype */
token = lsptok(NULL, &length); /* now read it */
local_node->ordtype = atol(token);
if (local_node->ordtype == SORTOP_ORDER)
{
token = lsptok(NULL, &length); /* get :sortop */
if (length == 0)
local_node->ord.sortop = NULL;
else
{
int i = -1;
local_node->ord.sortop = palloc(sizeof(Oid) * (INDEX_MAX_KEYS + 1));
do
{
i++;
Assert(i <= INDEX_MAX_KEYS);
token = lsptok(NULL, &length); /* now read it */
local_node->ord.sortop[i] = strtoul(token, NULL, 10);
} while (local_node->ord.sortop[i] != 0);
}
}
else
{
token = lsptok(NULL, &length); /* get :merge */
local_node->ord.merge = nodeRead(true); /* now read it */
}
return local_node;
}
/* ----------------
* _readPath
*
@ -1485,9 +1436,6 @@ _readPath()
token = lsptok(NULL, &length); /* now read it */
local_node->path_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathorder */
local_node->pathorder = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->pathkeys = nodeRead(true); /* now read it */
@ -1517,9 +1465,6 @@ _readIndexPath()
token = lsptok(NULL, &length); /* now read it */
local_node->path.path_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathorder */
local_node->path.pathorder = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */
@ -1529,6 +1474,9 @@ _readIndexPath()
token = lsptok(NULL, &length); /* get :indexqual */
local_node->indexqual = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :joinrelids */
local_node->joinrelids = toIntList(nodeRead(true));
return local_node;
}
@ -1545,7 +1493,6 @@ _readNestPath()
char *token;
int length;
local_node = makeNode(NestPath);
token = lsptok(NULL, &length); /* get :pathtype */
@ -1556,9 +1503,6 @@ _readNestPath()
token = lsptok(NULL, &length); /* now read it */
local_node->path.path_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathorder */
local_node->path.pathorder = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->path.pathkeys = nodeRead(true); /* now read it */
@ -1585,14 +1529,6 @@ _readNestPath()
local_node->innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :outerjoincost */
token = lsptok(NULL, &length); /* now read it */
local_node->path.outerjoincost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :joinid */
local_node->path.joinid = toIntList(nodeRead(true)); /* now read it */
return local_node;
}
@ -1621,9 +1557,6 @@ _readMergePath()
local_node->jpath.path.path_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathorder */
local_node->jpath.path.pathorder = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
@ -1650,14 +1583,6 @@ _readMergePath()
local_node->jpath.innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :outerjoincost */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.outerjoincost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :joinid */
local_node->jpath.path.joinid = toIntList(nodeRead(true)); /* now read it */
token = lsptok(NULL, &length); /* get :path_mergeclauses */
local_node->path_mergeclauses = nodeRead(true); /* now read it */
@ -1695,9 +1620,6 @@ _readHashPath()
local_node->jpath.path.path_cost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :pathorder */
local_node->jpath.path.pathorder = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :pathkeys */
local_node->jpath.path.pathkeys = nodeRead(true); /* now read it */
@ -1724,116 +1646,34 @@ _readHashPath()
local_node->jpath.innerjoinpath = NULL;
token = lsptok(NULL, &length); /* get :outerjoincost */
token = lsptok(NULL, &length); /* now read it */
local_node->jpath.path.outerjoincost = (Cost) atof(token);
token = lsptok(NULL, &length); /* get :joinid */
local_node->jpath.path.joinid = toIntList(nodeRead(true)); /* now read it */
token = lsptok(NULL, &length); /* get :path_hashclauses */
local_node->path_hashclauses = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :outerhashkeys */
local_node->outerhashkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :innerhashkeys */
local_node->innerhashkeys = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* _readOrderKey
* _readPathKeyItem
*
* OrderKey is a subclass of Node.
* PathKeyItem is a subclass of Node.
* ----------------
*/
static OrderKey *
_readOrderKey()
static PathKeyItem *
_readPathKeyItem()
{
OrderKey *local_node;
PathKeyItem *local_node;
char *token;
int length;
local_node = makeNode(OrderKey);
local_node = makeNode(PathKeyItem);
token = lsptok(NULL, &length); /* get :attribute_number */
token = lsptok(NULL, &length); /* get :sortop */
token = lsptok(NULL, &length); /* now read it */
local_node->attribute_number = atoi(token);
local_node->sortop = atol(token);
token = lsptok(NULL, &length); /* get :array_index */
token = lsptok(NULL, &length); /* now read it */
local_node->array_index = strtoul(token, NULL, 10);
return local_node;
}
/* ----------------
* _readJoinKey
*
* JoinKey is a subclass of Node.
* ----------------
*/
static JoinKey *
_readJoinKey()
{
JoinKey *local_node;
char *token;
int length;
local_node = makeNode(JoinKey);
token = lsptok(NULL, &length); /* get :outer */
local_node->outer = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :inner */
local_node->inner = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* _readMergeOrder
*
* MergeOrder is a subclass of Node.
* ----------------
*/
static MergeOrder *
_readMergeOrder()
{
MergeOrder *local_node;
char *token;
int length;
local_node = makeNode(MergeOrder);
token = lsptok(NULL, &length); /* get :join_operator */
token = lsptok(NULL, &length); /* now read it */
local_node->join_operator = atol(token);
token = lsptok(NULL, &length); /* get :left_operator */
token = lsptok(NULL, &length); /* now read it */
local_node->left_operator = atol(token);
token = lsptok(NULL, &length); /* get :right_operator */
token = lsptok(NULL, &length); /* now read it */
local_node->right_operator = atol(token);
token = lsptok(NULL, &length); /* get :left_type */
token = lsptok(NULL, &length); /* now read it */
local_node->left_type = atol(token);
token = lsptok(NULL, &length); /* get :right_type */
token = lsptok(NULL, &length); /* now read it */
local_node->right_type = atol(token);
token = lsptok(NULL, &length); /* get :key */
local_node->key = nodeRead(true); /* now read it */
return local_node;
}
@ -1858,76 +1698,30 @@ _readRestrictInfo()
token = lsptok(NULL, &length); /* get :selectivity */
token = lsptok(NULL, &length); /* now read it */
local_node->selectivity = atof(token);
token = lsptok(NULL, &length); /* get :indexids */
local_node->indexids = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :subclauseindices */
local_node->subclauseindices = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :mergejoinorder */
local_node->mergejoinorder = (MergeOrder *) nodeRead(true);
token = lsptok(NULL, &length); /* get :mergejoinoperator */
token = lsptok(NULL, &length); /* now read it */
local_node->mergejoinoperator = atol(token);
token = lsptok(NULL, &length); /* get :left_sortop */
token = lsptok(NULL, &length); /* now read it */
local_node->left_sortop = atol(token);
token = lsptok(NULL, &length); /* get :right_sortop */
token = lsptok(NULL, &length); /* now read it */
local_node->right_sortop = atol(token);
token = lsptok(NULL, &length); /* get :hashjoinoperator */
token = lsptok(NULL, &length); /* now read it */
local_node->hashjoinoperator = atol(token);
return local_node;
}
/* ----------------
* _readJoinMethod
*
* JoinMethod is a subclass of Node.
* ----------------
*/
static JoinMethod *
_readJoinMethod()
{
JoinMethod *local_node;
char *token;
int length;
local_node = makeNode(JoinMethod);
token = lsptok(NULL, &length); /* get :jmkeys */
local_node->jmkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :clauses */
local_node->clauses = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* _readHashInfo
*
* HashInfo is a subclass of JoinMethod.
* ----------------
*/
static HashInfo *
_readHashInfo()
{
HashInfo *local_node;
char *token;
int length;
local_node = makeNode(HashInfo);
token = lsptok(NULL, &length); /* get :hashop */
token = lsptok(NULL, &length); /* now read it */
local_node->hashop = strtoul(token, NULL, 10);
token = lsptok(NULL, &length); /* get :jmkeys */
local_node->jmethod.jmkeys = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :clauses */
local_node->jmethod.clauses = nodeRead(true); /* now read it */
return local_node;
}
/* ----------------
* _readJoinInfo()
*
@ -1949,20 +1743,6 @@ _readJoinInfo()
token = lsptok(NULL, &length); /* get :jinfo_restrictinfo */
local_node->jinfo_restrictinfo = nodeRead(true); /* now read it */
token = lsptok(NULL, &length); /* get :mergejoinable */
if (!strncmp(token, "true", 4))
local_node->mergejoinable = true;
else
local_node->mergejoinable = false;
token = lsptok(NULL, &length); /* get :hashjoinable */
if (!strncmp(token, "true", 4))
local_node->hashjoinable = true;
else
local_node->hashjoinable = false;
return local_node;
}
@ -2065,8 +1845,6 @@ parsePlanString(void)
return_value = _readTargetEntry();
else if (!strncmp(token, "RTE", length))
return_value = _readRangeTblEntry();
else if (!strncmp(token, "PATHORDER", length))
return_value = _readPathOrder();
else if (!strncmp(token, "PATH", length))
return_value = _readPath();
else if (!strncmp(token, "INDEXPATH", length))
@ -2077,20 +1855,12 @@ parsePlanString(void)
return_value = _readMergePath();
else if (!strncmp(token, "HASHPATH", length))
return_value = _readHashPath();
else if (!strncmp(token, "ORDERKEY", length))
return_value = _readOrderKey();
else if (!strncmp(token, "JOINKEY", length))
return_value = _readJoinKey();
else if (!strncmp(token, "MERGEORDER", length))
return_value = _readMergeOrder();
else if (!strncmp(token, "RETRICTINFO", length))
else if (!strncmp(token, "PATHKEYITEM", length))
return_value = _readPathKeyItem();
else if (!strncmp(token, "RESTRICTINFO", length))
return_value = _readRestrictInfo();
else if (!strncmp(token, "JOINMETHOD", length))
return_value = _readJoinMethod();
else if (!strncmp(token, "JOININFO", length))
return_value = _readJoinInfo();
else if (!strncmp(token, "HASHINFO", length))
return_value = _readHashInfo();
else if (!strncmp(token, "ITER", length))
return_value = _readIter();
else if (!strncmp(token, "QUERY", length))

View File

@ -1,37 +1,63 @@
Summary
-------
The optimizer generates optimial query plans by doing several steps:
The optimizer generates optimal query plans by doing a more-or-less
exhaustive search through the ways of executing the query. During
the planning/optimizing process, we build "Path" trees representing
the different ways of doing a query. We select the cheapest Path
that generates the desired relation and turn it into a Plan to pass
to the executor. (There is pretty much a one-to-one correspondence
between the Path and Plan trees, but Path nodes omit info that won't
be needed during planning, and include info needed for planning that
won't be needed by the executor.)
1) Take each relation in a query, and make a RelOptInfo structure for
it. Find each way of accessing the relation, called a Path, including
sequential and index scans, and add it to RelOptInfo.pathlist. Also
create RelOptInfo.joininfo that lists all the joins that involve this
relation. For example, the WHERE clause "tab1.col1 = tab2.col1"
generates a JoinInfo for tab1 listing tab2 as an unjoined relation, and
tab2's joininfo shows tab1 as an unjoined relation.
The best Path tree is found by a recursive process:
2) Join each RelOptInfo to other RelOptInfo as specified in
RelOptInfo.joininfo. At this point each RelOptInfo is a single
relation, so you are joining every relation to the other relations as
joined in the WHERE clause.
1) Take each base relation in the query, and make a RelOptInfo structure
for it. Find each potentially useful way of accessing the relation,
including sequential and index scans, and make a Path representing that
way. All the Paths made for a given relation are placed in its
RelOptInfo.pathlist. (Actually, we discard Paths that are obviously
inferior alternatives before they ever get into the pathlist --- what
ends up in the pathlist is the cheapest way of generating each potentially
useful sort ordering of the relation.) Also create RelOptInfo.joininfo
nodes that list all the joins that involve this relation. For example,
the WHERE clause "tab1.col1 = tab2.col1" generates a JoinInfo for tab1
listing tab2 as an unjoined relation, and also one for tab2 showing tab1
as an unjoined relation.
Joins occur using two RelOptInfos. One is outer, the other inner.
Outers drive lookups of values in the inner. In a nested loop, lookups
of values in the inner occur by scanning to find each matching inner
row. In a mergejoin, inner and outer rows are ordered, and are accessed
in order, so only one scan of inner is required to perform the entire
join. In a hashjoin, inner rows are hashed for lookups.
2) Consider joining each RelOptInfo to each other RelOptInfo specified in
its RelOptInfo.joininfo, and generate a Path for each possible join method.
At this stage each input RelOptInfo is a single relation, so we are joining
every relation to the other relations as joined in the WHERE clause. We
generate a new "join" RelOptInfo for each possible combination of two
"base" RelOptInfos, and put all the plausible paths for that combination
into the join RelOptInfo's pathlist. (As before, we keep only the cheapest
alternative that generates any one sort ordering of the result.)
Each unique join combination becomes a new RelOptInfo. The RelOptInfo
is now the joining of two relations. RelOptInfo.pathlist are various
paths to create the joined result, having different orderings depending
on the join method used.
Joins always occur using two RelOptInfos. One is outer, the other inner.
Outers drive lookups of values in the inner. In a nested loop, lookups of
values in the inner occur by scanning the inner path once per outer tuple
to find each matching inner row. In a mergejoin, inner and outer rows are
ordered, and are accessed in order, so only one scan is required to perform
the entire join: both inner and outer paths are scanned in-sync. (There's
not a lot of difference between inner and outer in a mergejoin...) In a
hashjoin, the inner is scanned first and all its rows are entered in a
hashtable, then the outer is scanned and for each row we lookup the join
key in the hashtable.
3) At this point, every RelOptInfo is joined to each other again, with
a new relation added to each RelOptInfo. This continues until all
relations have been joined into one RelOptInfo, and the cheapest Path is
chosen.
A Path for a join relation is actually a tree structure, with the top
Path node representing the join method. It has left and right subpaths
that represent the scan methods used for the two input relations.
3) If we only had two base relations, we are done: we just pick the
cheapest path for the join RelOptInfo. If we had more than two, we now
need to consider ways of joining join RelOptInfos to each other to make
join RelOptInfos that represent more than two base relations. This process
is repeated until we have finally built a RelOptInfo that represents all
the base relations in the query. Then we pick its cheapest Path.
For example:
SELECT *
FROM tab1, tab2, tab3, tab4
@ -67,8 +93,11 @@ Optimizer Functions
These directories take the Query structure returned by the parser, and
generate a plan used by the executor. The /plan directory generates the
plan, the /path generates all possible ways to join the tables, and
/prep handles special cases like inheritance. /utils is utility stuff.
actual output plan, the /path code generates all possible ways to join the
tables, and /prep handles special cases like inheritance. /util is utility
stuff. /geqo is the separate "genetic optimization" planner --- it does
a semi-random search rather than exhaustively considering all possible
join trees.
planner()
handle inheritance by processing separately
@ -136,8 +165,8 @@ planner()
Optimizer Structures
--------------------
Optimizer Data Structures
-------------------------
RelOptInfo - a relation or joined relations
@ -145,9 +174,39 @@ RelOptInfo - a relation or joined relations
JoinInfo - join clauses, including the relids needed for the join
Path - every way to generate a RelOptInfo(sequential,index,joins)
SeqScan - a plain Path node with nodeTag = T_SeqScan
IndexPath - index scans
NestPath - nested joins
NestPath - nested-loop joins
MergePath - merge joins
HashPath - hash joins
PathOrder - every ordering type (sort, merge of relations)
PathKeys - a data structure representing the ordering of a path
The optimizer spends a good deal of its time worrying about the ordering
of the tuples returned by a path. The reason this is useful is that by
knowing the sort ordering of a path, we may be able to use that path as
the left or right input of a mergejoin and avoid an explicit sort step.
Nestloops and hash joins don't really care what the order of their inputs
is, but mergejoin needs suitably ordered inputs. Therefore, all paths
generated during the optimization process are marked with their sort order
(to the extent that it is known) for possible use by a higher-level merge.
It is also possible to avoid an explicit sort step to implement a user's
ORDER BY clause if the final path has the right ordering already.
Currently, this is not very well implemented: we avoid generating a
redundant sort if the chosen path has the desired order, but we do not do
anything to encourage the selection of such a path --- so we only avoid the
sort if the path that would be chosen anyway (because it is cheapest
without regard to its ordering) is properly sorted. The path winnowing
process needs to be aware of the desired output order and account for the
cost of doing an explicit sort while it is choosing the best path.
When we are generating paths for a particular RelOptInfo, we discard a path
if it is more expensive than another known path that has the same or better
sort order. We will never discard a path that is the only known way to
achieve a given sort order. In this way, the next level up will have the
maximum freedom to build mergejoins without sorting, since it can pick from
any of the paths retained for its inputs.
See path/pathkeys.c for an explanation of the PathKeys data structure that
represents what is known about the sort order of a particular Path.

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_eval.c,v 1.42 1999/07/16 04:59:08 momjian Exp $
* $Id: geqo_eval.c,v 1.43 1999/08/16 02:17:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -23,9 +23,6 @@
#include "postgres.h"
#ifdef HAVE_LIMITS_H
#include <limits.h>
#ifndef MAXINT
#define MAXINT INT_MAX
#endif
#else
#include <values.h>
#endif

View File

@ -5,7 +5,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: geqo_misc.c,v 1.23 1999/07/17 20:17:09 momjian Exp $
* $Id: geqo_misc.c,v 1.24 1999/08/16 02:17:48 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -21,7 +21,10 @@
#include "postgres.h"
#include "optimizer/geqo_misc.h"
#include "nodes/print.h"
static float avg_pool(Pool *pool);
@ -213,32 +216,14 @@ geqo_print_path(Query *root, Path *path, int indent)
int size = path->parent->size;
int relid = lfirsti(path->parent->relids);
printf("%s(%d) size=%d cost=%f",
printf("%s(%d) size=%d cost=%f\n",
ptype, relid, size, path->path_cost);
if (nodeTag(path) == T_IndexPath)
if (IsA(path, IndexPath))
{
List *k,
*l;
printf(" pathkeys=");
foreach(k, path->pathkeys)
{
printf("(");
foreach(l, lfirst(k))
{
Var *var = lfirst(l);
printf("%d.%d", var->varnoold, var->varoattno);
if (lnext(l))
printf(", ");
}
printf(")");
if (lnext(k))
printf(", ");
}
printf(" pathkeys=");
print_pathkeys(path->pathkeys, root->rtable);
}
printf("\n");
}
}

View File

@ -4,7 +4,7 @@
# Makefile for optimizer/path
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.8 1999/02/20 15:27:42 momjian Exp $
# $Header: /cvsroot/pgsql/src/backend/optimizer/path/Makefile,v 1.9 1999/08/16 02:17:50 tgl Exp $
#
#-------------------------------------------------------------------------
@ -13,8 +13,8 @@ include ../../../Makefile.global
CFLAGS += -I../..
OBJS = allpaths.o clausesel.o costsize.o hashutils.o indxpath.o \
joinpath.o joinrels.o mergeutils.o orindxpath.o pathkeys.o prune.o
OBJS = allpaths.o clausesel.o costsize.o indxpath.o \
joinpath.o joinrels.o orindxpath.o pathkeys.o prune.o
all: SUBSYS.o

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.52 1999/07/30 22:34:17 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.53 1999/08/16 02:17:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -331,32 +331,14 @@ print_path(Query *root, Path *path, int indent)
int size = path->parent->size;
int relid = lfirsti(path->parent->relids);
printf("%s(%d) size=%d cost=%f",
printf("%s(%d) size=%d cost=%f\n",
ptype, relid, size, path->path_cost);
if (nodeTag(path) == T_IndexPath)
if (IsA(path, IndexPath))
{
List *k,
*l;
printf(" pathkeys=");
foreach(k, path->pathkeys)
{
printf("(");
foreach(l, lfirst(k))
{
Var *var = lfirst(l);
printf("%d.%d", var->varnoold, var->varoattno);
if (lnext(l))
printf(", ");
}
printf(")");
if (lnext(k))
printf(", ");
}
printf(" pathkeys=");
print_pathkeys(path->pathkeys, root->rtable);
}
printf("\n");
}
}

View File

@ -1,118 +0,0 @@
/*-------------------------------------------------------------------------
*
* hashutils.c
* Utilities for finding applicable merge clauses and pathkeys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/hashutils.c,v 1.18 1999/07/16 04:59:14 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/clauses.h"
#include "optimizer/paths.h"
static HashInfo *match_hashop_hashinfo(Oid hashop, List *hashinfo_list);
/*
* group_clauses_by_hashop
* If a join clause node in 'restrictinfo_list' is hashjoinable, store
* it within a hashinfo node containing other clause nodes with the same
* hash operator.
*
* 'restrictinfo_list' is the list of restrictinfo nodes
* 'inner_relids' is the list of relids in the inner join relation
* (used to determine whether a join var is inner or outer)
*
* Returns the new list of hashinfo nodes.
*
*/
List *
group_clauses_by_hashop(List *restrictinfo_list,
Relids inner_relids)
{
List *hashinfo_list = NIL;
RestrictInfo *restrictinfo;
List *i;
Oid hashjoinop;
foreach(i, restrictinfo_list)
{
restrictinfo = (RestrictInfo *) lfirst(i);
hashjoinop = restrictinfo->hashjoinoperator;
/*
* Create a new hashinfo node and add it to 'hashinfo_list' if one
* does not yet exist for this hash operator.
*/
if (hashjoinop)
{
Expr *clause = restrictinfo->clause;
Var *leftop = get_leftop(clause);
Var *rightop = get_rightop(clause);
HashInfo *xhashinfo;
JoinKey *joinkey;
xhashinfo = match_hashop_hashinfo(hashjoinop, hashinfo_list);
joinkey = makeNode(JoinKey);
if (intMember(leftop->varno, inner_relids))
{
joinkey->outer = rightop;
joinkey->inner = leftop;
}
else
{
joinkey->outer = leftop;
joinkey->inner = rightop;
}
if (xhashinfo == NULL)
{
xhashinfo = makeNode(HashInfo);
xhashinfo->hashop = hashjoinop;
xhashinfo->jmethod.jmkeys = NIL;
xhashinfo->jmethod.clauses = NIL;
hashinfo_list = lcons(xhashinfo, hashinfo_list);
}
xhashinfo->jmethod.clauses = lcons(clause,
xhashinfo->jmethod.clauses);
xhashinfo->jmethod.jmkeys = lcons(joinkey,
xhashinfo->jmethod.jmkeys);
}
}
return hashinfo_list;
}
/*
* match_hashop_hashinfo
* Searches the list 'hashinfo_list' for a hashinfo node whose hash op
* field equals 'hashop'.
*
* Returns the node if it exists.
*
*/
static HashInfo *
match_hashop_hashinfo(Oid hashop, List *hashinfo_list)
{
Oid key = 0;
HashInfo *xhashinfo = (HashInfo *) NULL;
List *i = NIL;
foreach(i, hashinfo_list)
{
xhashinfo = (HashInfo *) lfirst(i);
key = xhashinfo->hashop;
if (hashop == key)
{ /* found */
return xhashinfo; /* should be a hashinfo node ! */
}
}
return (HashInfo *) NIL;
}

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.68 1999/08/12 04:32:51 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.69 1999/08/16 02:17:50 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -27,8 +27,6 @@
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/cost.h"
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
@ -70,7 +68,8 @@ static void indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
List **clausegroups, List **outerrelids);
static List *index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
List *clausegroup_list, List *outerrelids_list);
static bool useful_for_mergejoin(RelOptInfo *index, List *clausegroup_list);
static bool useful_for_mergejoin(RelOptInfo *rel, RelOptInfo *index,
List *joininfo_list);
static bool match_index_to_operand(int indexkey, Var *operand,
RelOptInfo *rel, RelOptInfo *index);
static bool function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index);
@ -189,36 +188,34 @@ create_index_paths(Query *root,
restrictclauses));
/*
* 3. If this index can be used with any join clause, then create
* an index path for it even if there were no restriction clauses.
* 3. If this index can be used for a mergejoin, then create an
* index path for it even if there were no restriction clauses.
* (If there were, there is no need to make another index path.)
* This will allow the index to be considered as a base for a
* mergejoin in later processing.
* Also, create an innerjoin index path for each combination of
*/
if (restrictclauses == NIL &&
useful_for_mergejoin(rel, index, joininfo_list))
{
retval = lappend(retval,
create_index_path(root, rel, index, NIL));
}
/*
* 4. Create an innerjoin index path for each combination of
* other rels used in available join clauses. These paths will
* be considered as the inner side of nestloop joins against
* those sets of other rels.
* indexable_joinclauses() finds clauses that are potentially
* applicable to either case. useful_for_mergejoin() tests to
* see whether any of the join clauses might support a mergejoin.
* index_innerjoin() builds an innerjoin index path for each
* potential set of outer rels, which we add to the rel's
* innerjoin list.
* those sets of other rels. indexable_joinclauses() finds sets
* of clauses that can be used with each combination of outer rels,
* and index_innerjoin builds the paths themselves. We add the
* paths to the rel's innerjoin list, NOT to the result list.
*/
indexable_joinclauses(rel, index,
joininfo_list, restrictinfo_list,
&joinclausegroups,
&joinouterrelids);
if (joinclausegroups != NIL)
{
/* no need to create a plain path if we already did */
if (restrictclauses == NIL &&
useful_for_mergejoin(index, joinclausegroups))
retval = lappend(retval,
create_index_path(root, rel, index,
NIL));
rel->innerjoin = nconc(rel->innerjoin,
index_innerjoin(root, rel, index,
joinclausegroups,
@ -272,11 +269,11 @@ match_index_orclauses(RelOptInfo *rel,
* Add this index to the subclause index list for each
* subclause that it matches.
*/
restrictinfo->indexids =
restrictinfo->subclauseindices =
match_index_orclause(rel, index,
indexkey, xclass,
restrictinfo->clause->args,
restrictinfo->indexids);
restrictinfo->subclauseindices);
}
}
}
@ -439,7 +436,8 @@ group_clauses_by_indexkey(RelOptInfo *rel,
/*
* group_clauses_by_ikey_for_joins
* Generates a list of join clauses that can be used with an index.
* Generates a list of join clauses that can be used with an index
* to scan the inner side of a nestloop join.
*
* This is much like group_clauses_by_indexkey(), but we consider both
* join and restriction clauses. For each indexkey in the index, we
@ -447,7 +445,7 @@ group_clauses_by_indexkey(RelOptInfo *rel,
* will make useful indexquals if the index is being used to scan the
* inner side of a nestloop join. But there must be at least one matching
* join clause, or we return NIL indicating that this index isn't useful
* for joining.
* for nestloop joining.
*/
static List *
group_clauses_by_ikey_for_joins(RelOptInfo *rel,
@ -1156,7 +1154,7 @@ clause_pred_clause_test(Expr *predicate, Node *clause)
/*
* indexable_joinclauses
* Finds all groups of join clauses from among 'joininfo_list' that can
* be used in conjunction with 'index'.
* be used in conjunction with 'index' for the inner scan of a nestjoin.
*
* Each clause group comes from a single joininfo node plus the current
* rel's restrictinfo list. Therefore, every clause in the group references
@ -1224,7 +1222,7 @@ indexable_joinclauses(RelOptInfo *rel, RelOptInfo *index,
* 'rel' is the relation for which 'index' is defined
* 'clausegroup_list' is a list of lists of restrictinfo nodes which can use
* 'index'. Each sublist refers to the same set of outer rels.
* 'outerrelids_list' is a list of the required outer rels for each group
* 'outerrelids_list' is a list of the required outer rels for each sublist
* of join clauses.
*
* Returns a list of index pathnodes.
@ -1255,14 +1253,11 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
&npages,
&selec);
/* XXX this code ought to be merged with create_index_path */
/* XXX this code ought to be merged with create_index_path? */
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
pathnode->path.pathorder = makeNode(PathOrder);
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
pathnode->path.pathorder->ord.sortop = index->ordering;
pathnode->path.pathkeys = NIL;
pathnode->path.pathkeys = build_index_pathkeys(root, rel, index);
/* Note that we are making a pathnode for a single-scan indexscan;
* therefore, both indexid and indexqual should be single-element
@ -1272,10 +1267,8 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
pathnode->indexid = index->relids;
pathnode->indexqual = lcons(indexquals, NIL);
pathnode->indexkeys = index->indexkeys;
/* joinid saves the rels needed on the outer side of the join */
pathnode->path.joinid = lfirst(outerrelids_list);
/* joinrelids saves the rels needed on the outer side of the join */
pathnode->joinrelids = lfirst(outerrelids_list);
pathnode->path.path_cost = cost_index((Oid) lfirsti(index->relids),
(int) npages,
@ -1295,33 +1288,53 @@ index_innerjoin(Query *root, RelOptInfo *rel, RelOptInfo *index,
/*
* useful_for_mergejoin
* Determine whether the given index can support a mergejoin based
* on any join clause within the given list. The clauses have already
* been found to be relevant to the index by indexable_joinclauses.
* We just need to check whether any are mergejoin material.
* on any available join clause.
*
* 'index' is the index of interest.
* 'clausegroup_list' is a list of clause groups (sublists of restrictinfo
* nodes)
* We look to see whether the first indexkey of the index matches the
* left or right sides of any of the mergejoinable clauses and provides
* the ordering needed for that side. If so, the index is useful.
* Matching a second or later indexkey is not useful unless there is
* also a mergeclause for the first indexkey, so we need not consider
* secondary indexkeys at this stage.
*
* 'rel' is the relation for which 'index' is defined
* 'joininfo_list' is the list of JoinInfo nodes for 'rel'
*/
static bool
useful_for_mergejoin(RelOptInfo *index,
List *clausegroup_list)
useful_for_mergejoin(RelOptInfo *rel,
RelOptInfo *index,
List *joininfo_list)
{
int *indexkeys = index->indexkeys;
Oid *ordering = index->ordering;
List *i;
foreach(i, clausegroup_list)
if (!indexkeys || indexkeys[0] == 0 ||
!ordering || ordering[0] == InvalidOid)
return false; /* unordered index is not useful */
foreach(i, joininfo_list)
{
List *clausegroup = lfirst(i);
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
List *j;
foreach(j, clausegroup)
foreach(j, joininfo->jinfo_restrictinfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
if (is_joinable((Node *) restrictinfo->clause) &&
equal_path_merge_ordering(index->ordering,
restrictinfo->mergejoinorder))
return true;
if (restrictinfo->mergejoinoperator)
{
if (restrictinfo->left_sortop == ordering[0] &&
match_index_to_operand(indexkeys[0],
get_leftop(restrictinfo->clause),
rel, index))
return true;
if (restrictinfo->right_sortop == ordering[0] &&
match_index_to_operand(indexkeys[0],
get_rightop(restrictinfo->clause),
rel, index))
return true;
}
}
}
return false;
@ -1348,7 +1361,12 @@ match_index_to_operand(int indexkey,
/*
* Normal index.
*/
return match_indexkey_operand(indexkey, operand, rel);
if (IsA(operand, Var) &&
lfirsti(rel->relids) == operand->varno &&
indexkey == operand->varattno)
return true;
else
return false;
}
/*
@ -1384,7 +1402,7 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
/*
* Check that the arguments correspond to the same arguments used to
* create the functional index. To do this we must check that 1.
* refer to the right relatiion. 2. the args have the right attr.
* refer to the right relation. 2. the args have the right attr.
* numbers in the right order.
*/
i = 0;
@ -1402,6 +1420,9 @@ function_index_operand(Expr *funcOpnd, RelOptInfo *rel, RelOptInfo *index)
i++;
}
if (indexKeys[i] != 0)
return false; /* not enough arguments */
return true;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.44 1999/08/09 03:16:43 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinpath.c,v 1.45 1999/08/16 02:17:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -22,20 +22,26 @@
#include "optimizer/cost.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
static Path *best_innerjoin(List *join_paths, List *outer_relid);
static List *sort_inner_and_outer(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
List *mergeinfo_list);
static List *match_unsorted_outer(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
List *outerpath_list, Path *cheapest_inner, Path *best_innerjoin,
List *mergeinfo_list);
static List *match_unsorted_inner(RelOptInfo *joinrel, RelOptInfo *outerrel, RelOptInfo *innerrel,
List *innerpath_list, List *mergeinfo_list);
static List *sort_inner_and_outer(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *mergeclause_list);
static List *match_unsorted_outer(RelOptInfo *joinrel, RelOptInfo *outerrel,
RelOptInfo *innerrel, List *outerpath_list,
Path *cheapest_inner, Path *best_innerjoin,
List *mergeclause_list);
static List *match_unsorted_inner(RelOptInfo *joinrel, RelOptInfo *outerrel,
RelOptInfo *innerrel, List *innerpath_list,
List *mergeclause_list);
static List *hash_inner_and_outer(Query *root, RelOptInfo *joinrel,
RelOptInfo *outerrel, RelOptInfo *innerrel);
static Cost estimate_disbursion(Query *root, Var *var);
static List *select_mergejoin_clauses(List *restrictinfo_list);
/*
* update_rels_pathlist_for_joins
@ -43,19 +49,10 @@ static Cost estimate_disbursion(Query *root, Var *var);
* relations in the list 'joinrels.' Each unique path will be included
* in the join relation's 'pathlist' field.
*
* In postgres, n-way joins are handled left-only(permuting clauseless
* joins doesn't usually win much).
*
* if BushyPlanFlag is true, bushy tree plans will be generated
*
* 'joinrels' is the list of relation entries to be joined
*
* Modifies the pathlist field of each joinrel node to contain
* the unique join paths.
* If bushy trees are considered, may modify the relid field of the
* join rel nodes to flatten the lists.
*
* It does a destructive modification.
*/
void
update_rels_pathlist_for_joins(Query *root, List *joinrels)
@ -70,8 +67,8 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
RelOptInfo *innerrel;
RelOptInfo *outerrel;
Path *bestinnerjoin;
List *pathlist = NIL;
List *mergeinfo_list = NIL;
List *pathlist;
List *mergeclause_list = NIL;
/*
* On entry, joinrel->relids is a list of two sublists of relids,
@ -98,24 +95,26 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
get_join_rel(root, outerrelids);
/*
* Get the best inner join for match_unsorted_outer.
* Get the best inner join for match_unsorted_outer().
*/
bestinnerjoin = best_innerjoin(innerrel->innerjoin, outerrel->relids);
/*
* Find potential mergejoin clauses.
*/
if (_enable_mergejoin_)
mergeinfo_list = group_clauses_by_order(joinrel->restrictinfo,
innerrel->relids);
mergeclause_list = select_mergejoin_clauses(joinrel->restrictinfo);
/*
* 1. Consider mergejoin paths where both relations must be
* explicitly sorted.
*/
pathlist = sort_inner_and_outer(joinrel, outerrel,
innerrel, mergeinfo_list);
innerrel, mergeclause_list);
/*
* 2. Consider paths where the outer relation need not be
* explicitly sorted. This may include either nestloops and
* explicitly sorted. This includes both nestloops and
* mergejoins where the outer path is already ordered.
*/
pathlist = add_pathlist(joinrel, pathlist,
@ -123,21 +122,20 @@ update_rels_pathlist_for_joins(Query *root, List *joinrels)
outerrel,
innerrel,
outerrel->pathlist,
innerrel->cheapestpath,
innerrel->cheapestpath,
bestinnerjoin,
mergeinfo_list));
mergeclause_list));
/*
* 3. Consider paths where the inner relation need not be
* explicitly sorted. This may include nestloops and mergejoins
* the actual nestloop nodes were constructed in
* (match_unsorted_outer).
* explicitly sorted. This includes mergejoins only
* (nestloops were already built in match_unsorted_outer).
*/
pathlist = add_pathlist(joinrel, pathlist,
match_unsorted_inner(joinrel, outerrel,
innerrel,
innerrel->pathlist,
mergeinfo_list));
mergeclause_list));
/*
* 4. Consider paths where both outer and inner relations must be
@ -176,11 +174,13 @@ best_innerjoin(List *join_paths, Relids outer_relids)
{
Path *path = (Path *) lfirst(join_path);
/* path->joinid is the set of base rels that must be part of
Assert(IsA(path, IndexPath));
/* path->joinrelids is the set of base rels that must be part of
* outer_relids in order to use this inner path, because those
* rels are used in the index join quals of this inner path.
*/
if (is_subset(path->joinid, outer_relids) &&
if (is_subset(((IndexPath *) path)->joinrelids, outer_relids) &&
(cheapest == NULL ||
path_is_cheaper(path, cheapest)))
cheapest = path;
@ -196,8 +196,8 @@ best_innerjoin(List *join_paths, Relids outer_relids)
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'mergeinfo_list' is a list of nodes containing info on(mergejoinable)
* clauses for joining the relations
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses between these two relations
*
* Returns a list of mergejoin paths.
*/
@ -205,32 +205,59 @@ static List *
sort_inner_and_outer(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *mergeinfo_list)
List *mergeclause_list)
{
List *ms_list = NIL;
MergeInfo *xmergeinfo = (MergeInfo *) NULL;
MergePath *temp_node = (MergePath *) NULL;
List *path_list = NIL;
List *i;
List *outerkeys = NIL;
List *innerkeys = NIL;
List *merge_pathkeys = NIL;
foreach(i, mergeinfo_list)
/*
* Each possible ordering of the available mergejoin clauses will
* generate a differently-sorted result path at essentially the
* same cost. We have no basis for choosing one over another at
* this level of joining, but some sort orders may be more useful
* than others for higher-level mergejoins. Generating a path here
* for *every* permutation of mergejoin clauses doesn't seem like
* a winning strategy, however; the cost in planning time is too high.
*
* For now, we generate one path for each mergejoin clause, listing that
* clause first and the rest in random order. This should allow at least
* a one-clause mergejoin without re-sorting against any other possible
* mergejoin partner path. But if we've not guessed the right ordering
* of secondary clauses, we may end up evaluating clauses as qpquals when
* they could have been done as mergeclauses. We need to figure out a
* better way. (Two possible approaches: look at all the relevant index
* relations to suggest plausible sort orders, or make just one output
* path and somehow mark it as having a sort-order that can be rearranged
* freely.)
*/
foreach(i, mergeclause_list)
{
xmergeinfo = (MergeInfo *) lfirst(i);
RestrictInfo *restrictinfo = lfirst(i);
List *curclause_list;
List *outerkeys;
List *innerkeys;
List *merge_pathkeys;
MergePath *path_node;
outerkeys = make_pathkeys_from_joinkeys(xmergeinfo->jmethod.jmkeys,
outerrel->targetlist,
OUTER);
innerkeys = make_pathkeys_from_joinkeys(xmergeinfo->jmethod.jmkeys,
innerrel->targetlist,
INNER);
merge_pathkeys = new_join_pathkeys(outerkeys, joinrel->targetlist,
xmergeinfo->jmethod.clauses);
temp_node = create_mergejoin_path(joinrel,
/* Make a mergeclause list with this guy first. */
curclause_list = lcons(restrictinfo,
lremove(restrictinfo,
listCopy(mergeclause_list)));
/* Build sort pathkeys for both sides.
*
* Note: it's possible that the cheapest path will already be
* sorted properly --- create_mergejoin_path will detect that case
* and suppress an explicit sort step.
*/
outerkeys = make_pathkeys_for_mergeclauses(curclause_list,
outerrel->targetlist);
innerkeys = make_pathkeys_for_mergeclauses(curclause_list,
innerrel->targetlist);
/* Build pathkeys representing output sort order. */
merge_pathkeys = build_join_pathkeys(outerkeys, joinrel->targetlist,
curclause_list);
/* And now we can make the path. */
path_node = create_mergejoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
@ -238,40 +265,50 @@ sort_inner_and_outer(RelOptInfo *joinrel,
(Path *) outerrel->cheapestpath,
(Path *) innerrel->cheapestpath,
merge_pathkeys,
xmergeinfo->m_ordering,
xmergeinfo->jmethod.clauses,
get_actual_clauses(curclause_list),
outerkeys,
innerkeys);
ms_list = lappend(ms_list, temp_node);
path_list = lappend(path_list, path_node);
}
return ms_list;
return path_list;
}
/*
* match_unsorted_outer
* Creates possible join paths for processing a single join relation
* 'joinrel' by employing either iterative substitution or
* mergejoining on each of its possible outer paths(assuming that the
* outer relation need not be explicitly sorted).
* mergejoining on each of its possible outer paths (considering
* only outer paths that are already ordered well enough for merging).
*
* 1. The inner path is the cheapest available inner path.
* 2. Mergejoin wherever possible. Mergejoin are considered if there
* are mergejoinable join clauses between the outer and inner join
* relations such that the outer path is keyed on the variables
* appearing in the clauses. The corresponding inner merge path is
* either a path whose keys match those of the outer path(if such a
* path is available) or an explicit sort on the appropriate inner
* join keys, whichever is cheaper.
* We always generate a nestloop path for each available outer path.
* If an indexscan inner path exists that is compatible with this outer rel
* and cheaper than the cheapest general-purpose inner path, then we use
* the indexscan inner path; else we use the cheapest general-purpose inner.
*
* We also consider mergejoins if mergejoin clauses are available. We have
* two ways to generate the inner path for a mergejoin: use the cheapest
* inner path (sorting it if it's not suitably ordered already), or using an
* inner path that is already suitably ordered for the merge. If the
* cheapest inner path is suitably ordered, then by definition it's the one
* to use. Otherwise, we look for ordered paths that are cheaper than the
* cheapest inner + sort costs. If we have several mergeclauses, it could be
* that there is no inner path (or only a very expensive one) for the full
* list of mergeclauses, but better paths exist if we truncate the
* mergeclause list (thereby discarding some sort key requirements). So, we
* consider truncations of the mergeclause list as well as the full list.
* In any case, we find the cheapest suitable path and generate a single
* output mergejoin path. (Since all the possible mergejoins will have
* identical output pathkeys, there is no need to keep any but the cheapest.)
*
* 'joinrel' is the join relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'outerpath_list' is the list of possible outer paths
* 'cheapest_inner' is the cheapest inner path
* 'best_innerjoin' is the best inner index path(if any)
* 'mergeinfo_list' is a list of nodes containing info on mergejoinable
* clauses
* 'best_innerjoin' is the best inner index path (if any)
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses between these two relations
*
* Returns a list of possible join path nodes.
*/
@ -282,139 +319,139 @@ match_unsorted_outer(RelOptInfo *joinrel,
List *outerpath_list,
Path *cheapest_inner,
Path *best_innerjoin,
List *mergeinfo_list)
List *mergeclause_list)
{
Path *outerpath = (Path *) NULL;
List *jp_list = NIL;
List *temp_node = NIL;
List *merge_pathkeys = NIL;
Path *nestinnerpath = (Path *) NULL;
List *paths = NIL;
List *i = NIL;
PathOrder *outerpath_ordering = NULL;
List *path_list = NIL;
Path *nestinnerpath;
List *i;
/*
* We only use the best innerjoin indexpath if it is cheaper
* than the cheapest general-purpose inner path.
*/
if (best_innerjoin &&
path_is_cheaper(best_innerjoin, cheapest_inner))
nestinnerpath = best_innerjoin;
else
nestinnerpath = cheapest_inner;
foreach(i, outerpath_list)
{
List *clauses = NIL;
List *matchedJoinKeys = NIL;
List *matchedJoinClauses = NIL;
MergeInfo *xmergeinfo = NULL;
Path *outerpath = (Path *) lfirst(i);
List *mergeclauses;
List *merge_pathkeys;
List *innersortkeys;
Path *mergeinnerpath;
int mergeclausecount;
outerpath = (Path *) lfirst(i);
/* Look for useful mergeclauses (if any) */
mergeclauses = find_mergeclauses_for_pathkeys(outerpath->pathkeys,
mergeclause_list);
/*
* The result will have this sort order (even if it is implemented
* as a nestloop, and even if some of the mergeclauses are implemented
* by qpquals rather than as true mergeclauses):
*/
merge_pathkeys = build_join_pathkeys(outerpath->pathkeys,
joinrel->targetlist,
mergeclauses);
outerpath_ordering = outerpath->pathorder;
/* Always consider a nestloop join with this outer and best inner. */
path_list = lappend(path_list,
create_nestloop_path(joinrel,
outerrel,
outerpath,
nestinnerpath,
merge_pathkeys));
if (outerpath_ordering)
xmergeinfo = match_order_mergeinfo(outerpath_ordering,
mergeinfo_list);
/* Done with this outer path if no chance for a mergejoin */
if (mergeclauses == NIL)
continue;
if (xmergeinfo)
clauses = xmergeinfo->jmethod.clauses;
/* Compute the required ordering of the inner path */
innersortkeys = make_pathkeys_for_mergeclauses(mergeclauses,
innerrel->targetlist);
if (clauses)
/* Set up on the assumption that we will use the cheapest_inner */
mergeinnerpath = cheapest_inner;
mergeclausecount = length(mergeclauses);
/* If the cheapest_inner doesn't need to be sorted, it is the winner
* by definition.
*/
if (pathkeys_contained_in(innersortkeys,
cheapest_inner->pathkeys))
{
List *jmkeys = xmergeinfo->jmethod.jmkeys;
order_joinkeys_by_pathkeys(outerpath->pathkeys,
jmkeys,
clauses,
OUTER,
&matchedJoinKeys,
&matchedJoinClauses);
merge_pathkeys = new_join_pathkeys(outerpath->pathkeys,
joinrel->targetlist, clauses);
/* cheapest_inner is the winner */
innersortkeys = NIL; /* we do not need to sort it... */
}
else
merge_pathkeys = outerpath->pathkeys;
if (best_innerjoin &&
path_is_cheaper(best_innerjoin, cheapest_inner))
nestinnerpath = best_innerjoin;
else
nestinnerpath = cheapest_inner;
paths = lcons(create_nestloop_path(joinrel,
outerrel,
outerpath,
nestinnerpath,
merge_pathkeys),
NIL);
if (clauses && matchedJoinKeys)
{
bool path_is_cheaper_than_sort;
List *varkeys = NIL;
Path *mergeinnerpath = get_cheapest_path_for_joinkeys(
matchedJoinKeys,
outerpath_ordering,
innerrel->pathlist,
INNER);
/* look for a presorted path that's cheaper */
List *trialsortkeys = listCopy(innersortkeys);
Cost cheapest_cost;
int clausecount;
/* Should we use the mergeinner, or sort the cheapest inner? */
path_is_cheaper_than_sort = (bool) (mergeinnerpath &&
(mergeinnerpath->path_cost <
(cheapest_inner->path_cost +
cost_sort(matchedJoinKeys, innerrel->size,
innerrel->width))));
if (!path_is_cheaper_than_sort)
cheapest_cost = cheapest_inner->path_cost +
cost_sort(innersortkeys, innerrel->size, innerrel->width);
for (clausecount = mergeclausecount;
clausecount > 0;
clausecount--)
{
varkeys = make_pathkeys_from_joinkeys(matchedJoinKeys,
innerrel->targetlist,
INNER);
Path *trialinnerpath;
/* Look for an inner path ordered well enough to merge with
* the first 'clausecount' mergeclauses. NB: trialsortkeys
* is modified destructively, which is why we made a copy...
*/
trialinnerpath =
get_cheapest_path_for_pathkeys(innerrel->pathlist,
ltruncate(clausecount,
trialsortkeys));
if (trialinnerpath != NULL &&
trialinnerpath->path_cost < cheapest_cost)
{
/* Found a cheaper (or even-cheaper) sorted path */
cheapest_cost = trialinnerpath->path_cost;
mergeinnerpath = trialinnerpath;
mergeclausecount = clausecount;
innersortkeys = NIL; /* we will not need to sort it... */
}
}
/*
* Keep track of the cost of the outer path used with this
* ordered inner path for later processing in
* (match_unsorted_inner), since it isn't a sort and thus
* wouldn't otherwise be considered.
*/
if (path_is_cheaper_than_sort)
mergeinnerpath->outerjoincost = outerpath->path_cost;
else
mergeinnerpath = cheapest_inner;
temp_node = lcons(create_mergejoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
outerpath,
mergeinnerpath,
merge_pathkeys,
xmergeinfo->m_ordering,
matchedJoinClauses,
NIL,
varkeys),
paths);
}
else
temp_node = paths;
jp_list = nconc(jp_list, temp_node);
/* Finally, we can build the mergejoin path */
mergeclauses = ltruncate(mergeclausecount,
get_actual_clauses(mergeclauses));
path_list = lappend(path_list,
create_mergejoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
outerpath,
mergeinnerpath,
merge_pathkeys,
mergeclauses,
NIL,
innersortkeys));
}
return jp_list;
return path_list;
}
/*
* match_unsorted_inner
* Find the cheapest ordered join path for a given(ordered, unsorted)
* inner join path.
*
* Scans through each path available on an inner join relation and tries
* matching its ordering keys against those of mergejoin clauses.
* If 1. an appropriately_ordered inner path and matching mergeclause are
* found, and
* 2. sorting the cheapest outer path is cheaper than using an ordered
* but unsorted outer path(as was considered in
* (match_unsorted_outer)), then this merge path is considered.
* Generate mergejoin paths that use an explicit sort of the outer path
* with an already-ordered inner path.
*
* 'joinrel' is the join result relation
* 'outerrel' is the outer join relation
* 'innerrel' is the inner join relation
* 'innerpath_list' is the list of possible inner join paths
* 'mergeinfo_list' is a list of nodes containing info on mergejoinable
* clauses
* 'mergeclause_list' is a list of RestrictInfo nodes for available
* mergejoin clauses between these two relations
*
* Returns a list of possible merge paths.
*/
@ -423,78 +460,74 @@ match_unsorted_inner(RelOptInfo *joinrel,
RelOptInfo *outerrel,
RelOptInfo *innerrel,
List *innerpath_list,
List *mergeinfo_list)
List *mergeclause_list)
{
List *mp_list = NIL;
List *path_list = NIL;
List *i;
foreach(i, innerpath_list)
{
Path *innerpath = (Path *) lfirst(i);
PathOrder *innerpath_ordering = innerpath->pathorder;
MergeInfo *xmergeinfo = (MergeInfo *) NULL;
List *clauses = NIL;
List *matchedJoinKeys = NIL;
List *matchedJoinClauses = NIL;
List *mergeclauses;
if (innerpath_ordering)
xmergeinfo = match_order_mergeinfo(innerpath_ordering,
mergeinfo_list);
/* Look for useful mergeclauses (if any) */
mergeclauses = find_mergeclauses_for_pathkeys(innerpath->pathkeys,
mergeclause_list);
if (xmergeinfo)
clauses = ((JoinMethod *) xmergeinfo)->clauses;
if (clauses)
if (mergeclauses)
{
List *jmkeys = xmergeinfo->jmethod.jmkeys;
List *outersortkeys;
Path *mergeouterpath;
List *merge_pathkeys;
order_joinkeys_by_pathkeys(innerpath->pathkeys,
jmkeys,
clauses,
INNER,
&matchedJoinKeys,
&matchedJoinClauses);
}
/* Compute the required ordering of the outer path */
outersortkeys =
make_pathkeys_for_mergeclauses(mergeclauses,
outerrel->targetlist);
/*
* (match_unsorted_outer) if it is applicable. 'OuterJoinCost was
* set above in
*/
if (clauses && matchedJoinKeys)
{
Cost temp1;
/* Look for an outer path already ordered well enough to merge */
mergeouterpath =
get_cheapest_path_for_pathkeys(outerrel->pathlist,
outersortkeys);
temp1 = outerrel->cheapestpath->path_cost +
cost_sort(matchedJoinKeys, outerrel->size, outerrel->width);
if (innerpath->outerjoincost <= 0 /* unset? */
|| innerpath->outerjoincost > temp1)
/* Should we use the mergeouter, or sort the cheapest outer? */
if (mergeouterpath != NULL &&
mergeouterpath->path_cost <=
(outerrel->cheapestpath->path_cost +
cost_sort(outersortkeys, outerrel->size, outerrel->width)))
{
List *outerkeys = make_pathkeys_from_joinkeys(matchedJoinKeys,
outerrel->targetlist,
OUTER);
List *merge_pathkeys = new_join_pathkeys(outerkeys,
joinrel->targetlist,
clauses);
mp_list = lappend(mp_list,
create_mergejoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
(Path *) outerrel->cheapestpath,
innerpath,
merge_pathkeys,
xmergeinfo->m_ordering,
matchedJoinClauses,
outerkeys,
NIL));
/* Use mergeouterpath */
outersortkeys = NIL; /* no explicit sort step */
}
else
{
/* Use outerrel->cheapestpath, with the outersortkeys */
mergeouterpath = outerrel->cheapestpath;
}
/* Compute pathkeys the result will have */
merge_pathkeys = build_join_pathkeys(
outersortkeys ? outersortkeys : mergeouterpath->pathkeys,
joinrel->targetlist,
mergeclauses);
mergeclauses = get_actual_clauses(mergeclauses);
path_list = lappend(path_list,
create_mergejoin_path(joinrel,
outerrel->size,
innerrel->size,
outerrel->width,
innerrel->width,
mergeouterpath,
innerpath,
merge_pathkeys,
mergeclauses,
outersortkeys,
NIL));
}
}
return mp_list;
return path_list;
}
/*
@ -520,49 +553,23 @@ hash_inner_and_outer(Query *root,
foreach(i, joinrel->restrictinfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
Oid hashjoinop = restrictinfo->hashjoinoperator;
/* we consider only clauses previously marked hashjoinable */
if (hashjoinop)
if (restrictinfo->hashjoinoperator)
{
Expr *clause = restrictinfo->clause;
Var *leftop = get_leftop(clause);
Var *rightop = get_rightop(clause);
JoinKey *joinkey = makeNode(JoinKey);
List *joinkey_list;
List *outerkeys;
List *innerkeys;
Var *innerop;
Cost innerdisbursion;
List *hash_pathkeys;
HashPath *hash_path;
/* construct joinkey and pathkeys for this clause */
/* find the inner var and estimate its disbursion */
if (intMember(leftop->varno, innerrel->relids))
{
joinkey->outer = rightop;
joinkey->inner = leftop;
}
innerop = leftop;
else
{
joinkey->outer = leftop;
joinkey->inner = rightop;
}
joinkey_list = lcons(joinkey, NIL);
outerkeys = make_pathkeys_from_joinkeys(joinkey_list,
outerrel->targetlist,
OUTER);
innerkeys = make_pathkeys_from_joinkeys(joinkey_list,
innerrel->targetlist,
INNER);
innerdisbursion = estimate_disbursion(root, joinkey->inner);
/*
* We cannot assume that the output of the hashjoin appears in
* any particular order, so it should have NIL pathkeys.
*/
hash_pathkeys = NIL;
innerop = rightop;
innerdisbursion = estimate_disbursion(root, innerop);
hash_path = create_hashjoin_path(joinrel,
outerrel->size,
@ -571,11 +578,7 @@ hash_inner_and_outer(Query *root,
innerrel->width,
(Path *) outerrel->cheapestpath,
(Path *) innerrel->cheapestpath,
hash_pathkeys,
hashjoinop,
lcons(clause, NIL),
outerkeys,
innerkeys,
innerdisbursion);
hpath_list = lappend(hpath_list, hash_path);
}
@ -605,3 +608,36 @@ estimate_disbursion(Query *root, Var *var)
return (Cost) get_attdisbursion(relid, var->varattno, 0.1);
}
/*
* select_mergejoin_clauses
* Select mergejoin clauses that are usable for a particular join.
* Returns a list of RestrictInfo nodes for those clauses.
*
* Currently, all we need is the restrictinfo list of the joinrel.
* By definition, any mergejoinable clause in that list will work ---
* it must involve only vars in the join, or it wouldn't have been
* in the restrict list, and it must involve vars on both sides of
* the join, or it wouldn't have made it up to this level of join.
* Since we currently allow only simple Vars as the left and right
* sides of mergejoin clauses, that means the mergejoin clauses must
* be usable for this join. If we ever allow more complex expressions
* containing multiple Vars, we would need to check that each side
* of a potential joinclause uses only vars from one side of the join.
*/
static List *
select_mergejoin_clauses(List *restrictinfo_list)
{
List *result_list = NIL;
List *i;
foreach(i, restrictinfo_list)
{
RestrictInfo *restrictinfo = lfirst(i);
if (restrictinfo->mergejoinoperator != InvalidOid)
result_list = lcons(restrictinfo, result_list);
}
return result_list;
}

View File

@ -7,12 +7,22 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.38 1999/07/27 06:23:12 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.39 1999/08/16 02:17:51 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#ifdef HAVE_LIMITS_H
#include <limits.h>
#ifndef MAXINT
#define MAXINT INT_MAX
#endif
#else
#ifdef HAVE_VALUES_H
#include <values.h>
#endif
#endif
#include "optimizer/cost.h"
#include "optimizer/joininfo.h"
@ -20,17 +30,18 @@
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
static List *new_joininfo_list(List *joininfo_list, Relids join_relids);
static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, JoinInfo *jinfo);
static RelOptInfo *make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel,
JoinInfo *joininfo);
static RelOptInfo *make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel);
static List *new_join_tlist(List *tlist, int first_resdomno);
static void build_joinrel_restrict_and_join(RelOptInfo *joinrel,
List *joininfo_list,
Relids join_relids);
static void set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel);
/*
* make_rels_by_joins
* Find all possible joins for each of the outer join relations in
* 'outer_rels'. A rel node is created for each possible join relation,
* 'old_rels'. A rel node is created for each possible join relation,
* and the resulting list of nodes is returned. If at all possible, only
* those relations for which join clauses exist are considered. If none
* of these exist for a given relation, all remaining possibilities are
@ -41,19 +52,18 @@ static List *new_join_tlist(List *tlist, int first_resdomno);
List *
make_rels_by_joins(Query *root, List *old_rels)
{
List *joined_rels = NIL;
List *join_list = NIL;
List *r = NIL;
List *r;
foreach(r, old_rels)
{
RelOptInfo *old_rel = (RelOptInfo *) lfirst(r);
List *joined_rels;
if (!(joined_rels = make_rels_by_clause_joins(root, old_rel,
old_rel->joininfo,
NIL)))
{
/*
* Oops, we have a relation that is not joined to any other
* relation. Cartesian product time.
@ -73,16 +83,16 @@ make_rels_by_joins(Query *root, List *old_rels)
/*
* make_rels_by_clause_joins
* Determines whether joins can be performed between an outer relation
* 'outer_rel' and those relations within 'outer_rel's joininfo nodes
* (i.e., relations that participate in join clauses that 'outer_rel'
* participates in). This is possible if all but one of the relations
* contained within the join clauses of the joininfo node are already
* contained within 'outer_rel'.
* Build joins between an outer relation 'old_rel' and relations
* within old_rel's joininfo nodes
* (i.e., relations that participate in join clauses that 'old_rel'
* also participates in).
*
* 'outer_rel' is the relation entry for the outer relation
* 'joininfo_list' is a list of join clauses which 'outer_rel'
* 'old_rel' is the relation entry for the outer relation
* 'joininfo_list' is a list of join clauses which 'old_rel'
* participates in
* 'only_relids': if not NIL, only joins against base rels mentioned in
* only_relids are allowable.
*
* Returns a list of new join relations.
*/
@ -91,55 +101,55 @@ make_rels_by_clause_joins(Query *root, RelOptInfo *old_rel,
List *joininfo_list, Relids only_relids)
{
List *join_list = NIL;
List *i = NIL;
List *i;
foreach(i, joininfo_list)
{
JoinInfo *joininfo = (JoinInfo *) lfirst(i);
RelOptInfo *joined_rel;
Relids unjoined_relids = joininfo->unjoined_relids;
RelOptInfo *joined_rel;
if (unjoined_relids != NIL)
if (unjoined_relids == NIL)
continue; /* probably can't happen */
if (length(unjoined_relids) == 1 &&
(only_relids == NIL ||
/* geqo only wants certain relids to be joined to old_rel */
intMember(lfirsti(unjoined_relids), only_relids)))
{
if (length(unjoined_relids) == 1 &&
(only_relids == NIL ||
/* geqo only wants certain relids to make new rels */
intMember(lfirsti(unjoined_relids), only_relids)))
RelOptInfo *base_rel = get_base_rel(root,
lfirsti(unjoined_relids));
/* Left-sided join of outer rel against a single base rel */
joined_rel = make_join_rel(old_rel, base_rel);
join_list = lappend(join_list, joined_rel);
/* Consider right-sided plan as well */
if (length(old_rel->relids) > 1)
{
joined_rel = make_join_rel(old_rel,
get_base_rel(root,
lfirsti(unjoined_relids)),
joininfo);
joined_rel = make_join_rel(base_rel, old_rel);
join_list = lappend(join_list, joined_rel);
/* Right-sided plan */
if (length(old_rel->relids) > 1)
{
joined_rel = make_join_rel(
get_base_rel(root, lfirsti(unjoined_relids)),
old_rel,
joininfo);
join_list = lappend(join_list, joined_rel);
}
}
}
if (only_relids == NIL) /* no bushy from geqo */
if (only_relids == NIL) /* no bushy plans for geqo */
{
List *r;
/* Build "bushy" plans: join old_rel against all pre-existing
* joins of rels it doesn't already contain, if there is a
* suitable join clause.
*/
foreach(r, root->join_rel_list)
{
List *r;
RelOptInfo *join_rel = lfirst(r);
foreach(r, root->join_rel_list)
Assert(length(join_rel->relids) > 1);
if (is_subset(unjoined_relids, join_rel->relids) &&
nonoverlap_sets(old_rel->relids, join_rel->relids))
{
RelOptInfo *join_rel = lfirst(r);
Assert(length(join_rel->relids) > 1);
if (is_subset(unjoined_relids, join_rel->relids) &&
nonoverlap_sets(old_rel->relids, join_rel->relids))
{
joined_rel = make_join_rel(old_rel,
join_rel,
joininfo);
join_list = lappend(join_list, joined_rel);
}
joined_rel = make_join_rel(old_rel, join_rel);
join_list = lappend(join_list, joined_rel);
}
}
}
@ -150,32 +160,30 @@ make_rels_by_clause_joins(Query *root, RelOptInfo *old_rel,
/*
* make_rels_by_clauseless_joins
* Given an outer relation 'outer_rel' and a list of inner relations
* 'inner_rels', create a join relation between 'outer_rel' and each
* member of 'inner_rels' that isn't already included in 'outer_rel'.
* Given an outer relation 'old_rel' and a list of inner relations
* 'inner_rels', create a join relation between 'old_rel' and each
* member of 'inner_rels' that isn't already included in 'old_rel'.
*
* Returns a list of new join relations.
*/
List *
make_rels_by_clauseless_joins(RelOptInfo *old_rel, List *inner_rels)
{
RelOptInfo *inner_rel;
List *t_list = NIL;
List *i = NIL;
List *join_list = NIL;
List *i;
foreach(i, inner_rels)
{
inner_rel = (RelOptInfo *) lfirst(i);
RelOptInfo *inner_rel = (RelOptInfo *) lfirst(i);
if (nonoverlap_sets(inner_rel->relids, old_rel->relids))
{
t_list = lappend(t_list,
make_join_rel(old_rel,
inner_rel,
(JoinInfo *) NULL));
join_list = lappend(join_list,
make_join_rel(old_rel, inner_rel));
}
}
return t_list;
return join_list;
}
/*
@ -184,19 +192,38 @@ make_rels_by_clauseless_joins(RelOptInfo *old_rel, List *inner_rels)
*
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
* joined
* 'joininfo' is the joininfo node(join clause) containing both
* 'outer_rel' and 'inner_rel', if any exists
*
* Returns the new join relation node.
*/
static RelOptInfo *
make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel)
{
RelOptInfo *joinrel = makeNode(RelOptInfo);
List *joinrel_joininfo_list = NIL;
List *new_outer_tlist;
List *new_inner_tlist;
/*
* This function uses a trick to pass inner/outer rels as two sublists.
* The list will be flattened out in update_rels_pathlist_for_joins().
*/
joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL));
joinrel->indexed = false;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->size = 0;
joinrel->width = 0;
/* joinrel->targetlist = NIL;*/
joinrel->pathlist = NIL;
joinrel->cheapestpath = (Path *) NULL;
joinrel->pruneable = true;
joinrel->classlist = NULL;
joinrel->indexkeys = NULL;
joinrel->ordering = NULL;
joinrel->relam = InvalidOid;
joinrel->restrictinfo = NIL;
joinrel->joininfo = NIL;
joinrel->innerjoin = NIL;
/*
* Create a new tlist by removing irrelevant elements from both tlists
* of the outer and inner join relations and then merging the results
@ -205,44 +232,18 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
new_outer_tlist = new_join_tlist(outer_rel->targetlist, 1);
new_inner_tlist = new_join_tlist(inner_rel->targetlist,
length(new_outer_tlist) + 1);
joinrel->relids = NIL;
joinrel->indexed = false;
joinrel->pages = 0;
joinrel->tuples = 0;
joinrel->width = 0;
/* joinrel->targetlist = NIL;*/
joinrel->pathlist = NIL;
joinrel->cheapestpath = (Path *) NULL;
joinrel->pruneable = true;
joinrel->classlist = NULL;
joinrel->relam = InvalidOid;
joinrel->ordering = NULL;
joinrel->restrictinfo = NIL;
joinrel->joininfo = NULL;
joinrel->innerjoin = NIL;
joinrel->targetlist = nconc(new_outer_tlist, new_inner_tlist);
/*
* This function uses a trick to pass inner/outer rels as different
* lists, and then flattens it out later. bjm
* Construct restrict and join clause lists for the new joinrel.
*/
joinrel->relids = lcons(outer_rel->relids, lcons(inner_rel->relids, NIL));
build_joinrel_restrict_and_join(joinrel,
nconc(copyObject(outer_rel->joininfo),
copyObject(inner_rel->joininfo)),
nconc(listCopy(outer_rel->relids),
listCopy(inner_rel->relids)));
new_outer_tlist = nconc(new_outer_tlist, new_inner_tlist);
joinrel->targetlist = new_outer_tlist;
if (joininfo)
joinrel->restrictinfo = joininfo->jinfo_restrictinfo;
joinrel_joininfo_list = new_joininfo_list(
nconc(copyObject(outer_rel->joininfo),
copyObject(inner_rel->joininfo)),
nconc(listCopy(outer_rel->relids),
listCopy(inner_rel->relids)));
joinrel->joininfo = joinrel_joininfo_list;
set_joinrel_size(joinrel, outer_rel, inner_rel, joininfo);
set_joinrel_size(joinrel, outer_rel, inner_rel);
return joinrel;
}
@ -255,6 +256,9 @@ make_join_rel(RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *joininfo)
* for future joins, its 'joinlist' field must not be empty after removal
* of all relids in 'other_relids'.
*
* XXX this seems to be a dead test --- we don't keep track of joinlists
* for individual targetlist entries anymore, if we ever did...
*
* 'tlist' is the target list of one of the join relations
* 'other_relids' is a list of relids contained within the other
* join relation
@ -268,15 +272,14 @@ new_join_tlist(List *tlist,
int first_resdomno)
{
int resdomno = first_resdomno - 1;
TargetEntry *xtl = NULL;
List *t_list = NIL;
List *i = NIL;
List *i;
List *join_list = NIL;
bool in_final_tlist = false;
foreach(i, tlist)
{
xtl = lfirst(i);
TargetEntry *xtl = lfirst(i);
bool in_final_tlist;
/*
* XXX surely this is wrong? join_list is never changed? tgl
@ -286,7 +289,8 @@ new_join_tlist(List *tlist,
if (in_final_tlist)
{
resdomno += 1;
t_list = lappend(t_list, create_tl_element(get_expr(xtl), resdomno));
t_list = lappend(t_list,
create_tl_element(get_expr(xtl), resdomno));
}
}
@ -294,69 +298,81 @@ new_join_tlist(List *tlist,
}
/*
* new_joininfo_list
* Builds a join relation's joininfo list by checking for join clauses
* which still need to used in future joins involving this relation. A
* join clause is still needed if there are still relations in the clause
* not contained in the list of relations comprising this join relation.
* New joininfo nodes are only created and added to
* 'current_joininfo_list' if a node for a particular join hasn't already
* been created.
* build_joinrel_restrict_and_join
* Builds a join relation's restrictinfo and joininfo lists from the
* joininfo lists of the relations it joins. If a join clause from an
* input relation refers to base rels still not present in the joinrel,
* then it is still a join clause for the joinrel; we put it into an
* appropriate JoinInfo list for the joinrel. Otherwise, the clause is
* now a restrict clause for the joined relation, and we put it into
* the joinrel's restrictinfo list. (It will not need to be considered
* further up the join tree.)
*
* 'current_joininfo_list' contains a list of those joininfo nodes that
* have already been built
* 'joininfo_list' is the list of join clauses involving this relation
* 'join_relids' is a list of relids corresponding to the relations
* currently being joined
* 'joininfo_list' is a list of joininfo nodes from the relations being joined
* 'join_relids' is a list of all base relids in the new join relation
*
* Returns a list of joininfo nodes, new and old.
* NB: the elements of joininfo_list have all been COPIED and so can safely
* be destructively modified and/or inserted in the new joinrel's lists.
* The amount of copying going on here is probably vastly excessive,
* since we copied the underlying clauses as well...
*/
static List *
new_joininfo_list(List *joininfo_list, Relids join_relids)
static void
build_joinrel_restrict_and_join(RelOptInfo *joinrel,
List *joininfo_list,
Relids join_relids)
{
List *current_joininfo_list = NIL;
Relids new_unjoined_relids = NIL;
JoinInfo *other_joininfo = (JoinInfo *) NULL;
List *xjoininfo = NIL;
List *output_restrictinfo_list = NIL;
List *output_joininfo_list = NIL;
List *xjoininfo;
foreach(xjoininfo, joininfo_list)
{
List *or;
JoinInfo *joininfo = (JoinInfo *) lfirst(xjoininfo);
Relids new_unjoined_relids;
new_unjoined_relids = joininfo->unjoined_relids;
foreach(or, new_unjoined_relids)
new_unjoined_relids = set_differencei(joininfo->unjoined_relids,
join_relids);
if (new_unjoined_relids == NIL)
{
if (intMember(lfirsti(or), join_relids))
new_unjoined_relids = lremove((void *) lfirst(or), new_unjoined_relids);
/*
* Clauses in this JoinInfo list become restriction clauses
* for the joinrel, since they refer to no outside rels.
*
* Be careful to eliminate duplicates, since we will see the
* same clauses arriving from both input relations...
*/
output_restrictinfo_list =
LispUnion(output_restrictinfo_list,
joininfo->jinfo_restrictinfo);
}
joininfo->unjoined_relids = new_unjoined_relids;
if (new_unjoined_relids != NIL)
else
{
other_joininfo = joininfo_member(new_unjoined_relids,
current_joininfo_list);
if (other_joininfo)
JoinInfo *old_joininfo;
/*
* There might already be a JoinInfo with the same set of
* unjoined relids in output_joininfo_list; don't make a
* redundant entry.
*/
old_joininfo = joininfo_member(new_unjoined_relids,
output_joininfo_list);
if (old_joininfo)
{
other_joininfo->jinfo_restrictinfo = (List *)
LispUnion(joininfo->jinfo_restrictinfo,
other_joininfo->jinfo_restrictinfo);
old_joininfo->jinfo_restrictinfo =
LispUnion(old_joininfo->jinfo_restrictinfo,
joininfo->jinfo_restrictinfo);
}
else
{
other_joininfo = makeNode(JoinInfo);
other_joininfo->unjoined_relids = joininfo->unjoined_relids;
other_joininfo->jinfo_restrictinfo = joininfo->jinfo_restrictinfo;
other_joininfo->mergejoinable = joininfo->mergejoinable;
other_joininfo->hashjoinable = joininfo->hashjoinable;
current_joininfo_list = lcons(other_joininfo,
current_joininfo_list);
joininfo->unjoined_relids = new_unjoined_relids;
output_joininfo_list = lcons(joininfo,
output_joininfo_list);
}
}
}
return current_joininfo_list;
joinrel->restrictinfo = output_restrictinfo_list;
joinrel->joininfo = output_joininfo_list;
}
/*
@ -376,7 +392,12 @@ get_cheapest_complete_rel(List *join_rel_list)
/*
* find the relations that have no further joins, i.e., its joininfos
* all have unjoined_relids nil.
* all have unjoined_relids nil. (Actually, a JoinInfo shouldn't
* ever have nil unjoined_relids, so I think this code is overly
* complex. In fact it seems wrong; shouldn't we be looking for
* rels with complete relids lists??? Seems like a cartesian-product
* case could fail because sub-relations could have nil JoinInfo lists.
* Doesn't actually fail but I don't really understand why...)
*/
foreach(xrel, join_rel_list)
{
@ -404,26 +425,24 @@ get_cheapest_complete_rel(List *join_rel_list)
}
static void
set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel, RelOptInfo *inner_rel, JoinInfo *jinfo)
set_joinrel_size(RelOptInfo *joinrel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel)
{
double dtuples;
int ntuples;
float selec;
/*
* voodoo magic. but better than a size of 0. I have no idea why we
* didn't set the size before. -ay 2/95
/* avoid overflow ... probably, tuple estimates in RelOptInfo
* just ought to be double ...
*/
if (jinfo == NULL)
{
/* worst case: the cartesian product */
ntuples = outer_rel->tuples * inner_rel->tuples;
}
dtuples = (double) outer_rel->tuples * (double) inner_rel->tuples;
if (joinrel->restrictinfo != NULL)
dtuples *= product_selec(joinrel->restrictinfo);
if (dtuples >= MAXINT) /* avoid overflow */
ntuples = MAXINT;
else
{
selec = product_selec(jinfo->jinfo_restrictinfo);
/* ntuples = Min(outer_rel->tuples,inner_rel->tuples) * selec; */
ntuples = outer_rel->tuples * inner_rel->tuples * selec;
}
ntuples = (int) dtuples;
/*
* I bet sizes less than 1 will screw up optimization so make the best

View File

@ -1,131 +0,0 @@
/*-------------------------------------------------------------------------
*
* mergeutils.c
* Utilities for finding applicable merge clauses and pathkeys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/mergeutils.c,v 1.24 1999/07/16 04:59:15 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/clauses.h"
#include "optimizer/ordering.h"
#include "optimizer/paths.h"
/*
* group_clauses_by_order
* If a join clause node in 'restrictinfo_list' is mergejoinable, store
* it within a mergeinfo node containing other clause nodes with the same
* mergejoin ordering.
*
* XXX This is completely braindead: there is no reason anymore to segregate
* mergejoin clauses by join operator, since the executor can handle mergejoin
* clause sets with different operators in them. Instead, we ought to be
* building a MergeInfo for each potentially useful ordering of the input
* relations. But right now the optimizer's internal data structures do not
* support that (MergeInfo can only store one MergeOrder for a set of clauses).
* Something to fix next time...
*
* 'restrictinfo_list' is the list of restrictinfo nodes
* 'inner_relids' is the list of relids in the inner join relation
* (used to determine whether a join var is inner or outer)
*
* Returns the new list of mergeinfo nodes.
*
*/
List *
group_clauses_by_order(List *restrictinfo_list,
Relids inner_relids)
{
List *mergeinfo_list = NIL;
List *xrestrictinfo;
foreach(xrestrictinfo, restrictinfo_list)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(xrestrictinfo);
MergeOrder *merge_ordering = restrictinfo->mergejoinorder;
if (merge_ordering)
{
/*
* Create a new mergeinfo node and add it to 'mergeinfo_list'
* if one does not yet exist for this merge ordering.
*/
Expr *clause = restrictinfo->clause;
Var *leftop = get_leftop(clause);
Var *rightop = get_rightop(clause);
PathOrder *pathorder;
MergeInfo *xmergeinfo;
JoinKey *jmkeys;
pathorder = makeNode(PathOrder);
pathorder->ordtype = MERGE_ORDER;
pathorder->ord.merge = merge_ordering;
xmergeinfo = match_order_mergeinfo(pathorder, mergeinfo_list);
jmkeys = makeNode(JoinKey);
if (intMember(leftop->varno, inner_relids))
{
jmkeys->outer = rightop;
jmkeys->inner = leftop;
}
else
{
jmkeys->outer = leftop;
jmkeys->inner = rightop;
}
if (xmergeinfo == NULL)
{
xmergeinfo = makeNode(MergeInfo);
xmergeinfo->m_ordering = merge_ordering;
mergeinfo_list = lcons(xmergeinfo, mergeinfo_list);
}
xmergeinfo->jmethod.clauses = lcons(clause,
xmergeinfo->jmethod.clauses);
xmergeinfo->jmethod.jmkeys = lcons(jmkeys,
xmergeinfo->jmethod.jmkeys);
}
}
return mergeinfo_list;
}
/*
* match_order_mergeinfo
* Searches the list 'mergeinfo_list' for a mergeinfo node whose order
* field equals 'ordering'.
*
* Returns the node if it exists.
*
*/
MergeInfo *
match_order_mergeinfo(PathOrder *ordering, List *mergeinfo_list)
{
MergeOrder *xmergeorder;
List *xmergeinfo = NIL;
foreach(xmergeinfo, mergeinfo_list)
{
MergeInfo *mergeinfo = (MergeInfo *) lfirst(xmergeinfo);
xmergeorder = mergeinfo->m_ordering;
if ((ordering->ordtype == MERGE_ORDER &&
equal_merge_ordering(ordering->ord.merge, xmergeorder)) ||
(ordering->ordtype == SORTOP_ORDER &&
equal_path_merge_ordering(ordering->ord.sortop, xmergeorder)))
{
return mergeinfo;
}
}
return (MergeInfo *) NIL;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.31 1999/07/27 03:51:02 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/orindxpath.c,v 1.32 1999/08/16 02:17:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -66,12 +66,12 @@ create_or_index_paths(Query *root,
* saved by create_index_paths().
*/
if (restriction_is_or_clause(clausenode) &&
clausenode->indexids)
clausenode->subclauseindices)
{
bool all_indexable = true;
List *temp;
foreach(temp, clausenode->indexids)
foreach(temp, clausenode->subclauseindices)
{
if (lfirst(temp) == NIL)
{
@ -94,7 +94,7 @@ create_or_index_paths(Query *root,
best_or_subclause_indices(root,
rel,
clausenode->clause->args,
clausenode->indexids,
clausenode->subclauseindices,
&indexquals,
&indexids,
&cost,
@ -102,20 +102,17 @@ create_or_index_paths(Query *root,
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
pathnode->path.pathorder = makeNode(PathOrder);
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
/*
* This is an IndexScan, but the overall result will consist
* of tuples extracted in multiple passes (one for each
* subclause of the OR), so the result cannot be claimed
* to have any particular ordering.
*/
pathnode->path.pathorder->ord.sortop = NULL;
pathnode->path.pathkeys = NIL;
pathnode->indexqual = indexquals;
pathnode->indexid = indexids;
pathnode->indexqual = indexquals;
pathnode->joinrelids = NIL; /* no join clauses here */
pathnode->path.path_cost = cost;
clausenode->selectivity = (Cost) selec;

View File

@ -1,70 +1,84 @@
/*-------------------------------------------------------------------------
*
* joinutils.c
* Utilities for matching and building join and path keys
* pathkeys.c
* Utilities for matching and building path keys
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.13 1999/08/13 01:17:16 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/pathkeys.c,v 1.14 1999/08/16 02:17:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/paths.h"
#include "optimizer/tlist.h"
#include "optimizer/var.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
static int match_pathkey_joinkeys(List *pathkey, List *joinkeys,
int outer_or_inner);
static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
List *joinclauses);
static PathKeyItem *makePathKeyItem(Node *key, Oid sortop);
static bool pathkeyitem_equal(PathKeyItem *a, PathKeyItem *b);
static bool pathkeyitem_member(PathKeyItem *a, List *l);
static Var *find_indexkey_var(int indexkey, List *tlist);
static List *build_join_pathkey(List *pathkeys, List *join_rel_tlist,
List *joinclauses);
/*--------------------
* Explanation of Path.pathkeys
*
* Path.pathkeys is a List of List of Var nodes that represent the sort
* order of the result generated by the Path.
* Path.pathkeys is a List of Lists of PathKeyItem nodes that represent
* the sort order of the result generated by the Path. The n'th sublist
* represents the n'th sort key of the result.
*
* In single/base relation RelOptInfo's, the Path's represent various ways
* In single/base relation RelOptInfo's, the Paths represent various ways
* of scanning the relation and the resulting ordering of the tuples.
* Sequential scan Paths have NIL pathkeys, indicating no known ordering.
* Index scans have Path.pathkeys that represent the chosen index's ordering,
* if any. A single-key index would create a pathkey with a single sublist,
* e.g. ( (tab1_indexkey1) ). A multi-key index generates a sublist per key,
* e.g. ( (tab1_indexkey1) (tab1_indexkey2) ) which shows major sort by
* indexkey1 and minor sort by indexkey2.
* e.g. ( (tab1.indexkey1/sortop1) ). A multi-key index generates a sublist
* per key, e.g. ( (tab1.indexkey1/sortop1) (tab1.indexkey2/sortop2) ) which
* shows major sort by indexkey1 (ordering by sortop1) and minor sort by
* indexkey2 with sortop2.
*
* Note that a multi-pass indexscan (OR clause scan) has NIL pathkeys since
* we can say nothing about the overall order of its result. Also, an index
* scan on an unordered type of index generates no useful pathkeys. However,
* we can say nothing about the overall order of its result. Also, an
* indexscan on an unordered type of index generates NIL pathkeys. However,
* we can always create a pathkey by doing an explicit sort.
*
* Multi-relation RelOptInfo Path's are more complicated. Mergejoins are
* only performed with equijoins ("="). Because of this, the multi-relation
* path actually has more than one primary Var key. For example, a
* mergejoin Path of "tab1.col1 = tab2.col1" would generate pathkeys of
* ( (tab1.col1 tab2.col1) ), indicating that the major sort order of the
* Path can be taken to be *either* tab1.col1 or tab2.col1.
* only performed with equijoins ("="). Because of this, the resulting
* multi-relation path actually has more than one primary key. For example,
* a mergejoin using a clause "tab1.col1 = tab2.col1" would generate pathkeys
* of ( (tab1.col1/sortop1 tab2.col1/sortop2) ), indicating that the major
* sort order of the Path can be taken to be *either* tab1.col1 or tab2.col1.
* They are equal, so they are both primary sort keys. This allows future
* joins to use either Var as a pre-sorted key to prevent upper Mergejoins
* joins to use either var as a pre-sorted key to prevent upper Mergejoins
* from having to re-sort the Path. This is why pathkeys is a List of Lists.
*
* Note that while the order of the top list is meaningful (primary vs.
* secondary sort key), the order of each sublist is arbitrary.
* secondary sort key), the order of each sublist is arbitrary. No code
* working with pathkeys should generate a result that depends on the order
* of a pathkey sublist.
*
* We can actually keep all of the keys of the outer path of a merge or
* nestloop join, since the ordering of the outer path will be reflected
* in the result. We add to each pathkey sublist any inner vars that are
* equijoined to any of the outer vars in the sublist. In the nestloop
* case we have to be careful to consider only equijoin operators; the
* nestloop's join clauses might include non-equijoin operators.
* We keep a sortop associated with each PathKeyItem because cross-data-type
* mergejoins are possible; for example int4=int8 is mergejoinable. In this
* case we need to remember that the left var is ordered by int4lt while
* the right var is ordered by int8lt. So the different members of each
* sublist could have different sortops.
*
* When producing the pathkeys for a merge or nestloop join, we can keep
* all of the keys of the outer path, since the ordering of the outer path
* will be preserved in the result. We add to each pathkey sublist any inner
* vars that are equijoined to any of the outer vars in the sublist. In the
* nestloop case we have to be careful to consider only equijoin operators;
* the nestloop's join clauses might include non-equijoin operators.
* (Currently, we do this by considering only mergejoinable operators while
* making the pathkeys, since we have no separate marking for operators that
* are equijoins but aren't mergejoinable.)
@ -75,180 +89,174 @@ static List *new_join_pathkey(List *pathkeys, List *join_rel_tlist,
* executor might have to split the join into multiple batches. Therefore
* a Hashjoin is always given NIL pathkeys.
*
* Notice that pathkeys only say *what* is being ordered, and not *how*
* it is ordered. The actual sort ordering is indicated by a separate
* data structure, the PathOrder. The PathOrder provides a sort operator
* OID for each of the sublists of the path key. This is fairly bogus,
* since in cross-datatype cases we really want to keep track of more than
* one sort operator...
*
* -- bjm & tgl
*--------------------
*/
/*
* makePathKeyItem
* create a PathKeyItem node
*/
static PathKeyItem *
makePathKeyItem(Node *key, Oid sortop)
{
PathKeyItem *item = makeNode(PathKeyItem);
item->key = key;
item->sortop = sortop;
return item;
}
/****************************************************************************
* KEY COMPARISONS
* PATHKEY COMPARISONS
****************************************************************************/
/*
* order_joinkeys_by_pathkeys
* Attempts to match the keys of a path against the keys of join clauses.
* This is done by looking for a matching join key in 'joinkeys' for
* every path key in the list 'path.keys'. If there is a matching join key
* (not necessarily unique) for every path key, then the list of
* corresponding join keys and join clauses are returned in the order in
* which the keys matched the path keys.
* Compare two pathkey items for equality.
*
* 'pathkeys' is a list of path keys:
* ( ( (var) (var) ... ) ( (var) ... ) )
* 'joinkeys' is a list of join keys:
* ( (outer inner) (outer inner) ... )
* 'joinclauses' is a list of clauses corresponding to the join keys in
* 'joinkeys'
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
* in 'joinkeys'
* This is unlike straight equal() because when the two keys are both Vars,
* we want to apply the weaker var_equal() condition (doesn't check varnoold
* or varoattno). But if that fails, try equal() so that we recognize
* functional-index keys.
*/
static bool
pathkeyitem_equal (PathKeyItem *a, PathKeyItem *b)
{
Assert(a && IsA(a, PathKeyItem));
Assert(b && IsA(b, PathKeyItem));
if (a->sortop != b->sortop)
return false;
if (var_equal((Var *) a->key, (Var *) b->key))
return true;
return equal(a->key, b->key);
}
/*
* member() test using pathkeyitem_equal
*/
static bool
pathkeyitem_member (PathKeyItem *a, List *l)
{
List *i;
Assert(a && IsA(a, PathKeyItem));
foreach(i, l)
{
if (pathkeyitem_equal(a, (PathKeyItem *) lfirst(i)))
return true;
}
return false;
}
/*
* compare_pathkeys
* Compare two pathkeys to see if they are equivalent, and if not whether
* one is "better" than the other.
*
* Returns the join keys and corresponding join clauses in a list if all
* of the path keys were matched:
* (
* ( (outerkey0 innerkey0) ... (outerkeyN or innerkeyN) )
* ( clause0 ... clauseN )
* )
* and nil otherwise.
* A pathkey can be considered better than another if it is a superset:
* it contains all the keys of the other plus more. For example, either
* ((A) (B)) or ((A B)) is better than ((A)).
*
* Returns a list of matched join keys and a list of matched join clauses
* in pointers if valid order can be found.
* This gets called a lot, so it is optimized.
*/
PathKeysComparison
compare_pathkeys(List *keys1, List *keys2)
{
List *key1,
*key2;
bool key1_subsetof_key2 = true,
key2_subsetof_key1 = true;
for (key1 = keys1, key2 = keys2;
key1 != NIL && key2 != NIL;
key1 = lnext(key1), key2 = lnext(key2))
{
List *subkey1 = lfirst(key1);
List *subkey2 = lfirst(key2);
List *i;
/* We have to do this the hard way since the ordering of the subkey
* lists is arbitrary.
*/
if (key1_subsetof_key2)
{
foreach(i, subkey1)
{
if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey2))
{
key1_subsetof_key2 = false;
break;
}
}
}
if (key2_subsetof_key1)
{
foreach(i, subkey2)
{
if (! pathkeyitem_member((PathKeyItem *) lfirst(i), subkey1))
{
key2_subsetof_key1 = false;
break;
}
}
}
if (!key1_subsetof_key2 && !key2_subsetof_key1)
return PATHKEYS_DIFFERENT; /* no need to keep looking */
}
/* If we reached the end of only one list, the other is longer and
* therefore not a subset. (We assume the additional sublist(s)
* of the other list are not NIL --- no pathkey list should ever have
* a NIL sublist.)
*/
if (key1 != NIL)
key1_subsetof_key2 = false;
if (key2 != NIL)
key2_subsetof_key1 = false;
if (key1_subsetof_key2 && key2_subsetof_key1)
return PATHKEYS_EQUAL;
if (key1_subsetof_key2)
return PATHKEYS_BETTER2;
if (key2_subsetof_key1)
return PATHKEYS_BETTER1;
return PATHKEYS_DIFFERENT;
}
/*
* pathkeys_contained_in
* Common special case of compare_pathkeys: we just want to know
* if keys2 are at least as well sorted as keys1.
*/
bool
order_joinkeys_by_pathkeys(List *pathkeys,
List *joinkeys,
List *joinclauses,
int outer_or_inner,
List **matchedJoinKeysPtr,
List **matchedJoinClausesPtr)
pathkeys_contained_in(List *keys1, List *keys2)
{
List *matched_joinkeys = NIL;
List *matched_joinclauses = NIL;
List *pathkey = NIL;
List *i = NIL;
int matched_joinkey_index = -1;
int matched_keys = 0;
/*
* Reorder the joinkeys by picking out one that matches each pathkey,
* and create a new joinkey/joinclause list in pathkey order
*/
foreach(i, pathkeys)
switch (compare_pathkeys(keys1, keys2))
{
pathkey = lfirst(i);
matched_joinkey_index = match_pathkey_joinkeys(pathkey, joinkeys,
outer_or_inner);
if (matched_joinkey_index != -1)
{
matched_keys++;
if (matchedJoinKeysPtr)
{
JoinKey *joinkey = nth(matched_joinkey_index, joinkeys);
matched_joinkeys = lappend(matched_joinkeys, joinkey);
}
if (matchedJoinClausesPtr)
{
Expr *joinclause = nth(matched_joinkey_index,
joinclauses);
Assert(joinclauses);
matched_joinclauses = lappend(matched_joinclauses, joinclause);
}
}
else
/* A pathkey could not be matched. */
case PATHKEYS_EQUAL:
case PATHKEYS_BETTER2:
return true;
default:
break;
}
/*
* Did we fail to match all the joinkeys? Extra pathkeys are no
* problem.
*/
if (matched_keys != length(joinkeys))
{
if (matchedJoinKeysPtr)
*matchedJoinKeysPtr = NIL;
if (matchedJoinClausesPtr)
*matchedJoinClausesPtr = NIL;
return false;
}
if (matchedJoinKeysPtr)
*matchedJoinKeysPtr = matched_joinkeys;
if (matchedJoinClausesPtr)
*matchedJoinClausesPtr = matched_joinclauses;
return true;
return false;
}
/*
* match_pathkey_joinkeys
* Returns the 0-based index into 'joinkeys' of the first joinkey whose
* outer or inner pathkey matches any subkey of 'pathkey'.
* get_cheapest_path_for_pathkeys
* Find the cheapest path in 'paths' that satisfies the given pathkeys.
* Return NULL if no such path.
*
* All these keys are equivalent, so any of them can match. See above.
*/
static int
match_pathkey_joinkeys(List *pathkey,
List *joinkeys,
int outer_or_inner)
{
Var *key;
int pos;
List *i,
*x;
JoinKey *jk;
foreach(i, pathkey)
{
key = (Var *) lfirst(i);
pos = 0;
foreach(x, joinkeys)
{
jk = (JoinKey *) lfirst(x);
if (equal(key, extract_join_key(jk, outer_or_inner)))
return pos;
pos++;
}
}
return -1; /* no index found */
}
/*
* get_cheapest_path_for_joinkeys
* Attempts to find a path in 'paths' whose keys match a set of join
* keys 'joinkeys'. To match,
* 1. the path node ordering must equal 'ordering'.
* 2. each pathkey of a given path must match(i.e., be(equal) to) the
* appropriate pathkey of the corresponding join key in 'joinkeys',
* i.e., the Nth path key must match its pathkeys against the pathkey of
* the Nth join key in 'joinkeys'.
*
* 'joinkeys' is the list of key pairs to which the path keys must be
* matched
* 'ordering' is the ordering of the(outer) path to which 'joinkeys'
* must correspond
* 'paths' is a list of(inner) paths which are to be matched against
* each join key in 'joinkeys'
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
* in 'joinkeys'
*
* Find the cheapest path that matches the join keys
* 'paths' is a list of possible paths (either inner or outer)
* 'pathkeys' represents a required ordering
*/
Path *
get_cheapest_path_for_joinkeys(List *joinkeys,
PathOrder *ordering,
List *paths,
int outer_or_inner)
get_cheapest_path_for_pathkeys(List *paths, List *pathkeys)
{
Path *matched_path = NULL;
List *i;
@ -256,12 +264,8 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
foreach(i, paths)
{
Path *path = (Path *) lfirst(i);
int better_sort;
if (order_joinkeys_by_pathkeys(path->pathkeys, joinkeys, NIL,
outer_or_inner, NULL, NULL) &&
pathorder_match(ordering, path->pathorder, &better_sort) &&
better_sort == 0)
if (pathkeys_contained_in(pathkeys, path->pathkeys))
{
if (matched_path == NULL ||
path->path_cost < matched_path->path_cost)
@ -271,78 +275,116 @@ get_cheapest_path_for_joinkeys(List *joinkeys,
return matched_path;
}
/*
* make_pathkeys_from_joinkeys
* Builds a pathkey list for a path by pulling one of the pathkeys from
* a list of join keys 'joinkeys' and then finding the var node in the
* target list 'tlist' that corresponds to that pathkey.
*
* 'joinkeys' is a list of join key pairs
* 'tlist' is a relation target list
* 'outer_or_inner' is a flag that selects the desired pathkey of a join key
* in 'joinkeys'
*
* Returns a list of pathkeys: ((tlvar1)(tlvar2)...(tlvarN)).
* It is a list of lists because of multi-key indexes.
*/
List *
make_pathkeys_from_joinkeys(List *joinkeys,
List *tlist,
int outer_or_inner)
{
List *pathkeys = NIL;
List *jk;
foreach(jk, joinkeys)
{
JoinKey *jkey = (JoinKey *) lfirst(jk);
Var *key;
List *p,
*p2;
bool found = false;
key = (Var *) extract_join_key(jkey, outer_or_inner);
/* check to see if it is in the target list */
if (matching_tlist_var(key, tlist))
{
/*
* Include it in the pathkeys list if we haven't already done
* so
*/
foreach(p, pathkeys)
{
List *pathkey = lfirst(p);
foreach(p2, pathkey)
{
Var *pkey = lfirst(p2);
if (equal(key, pkey))
{
found = true;
break;
}
}
if (found)
break;
}
if (!found)
pathkeys = lappend(pathkeys, lcons(key, NIL));
}
}
return pathkeys;
}
/****************************************************************************
* NEW PATHKEY FORMATION
****************************************************************************/
/*
* new_join_pathkeys
* build_index_pathkeys
* Build a pathkeys list that describes the ordering induced by an index
* scan using the given index. (Note that an unordered index doesn't
* induce any ordering; such an index will have no sortop OIDS in
* its "ordering" field.)
*
* Vars in the resulting pathkeys list are taken from the rel's targetlist.
* If we can't find the indexkey in the targetlist, we assume that the
* ordering of that key is not interesting.
*/
List *
build_index_pathkeys(Query *root, RelOptInfo *rel, RelOptInfo *index)
{
List *retval = NIL;
int *indexkeys = index->indexkeys;
Oid *ordering = index->ordering;
if (!indexkeys || indexkeys[0] == 0 ||
!ordering || ordering[0] == InvalidOid)
return NIL; /* unordered index? */
if (index->indproc)
{
/* Functional index: build a representation of the function call */
int relid = lfirsti(rel->relids);
Oid reloid = getrelid(relid, root->rtable);
Func *funcnode = makeNode(Func);
List *funcargs = NIL;
funcnode->funcid = index->indproc;
funcnode->functype = get_func_rettype(index->indproc);
funcnode->funcisindex = false;
funcnode->funcsize = 0;
funcnode->func_fcache = NULL;
funcnode->func_tlist = NIL;
funcnode->func_planlist = NIL;
while (*indexkeys != 0)
{
int varattno = *indexkeys;
Oid vartypeid = get_atttype(reloid, varattno);
int32 type_mod = get_atttypmod(reloid, varattno);
funcargs = lappend(funcargs,
makeVar(relid, varattno, vartypeid, type_mod,
0, relid, varattno));
indexkeys++;
}
/* Make a one-sublist pathkeys list for the function expression */
retval = lcons(lcons(
makePathKeyItem((Node *) make_funcclause(funcnode, funcargs),
*ordering),
NIL), NIL);
}
else
{
/* Normal non-functional index */
List *rel_tlist = rel->targetlist;
while (*indexkeys != 0 && *ordering != InvalidOid)
{
Var *relvar = find_indexkey_var(*indexkeys, rel_tlist);
/* If we can find no tlist entry for the n'th sort key,
* then we're done generating pathkeys; any subsequent sort keys
* no longer apply, since we can't represent the ordering properly
* even if there are tlist entries for them.
*/
if (!relvar)
break;
/* OK, make a one-element sublist for this sort key */
retval = lappend(retval,
lcons(makePathKeyItem((Node *) relvar,
*ordering),
NIL));
indexkeys++;
ordering++;
}
}
return retval;
}
/*
* Find a var in a relation's targetlist that matches an indexkey attrnum.
*/
static Var *
find_indexkey_var(int indexkey, List *tlist)
{
List *temp;
foreach(temp, tlist)
{
Var *tle_var = get_expr(lfirst(temp));
if (IsA(tle_var, Var) && tle_var->varattno == indexkey)
return tle_var;
}
return NULL;
}
/*
* build_join_pathkeys
* Build the path keys for a join relation constructed by mergejoin or
* nestloop join. These keys should include all the path key vars of the
* outer path (since the join will retain the ordering of the outer path)
@ -362,21 +404,26 @@ make_pathkeys_from_joinkeys(List *joinkeys,
* the inner var will acquire the outer's ordering no matter which join
* method is actually used.
*
* All vars in the result are copied from the join relation's tlist, not from
* the given pathkeys or the join clauses. (Is that necessary? I suspect
* not --- tgl)
* We drop pathkeys that are not vars of the join relation's tlist,
* on the assumption that they are not interesting to higher levels.
* (Is this correct?? To support expression pathkeys we might want to
* check that all vars mentioned in the key are in the tlist, instead.)
*
* All vars in the result are taken from the join relation's tlist,
* not from the given pathkeys or joinclauses.
*
* 'outer_pathkeys' is the list of the outer path's path keys
* 'join_rel_tlist' is the target list of the join relation
* 'joinclauses' is the list of mergejoinable join clauses
* 'joinclauses' is the list of mergejoinable clauses to consider (note this
* is a list of RestrictInfos, not just bare qual clauses); can be NIL
*
* Returns the list of new path keys.
*
*/
List *
new_join_pathkeys(List *outer_pathkeys,
List *join_rel_tlist,
List *joinclauses)
build_join_pathkeys(List *outer_pathkeys,
List *join_rel_tlist,
List *joinclauses)
{
List *final_pathkeys = NIL;
List *i;
@ -386,11 +433,11 @@ new_join_pathkeys(List *outer_pathkeys,
List *outer_pathkey = lfirst(i);
List *new_pathkey;
new_pathkey = new_join_pathkey(outer_pathkey, join_rel_tlist,
joinclauses);
new_pathkey = build_join_pathkey(outer_pathkey, join_rel_tlist,
joinclauses);
/* if we can find no sortable vars for the n'th sort key,
* then we're done generating pathkeys; can't expect to order
* subsequent vars. Not clear that this can really happen.
* then we're done generating pathkeys; any subsequent sort keys
* no longer apply, since we can't represent the ordering properly.
*/
if (new_pathkey == NIL)
break;
@ -400,25 +447,22 @@ new_join_pathkeys(List *outer_pathkeys,
}
/*
* new_join_pathkey
* build_join_pathkey
* Generate an individual pathkey sublist, consisting of the outer vars
* already mentioned in 'pathkey' plus any inner vars that are joined to
* them (and thus can now also be considered path keys, per discussion
* at the top of this file).
*
* Note that each returned pathkey is the var node found in
* Note that each returned pathkey uses the var node found in
* 'join_rel_tlist' rather than the input pathkey or joinclause var node.
* (Is this important?) Also, we return a fully copied list
* that does not share any subnodes with existing data structures.
* (Is that important, either?)
*
* Returns a new pathkey (list of pathkey variables).
* (Is this important?)
*
* Returns a new pathkey (list of PathKeyItems).
*/
static List *
new_join_pathkey(List *pathkey,
List *join_rel_tlist,
List *joinclauses)
build_join_pathkey(List *pathkey,
List *join_rel_tlist,
List *joinclauses)
{
List *new_pathkey = NIL;
List *i,
@ -426,27 +470,193 @@ new_join_pathkey(List *pathkey,
foreach(i, pathkey)
{
Var *key = (Var *) lfirst(i);
PathKeyItem *key = (PathKeyItem *) lfirst(i);
Expr *tlist_key;
Assert(key);
Assert(key && IsA(key, PathKeyItem));
tlist_key = matching_tlist_var(key, join_rel_tlist);
if (tlist_key && !member(tlist_key, new_pathkey))
new_pathkey = lcons(copyObject(tlist_key), new_pathkey);
tlist_key = matching_tlist_var((Var *) key->key, join_rel_tlist);
if (tlist_key)
new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
key->sortop),
new_pathkey);
foreach(j, joinclauses)
{
Expr *joinclause = lfirst(j);
Expr *tlist_other_var;
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(j);
Expr *joinclause = restrictinfo->clause;
/* We assume the clause is a binary opclause... */
Var *l = get_leftop(joinclause);
Var *r = get_rightop(joinclause);
Var *other_var = NULL;
Oid other_sortop = InvalidOid;
tlist_other_var = matching_tlist_var(
other_join_clause_var(key, joinclause),
join_rel_tlist);
if (tlist_other_var && !member(tlist_other_var, new_pathkey))
new_pathkey = lcons(copyObject(tlist_other_var), new_pathkey);
if (var_equal((Var *) key->key, l))
{
other_var = r;
other_sortop = restrictinfo->right_sortop;
}
else if (var_equal((Var *) key->key, r))
{
other_var = l;
other_sortop = restrictinfo->left_sortop;
}
if (other_var && other_sortop)
{
tlist_key = matching_tlist_var(other_var, join_rel_tlist);
if (tlist_key)
new_pathkey = lcons(makePathKeyItem((Node *) tlist_key,
other_sortop),
new_pathkey);
}
}
}
return new_pathkey;
}
/****************************************************************************
* PATHKEYS AND MERGECLAUSES
****************************************************************************/
/*
* find_mergeclauses_for_pathkeys
* This routine attempts to find a set of mergeclauses that can be
* used with a specified ordering for one of the input relations.
* If successful, it returns a list of mergeclauses.
*
* 'pathkeys' is a pathkeys list showing the ordering of an input path.
* It doesn't matter whether it is for the inner or outer path.
* 'restrictinfos' is a list of mergejoinable restriction clauses for the
* join relation being formed.
*
* The result is NIL if no merge can be done, else a maximal list of
* usable mergeclauses (represented as a list of their restrictinfo nodes).
*
* XXX Ideally we ought to be considering context, ie what path orderings
* are available on the other side of the join, rather than just making
* an arbitrary choice among the mergeclause orders that will work for
* this side of the join.
*/
List *
find_mergeclauses_for_pathkeys(List *pathkeys, List *restrictinfos)
{
List *mergeclauses = NIL;
List *i;
foreach(i, pathkeys)
{
List *pathkey = lfirst(i);
RestrictInfo *matched_restrictinfo = NULL;
List *j;
/*
* We can match any of the keys in this pathkey sublist,
* since they're all equivalent. And we can match against
* either left or right side of any mergejoin clause we haven't
* used yet. For the moment we use a dumb "greedy" algorithm
* with no backtracking. Is it worth being any smarter to
* make a longer list of usable mergeclauses? Probably not.
*/
foreach(j, pathkey)
{
PathKeyItem *keyitem = lfirst(j);
Var *keyvar = (Var *) keyitem->key;
List *k;
if (! IsA(keyvar, Var))
continue; /* for now, only Vars can be mergejoined */
foreach(k, restrictinfos)
{
RestrictInfo *restrictinfo = lfirst(k);
Assert(restrictinfo->mergejoinoperator != InvalidOid);
if ((var_equal(keyvar, get_leftop(restrictinfo->clause)) ||
var_equal(keyvar, get_rightop(restrictinfo->clause))) &&
! member(restrictinfo, mergeclauses))
{
matched_restrictinfo = restrictinfo;
break;
}
}
if (matched_restrictinfo)
break;
}
/*
* If we didn't find a mergeclause, we're done --- any additional
* sort-key positions in the pathkeys are useless. (But we can
* still mergejoin if we found at least one mergeclause.)
*/
if (! matched_restrictinfo)
break;
/*
* If we did find a usable mergeclause for this sort-key position,
* add it to result list.
*/
mergeclauses = lappend(mergeclauses, matched_restrictinfo);
}
return mergeclauses;
}
/*
* make_pathkeys_for_mergeclauses
* Builds a pathkey list representing the explicit sort order that
* must be applied to a path in order to make it usable for the
* given mergeclauses.
*
* 'mergeclauses' is a list of RestrictInfos for mergejoin clauses
* that will be used in a merge join.
* 'tlist' is a relation target list for either the inner or outer
* side of the proposed join rel.
*
* Returns a pathkeys list that can be applied to the indicated relation.
*
* Note that it is not this routine's job to decide whether sorting is
* actually needed for a particular input path. Assume a sort is necessary;
* just make the keys, eh?
*/
List *
make_pathkeys_for_mergeclauses(List *mergeclauses, List *tlist)
{
List *pathkeys = NIL;
List *i;
foreach(i, mergeclauses)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(i);
Var *key;
Oid sortop;
/*
* Find the key and sortop needed for this mergeclause.
*
* We can use either side of the mergeclause, since we haven't yet
* committed to which side will be inner.
*/
Assert(restrictinfo->mergejoinoperator != InvalidOid);
key = (Var *) matching_tlist_var(get_leftop(restrictinfo->clause),
tlist);
sortop = restrictinfo->left_sortop;
if (! key)
{
key = (Var *) matching_tlist_var(get_rightop(restrictinfo->clause),
tlist);
sortop = restrictinfo->right_sortop;
}
if (! key)
elog(ERROR, "make_pathkeys_for_mergeclauses: can't find key");
/*
* Add a pathkey sublist for this sort item
*/
pathkeys = lappend(pathkeys,
lcons(makePathKeyItem((Node *) key, sortop),
NIL));
}
return pathkeys;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.42 1999/07/16 04:59:16 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/prune.c,v 1.43 1999/08/16 02:17:52 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,17 +18,15 @@
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
static List *merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids);
static List *merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels);
/*
* merge_rels_with_same_relids
* Removes any redundant relation entries from a list of rel nodes
* 'rel_list'. Obviously, the first relation can't be a duplicate.
* 'rel_list', merging their pathlists into the first non-duplicate
* relation entry for each value of relids.
*
* Returns the resulting list.
*
*/
void
merge_rels_with_same_relids(List *rel_list)
@ -37,17 +35,21 @@ merge_rels_with_same_relids(List *rel_list)
/*
* rel_list can shorten while running as duplicate relations are
* deleted
* deleted. Obviously, the first relation can't be a duplicate,
* so the list head pointer won't change.
*/
foreach(i, rel_list)
lnext(i) = merge_rel_with_same_relids((RelOptInfo *) lfirst(i), lnext(i));
{
lnext(i) = merge_rel_with_same_relids((RelOptInfo *) lfirst(i),
lnext(i));
}
}
/*
* merge_rel_with_same_relids
* Prunes those relations from 'unjoined_relids' that are redundant with
* Prunes those relations from 'unmerged_rels' that are redundant with
* 'rel'. A relation is redundant if it is built up of the same
* relations as 'rel'. Paths for the redundant relation are merged into
* relations as 'rel'. Paths for the redundant relations are merged into
* the pathlist of 'rel'.
*
* Returns a list of non-redundant relations, and sets the pathlist field
@ -55,50 +57,52 @@ merge_rels_with_same_relids(List *rel_list)
*
*/
static List *
merge_rel_with_same_relids(RelOptInfo *rel, Relids unjoined_relids)
merge_rel_with_same_relids(RelOptInfo *rel, List *unmerged_rels)
{
List *i = NIL;
List *result = NIL;
List *i;
foreach(i, unjoined_relids)
foreach(i, unmerged_rels)
{
RelOptInfo *unjoined_rel = (RelOptInfo *) lfirst(i);
if (same(rel->relids, unjoined_rel->relids))
RelOptInfo *unmerged_rel = (RelOptInfo *) lfirst(i);
if (same(rel->relids, unmerged_rel->relids))
{
/*
* This are on the same relations, so get the best of their
* pathlists.
* These rels are for the same set of base relations,
* so get the best of their pathlists. We assume it's
* ok to reassign a path to the other RelOptInfo without
* doing more than changing its parent pointer (cf. pathnode.c).
*/
rel->pathlist = add_pathlist(rel,
rel->pathlist,
unjoined_rel->pathlist);
unmerged_rel->pathlist);
}
else
result = lappend(result, unjoined_rel);
result = lappend(result, unmerged_rel);
}
return result;
}
/*
* rels_set_cheapest
* For each relation entry in 'rel_list' (which corresponds to a join
* relation), set pointers to the cheapest path
* For each relation entry in 'rel_list' (which should contain only join
* relations), set pointers to the cheapest path and compute rel size.
*/
void
rels_set_cheapest(List *rel_list)
{
List *x = NIL;
RelOptInfo *rel = (RelOptInfo *) NULL;
JoinPath *cheapest;
List *x;
foreach(x, rel_list)
{
rel = (RelOptInfo *) lfirst(x);
RelOptInfo *rel = (RelOptInfo *) lfirst(x);
JoinPath *cheapest;
cheapest = (JoinPath *) set_cheapest(rel, rel->pathlist);
if (IsA_JoinPath(cheapest))
rel->size = compute_joinrel_size(cheapest);
else
elog(ERROR, "non JoinPath called");
elog(ERROR, "rels_set_cheapest: non JoinPath found");
}
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.70 1999/08/12 04:32:53 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.71 1999/08/16 02:17:53 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -27,12 +27,8 @@
#include "utils/syscache.h"
#define NONAME_SORT 1
#define NONAME_MATERIAL 2
static List *switch_outer(List *clauses);
static Oid *generate_merge_input_sortorder(List *pathkeys,
MergeOrder *mergeorder);
static List *set_tlist_sort_info(List *tlist, List *pathkeys);
static Scan *create_scan_node(Path *best_path, List *tlist);
static Join *create_join_node(JoinPath *best_path, List *tlist);
static SeqScan *create_seqscan_node(Path *best_path, List *tlist,
@ -53,8 +49,7 @@ static List *fix_indxqual_sublist(List *indexqual, IndexPath *index_path,
Form_pg_index index);
static Node *fix_indxqual_operand(Node *node, IndexPath *index_path,
Form_pg_index index);
static Noname *make_noname(List *tlist, List *pathkeys, Oid *operators,
Plan *plan_node, int nonametype);
static Noname *make_noname(List *tlist, List *pathkeys, Plan *plan_node);
static IndexScan *make_indexscan(List *qptlist, List *qpqual, Index scanrelid,
List *indxid, List *indxqual, List *indxqualorig);
static NestLoop *make_nestloop(List *qptlist, List *qpqual, Plan *lefttree,
@ -482,9 +477,7 @@ create_nestloop_node(NestPath *best_path,
/* Materialize the inner join for speed reasons */
inner_node = (Plan *) make_noname(inner_tlist,
NIL,
NULL,
inner_node,
NONAME_MATERIAL);
inner_node);
}
join_node = make_nestloop(tlist,
@ -531,34 +524,18 @@ create_mergejoin_node(MergePath *best_path,
inner_tlist));
/*
* Create explicit sort paths for the outer and inner join paths if
* Create explicit sort nodes for the outer and inner join paths if
* necessary. The sort cost was already accounted for in the path.
*/
if (best_path->outersortkeys)
{
Oid *outer_order = generate_merge_input_sortorder(
best_path->outersortkeys,
best_path->jpath.path.pathorder->ord.merge);
outer_node = (Plan *) make_noname(outer_tlist,
best_path->outersortkeys,
outer_order,
outer_node,
NONAME_SORT);
}
outer_node);
if (best_path->innersortkeys)
{
Oid *inner_order = generate_merge_input_sortorder(
best_path->innersortkeys,
best_path->jpath.path.pathorder->ord.merge);
inner_node = (Plan *) make_noname(inner_tlist,
best_path->innersortkeys,
inner_order,
inner_node,
NONAME_SORT);
}
inner_node);
join_node = make_mergejoin(tlist,
qpqual,
@ -589,7 +566,7 @@ create_hashjoin_node(HashPath *best_path,
/*
* NOTE: there will always be exactly one hashclause in the list
* best_path->path_hashclauses (cf. hash_inner_and_outer()).
* We represent it as a list anyway for convenience with routines
* We represent it as a list anyway, for convenience with routines
* that want to work on lists of clauses.
*/
@ -782,9 +759,9 @@ fix_indxqual_operand(Node *node, IndexPath *index_path,
/*
* switch_outer
* Given a list of merge clauses, rearranges the elements within the
* clauses so the outer join variable is on the left and the inner is on
* the right. The original list is not touched; a modified list
* Given a list of merge or hash joinclauses, rearrange the elements within
* the clauses so the outer join variable is on the left and the inner is
* on the right. The original list is not touched; a modified list
* is returned.
*/
static List *
@ -796,15 +773,12 @@ switch_outer(List *clauses)
foreach(i, clauses)
{
Expr *clause = (Expr *) lfirst(i);
Node *op;
Var *op;
Assert(is_opclause((Node *) clause));
op = (Node *) get_rightop(clause);
Assert(op != (Node *) NULL);
if (IsA(op, ArrayRef)) /* I think this test is dead code ... tgl */
op = ((ArrayRef *) op)->refexpr;
Assert(IsA(op, Var));
if (var_is_outer((Var *) op))
op = get_rightop(clause);
Assert(op && IsA(op, Var));
if (var_is_outer(op))
{
/*
* Duplicate just enough of the structure to allow commuting
@ -826,80 +800,42 @@ switch_outer(List *clauses)
}
/*
* generate_merge_input_sortorder
* set_tlist_sort_info
* Sets the reskey and reskeyop fields of resdom nodes in a target list
* for a sort node.
*
* Generate the list of sort ops needed to sort one of the input paths for
* a merge. We may have to use either left or right sortop for each item,
* since the original mergejoin clause may or may not have been commuted
* (compare switch_outer above).
* 'tlist' is the target list
* 'pathkeys' is the desired pathkeys for the sort. NIL means no sort.
*
* XXX This is largely a crock. It works only because group_clauses_by_order
* only groups together mergejoin clauses that have identical MergeOrder info,
* which means we can safely use a single MergeOrder input to deal with all
* the data. There should be a more general data structure that allows coping
* with groups of mergejoin clauses that have different join operators.
*/
static Oid *
generate_merge_input_sortorder(List *pathkeys, MergeOrder *mergeorder)
{
int listlength = length(pathkeys);
Oid *result = (Oid *) palloc(sizeof(Oid) * (listlength + 1));
Oid *nextsortop = result;
List *p;
foreach(p, pathkeys)
{
Var *pkey = (Var *) lfirst((List *) lfirst(p));
Assert(IsA(pkey, Var));
if (pkey->vartype == mergeorder->left_type)
*nextsortop++ = mergeorder->left_operator;
else if (pkey->vartype == mergeorder->right_type)
*nextsortop++ = mergeorder->right_operator;
else
elog(ERROR,
"generate_merge_input_sortorder: can't handle data type %d",
pkey->vartype);
}
*nextsortop++ = InvalidOid;
return result;
}
/*
* set_noname_tlist_operators
* Sets the key and keyop fields of resdom nodes in a target list.
*
* 'tlist' is the target list
* 'pathkeys' is a list of N keys in the form((key1) (key2)...(keyn)),
* corresponding to vars in the target list that are to
* be sorted or hashed
* 'operators' is the corresponding list of N sort or hash operators
*
* Returns the modified-in-place target list.
* Returns the modified-in-place target list.
*/
static List *
set_noname_tlist_operators(List *tlist, List *pathkeys, Oid *operators)
set_tlist_sort_info(List *tlist, List *pathkeys)
{
int keyno = 1;
Node *pathkey;
Resdom *resdom;
List *i;
foreach(i, pathkeys)
{
pathkey = lfirst((List *) lfirst(i));
resdom = tlist_member((Var *) pathkey, tlist);
if (resdom)
{
List *keysublist = (List *) lfirst(i);
PathKeyItem *pathkey;
Resdom *resdom;
/*
* Order the resdom pathkey and replace the operator OID for
* each key with the regproc OID.
*/
resdom->reskey = keyno;
resdom->reskeyop = get_opcode(operators[keyno - 1]);
}
keyno += 1;
/*
* We can sort by any one of the sort key items listed in this
* sublist. For now, we always take the first one --- is there
* any way of figuring out which might be cheapest to execute?
* (For example, int4lt is likely much cheaper to execute than
* numericlt, but both might appear in the same pathkey sublist...)
*/
pathkey = lfirst(keysublist);
Assert(IsA(pathkey, PathKeyItem));
resdom = tlist_member((Var *) pathkey->key, tlist);
if (!resdom)
elog(ERROR, "set_tlist_sort_info: cannot find tlist item to sort");
resdom->reskey = keyno;
resdom->reskeyop = get_opcode(pathkey->sortop);
keyno++;
}
return tlist;
}
@ -909,7 +845,6 @@ set_noname_tlist_operators(List *tlist, List *pathkeys, Oid *operators)
* This is not critical, since the decisions have already been made,
* but it helps produce more reasonable-looking EXPLAIN output.
*/
static void
copy_costsize(Plan *dest, Plan *src)
{
@ -939,52 +874,44 @@ copy_costsize(Plan *dest, Plan *src)
* result returned for a sort will look like (SEQSCAN(SORT(plan_node)))
* or (SEQSCAN(MATERIAL(plan_node)))
*
* 'tlist' is the target list of the scan to be sorted or hashed
* 'pathkeys' is the list of keys which the sort or hash will be done on
* 'operators' is the operators with which the sort or hash is to be done
* (a list of operator OIDs)
* 'plan_node' is the node which yields tuples for the sort
* 'nonametype' indicates which operation(sort or hash) to perform
* 'tlist' is the target list of the scan to be sorted or materialized
* 'pathkeys' is the list of pathkeys by which the result is to be sorted
* (NIL implies no sort needed, just materialize it)
* 'plan_node' is the node which yields input tuples
*/
static Noname *
make_noname(List *tlist,
List *pathkeys,
Oid *operators,
Plan *plan_node,
int nonametype)
Plan *plan_node)
{
List *noname_tlist;
Noname *retval = NULL;
Noname *retval;
/* Create a new target list for the noname, with keys set. */
noname_tlist = set_noname_tlist_operators(new_unsorted_tlist(tlist),
pathkeys,
operators);
switch (nonametype)
/* Create a new target list for the noname, with sort keys set. */
noname_tlist = set_tlist_sort_info(new_unsorted_tlist(tlist),
pathkeys);
if (pathkeys != NIL)
{
case NONAME_SORT:
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
/* need to sort */
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
(Plan *) make_sort(noname_tlist,
_NONAME_RELATION_ID_,
plan_node,
length(pathkeys)));
break;
case NONAME_MATERIAL:
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
(Plan *) make_material(noname_tlist,
length(pathkeys)));
}
else
{
/* no sort */
retval = (Noname *) make_seqscan(tlist,
NIL,
_NONAME_RELATION_ID_,
(Plan *) make_material(noname_tlist,
_NONAME_RELATION_ID_,
plan_node,
length(pathkeys)));
break;
default:
elog(ERROR, "make_noname: unknown noname type %d", nonametype);
plan_node,
0));
}
return retval;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.36 1999/08/10 03:00:14 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.37 1999/08/16 02:17:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -28,11 +28,10 @@
static void add_restrict_and_join_to_rel(Query *root, Node *clause);
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 MergeOrder *mergejoinop(Expr *clause);
static Oid hashjoinop(Expr *clause);
static void check_mergejoinable(RestrictInfo *restrictinfo);
static void check_hashjoinable(RestrictInfo *restrictinfo);
/*****************************************************************************
@ -123,8 +122,8 @@ add_missing_vars_to_tlist(Query *root, List *tlist)
/*
* add_restrict_and_join_to_rels-
* Initializes RestrictInfo and JoinInfo fields of relation entries for all
* add_restrict_and_join_to_rels
* Fill RestrictInfo and JoinInfo lists of relation entries for all
* relations appearing within clauses. Creates new relation entries if
* necessary, adding them to *query_relation_list*.
*
@ -140,11 +139,11 @@ add_restrict_and_join_to_rels(Query *root, List *clauses)
}
/*
* add_restrict_and_join_to_rel-
* add_restrict_and_join_to_rel
* Add clause information to either the 'RestrictInfo' or 'JoinInfo' field
* of a relation entry (depending on whether or not the clause is a join)
* by creating a new RestrictInfo node and setting appropriate fields
* within the nodes.
* (depending on whether the clause is a join) of each base relation
* mentioned in the clause. A RestrictInfo node is created and added to
* the appropriate list for each rel.
*/
static void
add_restrict_and_join_to_rel(Query *root, Node *clause)
@ -154,9 +153,11 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
List *vars;
restrictinfo->clause = (Expr *) clause;
restrictinfo->indexids = NIL;
restrictinfo->mergejoinorder = (MergeOrder *) NULL;
restrictinfo->hashjoinoperator = (Oid) 0;
restrictinfo->subclauseindices = NIL;
restrictinfo->mergejoinoperator = InvalidOid;
restrictinfo->left_sortop = InvalidOid;
restrictinfo->right_sortop = InvalidOid;
restrictinfo->hashjoinoperator = InvalidOid;
/*
* The selectivity of the clause must be computed regardless of
@ -196,7 +197,7 @@ add_restrict_and_join_to_rel(Query *root, Node *clause)
/*
* add_join_info_to_rels
* For every relation participating in a join clause, add 'restrictinfo' to
* the appropriate joininfo node (creating a new one and adding it to the
* the appropriate joininfo list (creating a new one and adding it to the
* appropriate rel node if necessary).
*
* 'restrictinfo' describes the join clause
@ -211,21 +212,22 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
/* For every relid, find the joininfo, and add the proper join entries */
foreach(join_relid, join_relids)
{
int cur_relid = lfirsti(join_relid);
JoinInfo *joininfo;
Relids unjoined_relids = NIL;
List *rel;
List *otherrel;
/* Get the relids not equal to the current relid */
foreach(rel, join_relids)
foreach(otherrel, join_relids)
{
if (lfirsti(rel) != lfirsti(join_relid))
unjoined_relids = lappendi(unjoined_relids, lfirsti(rel));
if (lfirsti(otherrel) != cur_relid)
unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
}
/*
* Find or make the joininfo node for this combination of rels
*/
joininfo = find_joininfo_node(get_base_rel(root, lfirsti(join_relid)),
joininfo = find_joininfo_node(get_base_rel(root, cur_relid),
unjoined_relids);
/*
@ -247,12 +249,8 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
/*
* set_joininfo_mergeable_hashable
* Set the MergeJoinable or HashJoinable field for every joininfo node
* (within a rel node) and the mergejoinorder or hashjoinop field for
* each restrictinfo node (within a joininfo node) for all relations in a
* query.
*
* Returns nothing.
* Examine each join clause used in a query and set the merge and hash
* info fields in those that are mergejoinable or hashjoinable.
*/
void
set_joininfo_mergeable_hashable(List *rel_list)
@ -272,111 +270,102 @@ set_joininfo_mergeable_hashable(List *rel_list)
foreach(z, joininfo->jinfo_restrictinfo)
{
RestrictInfo *restrictinfo = (RestrictInfo *) lfirst(z);
Expr *clause = restrictinfo->clause;
if (is_joinable((Node *) clause))
{
if (_enable_mergejoin_)
{
MergeOrder *sortop = mergejoinop(clause);
if (sortop)
{
restrictinfo->mergejoinorder = sortop;
joininfo->mergejoinable = true;
}
}
if (_enable_hashjoin_)
{
Oid hashop = hashjoinop(clause);
if (hashop)
{
restrictinfo->hashjoinoperator = hashop;
joininfo->hashjoinable = true;
}
}
}
if (_enable_mergejoin_)
check_mergejoinable(restrictinfo);
if (_enable_hashjoin_)
check_hashjoinable(restrictinfo);
}
}
}
}
/*
* mergejoinop
* Returns a MergeOrder node for 'clause' iff 'clause' is mergejoinable,
* i.e., both operands are single vars and the operator is
* a mergejoinable operator.
* check_mergejoinable
* If the restrictinfo's clause is mergejoinable, set the mergejoin
* info fields in the restrictinfo.
*
* Currently, we support mergejoin for binary opclauses where
* both operands are simple Vars and the operator is a mergejoinable
* operator. (Note: since we are only examining clauses that were
* 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 MergeOrder *
mergejoinop(Expr *clause)
static void
check_mergejoinable(RestrictInfo *restrictinfo)
{
Expr *clause = restrictinfo->clause;
Var *left,
*right;
Oid opno,
leftOp,
rightOp;
bool sortable;
if (!is_opclause((Node *) clause))
return NULL;
if (! is_opclause((Node *) clause))
return;
left = get_leftop(clause);
right = get_rightop(clause);
/* caution: is_opclause accepts more than I do, so check it */
if (!right)
return NULL; /* unary opclauses need not apply */
if (! right)
return; /* unary opclauses need not apply */
if (!IsA(left, Var) || !IsA(right, Var))
return NULL;
return;
opno = ((Oper *) clause->oper)->opno;
sortable = op_mergejoinable(opno,
left->vartype,
right->vartype,
&leftOp,
&rightOp);
if (sortable)
if (op_mergejoinable(opno,
left->vartype,
right->vartype,
&leftOp,
&rightOp))
{
MergeOrder *morder = makeNode(MergeOrder);
morder->join_operator = opno;
morder->left_operator = leftOp;
morder->right_operator = rightOp;
morder->left_type = left->vartype;
morder->right_type = right->vartype;
return morder;
restrictinfo->mergejoinoperator = opno;
restrictinfo->left_sortop = leftOp;
restrictinfo->right_sortop = rightOp;
}
else
return NULL;
}
/*
* hashjoinop
* Returns the hashjoin operator iff 'clause' is hashjoinable,
* i.e., both operands are single vars and the operator is
* a hashjoinable operator.
* check_hashjoinable
* If the restrictinfo's clause is hashjoinable, set the hashjoin
* info fields in the restrictinfo.
*
* Currently, we support hashjoin for binary opclauses where
* both operands are simple Vars and the operator is a hashjoinable
* operator. (Note: since we are only examining clauses that were
* 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 Oid
hashjoinop(Expr *clause)
static void
check_hashjoinable(RestrictInfo *restrictinfo)
{
Expr *clause = restrictinfo->clause;
Var *left,
*right;
Oid opno;
if (!is_opclause((Node *) clause))
return InvalidOid;
if (! is_opclause((Node *) clause))
return;
left = get_leftop(clause);
right = get_rightop(clause);
/* caution: is_opclause accepts more than I do, so check it */
if (!right)
return InvalidOid; /* unary opclauses need not apply */
if (! right)
return; /* unary opclauses need not apply */
if (!IsA(left, Var) || !IsA(right, Var))
return InvalidOid;
return;
return op_hashjoinable(((Oper *) clause->oper)->opno,
left->vartype,
right->vartype);
opno = ((Oper *) clause->oper)->opno;
if (op_hashjoinable(opno,
left->vartype,
right->vartype))
{
restrictinfo->hashjoinoperator = opno;
}
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.37 1999/07/17 20:17:17 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.38 1999/08/16 02:17:55 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -318,34 +318,29 @@ find_all_inheritors(Relids unexamined_relids,
Relids examined_relids)
{
List *new_inheritors = NIL;
List *new_examined_relids = NIL;
List *new_unexamined_relids = NIL;
List *new_examined_relids;
List *new_unexamined_relids;
List *rels;
/*
* Find all relations which inherit from members of
* 'unexamined_relids' and store them in 'new_inheritors'.
*/
List *rels = NIL;
List *newrels = NIL;
foreach(rels, unexamined_relids)
{
newrels = (List *) LispUnioni(find_inheritance_children(lfirsti(rels)),
newrels);
new_inheritors = LispUnioni(new_inheritors,
find_inheritance_children(lfirsti(rels)));
}
new_inheritors = newrels;
new_examined_relids = (List *) LispUnioni(examined_relids, unexamined_relids);
new_examined_relids = LispUnioni(examined_relids, unexamined_relids);
new_unexamined_relids = set_differencei(new_inheritors,
new_examined_relids);
if (new_unexamined_relids == NULL)
if (new_unexamined_relids == NIL)
return new_examined_relids;
else
{
return (find_all_inheritors(new_unexamined_relids,
new_examined_relids));
}
return find_all_inheritors(new_unexamined_relids,
new_examined_relids);
}
/*

View File

@ -4,7 +4,7 @@
# Makefile for optimizer/util
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.8 1999/02/05 19:59:28 momjian Exp $
# $Header: /cvsroot/pgsql/src/backend/optimizer/util/Makefile,v 1.9 1999/08/16 02:17:56 tgl Exp $
#
#-------------------------------------------------------------------------
@ -14,7 +14,7 @@ include ../../../Makefile.global
CFLAGS += -I../..
OBJS = restrictinfo.o clauses.o indexnode.o plancat.o \
joininfo.o keys.o ordering.o pathnode.o relnode.o tlist.o var.o
joininfo.o pathnode.o relnode.o tlist.o var.o
# not ready yet: predmig.o xfunc.o

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.46 1999/08/12 04:32:54 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.47 1999/08/16 02:17:56 tgl Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -164,7 +164,7 @@ make_funcclause(Func *func, List *funcargs)
{
Expr *expr = makeNode(Expr);
expr->typeOid = InvalidOid; /* assume type checking done */
expr->typeOid = func->functype;
expr->opType = FUNC_EXPR;
expr->oper = (Node *) func;
expr->args = funcargs;
@ -416,43 +416,6 @@ NumRelids(Node *clause)
return result;
}
/*
* is_joinable
*
* Returns t iff 'clause' is a valid join clause.
*
*/
bool
is_joinable(Node *clause)
{
Node *leftop,
*rightop;
if (!is_opclause(clause))
return false;
leftop = (Node *) get_leftop((Expr *) clause);
rightop = (Node *) get_rightop((Expr *) clause);
if (!rightop)
return false; /* unary opclauses need not apply */
/*
* One side of the clause (i.e. left or right operands) must either be
* a var node ...
*/
if (IsA(leftop, Var) || IsA(rightop, Var))
return true;
/*
* ... or a func node.
*/
if (is_funcclause(leftop) || is_funcclause(rightop))
return true;
return false;
}
/*
* fix_opids
* Calculate opid field from opno for each Oper node in given tree.

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.19 1999/07/16 04:59:24 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/indexnode.c,v 1.20 1999/08/16 02:17:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -39,21 +39,20 @@ find_relation_indices(Query *root, RelOptInfo *rel)
/*
* find_secondary_index
* Creates a list of index path nodes containing information for each
* Creates a list of RelOptInfo nodes containing information for each
* secondary index defined on a relation by searching through the index
* catalog.
*
* 'relid' is the OID of the relation for which indices are being located
*
* Returns a list of new index nodes.
*
* Returns a list of new index RelOptInfo nodes.
*/
static List *
find_secondary_index(Query *root, Oid relid)
{
IdxInfoRetval indexinfo;
List *indexes = NIL;
bool first = TRUE;
bool first = true;
while (index_info(root, first, relid, &indexinfo))
{
@ -63,9 +62,9 @@ find_secondary_index(Query *root, Oid relid)
indexnode->relam = indexinfo.relam;
indexnode->pages = indexinfo.pages;
indexnode->tuples = indexinfo.tuples;
indexnode->classlist = indexinfo.classlist;
indexnode->indexkeys = indexinfo.indexkeys;
indexnode->ordering = indexinfo.orderOprs;
indexnode->classlist = indexinfo.classlist;
indexnode->indproc = indexinfo.indproc;
indexnode->indpred = (List *) indexinfo.indpred;
@ -81,7 +80,7 @@ find_secondary_index(Query *root, Oid relid)
indexnode->innerjoin = NIL;
indexes = lcons(indexnode, indexes);
first = FALSE;
first = false;
}
return indexes;

View File

@ -7,14 +7,13 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.23 1999/07/16 04:59:25 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.24 1999/08/16 02:17:57 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/clauses.h"
#include "optimizer/joininfo.h"
@ -51,8 +50,8 @@ joininfo_member(List *join_relids, List *joininfo_list)
/*
* find_joininfo_node
* Find the joininfo node within a relation entry corresponding
* to a join between 'this_rel' and the relations in 'join_relids'. A
* new node is created and added to the relation entry's joininfo
* to a join between 'this_rel' and the relations in 'join_relids'.
* A new node is created and added to the relation entry's joininfo
* field if the desired one can't be found.
*
* Returns a joininfo node.
@ -69,40 +68,7 @@ find_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
joininfo = makeNode(JoinInfo);
joininfo->unjoined_relids = join_relids;
joininfo->jinfo_restrictinfo = NIL;
joininfo->mergejoinable = false;
joininfo->hashjoinable = false;
this_rel->joininfo = lcons(joininfo, this_rel->joininfo);
}
return joininfo;
}
/*
* other_join_clause_var
* Determines whether a var node is contained within a joinclause
* of the form(op var var).
*
* Returns the other var node in the joinclause if it is, nil if not.
*
*/
Var *
other_join_clause_var(Var *var, Expr *clause)
{
Var *retval;
Var *l,
*r;
retval = (Var *) NULL;
if (var != NULL && is_joinable((Node *) clause))
{
l = (Var *) get_leftop(clause);
r = (Var *) get_rightop(clause);
if (equal(var, l))
retval = r;
else if (equal(var, r))
retval = l;
}
return retval;
}

View File

@ -1,237 +0,0 @@
/*-------------------------------------------------------------------------
*
* keys.c
* Key manipulation routines
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/keys.c,v 1.23 1999/07/15 22:39:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "optimizer/keys.h"
static Expr *matching2_tlvar(int var, List *tlist, bool (*test) ());
static bool equal_indexkey_var(int index_key, Var *var);
/*
* 1. index key
* one of:
* attnum
* (attnum arrayindex)
* 2. path key
* (subkey1 ... subkeyN)
* where subkeyI is a var node
* note that the 'Keys field is a list of these
* 3. join key
* (outer_subkey inner_subkey)
* where each subkey is a var node
* 4. sort key
* one of:
* SortKey node
* number
* nil
* (may also refer to the 'SortKey field of a SortKey node,
* which looks exactly like an index key)
*
*/
/*
* match_indexkey_operand
* Returns t iff an index key 'index_key' matches the given clause
* operand.
*
*/
bool
match_indexkey_operand(int indexkey, Var *operand, RelOptInfo *rel)
{
if (IsA(operand, Var) &&
(lfirsti(rel->relids) == operand->varno) &&
equal_indexkey_var(indexkey, operand))
return true;
else
return false;
}
/*
* equal_indexkey_var
* Returns t iff an index key 'index_key' matches the corresponding
* fields of var node 'var'.
*
*/
static bool
equal_indexkey_var(int index_key, Var *var)
{
if (index_key == var->varattno)
return true;
else
return false;
}
/*
* extract_join_key
* Returns the subkey in a join key corresponding to the outer or inner
* relation.
*
*/
Var *
extract_join_key(JoinKey *jk, int outer_or_inner)
{
Var *retval;
switch (outer_or_inner)
{
case OUTER:
retval = jk->outer;
break;
case INNER:
retval = jk->inner;
break;
default: /* do nothing */
elog(DEBUG, "extract_join_key with neither INNER or OUTER");
retval = NULL;
}
return retval;
}
/*
* pathkeys_match
* Returns t iff two sets of path keys are equivalent. They are
* equivalent if the first Var nodes match the second Var nodes.
*
* See the top of optimizer/path/pathkeys.c for a description of pathkeys.
* Each pathkey is ordered by its join order, so they not pre-ordered to
* match. We must search them ourselves.
*
* This gets called a lot, so it is optimized.
*/
bool
pathkeys_match(List *keys1, List *keys2, int *better_key)
{
List *key1,
*key2;
bool key1_subsetof_key2 = true,
key2_subsetof_key1 = true;
for (key1 = keys1, key2 = keys2;
key1 != NIL && key2 != NIL;
key1 = lnext(key1), key2 = lnext(key2))
{
List *i;
if (key1_subsetof_key2)
foreach(i, lfirst(key1))
{
Var *subkey = lfirst(i);
if (!member(subkey, lfirst(key2)))
{
key1_subsetof_key2 = false;
break;
}
}
if (key2_subsetof_key1)
foreach(i, lfirst(key2))
{
Var *subkey = lfirst(i);
if (!member(subkey, lfirst(key1)))
{
key2_subsetof_key1 = false;
break;
}
}
if (!key1_subsetof_key2 && !key2_subsetof_key1)
break; /* no need to continue comparisons. */
}
if (!key1_subsetof_key2 && !key2_subsetof_key1)
{
*better_key = 0;
return false;
}
if (key1_subsetof_key2 && !key2_subsetof_key1)
{
*better_key = 2;
return true;
}
if (!key1_subsetof_key2 && key2_subsetof_key1)
{
*better_key = 1;
return true;
}
*better_key = 0;
return true;
}
/*
* collect_index_pathkeys
* Creates a list of subkeys by retrieving var nodes corresponding to
* each index key in 'index_keys' from the relation's target list
* 'tlist'. If the key is not in the target list, the key is irrelevant
* and is thrown away. The returned subkey list is of the form:
* ((var1) (var2) ... (varn))
*
* 'index_keys' is a list of index keys
* 'tlist' is a relation target list
*
* Returns the list of cons'd subkeys.
*
*/
/* This function is identical to matching_tlvar and tlistentry_member.
* They should be merged.
*/
static Expr *
matching2_tlvar(int var, List *tlist, bool (*test) ())
{
TargetEntry *tlentry = NULL;
if (var)
{
List *temp;
foreach(temp, tlist)
{
if ((*test) (var, get_expr(lfirst(temp))))
{
tlentry = lfirst(temp);
break;
}
}
}
if (tlentry)
return (Expr *) get_expr(tlentry);
else
return (Expr *) NULL;
}
List *
collect_index_pathkeys(int *index_keys, List *tlist)
{
List *retval = NIL;
Assert(index_keys != NULL);
while (index_keys[0] != 0)
{
Expr *mvar;
mvar = matching2_tlvar(index_keys[0],
tlist,
equal_indexkey_var);
if (mvar)
retval = lappend(retval, lcons(mvar, NIL));
index_keys++;
}
return retval;
}

View File

@ -1,169 +0,0 @@
/*-------------------------------------------------------------------------
*
* ordering.c
* Routines to manipulate and compare merge and path orderings
*
* Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/Attic/ordering.c,v 1.17 1999/07/15 22:39:31 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include <sys/types.h>
#include "postgres.h"
#include "optimizer/ordering.h"
static bool sortops_order_match(Oid *ordering1, Oid *ordering2,
int *better_sort);
/*
* equal_path_ordering
* Returns t iff two path orderings are equal.
*
*/
bool
pathorder_match(PathOrder *path_ordering1,
PathOrder *path_ordering2,
int *better_sort)
{
*better_sort = 0;
if (path_ordering1 == path_ordering2)
return true;
if (!path_ordering2)
{
*better_sort = 1;
return true;
}
if (!path_ordering1)
{
*better_sort = 2;
return true;
}
if (path_ordering1->ordtype == MERGE_ORDER &&
path_ordering2->ordtype == MERGE_ORDER)
return equal(path_ordering1->ord.merge, path_ordering2->ord.merge);
else if (path_ordering1->ordtype == SORTOP_ORDER &&
path_ordering2->ordtype == SORTOP_ORDER)
{
return sortops_order_match(path_ordering1->ord.sortop,
path_ordering2->ord.sortop,
better_sort);
}
else if (path_ordering1->ordtype == MERGE_ORDER &&
path_ordering2->ordtype == SORTOP_ORDER)
{
if (!path_ordering2->ord.sortop)
{
*better_sort = 1;
return true;
}
return path_ordering1->ord.merge->left_operator == path_ordering2->ord.sortop[0];
}
else
{
if (!path_ordering1->ord.sortop)
{
*better_sort = 2;
return true;
}
return path_ordering1->ord.sortop[0] == path_ordering2->ord.merge->left_operator;
}
}
/*
* equal_path_merge_ordering
* Returns t iff a path ordering is usable for ordering a merge join.
*
* XXX Presently, this means that the first sortop of the path matches
* either of the merge sortops. Is there a "right" and "wrong"
* sortop to match?
*
*/
bool
equal_path_merge_ordering(Oid *path_ordering,
MergeOrder *merge_ordering)
{
if (path_ordering == NULL || merge_ordering == NULL)
return false;
if (path_ordering[0] == merge_ordering->left_operator ||
path_ordering[0] == merge_ordering->right_operator)
return true;
else
return false;
}
/*
* equal_merge_ordering
* Returns t iff two merge orderings are equal.
*
*/
bool
equal_merge_ordering(MergeOrder *merge_ordering1,
MergeOrder *merge_ordering2)
{
return equal(merge_ordering1, merge_ordering2);
}
/*
* sortops
*
*/
/*
* equal_sort_ops_order -
* Returns true iff the sort operators are in the same order.
*/
static bool
sortops_order_match(Oid *ordering1, Oid *ordering2, int *better_sort)
{
int i = 0;
*better_sort = 0;
if (ordering1 == ordering2)
return true;
if (!ordering2)
{
*better_sort = 1;
return true;
}
if (!ordering1)
{
*better_sort = 2;
return true;
}
while (ordering1[i] != 0 && ordering2[i] != 0)
{
if (ordering1[i] != ordering2[i])
break;
i++;
}
if (ordering1[i] != 0 && ordering2[i] == 0)
{
*better_sort = 1;
return true;
}
if (ordering1[i] == 0 && ordering2[i] != 0)
{
*better_sort = 2;
return true;
}
return ordering1[i] == 0 && ordering2[i] == 0;
}

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.53 1999/08/06 04:00:17 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.54 1999/08/16 02:17:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,16 +18,12 @@
#include "optimizer/cost.h"
#include "optimizer/keys.h"
#include "optimizer/ordering.h"
#include "optimizer/pathnode.h"
#include "optimizer/paths.h"
#include "optimizer/plancat.h"
#include "optimizer/restrictinfo.h"
#include "parser/parsetree.h"
static Path *better_path(Path *new_path, List *unique_paths, bool *is_new);
/*****************************************************************************
* MISC. PATH UTILITIES
@ -84,186 +80,104 @@ set_cheapest(RelOptInfo *parent_rel, List *pathlist)
/*
* add_pathlist
* For each path in the list 'new_paths', add to the list 'unique_paths'
* only those paths that are unique (i.e., unique ordering and ordering
* keys). Should a conflict arise, the more expensive path is thrown out,
* thereby pruning the plan space. But we don't prune if xfunc
* told us not to.
* Construct an output path list by adding to old_paths each path in
* new_paths that is worth considering --- that is, it has either a
* better sort order (better pathkeys) or cheaper cost than any of the
* existing old paths.
*
* Unless parent_rel->pruneable is false, we also remove from the output
* pathlist any old paths that are dominated by added path(s) --- that is,
* some new path is both cheaper and at least as well ordered.
*
* Note: the list old_paths is destructively modified, and in fact is
* turned into the output list.
*
* 'parent_rel' is the relation entry to which these paths correspond.
* 'old_paths' is the list of previously accepted paths for parent_rel.
* 'new_paths' is a list of potential new paths.
*
* Returns the list of unique pathnodes.
*
* Returns the updated list of interesting pathnodes.
*/
List *
add_pathlist(RelOptInfo *parent_rel, List *unique_paths, List *new_paths)
add_pathlist(RelOptInfo *parent_rel, List *old_paths, List *new_paths)
{
List *p1;
foreach(p1, new_paths)
{
Path *new_path = (Path *) lfirst(p1);
Path *old_path;
bool is_new;
bool accept_new = true; /* unless we find a superior old path */
List *p2_prev = NIL;
List *p2;
/* Is this new path already in unique_paths? */
if (member(new_path, unique_paths))
continue;
/* Find best matching path */
old_path = better_path(new_path, unique_paths, &is_new);
if (is_new)
/*
* Loop to check proposed new path against old paths. Note it is
* possible for more than one old path to be tossed out because
* new_path dominates it.
*/
foreach(p2, old_paths)
{
/* This is a brand new path. */
new_path->parent = parent_rel;
unique_paths = lcons(new_path, unique_paths);
}
else if (old_path == NULL)
{
; /* do nothing if path is not cheaper */
}
else if (old_path != NULL)
{ /* (IsA(old_path,Path)) { */
new_path->parent = parent_rel;
if (!parent_rel->pruneable)
unique_paths = lcons(new_path, unique_paths);
else
unique_paths = lcons(new_path,
LispRemove(old_path, unique_paths));
}
}
return unique_paths;
}
Path *old_path = (Path *) lfirst(p2);
bool remove_old = false; /* unless new proves superior */
/*
* better_path
* Determines whether 'new_path' has the same ordering and keys as some
* path in the list 'unique_paths'. If there is a redundant path,
* eliminate the more expensive path.
*
* Returns:
* The old path - if 'new_path' matches some path in 'unique_paths' and is
* cheaper
* nil - if 'new_path' matches but isn't cheaper
* t - if there is no path in the list with the same ordering and keys
*
*/
static Path *
better_path(Path *new_path, List *unique_paths, bool *is_new)
{
Path *path = (Path *) NULL;
List *temp = NIL;
int better_key;
int better_sort;
#ifdef OPTDUP_DEBUG
printf("better_path entry\n");
printf("new\n");
pprint(new_path);
printf("unique_paths\n");
pprint(unique_paths);
#endif
foreach(temp, unique_paths)
{
path = (Path *) lfirst(temp);
#ifdef OPTDUP_DEBUG
if (!pathkeys_match(new_path->pathkeys, path->pathkeys, &better_key) ||
better_key != 0)
{
printf("betterkey = %d\n", better_key);
printf("newpath\n");
pprint(new_path->pathkeys);
printf("oldpath\n");
pprint(path->pathkeys);
if (path->pathkeys && new_path->pathkeys &&
length(lfirst(path->pathkeys)) >= 2 /* &&
* length(lfirst(path->pa
* thkeys)) <
* length(lfirst(new_path
->pathkeys)) */ )
sleep(0); /* set breakpoint here */
}
if (!pathorder_match(new_path->pathorder, path->pathorder,
&better_sort) ||
better_sort != 0)
{
printf("neword\n");
pprint(new_path->pathorder);
printf("oldord\n");
pprint(path->pathorder);
}
#endif
if (pathkeys_match(new_path->pathkeys, path->pathkeys,
&better_key) &&
pathorder_match(new_path->pathorder, path->pathorder,
&better_sort))
{
switch (compare_pathkeys(new_path->pathkeys, old_path->pathkeys))
{
case PATHKEYS_EQUAL:
if (new_path->path_cost < old_path->path_cost)
remove_old = true; /* new dominates old */
else
accept_new = false; /* old equals or dominates new */
break;
case PATHKEYS_BETTER1:
if (new_path->path_cost <= old_path->path_cost)
remove_old = true; /* new dominates old */
break;
case PATHKEYS_BETTER2:
if (new_path->path_cost >= old_path->path_cost)
accept_new = false; /* old dominates new */
break;
case PATHKEYS_DIFFERENT:
/* keep both paths, since they have different ordering */
break;
}
/*
* Replace pathkeys that match exactly, {{1,2}}, {{1,2}}
* Replace pathkeys {{1,2}} with {{1,2,3}}} if the latter is
* not more expensive and replace unordered path with ordered
* path if it is not more expensive. Favor sorted keys over
* unsorted keys in the same way.
* Remove current element from old_list if dominated by new,
* unless xfunc told us not to remove any paths.
*/
/* same keys, and new is cheaper, use it */
if ((better_key == 0 && better_sort == 0 &&
new_path->path_cost < path->path_cost) ||
/* new is better, and cheaper, use it */
(((better_key == 1 && better_sort != 2) ||
(better_key != 2 && better_sort == 1)) &&
new_path->path_cost <= path->path_cost))
if (remove_old && parent_rel->pruneable)
{
#ifdef OPTDUP_DEBUG
printf("replace with new %p old %p better key %d better sort %d\n", &new_path, &path, better_key, better_sort);
printf("new\n");
pprint(new_path);
printf("old\n");
pprint(path);
#endif
*is_new = false;
return path;
if (p2_prev)
lnext(p2_prev) = lnext(p2);
else
old_paths = lnext(p2);
}
else
p2_prev = p2;
/* same keys, new is more expensive, stop */
if ((better_key == 0 && better_sort == 0 &&
new_path->path_cost >= path->path_cost) ||
/*
* If we found an old path that dominates new_path, we can quit
* scanning old_paths; we will not add new_path, and we assume
* new_path cannot dominate any other elements of old_paths.
*/
if (! accept_new)
break;
}
/* old is better, and less expensive, stop */
(((better_key == 2 && better_sort != 1) ||
(better_key != 1 && better_sort == 2)) &&
new_path->path_cost >= path->path_cost))
{
#ifdef OPTDUP_DEBUG
printf("skip new %p old %p better key %d better sort %d\n", &new_path, &path, better_key, better_sort);
printf("new\n");
pprint(new_path);
printf("old\n");
pprint(path);
#endif
*is_new = false;
return NULL;
}
if (accept_new)
{
/* Accept the path. Note that it will now be eligible to be
* compared against the additional elements of new_paths...
*/
new_path->parent = parent_rel; /* not redundant, see prune.c */
old_paths = lcons(new_path, old_paths);
}
}
#ifdef OPTDUP_DEBUG
printf("add new %p old %p better key %d better sort %d\n", &new_path, &path, better_key, better_sort);
printf("new\n");
pprint(new_path);
#endif
*is_new = true;
return NULL;
return old_paths;
}
/*****************************************************************************
* PATH NODE CREATION ROUTINES
*****************************************************************************/
@ -277,19 +191,15 @@ better_path(Path *new_path, List *unique_paths, bool *is_new)
Path *
create_seqscan_path(RelOptInfo *rel)
{
int relid = 0;
Path *pathnode = makeNode(Path);
int relid = 0;
pathnode->pathtype = T_SeqScan;
pathnode->parent = rel;
pathnode->path_cost = 0.0;
pathnode->pathorder = makeNode(PathOrder);
pathnode->pathorder->ordtype = SORTOP_ORDER;
pathnode->pathorder->ord.sortop = NULL;
pathnode->pathkeys = NIL;
pathnode->pathkeys = NIL; /* seqscan has unordered result */
if (rel->relids != NULL)
if (rel->relids != NIL) /* can this happen? */
relid = lfirsti(rel->relids);
pathnode->path_cost = cost_seqscan(relid,
@ -319,12 +229,10 @@ create_index_path(Query *root,
pathnode->path.pathtype = T_IndexScan;
pathnode->path.parent = rel;
pathnode->path.pathorder = makeNode(PathOrder);
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
pathnode->path.pathorder->ord.sortop = index->ordering;
pathnode->path.pathkeys = NIL;
pathnode->path.pathkeys = build_index_pathkeys(root, rel, index);
/* Note that we are making a pathnode for a single-scan indexscan;
/*
* Note that we are making a pathnode for a single-scan indexscan;
* therefore, both indexid and indexqual should be single-element
* lists. We initialize indexqual to contain one empty sublist,
* representing a single index traversal with no index restriction
@ -334,29 +242,7 @@ create_index_path(Query *root,
Assert(length(index->relids) == 1);
pathnode->indexid = index->relids;
pathnode->indexqual = lcons(NIL, NIL);
pathnode->indexkeys = index->indexkeys;
/*
* The index must have an ordering for the path to have (ordering)
* keys, and vice versa.
*/
if (pathnode->path.pathorder->ord.sortop)
{
pathnode->path.pathkeys = collect_index_pathkeys(index->indexkeys,
rel->targetlist);
/*
* Check that the keys haven't 'disappeared', since they may no
* longer be in the target list (i.e., index keys that are not
* relevant to the scan are not applied to the scan path node, so
* if no index keys were found, we can't order the path).
*/
if (pathnode->path.pathkeys == NULL)
pathnode->path.pathorder->ord.sortop = NULL;
}
else
pathnode->path.pathkeys = NULL;
pathnode->joinrelids = NIL; /* no join clauses here */
if (restriction_clauses == NIL)
{
@ -377,7 +263,7 @@ create_index_path(Query *root,
{
/*
* Compute scan cost for the case when 'index' is used with
* restriction clause(s).
* restriction clause(s). Also, place indexqual in path node.
*/
List *indexquals;
float npages;
@ -439,9 +325,9 @@ create_index_path(Query *root,
*
* 'joinrel' is the join relation.
* 'outer_rel' is the outer join relation
* 'outer_path' is the outer join path.
* 'inner_path' is the inner join path.
* 'pathkeys' are the keys of the path
* 'outer_path' is the outer path
* 'inner_path' is the inner path
* 'pathkeys' are the path keys of the new join path
*
* Returns the resulting path node.
*
@ -461,23 +347,6 @@ create_nestloop_path(RelOptInfo *joinrel,
pathnode->innerjoinpath = inner_path;
pathnode->pathinfo = joinrel->restrictinfo;
pathnode->path.pathkeys = pathkeys;
pathnode->path.joinid = NIL;
pathnode->path.outerjoincost = (Cost) 0.0;
pathnode->path.pathorder = makeNode(PathOrder);
if (pathkeys)
{
pathnode->path.pathorder->ordtype = outer_path->pathorder->ordtype;
if (outer_path->pathorder->ordtype == SORTOP_ORDER)
pathnode->path.pathorder->ord.sortop = outer_path->pathorder->ord.sortop;
else
pathnode->path.pathorder->ord.merge = outer_path->pathorder->ord.merge;
}
else
{
pathnode->path.pathorder->ordtype = SORTOP_ORDER;
pathnode->path.pathorder->ord.sortop = NULL;
}
pathnode->path.path_cost = cost_nestloop(outer_path->path_cost,
inner_path->path_cost,
@ -502,8 +371,7 @@ create_nestloop_path(RelOptInfo *joinrel,
* 'innerwidth' is the number of bytes per tuple in the inner relation
* 'outer_path' is the outer path
* 'inner_path' is the inner path
* 'pathkeys' are the new keys of the join relation
* 'order' is the sort order required for the merge
* 'pathkeys' are the path keys of the new join path
* 'mergeclauses' are the applicable join/restriction clauses
* 'outersortkeys' are the sort varkeys for the outer relation
* 'innersortkeys' are the sort varkeys for the inner relation
@ -518,22 +386,29 @@ create_mergejoin_path(RelOptInfo *joinrel,
Path *outer_path,
Path *inner_path,
List *pathkeys,
MergeOrder *order,
List *mergeclauses,
List *outersortkeys,
List *innersortkeys)
{
MergePath *pathnode = makeNode(MergePath);
/*
* If the given paths are already well enough ordered, we can skip
* doing an explicit sort.
*/
if (outersortkeys &&
pathkeys_contained_in(outersortkeys, outer_path->pathkeys))
outersortkeys = NIL;
if (innersortkeys &&
pathkeys_contained_in(innersortkeys, inner_path->pathkeys))
innersortkeys = NIL;
pathnode->jpath.path.pathtype = T_MergeJoin;
pathnode->jpath.path.parent = joinrel;
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.pathinfo = joinrel->restrictinfo;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->jpath.path.pathorder = makeNode(PathOrder);
pathnode->jpath.path.pathorder->ordtype = MERGE_ORDER;
pathnode->jpath.path.pathorder->ord.merge = order;
pathnode->path_mergeclauses = mergeclauses;
pathnode->outersortkeys = outersortkeys;
pathnode->innersortkeys = innersortkeys;
@ -560,15 +435,11 @@ create_mergejoin_path(RelOptInfo *joinrel,
* 'innerwidth' is the number of bytes per tuple in the inner relation
* 'outer_path' is the cheapest outer path
* 'inner_path' is the cheapest inner path
* 'pathkeys' are the path keys of the new join path
* 'operator' is the hashjoin operator
* 'hashclauses' is a list of the hash join clause (always a 1-element list)
* 'outerkeys' are the sort varkeys for the outer relation
* 'innerkeys' are the sort varkeys for the inner relation
* 'innerdisbursion' is an estimate of the disbursion of the inner hash key
*
*/
HashPath *
HashPath *
create_hashjoin_path(RelOptInfo *joinrel,
int outersize,
int innersize,
@ -576,11 +447,7 @@ create_hashjoin_path(RelOptInfo *joinrel,
int innerwidth,
Path *outer_path,
Path *inner_path,
List *pathkeys,
Oid operator,
List *hashclauses,
List *outerkeys,
List *innerkeys,
Cost innerdisbursion)
{
HashPath *pathnode = makeNode(HashPath);
@ -590,16 +457,9 @@ create_hashjoin_path(RelOptInfo *joinrel,
pathnode->jpath.outerjoinpath = outer_path;
pathnode->jpath.innerjoinpath = inner_path;
pathnode->jpath.pathinfo = joinrel->restrictinfo;
pathnode->jpath.path.pathkeys = pathkeys;
pathnode->jpath.path.pathorder = makeNode(PathOrder);
pathnode->jpath.path.pathorder->ordtype = SORTOP_ORDER;
pathnode->jpath.path.pathorder->ord.sortop = NULL;
pathnode->jpath.path.outerjoincost = (Cost) 0.0;
pathnode->jpath.path.joinid = (Relids) NULL;
/* pathnode->hashjoinoperator = operator; */
/* A hashjoin never has pathkeys, since its ordering is unpredictable */
pathnode->jpath.path.pathkeys = NIL;
pathnode->path_hashclauses = hashclauses;
pathnode->outerhashkeys = outerkeys;
pathnode->innerhashkeys = innerkeys;
pathnode->jpath.path.path_cost = cost_hashjoin(outer_path->path_cost,
inner_path->path_cost,
outersize, innersize,

View File

@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.18 1999/07/16 03:13:05 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.19 1999/08/16 02:17:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,10 +29,9 @@
RelOptInfo *
get_base_rel(Query *root, int relid)
{
Relids relids;
Relids relids = lconsi(relid, NIL);
RelOptInfo *rel;
relids = lconsi(relid, NIL);
rel = rel_member(relids, root->base_rel_list);
if (rel == NULL)
{
@ -41,28 +40,26 @@ get_base_rel(Query *root, int relid)
rel->indexed = false;
rel->pages = 0;
rel->tuples = 0;
rel->size = 0;
rel->width = 0;
rel->targetlist = NIL;
rel->pathlist = NIL;
rel->cheapestpath = (Path *) NULL;
rel->pruneable = true;
rel->classlist = NULL;
rel->indexkeys = NULL;
rel->ordering = NULL;
rel->relam = InvalidOid;
rel->indproc = InvalidOid;
rel->indpred = NIL;
rel->restrictinfo = NIL;
rel->joininfo = NIL;
rel->innerjoin = NIL;
root->base_rel_list = lcons(rel, root->base_rel_list);
/*
* ??? the old lispy C code (get_rel) do a listp(relid) here but
* that can never happen since we already established relid is not
* a list. -ay 10/94
*/
if (relid < 0)
{
/*
* If the relation is a materialized relation, assume
* constants for sizes.
@ -72,18 +69,12 @@ get_base_rel(Query *root, int relid)
}
else
{
bool hasindex;
int pages,
tuples;
/*
* Otherwise, retrieve relation characteristics from the
* Otherwise, retrieve relation statistics from the
* system catalogs.
*/
relation_info(root, relid, &hasindex, &pages, &tuples);
rel->indexed = hasindex;
rel->pages = pages;
rel->tuples = tuples;
relation_info(root, relid,
&rel->indexed, &rel->pages, &rel->tuples);
}
}
return rel;
@ -111,16 +102,16 @@ get_join_rel(Query *root, Relids relid)
RelOptInfo *
rel_member(Relids relids, List *rels)
{
List *temp = NIL;
List *temprelid = NIL;
if (relids != NIL && rels != NIL)
{
List *temp;
foreach(temp, rels)
{
temprelid = ((RelOptInfo *) lfirst(temp))->relids;
if (same(temprelid, relids))
return (RelOptInfo *) (lfirst(temp));
RelOptInfo *rel = (RelOptInfo *) lfirst(temp);
if (same(rel->relids, relids))
return rel;
}
}
return NULL;

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: nodes.h,v 1.50 1999/07/15 15:21:17 momjian Exp $
* $Id: nodes.h,v 1.51 1999/08/16 02:17:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -64,27 +64,21 @@ typedef enum NodeTag
T_Func,
T_Array,
T_ArrayRef,
T_Iter,
/*---------------------
* TAGS FOR INNER PLAN NODES (relation.h)
* TAGS FOR PLANNER NODES (relation.h)
*---------------------
*/
T_RelOptInfo = 200,
T_PathOrder,
T_Path,
T_IndexPath,
T_NestPath,
T_MergePath,
T_HashPath,
T_OrderKey,
T_JoinKey,
T_MergeOrder,
T_PathKeyItem,
T_RestrictInfo,
T_JoinMethod,
T_HashInfo,
T_MergeInfo,
T_JoinInfo,
T_Iter,
T_Stream,
/*---------------------
@ -229,28 +223,27 @@ typedef struct Node
NodeTag type;
} Node;
#define nodeTag(_node_) ((Node*)_node_)->type
#define nodeTag(nodeptr) (((Node*)(nodeptr))->type)
#define makeNode(_node_) (_node_*)newNode(sizeof(_node_),T_##_node_)
#define NodeSetTag(n, t) ((Node *)n)->type = t
#define makeNode(_type_) ((_type_ *) newNode(sizeof(_type_),T_##_type_))
#define NodeSetTag(nodeptr,t) (((Node*)(nodeptr))->type = (t))
#define IsA(_node_,_tag_) (nodeTag(_node_) == T_##_tag_)
#define IsA(nodeptr,_type_) (nodeTag(nodeptr) == T_##_type_)
/* ----------------------------------------------------------------
* IsA functions (no inheritence any more)
* IsA functions (no inheritance any more)
* ----------------------------------------------------------------
*/
#define IsA_JoinPath(jp) \
(nodeTag(jp)==T_NestPath || nodeTag(jp)==T_MergePath || \
nodeTag(jp)==T_HashPath)
(IsA(jp, NestPath) || IsA(jp, MergePath) || IsA(jp, HashPath))
#define IsA_Join(j) \
(nodeTag(j)==T_Join || nodeTag(j)==T_NestLoop || \
nodeTag(j)==T_MergeJoin || nodeTag(j)==T_HashJoin)
#define IsA_Join(jp) \
(IsA(jp, Join) || IsA(jp, NestLoop) || \
IsA(jp, MergeJoin) || IsA(jp, HashJoin))
#define IsA_Noname(t) \
(nodeTag(t)==T_Noname || nodeTag(t)==T_Material || nodeTag(t)==T_Sort || \
nodeTag(t)==T_Unique)
(IsA(t, Noname) || IsA(t, Material) || IsA(t, Sort) || \
IsA(t, Unique))
/* ----------------------------------------------------------------
* extern declarations follow
@ -262,6 +255,9 @@ typedef struct Node
*/
extern Node *newNode(Size size, NodeTag tag);
/*
* nodes/{outfuncs.c,print.c}
*/
extern char *nodeToString(void *obj);
/*

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pg_list.h,v 1.12 1999/07/15 23:03:55 momjian Exp $
* $Id: pg_list.h,v 1.13 1999/08/16 02:17:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -35,9 +35,9 @@ typedef struct Value
} val;
} Value;
#define intVal(v) (((Value *)v)->val.ival)
#define floatVal(v) (((Value *)v)->val.dval)
#define strVal(v) (((Value *)v)->val.str)
#define intVal(v) (((Value *)(v))->val.ival)
#define floatVal(v) (((Value *)(v))->val.dval)
#define strVal(v) (((Value *)(v))->val.str)
/*----------------------
@ -66,7 +66,7 @@ typedef struct List
/* pointer version of the list (where it makes a difference) */
#define lfirst(l) ((l)->elem.ptr_value)
#define lnext(l) ((l)->next)
#define lsecond(l) (lfirst(lnext(l)))
#define lsecond(l) lfirst(lnext(l))
#define lfirsti(l) ((l)->elem.int_value)
@ -75,7 +75,7 @@ typedef struct List
* a convenience macro which loops through the list
*/
#define foreach(_elt_,_list_) \
for(_elt_=_list_; _elt_!=NIL;_elt_=lnext(_elt_))
for(_elt_=(_list_); _elt_!=NIL; _elt_=lnext(_elt_))
/*
@ -84,34 +84,34 @@ typedef struct List
extern int length(List *list);
extern List *nconc(List *list1, List *list2);
extern List *lcons(void *datum, List *list);
extern bool member(void *foo, List *bar);
extern List *lconsi(int datum, List *list);
extern bool member(void *datum, List *list);
extern bool intMember(int datum, List *list);
extern Value *makeInteger(long i);
extern Value *makeFloat(double d);
extern Value *makeString(char *str);
extern List *makeList(void *elem,...);
extern List *lappend(List *list, void *obj);
extern List *lappend(List *list, void *datum);
extern List *lappendi(List *list, int datum);
extern List *lremove(void *elem, List *list);
extern void freeList(List *list);
extern List *LispRemove(void *elem, List *list);
extern List *ltruncate(int n, List *list);
extern void *nth(int n, List *l);
extern int nthi(int n, List *l);
extern void set_nth(List *l, int n, void *elem);
List *lconsi(int datum, List *list);
List *lappendi(List *list, int datum);
extern bool intMember(int, List *);
extern List *set_difference(List *list1, List *list2);
extern List *set_differencei(List *list1, List *list2);
extern List *LispUnion(List *list1, List *list2);
extern List *LispUnioni(List *list1, List *list2);
extern bool same(List *list1, List *list2);
extern int nthi(int n, List *l);
extern List *set_difference(List *, List *);
extern List *set_differencei(List *, List *);
extern List *LispUnion(List *foo, List *bar);
extern List *LispUnioni(List *foo, List *bar);
extern bool same(List *foo, List *bar);
extern void freeList(List *list);
/* should be in nodes.h but needs List */
/* in copyfuncs.c */
extern List *listCopy(List *);
extern List *listCopy(List *list);
#endif /* PG_LIST_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: primnodes.h,v 1.32 1999/07/18 03:45:01 tgl Exp $
* $Id: primnodes.h,v 1.33 1999/08/16 02:17:39 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -255,6 +255,20 @@ typedef struct Func
List *func_planlist;
} Func;
/* ----------------
* Iter
* can anyone explain what this is for? Seems to have something to do
* with evaluation of functions that return sets...
* ----------------
*/
typedef struct Iter
{
NodeTag type;
Node *iterexpr;
Oid itertype; /* type of the iter expr (use for type
* checking) */
} Iter;
/* ----------------
* Aggref
* aggname - name of the aggregate

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: relation.h,v 1.37 1999/07/27 03:51:09 tgl Exp $
* $Id: relation.h,v 1.38 1999/08/16 02:17:40 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -18,65 +18,75 @@
/*
* Relids
* List of relation identifiers (indexes into the rangetable).
*
* Note: these are lists of integers, not Nodes.
*/
typedef List *Relids;
/*
* RelOptInfo
* Per-base-relation information
* Per-relation information for planning/optimization
*
* Parts of this data structure are specific to various scan and join
* mechanisms. It didn't seem worth creating new node types for them.
*
* relids - List of relation indentifiers
* relids - List of base-relation identifiers; it is a base relation
* if there is just one, a join relation if more than one
* indexed - true if the relation has secondary indices
* pages - number of pages in the relation
* tuples - number of tuples in the relation
* size - number of tuples in the relation after restrictions clauses
* have been applied
* width - number of bytes per tuple in the relation after the
* size - estimated number of tuples in the relation after restriction
* clauses have been applied
* width - avg. number of bytes per tuple in the relation after the
* appropriate projections have been done
* targetlist - List of TargetList nodes
* pathlist - List of Path nodes, one for each possible method of
* generating the relation
* cheapestpath - least expensive Path (regardless of final order)
* pruneable - flag to let the planner know whether it can prune the plan
* space of this RelOptInfo or not.
* pathlist - List of Path nodes, one for each potentially useful
* method of generating the relation
* cheapestpath - least expensive Path (regardless of ordering)
* pruneable - flag to let the planner know whether it can prune the
* pathlist of this RelOptInfo or not.
*
* * If the relation is a (secondary) index it will have the following
* three fields:
* fields set:
*
* classlist - List of PG_AMOPCLASS OIDs for the index
* indexkeys - List of base-relation attribute numbers that are index keys
* ordering - List of PG_OPERATOR OIDs which order the indexscan result
* relam - the OID of the pg_am of the index
*
* NB. the last element of the arrays classlist, indexkeys and ordering
* is always 0.
*
* Index relations do not participate in the join tree in the way
* that regular base relations do, but it is still convenient to
* represent them by RelOptInfos.
*
* * The presence of the remaining fields depends on the restrictions
* and joins which the relation participates in:
* and joins that the relation participates in:
*
* restrictinfo - List of RestrictInfo nodes, containing info about each
* qualification clause in which this relation participates
* joininfo - List of JoinInfo nodes, containing info about each join
* clause in which this relation participates
* innerjoin - List of Path nodes that represent indices that may be used
* as inner paths of nestloop joins
*
* NB. the last element of the arrays classlist, indexkeys and ordering
* is always 0. 2/95 - ay
* as inner paths of nestloop joins. This field is non-null
* only for base rels, since join rels have no indices.
*/
typedef struct RelOptInfo
{
NodeTag type;
/* all relations: */
Relids relids; /* integer list of base relids involved */
/* all relations included in this RelOptInfo */
Relids relids; /* integer list of base relids */
/* catalog statistics information */
bool indexed;
int pages;
int tuples;
/* estimates generated by planner. XXX int is probably too small... */
int size;
int width;
@ -89,65 +99,66 @@ typedef struct RelOptInfo
/* used solely by indices: */
Oid *classlist; /* classes of AM operators */
int *indexkeys; /* keys over which we're indexing */
Oid *ordering; /* OIDs of sort operators for each key */
Oid relam; /* OID of the access method (in pg_am) */
Oid indproc;
List *indpred;
Oid indproc; /* if a functional index */
List *indpred; /* if a partial index */
/* used by various scans and joins: */
Oid *ordering; /* OID of operators in sort order */
List *restrictinfo; /* RestrictInfo structures */
List *joininfo; /* JoinInfo structures */
List *innerjoin;
List *innerjoin; /* potential indexscans for nestloop joins */
/* innerjoin indexscans are not in the main pathlist because they are
* not usable except in specific join contexts; we have to test before
* seeing whether they can be used.
*/
} RelOptInfo;
extern Var *get_expr(TargetEntry *foo);
extern Var *get_groupclause_expr(GroupClause *groupClause, List *targetList);
/*
* PathKeys
*
* The sort ordering of a path is represented by a list of sublists of
* PathKeyItem nodes. An empty list implies no known ordering. Otherwise
* the first sublist represents the primary sort key, the second the
* first secondary sort key, etc. Each sublist contains one or more
* PathKeyItem nodes, each of which can be taken as the attribute that
* appears at that sort position. (See the top of optimizer/path/pathkeys.c
* for more information.)
*/
typedef struct MergeOrder
{
NodeTag type;
Oid join_operator;
Oid left_operator;
Oid right_operator;
Oid left_type;
Oid right_type;
} MergeOrder;
typedef enum OrderType
{
MERGE_ORDER, SORTOP_ORDER
} OrderType;
typedef struct PathOrder
typedef struct PathKeyItem
{
NodeTag type;
OrderType ordtype;
union
{
Oid *sortop;
MergeOrder *merge;
} ord;
} PathOrder;
Node *key; /* the item that is ordered */
Oid sortop; /* the ordering operator ('<' op) */
/*
* key typically points to a Var node, ie a relation attribute,
* but it can also point to a Func clause representing the value
* indexed by a functional index. Someday we might allow arbitrary
* expressions as path keys, so don't assume more than you must.
*/
} PathKeyItem;
/*
* Type "Path" is used as-is for sequential-scan paths. For other
* path types it is the first component of a larger struct.
*/
typedef struct Path
{
NodeTag type;
RelOptInfo *parent;
Cost path_cost;
RelOptInfo *parent; /* the relation this path can build */
NodeTag pathtype;
Cost path_cost; /* estimated execution cost of path */
PathOrder *pathorder;
NodeTag pathtype; /* tag identifying scan/join method */
/* XXX why is pathtype separate from the NodeTag? */
List *pathkeys; /* This is a List of List of Var nodes.
* See the top of
* optimizer/path/pathkeys.c for more
* information. */
Cost outerjoincost;
Relids joinid;
List *pathkeys; /* sort ordering of path's output */
/* pathkeys is a List of Lists of PathKeyItem nodes; see above */
} Path;
/*----------
@ -156,129 +167,163 @@ typedef struct Path
* different index during each scan. The result is the union (OR) of all the
* tuples matched during any scan. (The executor is smart enough not to return
* the same tuple more than once, even if it is matched in multiple scans.)
*
* 'indexid' is a list of index relation OIDs, one per scan to be performed.
* 'indexqual' is a list of index qualifications, also one per scan.
* Each entry in 'indexqual' is a sublist of qualification expressions with
* implicit AND semantics across the sublist items. Each one of the sublist
* items must be an operator expression of the form (var op something) or
* (something op var), where the var is a field the associated index keys on
* and the op is a member of the operator class of the index.
* implicit AND semantics across the sublist items. Only expressions that
* are usable as indexquals (as determined by indxpath.c) may appear here.
*
* NOTE that the semantics of the top-level list in 'indexqual' is OR
* combination, while the sublists are implicitly AND combinations!
*----------
*/
typedef struct IndexPath
{
Path path;
List *indexid;
List *indexqual;
int *indexkeys; /* to transform heap attnos into index
* ones */
/*
* joinrelids is only used in IndexPaths that are constructed for use
* as the inner path of a nestloop join. These paths have indexquals
* that refer to values of other rels, so those other rels must be
* included in the outer joinrel in order to make a usable join.
*/
Relids joinrelids; /* other rels mentioned in indexqual */
} IndexPath;
typedef struct NestPath
/*
* All join-type paths share these fields.
*/
typedef struct JoinPath
{
Path path;
List *pathinfo;
Path *outerjoinpath;
Path *innerjoinpath;
} NestPath;
typedef NestPath JoinPath;
List *pathinfo; /* copy of parent->restrictinfo; REMOVE? */
Path *outerjoinpath; /* path for the outer side of the join */
Path *innerjoinpath; /* path for the inner side of the join */
} JoinPath;
/*
* A nested-loop path needs no special fields.
*/
typedef JoinPath NestPath;
/*
* A mergejoin path has these fields.
*
* Note that the mergeclauses are a subset of the parent relation's
* restriction-clause list. Any join clauses that are not mergejoinable
* appear only in the parent's restrict list, and must be checked by a
* qpqual at execution time.
*/
typedef struct MergePath
{
JoinPath jpath;
List *path_mergeclauses;
List *path_mergeclauses; /* join clauses used for merge */
/*
* outersortkeys (resp. innersortkeys) is NIL if the outer path
* (resp. inner path) is already ordered appropriately for the
* mergejoin. If it is not NIL then it is a PathKeys list describing
* the ordering that must be created by an explicit sort step.
*/
List *outersortkeys;
List *innersortkeys;
} MergePath;
/*
* A hashjoin path has these fields.
*
* The remarks above for mergeclauses apply for hashclauses as well.
* However, hashjoin does not care what order its inputs appear in,
* so we have no need for sortkeys.
*/
typedef struct HashPath
{
JoinPath jpath;
List *path_hashclauses;
List *outerhashkeys;
List *innerhashkeys;
List *path_hashclauses; /* join clauses used for hashing */
} HashPath;
/*
* Keys
*/
typedef struct OrderKey
{
NodeTag type;
int attribute_number;
Index array_index;
} OrderKey;
typedef struct JoinKey
{
NodeTag type;
Var *outer;
Var *inner;
} JoinKey;
/*
* Restriction clause info.
*
* We create one of these for each AND sub-clause of a restriction condition
* (WHERE clause). Since the restriction clauses are logically ANDed, we
* can use any one of them or any subset of them to filter out tuples,
* without having to evaluate the rest. The RestrictInfo node itself stores
* data used by the optimizer while choosing the best query plan.
*
* A restriction clause will appear in the restrictinfo list of a RelOptInfo
* that describes exactly the set of base relations referenced by the
* restriction clause. It is not possible to apply the clause at any lower
* nesting level, and there is little point in delaying its evaluation to
* higher nesting levels. (The "predicate migration" code was once intended
* to push restriction clauses up and down the plan tree, but it's dead code
* and is unlikely to be resurrected in the foreseeable future.)
*
* If a restriction clause references more than one base rel, it will also
* appear in the JoinInfo list of every RelOptInfo that describes a strict
* subset of the base rels mentioned in the clause. The JoinInfo lists are
* used to drive join tree building by selecting plausible join candidates.
*
* In general, the referenced clause might be arbitrarily complex. The
* kinds of clauses we can handle as indexscan quals, mergejoin clauses,
* or hashjoin clauses are fairly limited --- the code for each kind of
* path is responsible for identifying the restrict clauses it can use
* and ignoring the rest. Clauses not implemented by an indexscan,
* mergejoin, or hashjoin will be placed in the qpqual field of the
* final Plan node, where they will be enforced by general-purpose
* qual-expression-evaluation code. (But we are still entitled to count
* their selectivity when estimating the result tuple count, if we
* can guess what it is...)
*/
typedef struct RestrictInfo
{
NodeTag type;
Expr *clause; /* the represented subclause of WHERE cond */
Expr *clause; /* the represented clause of WHERE cond */
Cost selectivity; /* estimated selectivity */
List *indexids; /* subclause index IDs if clause is an OR */
/* mergejoin only */
MergeOrder *mergejoinorder;
/* only used if clause is an OR clause: */
List *subclauseindices; /* lists of indexes matching subclauses */
/* hashjoin only */
Oid hashjoinoperator;
/* valid if clause is mergejoinable, else InvalidOid: */
Oid mergejoinoperator; /* copy of clause operator */
Oid left_sortop; /* leftside sortop needed for mergejoin */
Oid right_sortop; /* rightside sortop needed for mergejoin */
/* valid if clause is hashjoinable, else InvalidOid: */
Oid hashjoinoperator; /* copy of clause operator */
} RestrictInfo;
typedef struct JoinMethod
{
NodeTag type;
List *jmkeys;
List *clauses;
} JoinMethod;
typedef struct HashInfo
{
JoinMethod jmethod;
Oid hashop;
} HashInfo;
typedef struct MergeInfo
{
JoinMethod jmethod;
MergeOrder *m_ordering;
} MergeInfo;
/*
* Join clause info.
*
* We make a list of these for each RelOptInfo, containing info about
* all the join clauses this RelOptInfo participates in. (For this
* purpose, a "join clause" is a WHERE clause that mentions both vars
* belonging to this relation and vars belonging to relations not yet
* joined to it.) We group these clauses according to the set of
* other base relations (unjoined relations) mentioned in them.
* There is one JoinInfo for each distinct set of unjoined_relids,
* and its jinfo_restrictinfo lists the clause(s) that use that set
* of other relations.
*/
typedef struct JoinInfo
{
NodeTag type;
Relids unjoined_relids;
List *jinfo_restrictinfo;
bool mergejoinable;
bool hashjoinable;
Relids unjoined_relids; /* some rels not yet part of my RelOptInfo */
List *jinfo_restrictinfo; /* relevant RestrictInfos */
} JoinInfo;
typedef struct Iter
{
NodeTag type;
Node *iterexpr;
Oid itertype; /* type of the iter expr (use for type
* checking) */
} Iter;
/*
* Stream:
* A stream represents a root-to-leaf path in a plan tree (i.e. a tree of
@ -287,6 +332,9 @@ typedef struct Iter
* is used to make Path nodes and clauses look similar, so that Predicate
* Migration can run.
*
* XXX currently, Predicate Migration is dead code, and so is this node type.
* Probably should remove support for it.
*
* pathptr -- pointer to the current path node
* cinfo -- if NULL, this stream node referes to the path node.
* Otherwise this is a pointer to the current clause.
@ -306,8 +354,8 @@ typedef struct Stream
Path *pathptr;
RestrictInfo *cinfo;
int *clausetype;
struct Stream *upstream;
struct Stream *downstream;
StreamPtr upstream;
StreamPtr downstream;
bool groupup;
Cost groupcost;
Cost groupsel;

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: clauses.h,v 1.27 1999/08/12 04:32:49 tgl Exp $
* $Id: clauses.h,v 1.28 1999/08/16 02:17:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -40,7 +40,6 @@ extern List *make_ands_implicit(Expr *clause);
extern List *pull_constant_clauses(List *quals, List **constantQual);
extern void clause_get_relids_vars(Node *clause, Relids *relids, List **vars);
extern int NumRelids(Node *clause);
extern bool is_joinable(Node *clause);
extern List *fix_opids(List *clauses);
extern void get_relattval(Node *clause, int targetrelid,
int *relid, AttrNumber *attno,

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: joininfo.h,v 1.13 1999/07/15 15:21:22 momjian Exp $
* $Id: joininfo.h,v 1.14 1999/08/16 02:17:44 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -17,6 +17,5 @@
extern JoinInfo *joininfo_member(List *join_relids, List *joininfo_list);
extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, List *join_relids);
extern Var *other_join_clause_var(Var *var, Expr *clause);
#endif /* JOININFO_H */

View File

@ -1,23 +0,0 @@
/*-------------------------------------------------------------------------
*
* keys.h
* prototypes for keys.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: keys.h,v 1.16 1999/07/15 15:21:22 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef KEYS_H
#define KEYS_H
#include "nodes/relation.h"
extern bool match_indexkey_operand(int indexkey, Var *operand, RelOptInfo *rel);
extern Var *extract_join_key(JoinKey *jk, int outer_or_inner);
extern bool pathkeys_match(List *keys1, List *keys2, int *better_key);
extern List *collect_index_pathkeys(int *index_keys, List *tlist);
#endif /* KEYS_H */

View File

@ -1,25 +0,0 @@
/*-------------------------------------------------------------------------
*
* ordering.h
* prototypes for ordering.c.
*
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: ordering.h,v 1.15 1999/07/15 23:03:58 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#ifndef ORDERING_H
#define ORDERING_H
#include "nodes/relation.h"
extern bool pathorder_match(PathOrder *path_ordering1,
PathOrder *path_ordering2, int *better_sort);
extern bool equal_path_merge_ordering(Oid *path_ordering,
MergeOrder *merge_ordering);
extern bool equal_merge_ordering(MergeOrder *merge_ordering1,
MergeOrder *merge_ordering2);
#endif /* ORDERING_H */

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: pathnode.h,v 1.20 1999/08/06 04:00:13 tgl Exp $
* $Id: pathnode.h,v 1.21 1999/08/16 02:17:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -20,22 +20,26 @@
*/
extern bool path_is_cheaper(Path *path1, Path *path2);
extern Path *set_cheapest(RelOptInfo *parent_rel, List *pathlist);
extern List *add_pathlist(RelOptInfo *parent_rel, List *unique_paths,
List *new_paths);
extern List *add_pathlist(RelOptInfo *parent_rel, List *old_paths,
List *new_paths);
extern Path *create_seqscan_path(RelOptInfo *rel);
extern IndexPath *create_index_path(Query *root, RelOptInfo *rel, RelOptInfo *index,
List *restriction_clauses);
extern NestPath *create_nestloop_path(RelOptInfo *joinrel, RelOptInfo *outer_rel,
Path *outer_path, Path *inner_path, List *pathkeys);
extern IndexPath *create_index_path(Query *root, RelOptInfo *rel,
RelOptInfo *index, List *restriction_clauses);
extern NestPath *create_nestloop_path(RelOptInfo *joinrel,
RelOptInfo *outer_rel, Path *outer_path, Path *inner_path,
List *pathkeys);
extern MergePath *create_mergejoin_path(RelOptInfo *joinrel, int outersize,
int innersize, int outerwidth, int innerwidth, Path *outer_path,
Path *inner_path, List *pathkeys, MergeOrder *order,
List *mergeclauses, List *outersortkeys, List *innersortkeys);
int innersize, int outerwidth, int innerwidth, Path *outer_path,
Path *inner_path, List *pathkeys,
List *mergeclauses, List *outersortkeys, List *innersortkeys);
extern HashPath *create_hashjoin_path(RelOptInfo *joinrel, int outersize,
int innersize, int outerwidth, int innerwidth, Path *outer_path,
Path *inner_path, List *pathkeys, Oid operator, List *hashclauses,
List *outerkeys, List *innerkeys, Cost innerdisbursion);
Path *inner_path, List *hashclauses, Cost innerdisbursion);
/*
* prototypes for rel.c

View File

@ -7,7 +7,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: paths.h,v 1.33 1999/07/27 06:23:11 tgl Exp $
* $Id: paths.h,v 1.34 1999/08/16 02:17:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -43,35 +43,28 @@ extern void update_rels_pathlist_for_joins(Query *root, List *joinrels);
extern List *create_or_index_paths(Query *root, RelOptInfo *rel, List *clauses);
/*
* hashutils.c
* routines to deal with hash keys and clauses
* pathkeys.c
* utilities for matching and building path keys
*/
extern List *group_clauses_by_hashop(List *restrictinfo_list,
Relids inner_relids);
typedef enum
{
PATHKEYS_EQUAL, /* pathkeys are identical */
PATHKEYS_BETTER1, /* pathkey 1 is a superset of pathkey 2 */
PATHKEYS_BETTER2, /* vice versa */
PATHKEYS_DIFFERENT /* neither pathkey includes the other */
} PathKeysComparison;
/*
* joinutils.c
* generic join method key/clause routines
*/
extern bool order_joinkeys_by_pathkeys(List *pathkeys,
List *joinkeys, List *joinclauses, int outer_or_inner,
List **matchedJoinKeysPtr,
List **matchedJoinClausesPtr);
extern List *make_pathkeys_from_joinkeys(List *joinkeys, List *tlist,
int outer_or_inner);
extern Path *get_cheapest_path_for_joinkeys(List *joinkeys,
PathOrder *ordering, List *paths, int outer_or_inner);
extern List *new_join_pathkeys(List *outer_pathkeys,
List *join_rel_tlist, List *joinclauses);
/*
* mergeutils.c
* routines to deal with merge keys and clauses
*/
extern List *group_clauses_by_order(List *restrictinfo_list,
Relids inner_relids);
extern MergeInfo *match_order_mergeinfo(PathOrder *ordering,
List *mergeinfo_list);
extern PathKeysComparison compare_pathkeys(List *keys1, List *keys2);
extern bool pathkeys_contained_in(List *keys1, List *keys2);
extern Path *get_cheapest_path_for_pathkeys(List *paths, List *pathkeys);
extern List *build_index_pathkeys(Query *root, RelOptInfo *rel,
RelOptInfo *index);
extern List *build_join_pathkeys(List *outer_pathkeys,
List *join_rel_tlist, List *joinclauses);
extern List *find_mergeclauses_for_pathkeys(List *pathkeys,
List *restrictinfos);
extern List *make_pathkeys_for_mergeclauses(List *mergeclauses,
List *tlist);
/*
* joinrels.c

View File

@ -6,7 +6,7 @@
*
* Copyright (c) 1994, Regents of the University of California
*
* $Id: tlist.h,v 1.19 1999/07/15 15:21:23 momjian Exp $
* $Id: tlist.h,v 1.20 1999/08/16 02:17:45 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@ -30,4 +30,7 @@ extern List *flatten_tlist(List *tlist);
extern List *flatten_tlist_vars(List *full_tlist,
List *flat_tlist);
extern Var *get_expr(TargetEntry *tle);
extern Var *get_groupclause_expr(GroupClause *groupClause, List *targetList);
#endif /* TLIST_H */