Commit e6381966 authored by Tom Lane's avatar Tom Lane

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.
parent 08320bfb
......@@ -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
* ----------------
*/
static OrderKey *
_copyOrderKey(OrderKey *from)
{
OrderKey *newnode = makeNode(OrderKey);
/* ----------------
* copy remainder of node
* ----------------
*/
newnode->attribute_number = from->attribute_number;
newnode->array_index = from->array_index;
return newnode;
}
/* ----------------
* _copyJoinKey
* _copyPathKeyItem
* ----------------
*/
static JoinKey *
_copyJoinKey(JoinKey *from)
static PathKeyItem *
_copyPathKeyItem(PathKeyItem *from)
{
JoinKey *newnode = makeNode(JoinKey);
PathKeyItem *newnode = makeNode(PathKeyItem);
/* ----------------
* 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;
......
......@@ -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;
......@@ -355,65 +321,33 @@ _equalNestPath(NestPath *a, NestPath *b)
}
static bool
_equalMergePath(MergePath *a, MergePath *b)
{
if (!_equalNestPath((NestPath *) a, (NestPath *) b))
return false;
if (!equal(a->path_mergeclauses, b->path_mergeclauses))
return false;
if (!equal(a->outersortkeys, b->outersortkeys))
return false;
if (!equal(a->innersortkeys, b->innersortkeys))
return false;
return true;
}
static bool
_equalHashPath(HashPath *a, HashPath *b)
{
if (!_equalNestPath((NestPath *) a, (NestPath *) 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)
_equalNestPath(NestPath *a, NestPath *b)
{
if (!equal(a->outer, b->outer))
return false;
if (!equal(a->inner, b->inner))
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
return true;
}
static bool
_equalMergeOrder(MergeOrder *a, MergeOrder *b)
_equalMergePath(MergePath *a, MergePath *b)
{
if (a->join_operator != b->join_operator)
return false;
if (a->left_operator != b->left_operator)
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
if (a->right_operator != b->right_operator)
if (!equal(a->path_mergeclauses, b->path_mergeclauses))
return false;
if (a->left_type != b->left_type)
if (!equal(a->outersortkeys, b->outersortkeys))
return false;
if (a->right_type != b->right_type)
if (!equal(a->innersortkeys, b->innersortkeys))
return false;
return true;
}
static bool
_equalHashInfo(HashInfo *a, HashInfo *b)
_equalHashPath(HashPath *a, HashPath *b)
{
if (!_equalJoinMethod((JoinMethod *) a, (JoinMethod *) b))
if (!_equalJoinPath((JoinPath *) a, (JoinPath *) b))
return false;
if (a->hashop != b->hashop)
if (!equal(a->path_hashclauses, b->path_hashclauses))
return false;
return true;
}
......@@ -458,30 +392,32 @@ _equalSubPlan(SubPlan *a, SubPlan *b)
}
static bool
_equalJoinInfo(JoinInfo *a, JoinInfo *b)
_equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
{
if (!equal(a->unjoined_relids, b->unjoined_relids))
if (!equal(a->clause, b->clause))
return false;
if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo))
/* do not check selectivity because of roundoff error worries */
if (!equal(a->subclauseindices, b->subclauseindices))
return false;
if (a->mergejoinoperator != b->mergejoinoperator)
return false;
if (a->mergejoinable != b->mergejoinable)
if (a->left_sortop != b->left_sortop)
return false;
if (a->hashjoinable != b->hashjoinable)
if (a->right_sortop != b->right_sortop)
return false;
if (a->hashjoinoperator != b->hashjoinoperator)
return false;
return true;
}
static bool
_equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
_equalJoinInfo(JoinInfo *a, JoinInfo *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 (!equali(a->unjoined_relids, b->unjoined_relids))
return false;
if (a->hashjoinoperator != b->hashjoinoperator)
if (!equal(a->jinfo_restrictinfo, b->jinfo_restrictinfo))
return false;
return equal(a->indexids, b->indexids);
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;
......
......@@ -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
* ----------------
*/
static void
_freeOrderKey(OrderKey *node)
{
pfree(node);
}
/* ----------------
* _freeJoinKey
* _freePathKeyItem
* ----------------
*/
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
* ----------------
*/
FreeJoinMethodFields((JoinMethod *) node);
pfree(node);
}
/* ----------------
* _freeMInfo
* ----------------
*/
static void
_freeMergeInfo(MergeInfo *node)
{
/* ----------------
* free remainder of node
* ----------------
/* this is certainly wrong? index RelOptInfos don't belong to
* RestrictInfo...
*/
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;
......
......@@ -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
*
......
......@@ -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);
......
......@@ -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.
*/
static void
_outOrderKey(StringInfo str, OrderKey *node)
{
appendStringInfo(str,
" ORDERKEY :attribute_number %d :array_index %d ",
node->attribute_number,
node->array_index);
}
/*
* JoinKey is a subclass of Node.
* PathKeyItem is a subclass of Node.
*/
static void
_outJoinKey(StringInfo str, JoinKey *node)
_outPathKeyItem(StringInfo str, PathKeyItem *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;
......
......@@ -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(", ");
}
......
This diff is collapsed.
Summary
-------
The optimizer generates optimial query plans by doing several steps:
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.
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.
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.
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.
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.
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.)
The best Path tree is found by a recursive process:
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.
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.)
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.
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.
......@@ -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
......
......@@ -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(", ");
}
print_pathkeys(path->pathkeys, root->rtable);
}
printf("\n");
}
}
......
......@@ -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
......
......@@ -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(", ");
}
print_pathkeys(path->pathkeys, root->rtable);
}
printf("\n");
}
}
......
/*-------------------------------------------------------------------------
*
* 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;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*-------------------------------------------------------------------------
*
* 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;
}
......@@ -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;
......
This diff is collapsed.
......@@ -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");
}
}
This diff is collapsed.
......@@ -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 $
*
*-------------------------------------------------------------------------
*/
......@@ -30,9 +30,8 @@ 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);
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;
}
}
check_mergejoinable(restrictinfo);
if (_enable_hashjoin_)
{
Oid hashop = hashjoinop(clause);
if (hashop)
{
restrictinfo->hashjoinoperator = hashop;
joininfo->hashjoinable = true;
}
}
}
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,
if (op_mergejoinable(opno,
left->vartype,
right->vartype,
&leftOp,
&rightOp);
if (sortable)
&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;
opno = ((Oper *) clause->oper)->opno;
return op_hashjoinable(((Oper *) clause->oper)->opno,
if (op_hashjoinable(opno,
left->vartype,
right->vartype);
right->vartype))
{
restrictinfo->hashjoinoperator = opno;
}
}
This diff is collapsed.
......@@ -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
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment