Commit bdfbfde1 authored by Tom Lane's avatar Tom Lane

IN clauses appearing at top level of WHERE can now be handled as joins.

There are two implementation techniques: the executor understands a new
JOIN_IN jointype, which emits at most one matching row per left-hand row,
or the result of the IN's sub-select can be fed through a DISTINCT filter
and then joined as an ordinary relation.
Along the way, some minor code cleanup in the optimizer; notably, break
out most of the jointree-rearrangement preprocessing in planner.c and
put it in a new file prep/prepjointree.c.
parent be2b660e
<!-- <!--
$Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.178 2003/01/11 21:02:49 momjian Exp $ $Header: /cvsroot/pgsql/doc/src/sgml/release.sgml,v 1.179 2003/01/20 18:54:44 tgl Exp $
--> -->
<appendix id="release"> <appendix id="release">
...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without ...@@ -24,6 +24,7 @@ CDATA means the content is "SGML-free", so you can write without
worries about funny characters. worries about funny characters.
--> -->
<literallayout><![CDATA[ <literallayout><![CDATA[
Performance of "foo IN (SELECT ...)" queries has been considerably improved
FETCH 0 now re-fetches cursor's current row, per SQL spec FETCH 0 now re-fetches cursor's current row, per SQL spec
Revised executor state representation; plan trees are read-only to executor now Revised executor state representation; plan trees are read-only to executor now
Information schema Information schema
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.46 2002/12/30 15:21:20 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeHashjoin.c,v 1.47 2003/01/20 18:54:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -95,6 +95,15 @@ ExecHashJoin(HashJoinState *node) ...@@ -95,6 +95,15 @@ ExecHashJoin(HashJoinState *node)
node->js.ps.ps_TupFromTlist = false; node->js.ps.ps_TupFromTlist = false;
} }
/*
* If we're doing an IN join, we want to return at most one row per
* outer tuple; so we can stop scanning the inner scan if we matched on
* the previous try.
*/
if (node->js.jointype == JOIN_IN &&
node->hj_MatchedOuter)
node->hj_NeedNewOuter = true;
/* /*
* Reset per-tuple memory context to free any expression evaluation * Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't * storage allocated in the previous tuple cycle. Note this can't
...@@ -353,6 +362,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate) ...@@ -353,6 +362,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate)
switch (node->join.jointype) switch (node->join.jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
case JOIN_IN:
break; break;
case JOIN_LEFT: case JOIN_LEFT:
hjstate->hj_NullInnerTupleSlot = hjstate->hj_NullInnerTupleSlot =
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.55 2002/12/15 16:17:46 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.56 2003/01/20 18:54:45 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -381,6 +381,7 @@ ExecMergeJoin(MergeJoinState *node) ...@@ -381,6 +381,7 @@ ExecMergeJoin(MergeJoinState *node)
switch (node->js.jointype) switch (node->js.jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
case JOIN_IN:
doFillOuter = false; doFillOuter = false;
doFillInner = false; doFillInner = false;
break; break;
...@@ -581,9 +582,15 @@ ExecMergeJoin(MergeJoinState *node) ...@@ -581,9 +582,15 @@ ExecMergeJoin(MergeJoinState *node)
* the econtext's tuple pointers were set up before * the econtext's tuple pointers were set up before
* checking the merge qual, so we needn't do it again. * checking the merge qual, so we needn't do it again.
*/ */
qualResult = (joinqual == NIL || if (node->js.jointype == JOIN_IN &&
ExecQual(joinqual, econtext, false)); node->mj_MatchedOuter)
MJ_DEBUG_QUAL(joinqual, qualResult); qualResult = false;
else
{
qualResult = (joinqual == NIL ||
ExecQual(joinqual, econtext, false));
MJ_DEBUG_QUAL(joinqual, qualResult);
}
if (qualResult) if (qualResult)
{ {
...@@ -1452,6 +1459,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate) ...@@ -1452,6 +1459,7 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate)
switch (node->join.jointype) switch (node->join.jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
case JOIN_IN:
break; break;
case JOIN_LEFT: case JOIN_LEFT:
mergestate->mj_NullInnerTupleSlot = mergestate->mj_NullInnerTupleSlot =
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.29 2002/12/15 16:17:46 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/executor/nodeNestloop.c,v 1.30 2003/01/20 18:54:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,6 +101,15 @@ ExecNestLoop(NestLoopState *node) ...@@ -101,6 +101,15 @@ ExecNestLoop(NestLoopState *node)
node->js.ps.ps_TupFromTlist = false; node->js.ps.ps_TupFromTlist = false;
} }
/*
* If we're doing an IN join, we want to return at most one row per
* outer tuple; so we can stop scanning the inner scan if we matched on
* the previous try.
*/
if (node->js.jointype == JOIN_IN &&
node->nl_MatchedOuter)
node->nl_NeedNewOuter = true;
/* /*
* Reset per-tuple memory context to free any expression evaluation * Reset per-tuple memory context to free any expression evaluation
* storage allocated in the previous tuple cycle. Note this can't * storage allocated in the previous tuple cycle. Note this can't
...@@ -312,6 +321,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate) ...@@ -312,6 +321,7 @@ ExecInitNestLoop(NestLoop *node, EState *estate)
switch (node->join.jointype) switch (node->join.jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
case JOIN_IN:
break; break;
case JOIN_LEFT: case JOIN_LEFT:
nlstate->nl_NullInnerTupleSlot = nlstate->nl_NullInnerTupleSlot =
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.236 2003/01/15 19:35:35 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.237 2003/01/20 18:54:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1095,6 +1095,21 @@ _copyJoinInfo(JoinInfo *from) ...@@ -1095,6 +1095,21 @@ _copyJoinInfo(JoinInfo *from)
return newnode; return newnode;
} }
/*
* _copyInClauseInfo
*/
static InClauseInfo *
_copyInClauseInfo(InClauseInfo *from)
{
InClauseInfo *newnode = makeNode(InClauseInfo);
COPY_INTLIST_FIELD(lefthand);
COPY_INTLIST_FIELD(righthand);
COPY_NODE_FIELD(sub_targetlist);
return newnode;
}
/* **************************************************************** /* ****************************************************************
* parsenodes.h copy functions * parsenodes.h copy functions
* **************************************************************** * ****************************************************************
...@@ -1424,9 +1439,9 @@ _copyQuery(Query *from) ...@@ -1424,9 +1439,9 @@ _copyQuery(Query *from)
/* /*
* We do not copy the planner internal fields: base_rel_list, * We do not copy the planner internal fields: base_rel_list,
* other_rel_list, join_rel_list, equi_key_list, query_pathkeys, * other_rel_list, join_rel_list, equi_key_list, in_info_list,
* hasJoinRTEs. That would get us into copying RelOptInfo/Path * query_pathkeys, hasJoinRTEs. That would get us into copying
* trees, which we don't want to do. * RelOptInfo/Path trees, which we don't want to do.
*/ */
return newnode; return newnode;
...@@ -2490,6 +2505,9 @@ copyObject(void *from) ...@@ -2490,6 +2505,9 @@ copyObject(void *from)
case T_JoinInfo: case T_JoinInfo:
retval = _copyJoinInfo(from); retval = _copyJoinInfo(from);
break; break;
case T_InClauseInfo:
retval = _copyInClauseInfo(from);
break;
/* /*
* VALUE NODES * VALUE NODES
......
...@@ -18,7 +18,7 @@ ...@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.180 2003/01/15 19:35:37 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/equalfuncs.c,v 1.181 2003/01/20 18:54:46 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -486,6 +486,16 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b) ...@@ -486,6 +486,16 @@ _equalJoinInfo(JoinInfo *a, JoinInfo *b)
return true; return true;
} }
static bool
_equalInClauseInfo(InClauseInfo *a, InClauseInfo *b)
{
COMPARE_INTLIST_FIELD(lefthand);
COMPARE_INTLIST_FIELD(righthand);
COMPARE_NODE_FIELD(sub_targetlist);
return true;
}
/* /*
* Stuff from parsenodes.h * Stuff from parsenodes.h
...@@ -518,9 +528,9 @@ _equalQuery(Query *a, Query *b) ...@@ -518,9 +528,9 @@ _equalQuery(Query *a, Query *b)
/* /*
* We do not check the internal-to-the-planner fields: base_rel_list, * We do not check the internal-to-the-planner fields: base_rel_list,
* other_rel_list, join_rel_list, equi_key_list, query_pathkeys, * other_rel_list, join_rel_list, equi_key_list, in_info_list,
* hasJoinRTEs. They might not be set yet, and in any case they should * query_pathkeys, hasJoinRTEs. They might not be set yet, and in any
* be derivable from the other fields. * case they should be derivable from the other fields.
*/ */
return true; return true;
} }
...@@ -1618,6 +1628,9 @@ equal(void *a, void *b) ...@@ -1618,6 +1628,9 @@ equal(void *a, void *b)
case T_JoinInfo: case T_JoinInfo:
retval = _equalJoinInfo(a, b); retval = _equalJoinInfo(a, b);
break; break;
case T_InClauseInfo:
retval = _equalInClauseInfo(a, b);
break;
/* /*
* LIST NODES * LIST NODES
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.43 2002/12/17 01:18:18 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/list.c,v 1.44 2003/01/20 18:54:47 tgl Exp $
* *
* NOTES * NOTES
* XXX a few of the following functions are duplicated to handle * XXX a few of the following functions are duplicated to handle
...@@ -638,10 +638,10 @@ lreverse(List *l) ...@@ -638,10 +638,10 @@ lreverse(List *l)
} }
/* /*
* Return t if two integer lists have no members in common. * Return t if two integer lists have any members in common.
*/ */
bool bool
nonoverlap_setsi(List *list1, List *list2) overlap_setsi(List *list1, List *list2)
{ {
List *x; List *x;
...@@ -650,9 +650,9 @@ nonoverlap_setsi(List *list1, List *list2) ...@@ -650,9 +650,9 @@ nonoverlap_setsi(List *list1, List *list2)
int e = lfirsti(x); int e = lfirsti(x);
if (intMember(e, list2)) if (intMember(e, list2))
return false; return true;
} }
return true; return false;
} }
/* /*
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.193 2003/01/15 19:35:39 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/nodes/outfuncs.c,v 1.194 2003/01/20 18:54:47 tgl Exp $
* *
* NOTES * NOTES
* Every node type that can appear in stored rules' parsetrees *must* * Every node type that can appear in stored rules' parsetrees *must*
...@@ -905,6 +905,18 @@ _outMaterialPath(StringInfo str, MaterialPath *node) ...@@ -905,6 +905,18 @@ _outMaterialPath(StringInfo str, MaterialPath *node)
WRITE_NODE_FIELD(subpath); WRITE_NODE_FIELD(subpath);
} }
static void
_outUniquePath(StringInfo str, UniquePath *node)
{
WRITE_NODE_TYPE("UNIQUEPATH");
_outPathInfo(str, (Path *) node);
WRITE_NODE_FIELD(subpath);
WRITE_BOOL_FIELD(use_hash);
WRITE_FLOAT_FIELD(rows, "%.0f");
}
static void static void
_outNestPath(StringInfo str, NestPath *node) _outNestPath(StringInfo str, NestPath *node)
{ {
...@@ -969,6 +981,16 @@ _outJoinInfo(StringInfo str, JoinInfo *node) ...@@ -969,6 +981,16 @@ _outJoinInfo(StringInfo str, JoinInfo *node)
WRITE_NODE_FIELD(jinfo_restrictinfo); WRITE_NODE_FIELD(jinfo_restrictinfo);
} }
static void
_outInClauseInfo(StringInfo str, InClauseInfo *node)
{
WRITE_NODE_TYPE("INCLAUSEINFO");
WRITE_INTLIST_FIELD(lefthand);
WRITE_INTLIST_FIELD(righthand);
WRITE_NODE_FIELD(sub_targetlist);
}
/***************************************************************************** /*****************************************************************************
* *
* Stuff from parsenodes.h. * Stuff from parsenodes.h.
...@@ -1563,6 +1585,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1563,6 +1585,9 @@ _outNode(StringInfo str, void *obj)
case T_MaterialPath: case T_MaterialPath:
_outMaterialPath(str, obj); _outMaterialPath(str, obj);
break; break;
case T_UniquePath:
_outUniquePath(str, obj);
break;
case T_NestPath: case T_NestPath:
_outNestPath(str, obj); _outNestPath(str, obj);
break; break;
...@@ -1581,6 +1606,9 @@ _outNode(StringInfo str, void *obj) ...@@ -1581,6 +1606,9 @@ _outNode(StringInfo str, void *obj)
case T_JoinInfo: case T_JoinInfo:
_outJoinInfo(str, obj); _outJoinInfo(str, obj);
break; break;
case T_InClauseInfo:
_outInClauseInfo(str, obj);
break;
case T_CreateStmt: case T_CreateStmt:
_outCreateStmt(str, obj); _outCreateStmt(str, obj);
......
...@@ -263,6 +263,7 @@ RelOptInfo - a relation or joined relations ...@@ -263,6 +263,7 @@ RelOptInfo - a relation or joined relations
AppendPath - append multiple subpaths together AppendPath - append multiple subpaths together
ResultPath - a Result plan node (used for variable-free tlist or qual) ResultPath - a Result plan node (used for variable-free tlist or qual)
MaterialPath - a Material plan node MaterialPath - a Material plan node
UniquePath - remove duplicate rows
NestPath - nested-loop joins NestPath - nested-loop joins
MergePath - merge joins MergePath - merge joins
HashPath - hash joins HashPath - hash joins
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.60 2002/12/16 21:30:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_eval.c,v 1.61 2003/01/20 18:54:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,8 +22,8 @@ ...@@ -22,8 +22,8 @@
#include "postgres.h" #include "postgres.h"
#include <float.h> #include <float.h>
#include <math.h>
#include <limits.h> #include <limits.h>
#include <math.h>
#include "optimizer/geqo.h" #include "optimizer/geqo.h"
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
...@@ -91,7 +91,10 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene) ...@@ -91,7 +91,10 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
* XXX geqo does not currently support optimization for partial result * XXX geqo does not currently support optimization for partial result
* retrieval --- how to fix? * retrieval --- how to fix?
*/ */
fitness = joinrel->cheapest_total_path->total_cost; if (joinrel)
fitness = joinrel->cheapest_total_path->total_cost;
else
fitness = DBL_MAX;
/* restore join_rel_list */ /* restore join_rel_list */
root->join_rel_list = savelist; root->join_rel_list = savelist;
...@@ -113,7 +116,7 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene) ...@@ -113,7 +116,7 @@ geqo_eval(Query *root, List *initial_rels, Gene *tour, int num_gene)
* 'tour' is the proposed join order, of length 'num_gene' * 'tour' is the proposed join order, of length 'num_gene'
* *
* Returns a new join relation whose cheapest path is the best plan for * Returns a new join relation whose cheapest path is the best plan for
* this join order. * this join order. NB: will return NULL if join order is invalid.
* *
* Note that at each step we consider using the next rel as both left and * Note that at each step we consider using the next rel as both left and
* right side of a join. However, we cannot build general ("bushy") plan * right side of a join. However, we cannot build general ("bushy") plan
...@@ -154,6 +157,10 @@ gimme_tree(Query *root, List *initial_rels, ...@@ -154,6 +157,10 @@ gimme_tree(Query *root, List *initial_rels,
*/ */
new_rel = make_join_rel(root, joinrel, inner_rel, JOIN_INNER); new_rel = make_join_rel(root, joinrel, inner_rel, JOIN_INNER);
/* Fail if join order is not valid */
if (new_rel == NULL)
return NULL;
/* Find and save the cheapest paths for this rel */ /* Find and save the cheapest paths for this rel */
set_cheapest(new_rel); set_cheapest(new_rel);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.33 2002/12/16 21:30:29 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/geqo/geqo_main.c,v 1.34 2003/01/20 18:54:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -228,20 +228,25 @@ geqo(Query *root, int number_of_rels, List *initial_rels) ...@@ -228,20 +228,25 @@ geqo(Query *root, int number_of_rels, List *initial_rels)
#endif #endif
/* got the cheapest query tree processed by geqo; /*
first element of the population indicates the best query tree */ * got the cheapest query tree processed by geqo;
* first element of the population indicates the best query tree
*/
best_tour = (Gene *) pool->data[0].string; best_tour = (Gene *) pool->data[0].string;
/* root->join_rel_list will be modified during this ! */ /* root->join_rel_list will be modified during this ! */
best_rel = gimme_tree(root, initial_rels, best_rel = gimme_tree(root, initial_rels,
best_tour, pool->string_length); best_tour, pool->string_length);
/* DBG: show the query plan if (best_rel == NULL)
print_plan(best_plan, root); elog(ERROR, "geqo: failed to make a valid plan");
DBG */
/* DBG: show the query plan */
#ifdef NOT_USED
print_plan(best_plan, root);
#endif
/* ... free memory stuff */ /* ... free memory stuff */
free_chromo(momma); free_chromo(momma);
free_chromo(daddy); free_chromo(daddy);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.93 2002/11/30 05:21:02 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/allpaths.c,v 1.94 2003/01/20 18:54:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -750,6 +750,10 @@ print_path(Query *root, Path *path, int indent) ...@@ -750,6 +750,10 @@ print_path(Query *root, Path *path, int indent)
ptype = "Material"; ptype = "Material";
subpath = ((MaterialPath *) path)->subpath; subpath = ((MaterialPath *) path)->subpath;
break; break;
case T_UniquePath:
ptype = "Unique";
subpath = ((UniquePath *) path)->subpath;
break;
case T_NestPath: case T_NestPath:
ptype = "NestLoop"; ptype = "NestLoop";
join = true; join = true;
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.100 2003/01/15 19:35:39 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/costsize.c,v 1.101 2003/01/20 18:54:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1024,12 +1024,17 @@ cost_hashjoin(Path *path, Query *root, ...@@ -1024,12 +1024,17 @@ cost_hashjoin(Path *path, Query *root,
* Bias against putting larger relation on inside. We don't want an * Bias against putting larger relation on inside. We don't want an
* absolute prohibition, though, since larger relation might have * absolute prohibition, though, since larger relation might have
* better bucketsize --- and we can't trust the size estimates * better bucketsize --- and we can't trust the size estimates
* unreservedly, anyway. Instead, inflate the startup cost by the * unreservedly, anyway. Instead, inflate the run cost by the
* square root of the size ratio. (Why square root? No real good * square root of the size ratio. (Why square root? No real good
* reason, but it seems reasonable...) * reason, but it seems reasonable...)
*
* Note: before 7.4 we implemented this by inflating startup cost;
* but if there's a disable_cost component in the input paths'
* startup cost, that unfairly penalizes the hash. Probably it'd
* be better to keep track of disable penalty separately from cost.
*/ */
if (innerbytes > outerbytes && outerbytes > 0) if (innerbytes > outerbytes && outerbytes > 0)
startup_cost *= sqrt(innerbytes / outerbytes); run_cost *= sqrt(innerbytes / outerbytes);
path->startup_cost = startup_cost; path->startup_cost = startup_cost;
path->total_cost = startup_cost + run_cost; path->total_cost = startup_cost + run_cost;
...@@ -1492,22 +1497,26 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel, ...@@ -1492,22 +1497,26 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
JoinType jointype, JoinType jointype,
List *restrictlist) List *restrictlist)
{ {
Selectivity selec;
double temp; double temp;
UniquePath *upath;
/* Start with the Cartesian product */
temp = outer_rel->rows * inner_rel->rows;
/* /*
* Apply join restrictivity. Note that we are only considering * Compute joinclause selectivity. Note that we are only considering
* clauses that become restriction clauses at this join level; we are * clauses that become restriction clauses at this join level; we are
* not double-counting them because they were not considered in * not double-counting them because they were not considered in
* estimating the sizes of the component rels. * estimating the sizes of the component rels.
*/ */
temp *= restrictlist_selectivity(root, selec = restrictlist_selectivity(root,
restrictlist, restrictlist,
0); 0);
/* /*
* Normally, we multiply size of Cartesian product by selectivity.
* But for JOIN_IN, we just multiply the lefthand size by the selectivity
* (is that really right?). For UNIQUE_OUTER or UNIQUE_INNER, use
* the estimated number of distinct rows (again, is that right?)
*
* If we are doing an outer join, take that into account: the output * If we are doing an outer join, take that into account: the output
* must be at least as large as the non-nullable input. (Is there any * must be at least as large as the non-nullable input. (Is there any
* chance of being even smarter?) * chance of being even smarter?)
...@@ -1515,24 +1524,45 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel, ...@@ -1515,24 +1524,45 @@ set_joinrel_size_estimates(Query *root, RelOptInfo *rel,
switch (jointype) switch (jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
temp = outer_rel->rows * inner_rel->rows * selec;
break; break;
case JOIN_LEFT: case JOIN_LEFT:
temp = outer_rel->rows * inner_rel->rows * selec;
if (temp < outer_rel->rows) if (temp < outer_rel->rows)
temp = outer_rel->rows; temp = outer_rel->rows;
break; break;
case JOIN_RIGHT: case JOIN_RIGHT:
temp = outer_rel->rows * inner_rel->rows * selec;
if (temp < inner_rel->rows) if (temp < inner_rel->rows)
temp = inner_rel->rows; temp = inner_rel->rows;
break; break;
case JOIN_FULL: case JOIN_FULL:
temp = outer_rel->rows * inner_rel->rows * selec;
if (temp < outer_rel->rows) if (temp < outer_rel->rows)
temp = outer_rel->rows; temp = outer_rel->rows;
if (temp < inner_rel->rows) if (temp < inner_rel->rows)
temp = inner_rel->rows; temp = inner_rel->rows;
break; break;
case JOIN_IN:
temp = outer_rel->rows * selec;
break;
case JOIN_REVERSE_IN:
temp = inner_rel->rows * selec;
break;
case JOIN_UNIQUE_OUTER:
upath = create_unique_path(root, outer_rel,
outer_rel->cheapest_total_path);
temp = upath->rows * inner_rel->rows * selec;
break;
case JOIN_UNIQUE_INNER:
upath = create_unique_path(root, inner_rel,
inner_rel->cheapest_total_path);
temp = outer_rel->rows * upath->rows * selec;
break;
default: default:
elog(ERROR, "set_joinrel_size_estimates: unsupported join type %d", elog(ERROR, "set_joinrel_size_estimates: unsupported join type %d",
(int) jointype); (int) jointype);
temp = 0; /* keep compiler quiet */
break; break;
} }
......
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.131 2003/01/15 19:35:39 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/indxpath.c,v 1.132 2003/01/20 18:54:49 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1401,11 +1401,13 @@ best_inner_indexscan(Query *root, RelOptInfo *rel, ...@@ -1401,11 +1401,13 @@ best_inner_indexscan(Query *root, RelOptInfo *rel,
MemoryContext oldcontext; MemoryContext oldcontext;
/* /*
* Nestloop only supports inner and left joins. * Nestloop only supports inner, left, and IN joins.
*/ */
switch (jointype) switch (jointype)
{ {
case JOIN_INNER: case JOIN_INNER:
case JOIN_IN:
case JOIN_UNIQUE_OUTER:
isouterjoin = false; isouterjoin = false;
break; break;
case JOIN_LEFT: case JOIN_LEFT:
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.58 2002/12/16 21:30:30 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/path/joinrels.c,v 1.59 2003/01/20 18:54:51 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -172,7 +172,7 @@ make_rels_by_joins(Query *root, int level, List **joinrels) ...@@ -172,7 +172,7 @@ make_rels_by_joins(Query *root, int level, List **joinrels)
jrel = make_join_rel(root, old_rel, new_rel, jrel = make_join_rel(root, old_rel, new_rel,
JOIN_INNER); JOIN_INNER);
/* Avoid making duplicate entries ... */ /* Avoid making duplicate entries ... */
if (!ptrMember(jrel, result_rels)) if (jrel && !ptrMember(jrel, result_rels))
result_rels = lcons(jrel, result_rels); result_rels = lcons(jrel, result_rels);
break; /* need not consider more break; /* need not consider more
* joininfos */ * joininfos */
...@@ -276,10 +276,9 @@ make_rels_by_clause_joins(Query *root, ...@@ -276,10 +276,9 @@ make_rels_by_clause_joins(Query *root,
/* /*
* Avoid entering same joinrel into our output list more * Avoid entering same joinrel into our output list more
* than once. (make_rels_by_joins doesn't really care, * than once.
* but GEQO does.)
*/ */
if (!ptrMember(jrel, result)) if (jrel && !ptrMember(jrel, result))
result = lcons(jrel, result); result = lcons(jrel, result);
} }
} }
...@@ -323,7 +322,8 @@ make_rels_by_clauseless_joins(Query *root, ...@@ -323,7 +322,8 @@ make_rels_by_clauseless_joins(Query *root,
* As long as given other_rels are distinct, don't need to * As long as given other_rels are distinct, don't need to
* test to see if jrel is already part of output list. * test to see if jrel is already part of output list.
*/ */
result = lcons(jrel, result); if (jrel)
result = lcons(jrel, result);
} }
} }
...@@ -367,6 +367,9 @@ make_jointree_rel(Query *root, Node *jtnode) ...@@ -367,6 +367,9 @@ make_jointree_rel(Query *root, Node *jtnode)
/* Make this join rel */ /* Make this join rel */
rel = make_join_rel(root, lrel, rrel, j->jointype); rel = make_join_rel(root, lrel, rrel, j->jointype);
if (rel == NULL)
elog(ERROR, "make_jointree_rel: invalid join order!?");
/* /*
* Since we are only going to consider this one way to do it, * Since we are only going to consider this one way to do it,
* we're done generating Paths for this joinrel and can now select * we're done generating Paths for this joinrel and can now select
...@@ -395,19 +398,121 @@ make_jointree_rel(Query *root, Node *jtnode) ...@@ -395,19 +398,121 @@ make_jointree_rel(Query *root, Node *jtnode)
* created with the two rels as outer and inner rel. * created with the two rels as outer and inner rel.
* (The join rel may already contain paths generated from other * (The join rel may already contain paths generated from other
* pairs of rels that add up to the same set of base rels.) * pairs of rels that add up to the same set of base rels.)
*
* NB: will return NULL if attempted join is not valid. This can only
* happen when working with IN clauses that have been turned into joins.
*/ */
RelOptInfo * RelOptInfo *
make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2, make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
JoinType jointype) JoinType jointype)
{ {
List *joinrelids;
RelOptInfo *joinrel; RelOptInfo *joinrel;
List *restrictlist; List *restrictlist;
/* We should never try to join two overlapping sets of rels. */
Assert(nonoverlap_setsi(rel1->relids, rel2->relids));
/* Construct Relids set that identifies the joinrel. */
joinrelids = nconc(listCopy(rel1->relids), listCopy(rel2->relids));
/*
* If we are implementing IN clauses as joins, there are some joins
* that are illegal. Check to see if the proposed join is trouble.
* We can skip the work if looking at an outer join, however, because
* only top-level joins might be affected.
*/
if (jointype == JOIN_INNER)
{
List *l;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
/*
* Cannot join if proposed join contains part, but only
* part, of the RHS, *and* it contains rels not in the RHS.
*
* Singleton RHS cannot be a problem, so skip expensive tests.
*/
if (length(ininfo->righthand) > 1 &&
overlap_setsi(ininfo->righthand, joinrelids) &&
!is_subseti(ininfo->righthand, joinrelids) &&
!is_subseti(joinrelids, ininfo->righthand))
{
freeList(joinrelids);
return NULL;
}
/*
* No issue unless we are looking at a join of the IN's RHS
* to other stuff.
*/
if (! (length(ininfo->righthand) < length(joinrelids) &&
is_subseti(ininfo->righthand, joinrelids)))
continue;
/*
* If we already joined IN's RHS to any part of its LHS in either
* input path, then this join is not constrained (the necessary
* work was done at a lower level).
*/
if (overlap_setsi(ininfo->lefthand, rel1->relids) &&
is_subseti(ininfo->righthand, rel1->relids))
continue;
if (overlap_setsi(ininfo->lefthand, rel2->relids) &&
is_subseti(ininfo->righthand, rel2->relids))
continue;
/*
* JOIN_IN technique will work if outerrel includes LHS and
* innerrel is exactly RHS; conversely JOIN_REVERSE_IN handles
* RHS/LHS.
*
* JOIN_UNIQUE_OUTER will work if outerrel is exactly RHS;
* conversely JOIN_UNIQUE_INNER will work if innerrel is
* exactly RHS.
*
* But none of these will work if we already found another IN
* that needs to trigger here.
*/
if (jointype != JOIN_INNER)
{
freeList(joinrelids);
return NULL;
}
if (is_subseti(ininfo->lefthand, rel1->relids) &&
sameseti(ininfo->righthand, rel2->relids))
{
jointype = JOIN_IN;
}
else if (is_subseti(ininfo->lefthand, rel2->relids) &&
sameseti(ininfo->righthand, rel1->relids))
{
jointype = JOIN_REVERSE_IN;
}
else if (sameseti(ininfo->righthand, rel1->relids))
{
jointype = JOIN_UNIQUE_OUTER;
}
else if (sameseti(ininfo->righthand, rel2->relids))
{
jointype = JOIN_UNIQUE_INNER;
}
else
{
/* invalid join path */
freeList(joinrelids);
return NULL;
}
}
}
/* /*
* Find or build the join RelOptInfo, and compute the restrictlist * Find or build the join RelOptInfo, and compute the restrictlist
* that goes with this particular joining. * that goes with this particular joining.
*/ */
joinrel = build_join_rel(root, rel1, rel2, jointype, &restrictlist); joinrel = build_join_rel(root, joinrelids, rel1, rel2, jointype,
&restrictlist);
/* /*
* Consider paths using each rel as both outer and inner. * Consider paths using each rel as both outer and inner.
...@@ -438,11 +543,43 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2, ...@@ -438,11 +543,43 @@ make_join_rel(Query *root, RelOptInfo *rel1, RelOptInfo *rel2,
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT, add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_LEFT,
restrictlist); restrictlist);
break; break;
case JOIN_IN:
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_IN,
restrictlist);
/* REVERSE_IN isn't supported by joinpath.c */
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER,
restrictlist);
break;
case JOIN_REVERSE_IN:
/* REVERSE_IN isn't supported by joinpath.c */
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_IN,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_INNER,
restrictlist);
break;
case JOIN_UNIQUE_OUTER:
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_OUTER,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_INNER,
restrictlist);
break;
case JOIN_UNIQUE_INNER:
add_paths_to_joinrel(root, joinrel, rel1, rel2, JOIN_UNIQUE_INNER,
restrictlist);
add_paths_to_joinrel(root, joinrel, rel2, rel1, JOIN_UNIQUE_OUTER,
restrictlist);
break;
default: default:
elog(ERROR, "make_join_rel: unsupported join type %d", elog(ERROR, "make_join_rel: unsupported join type %d",
(int) jointype); (int) jointype);
break; break;
} }
freeList(joinrelids);
return joinrel; return joinrel;
} }
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.131 2003/01/15 23:10:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.132 2003/01/20 18:54:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
#include "optimizer/var.h" #include "optimizer/var.h"
#include "parser/parse_clause.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -36,6 +37,7 @@ static Join *create_join_plan(Query *root, JoinPath *best_path); ...@@ -36,6 +37,7 @@ static Join *create_join_plan(Query *root, JoinPath *best_path);
static Append *create_append_plan(Query *root, AppendPath *best_path); static Append *create_append_plan(Query *root, AppendPath *best_path);
static Result *create_result_plan(Query *root, ResultPath *best_path); static Result *create_result_plan(Query *root, ResultPath *best_path);
static Material *create_material_plan(Query *root, MaterialPath *best_path); static Material *create_material_plan(Query *root, MaterialPath *best_path);
static Plan *create_unique_plan(Query *root, UniquePath *best_path);
static SeqScan *create_seqscan_plan(Path *best_path, List *tlist, static SeqScan *create_seqscan_plan(Path *best_path, List *tlist,
List *scan_clauses); List *scan_clauses);
static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path, static IndexScan *create_indexscan_plan(Query *root, IndexPath *best_path,
...@@ -146,6 +148,10 @@ create_plan(Query *root, Path *best_path) ...@@ -146,6 +148,10 @@ create_plan(Query *root, Path *best_path)
plan = (Plan *) create_material_plan(root, plan = (Plan *) create_material_plan(root,
(MaterialPath *) best_path); (MaterialPath *) best_path);
break; break;
case T_Unique:
plan = (Plan *) create_unique_plan(root,
(UniquePath *) best_path);
break;
default: default:
elog(ERROR, "create_plan: unknown pathtype %d", elog(ERROR, "create_plan: unknown pathtype %d",
best_path->pathtype); best_path->pathtype);
...@@ -399,6 +405,97 @@ create_material_plan(Query *root, MaterialPath *best_path) ...@@ -399,6 +405,97 @@ create_material_plan(Query *root, MaterialPath *best_path)
return plan; return plan;
} }
/*
* create_unique_plan
* Create a Unique plan for 'best_path' and (recursively) plans
* for its subpaths.
*
* Returns a Plan node.
*/
static Plan *
create_unique_plan(Query *root, UniquePath *best_path)
{
Plan *plan;
Plan *subplan;
List *sub_targetlist;
List *l;
subplan = create_plan(root, best_path->subpath);
/*
* If the subplan came from an IN subselect (currently always the case),
* we need to instantiate the correct output targetlist for the subselect,
* rather than using the flattened tlist.
*/
sub_targetlist = NIL;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (sameseti(ininfo->righthand, best_path->path.parent->relids))
{
sub_targetlist = ininfo->sub_targetlist;
break;
}
}
if (sub_targetlist)
{
/*
* Transform list of plain Vars into targetlist
*/
List *newtlist = NIL;
int resno = 1;
foreach(l, sub_targetlist)
{
Node *tlexpr = lfirst(l);
TargetEntry *tle;
tle = makeTargetEntry(makeResdom(resno,
exprType(tlexpr),
exprTypmod(tlexpr),
NULL,
false),
(Expr *) tlexpr);
newtlist = lappend(newtlist, tle);
resno++;
}
/*
* If the top plan node can't do projections, we need to add a
* Result node to help it along.
*
* Currently, the only non-projection-capable plan type
* we can see here is Append.
*/
if (IsA(subplan, Append))
subplan = (Plan *) make_result(newtlist, NULL, subplan);
else
subplan->targetlist = newtlist;
}
if (best_path->use_hash)
{
elog(ERROR, "create_unique_plan: hash case not implemented yet");
plan = NULL;
}
else
{
List *sort_tlist;
List *sortList;
sort_tlist = new_unsorted_tlist(subplan->targetlist);
sortList = addAllTargetsToSortList(NIL, sort_tlist);
plan = (Plan *) make_sort_from_sortclauses(root, sort_tlist,
subplan, sortList);
plan = (Plan *) make_unique(sort_tlist, plan, sortList);
}
plan->plan_rows = best_path->rows;
return plan;
}
/***************************************************************************** /*****************************************************************************
* *
...@@ -1548,6 +1645,52 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree, ...@@ -1548,6 +1645,52 @@ make_sort_from_pathkeys(Query *root, Plan *lefttree,
return make_sort(root, sort_tlist, lefttree, numsortkeys); return make_sort(root, sort_tlist, lefttree, numsortkeys);
} }
/*
* make_sort_from_sortclauses
* Create sort plan to sort according to given sortclauses
*
* 'tlist' is the targetlist
* 'lefttree' is the node which yields input tuples
* 'sortcls' is a list of SortClauses
*/
Sort *
make_sort_from_sortclauses(Query *root, List *tlist,
Plan *lefttree, List *sortcls)
{
List *sort_tlist;
List *i;
int keyno = 0;
/*
* First make a copy of the tlist so that we don't corrupt the
* original.
*/
sort_tlist = new_unsorted_tlist(tlist);
foreach(i, sortcls)
{
SortClause *sortcl = (SortClause *) lfirst(i);
TargetEntry *tle = get_sortgroupclause_tle(sortcl, sort_tlist);
Resdom *resdom = tle->resdom;
/*
* Check for the possibility of duplicate order-by clauses --- the
* parser should have removed 'em, but the executor will get
* terribly confused if any get through!
*/
if (resdom->reskey == 0)
{
/* OK, insert the ordering info needed by the executor. */
resdom->reskey = ++keyno;
resdom->reskeyop = sortcl->sortop;
}
}
Assert(keyno > 0);
return make_sort(root, sort_tlist, lefttree, keyno);
}
Material * Material *
make_material(List *tlist, Plan *lefttree) make_material(List *tlist, Plan *lefttree)
{ {
......
...@@ -8,13 +8,12 @@ ...@@ -8,13 +8,12 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.81 2003/01/15 19:35:40 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.82 2003/01/20 18:54:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_operator.h" #include "catalog/pg_operator.h"
#include "catalog/pg_type.h" #include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
...@@ -579,6 +578,11 @@ distribute_qual_to_rels(Query *root, Node *clause, ...@@ -579,6 +578,11 @@ distribute_qual_to_rels(Query *root, Node *clause,
* the appropriate joininfo list (creating a new list and adding it to the * the appropriate joininfo list (creating a new list and adding it to the
* appropriate rel node if necessary). * appropriate rel node if necessary).
* *
* Note that the same copy of the restrictinfo node is linked to by all the
* lists it is in. This allows us to exploit caching of information about
* the restriction clause (but we must be careful that the information does
* not depend on context).
*
* 'restrictinfo' describes the join clause * 'restrictinfo' describes the join clause
* 'join_relids' is the list of relations participating in the join clause * 'join_relids' is the list of relations participating in the join clause
*/ */
...@@ -602,12 +606,13 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo, ...@@ -602,12 +606,13 @@ add_join_info_to_rels(Query *root, RestrictInfo *restrictinfo,
if (lfirsti(otherrel) != cur_relid) if (lfirsti(otherrel) != cur_relid)
unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel)); unjoined_relids = lappendi(unjoined_relids, lfirsti(otherrel));
} }
Assert(unjoined_relids != NIL);
/* /*
* Find or make the joininfo node for this combination of rels, * Find or make the joininfo node for this combination of rels,
* and add the restrictinfo node to it. * and add the restrictinfo node to it.
*/ */
joininfo = find_joininfo_node(find_base_rel(root, cur_relid), joininfo = make_joininfo_node(find_base_rel(root, cur_relid),
unjoined_relids); unjoined_relids);
joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo, joininfo->jinfo_restrictinfo = lappend(joininfo->jinfo_restrictinfo,
restrictinfo); restrictinfo);
...@@ -731,7 +736,7 @@ exprs_known_equal(Query *root, Node *item1, Node *item2) ...@@ -731,7 +736,7 @@ exprs_known_equal(Query *root, Node *item1, Node *item2)
{ {
JoinInfo *joininfo = find_joininfo_node(rel1, relids); JoinInfo *joininfo = find_joininfo_node(rel1, relids);
restrictlist = joininfo->jinfo_restrictinfo; restrictlist = joininfo ? joininfo->jinfo_restrictinfo : NIL;
} }
/* /*
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.73 2003/01/15 19:35:40 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.74 2003/01/20 18:54:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -108,6 +108,8 @@ query_planner(Query *root, List *tlist, double tuple_fraction, ...@@ -108,6 +108,8 @@ query_planner(Query *root, List *tlist, double tuple_fraction,
/* /*
* init planner lists to empty * init planner lists to empty
*
* NOTE: in_info_list was set up by subquery_planner, do not touch here
*/ */
root->base_rel_list = NIL; root->base_rel_list = NIL;
root->other_rel_list = NIL; root->other_rel_list = NIL;
......
This diff is collapsed.
...@@ -9,7 +9,7 @@ ...@@ -9,7 +9,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.90 2003/01/15 23:10:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/setrefs.c,v 1.91 2003/01/20 18:54:52 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -31,6 +31,7 @@ typedef struct ...@@ -31,6 +31,7 @@ typedef struct
List *outer_tlist; List *outer_tlist;
List *inner_tlist; List *inner_tlist;
Index acceptable_rel; Index acceptable_rel;
bool tlists_have_non_vars;
} join_references_context; } join_references_context;
typedef struct typedef struct
...@@ -44,11 +45,13 @@ static void fix_expr_references(Plan *plan, Node *node); ...@@ -44,11 +45,13 @@ static void fix_expr_references(Plan *plan, Node *node);
static bool fix_expr_references_walker(Node *node, void *context); static bool fix_expr_references_walker(Node *node, void *context);
static void set_join_references(Join *join, List *rtable); static void set_join_references(Join *join, List *rtable);
static void set_uppernode_references(Plan *plan, Index subvarno); static void set_uppernode_references(Plan *plan, Index subvarno);
static bool targetlist_has_non_vars(List *tlist);
static List *join_references(List *clauses, static List *join_references(List *clauses,
List *rtable, List *rtable,
List *outer_tlist, List *outer_tlist,
List *inner_tlist, List *inner_tlist,
Index acceptable_rel); Index acceptable_rel,
bool tlists_have_non_vars);
static Node *join_references_mutator(Node *node, static Node *join_references_mutator(Node *node,
join_references_context *context); join_references_context *context);
static Node *replace_vars_with_subplan_refs(Node *node, static Node *replace_vars_with_subplan_refs(Node *node,
...@@ -175,7 +178,10 @@ set_plan_references(Plan *plan, List *rtable) ...@@ -175,7 +178,10 @@ set_plan_references(Plan *plan, List *rtable)
rtable, rtable,
NIL, NIL,
plan->lefttree->targetlist, plan->lefttree->targetlist,
(Index) 0); (Index) 0,
targetlist_has_non_vars(plan->lefttree->targetlist));
fix_expr_references(plan,
(Node *) ((Hash *) plan)->hashkeys);
break; break;
case T_Material: case T_Material:
case T_Sort: case T_Sort:
...@@ -308,23 +314,30 @@ set_join_references(Join *join, List *rtable) ...@@ -308,23 +314,30 @@ set_join_references(Join *join, List *rtable)
Plan *inner_plan = join->plan.righttree; Plan *inner_plan = join->plan.righttree;
List *outer_tlist = outer_plan->targetlist; List *outer_tlist = outer_plan->targetlist;
List *inner_tlist = inner_plan->targetlist; List *inner_tlist = inner_plan->targetlist;
bool tlists_have_non_vars;
tlists_have_non_vars = targetlist_has_non_vars(outer_tlist) ||
targetlist_has_non_vars(inner_tlist);
/* All join plans have tlist, qual, and joinqual */ /* All join plans have tlist, qual, and joinqual */
join->plan.targetlist = join_references(join->plan.targetlist, join->plan.targetlist = join_references(join->plan.targetlist,
rtable, rtable,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
tlists_have_non_vars);
join->plan.qual = join_references(join->plan.qual, join->plan.qual = join_references(join->plan.qual,
rtable, rtable,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
tlists_have_non_vars);
join->joinqual = join_references(join->joinqual, join->joinqual = join_references(join->joinqual,
rtable, rtable,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
tlists_have_non_vars);
/* Now do join-type-specific stuff */ /* Now do join-type-specific stuff */
if (IsA(join, NestLoop)) if (IsA(join, NestLoop))
...@@ -350,12 +363,14 @@ set_join_references(Join *join, List *rtable) ...@@ -350,12 +363,14 @@ set_join_references(Join *join, List *rtable)
rtable, rtable,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
tlists_have_non_vars);
innerscan->indxqual = join_references(innerscan->indxqual, innerscan->indxqual = join_references(innerscan->indxqual,
rtable, rtable,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
tlists_have_non_vars);
/* /*
* We must fix the inner qpqual too, if it has join clauses * We must fix the inner qpqual too, if it has join clauses
* (this could happen if the index is lossy: some indxquals * (this could happen if the index is lossy: some indxquals
...@@ -366,7 +381,8 @@ set_join_references(Join *join, List *rtable) ...@@ -366,7 +381,8 @@ set_join_references(Join *join, List *rtable)
rtable, rtable,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
tlists_have_non_vars);
} }
} }
else if (IsA(inner_plan, TidScan)) else if (IsA(inner_plan, TidScan))
...@@ -378,7 +394,8 @@ set_join_references(Join *join, List *rtable) ...@@ -378,7 +394,8 @@ set_join_references(Join *join, List *rtable)
rtable, rtable,
outer_tlist, outer_tlist,
NIL, NIL,
innerrel); innerrel,
tlists_have_non_vars);
} }
} }
else if (IsA(join, MergeJoin)) else if (IsA(join, MergeJoin))
...@@ -389,7 +406,8 @@ set_join_references(Join *join, List *rtable) ...@@ -389,7 +406,8 @@ set_join_references(Join *join, List *rtable)
rtable, rtable,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
tlists_have_non_vars);
} }
else if (IsA(join, HashJoin)) else if (IsA(join, HashJoin))
{ {
...@@ -399,7 +417,8 @@ set_join_references(Join *join, List *rtable) ...@@ -399,7 +417,8 @@ set_join_references(Join *join, List *rtable)
rtable, rtable,
outer_tlist, outer_tlist,
inner_tlist, inner_tlist,
(Index) 0); (Index) 0,
tlists_have_non_vars);
} }
} }
...@@ -433,22 +452,7 @@ set_uppernode_references(Plan *plan, Index subvarno) ...@@ -433,22 +452,7 @@ set_uppernode_references(Plan *plan, Index subvarno)
else else
subplan_targetlist = NIL; subplan_targetlist = NIL;
/* tlist_has_non_vars = targetlist_has_non_vars(subplan_targetlist);
* Detect whether subplan tlist has any non-Vars (typically it won't
* because it's been flattened). This allows us to save comparisons
* in common cases.
*/
tlist_has_non_vars = false;
foreach(l, subplan_targetlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->expr && !IsA(tle->expr, Var))
{
tlist_has_non_vars = true;
break;
}
}
output_targetlist = NIL; output_targetlist = NIL;
foreach(l, plan->targetlist) foreach(l, plan->targetlist)
...@@ -473,6 +477,27 @@ set_uppernode_references(Plan *plan, Index subvarno) ...@@ -473,6 +477,27 @@ set_uppernode_references(Plan *plan, Index subvarno)
tlist_has_non_vars); tlist_has_non_vars);
} }
/*
* targetlist_has_non_vars --- are there any non-Var entries in tlist?
*
* In most cases, subplan tlists will be "flat" tlists with only Vars.
* Checking for this allows us to save comparisons in common cases.
*/
static bool
targetlist_has_non_vars(List *tlist)
{
List *l;
foreach(l, tlist)
{
TargetEntry *tle = (TargetEntry *) lfirst(l);
if (tle->expr && !IsA(tle->expr, Var))
return true;
}
return false;
}
/* /*
* join_references * join_references
* Creates a new set of targetlist entries or join qual clauses by * Creates a new set of targetlist entries or join qual clauses by
...@@ -505,7 +530,8 @@ join_references(List *clauses, ...@@ -505,7 +530,8 @@ join_references(List *clauses,
List *rtable, List *rtable,
List *outer_tlist, List *outer_tlist,
List *inner_tlist, List *inner_tlist,
Index acceptable_rel) Index acceptable_rel,
bool tlists_have_non_vars)
{ {
join_references_context context; join_references_context context;
...@@ -513,6 +539,7 @@ join_references(List *clauses, ...@@ -513,6 +539,7 @@ join_references(List *clauses,
context.outer_tlist = outer_tlist; context.outer_tlist = outer_tlist;
context.inner_tlist = inner_tlist; context.inner_tlist = inner_tlist;
context.acceptable_rel = acceptable_rel; context.acceptable_rel = acceptable_rel;
context.tlists_have_non_vars = tlists_have_non_vars;
return (List *) join_references_mutator((Node *) clauses, &context); return (List *) join_references_mutator((Node *) clauses, &context);
} }
...@@ -554,6 +581,42 @@ join_references_mutator(Node *node, ...@@ -554,6 +581,42 @@ join_references_mutator(Node *node,
/* No referent found for Var */ /* No referent found for Var */
elog(ERROR, "join_references: variable not in subplan target lists"); elog(ERROR, "join_references: variable not in subplan target lists");
} }
/* Try matching more complex expressions too, if tlists have any */
if (context->tlists_have_non_vars)
{
Resdom *resdom;
resdom = tlist_member(node, context->outer_tlist);
if (resdom)
{
/* Found a matching subplan output expression */
Var *newvar;
newvar = makeVar(OUTER,
resdom->resno,
resdom->restype,
resdom->restypmod,
0);
newvar->varnoold = 0; /* wasn't ever a plain Var */
newvar->varoattno = 0;
return (Node *) newvar;
}
resdom = tlist_member(node, context->inner_tlist);
if (resdom)
{
/* Found a matching subplan output expression */
Var *newvar;
newvar = makeVar(INNER,
resdom->resno,
resdom->restype,
resdom->restypmod,
0);
newvar->varnoold = 0; /* wasn't ever a plain Var */
newvar->varoattno = 0;
return (Node *) newvar;
}
}
return expression_tree_mutator(node, return expression_tree_mutator(node,
join_references_mutator, join_references_mutator,
(void *) context); (void *) context);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.67 2003/01/17 02:01:11 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.68 2003/01/20 18:54:53 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -23,9 +23,11 @@ ...@@ -23,9 +23,11 @@
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "optimizer/planner.h" #include "optimizer/planner.h"
#include "optimizer/subselect.h" #include "optimizer/subselect.h"
#include "optimizer/var.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "parser/parse_relation.h"
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/syscache.h" #include "utils/syscache.h"
...@@ -62,7 +64,8 @@ typedef struct finalize_primnode_results ...@@ -62,7 +64,8 @@ typedef struct finalize_primnode_results
static List *convert_sublink_opers(List *lefthand, List *operOids, static List *convert_sublink_opers(List *lefthand, List *operOids,
List *targetlist, List **paramIds); List *targetlist, int rtindex,
List **righthandIds);
static bool subplan_is_hashable(SubLink *slink, SubPlan *node); static bool subplan_is_hashable(SubLink *slink, SubPlan *node);
static Node *replace_correlation_vars_mutator(Node *node, void *context); static Node *replace_correlation_vars_mutator(Node *node, void *context);
static Node *process_sublinks_mutator(Node *node, bool *isTopQual); static Node *process_sublinks_mutator(Node *node, bool *isTopQual);
...@@ -289,6 +292,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) ...@@ -289,6 +292,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
exprs = convert_sublink_opers(lefthand, exprs = convert_sublink_opers(lefthand,
slink->operOids, slink->operOids,
plan->targetlist, plan->targetlist,
0,
&node->paramIds); &node->paramIds);
node->setParam = nconc(node->setParam, listCopy(node->paramIds)); node->setParam = nconc(node->setParam, listCopy(node->paramIds));
PlannerInitPlan = lappend(PlannerInitPlan, node); PlannerInitPlan = lappend(PlannerInitPlan, node);
...@@ -393,6 +397,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) ...@@ -393,6 +397,7 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
node->exprs = convert_sublink_opers(lefthand, node->exprs = convert_sublink_opers(lefthand,
slink->operOids, slink->operOids,
plan->targetlist, plan->targetlist,
0,
&node->paramIds); &node->paramIds);
/* /*
...@@ -424,26 +429,32 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual) ...@@ -424,26 +429,32 @@ make_subplan(SubLink *slink, List *lefthand, bool isTopQual)
/* /*
* convert_sublink_opers: given a lefthand-expressions list and a list of * convert_sublink_opers: given a lefthand-expressions list and a list of
* operator OIDs, build a list of actually executable expressions. The * operator OIDs, build a list of actually executable expressions. The
* righthand sides of the expressions are Params representing the results * righthand sides of the expressions are Params or Vars representing the
* of the sub-select. * results of the sub-select.
* *
* The paramids of the Params created are returned in the *paramIds list. * If rtindex is 0, we build Params to represent the sub-select outputs.
* The paramids of the Params created are returned in the *righthandIds list.
*
* If rtindex is not 0, we build Vars using that rtindex as varno. The
* Vars themselves are returned in *righthandIds (this is a bit of a type
* cheat, but we can get away with it).
*/ */
static List * static List *
convert_sublink_opers(List *lefthand, List *operOids, convert_sublink_opers(List *lefthand, List *operOids,
List *targetlist, List **paramIds) List *targetlist, int rtindex,
List **righthandIds)
{ {
List *result = NIL; List *result = NIL;
List *lst; List *lst;
*paramIds = NIL; *righthandIds = NIL;
foreach(lst, operOids) foreach(lst, operOids)
{ {
Oid opid = (Oid) lfirsti(lst); Oid opid = (Oid) lfirsti(lst);
Node *leftop = lfirst(lefthand); Node *leftop = lfirst(lefthand);
TargetEntry *te = lfirst(targetlist); TargetEntry *te = lfirst(targetlist);
Param *prm; Node *rightop;
Operator tup; Operator tup;
Form_pg_operator opform; Form_pg_operator opform;
Node *left, Node *left,
...@@ -451,12 +462,28 @@ convert_sublink_opers(List *lefthand, List *operOids, ...@@ -451,12 +462,28 @@ convert_sublink_opers(List *lefthand, List *operOids,
Assert(!te->resdom->resjunk); Assert(!te->resdom->resjunk);
/* Make the Param node representing the subplan's result */ if (rtindex)
prm = generate_new_param(te->resdom->restype, {
te->resdom->restypmod); /* Make the Var node representing the subplan's result */
rightop = (Node *) makeVar(rtindex,
/* Record its ID */ te->resdom->resno,
*paramIds = lappendi(*paramIds, prm->paramid); te->resdom->restype,
te->resdom->restypmod,
0);
/* Record it for caller */
*righthandIds = lappend(*righthandIds, rightop);
}
else
{
/* Make the Param node representing the subplan's result */
Param *prm;
prm = generate_new_param(te->resdom->restype,
te->resdom->restypmod);
/* Record its ID */
*righthandIds = lappendi(*righthandIds, prm->paramid);
rightop = (Node *) prm;
}
/* Look up the operator to get its declared input types */ /* Look up the operator to get its declared input types */
tup = SearchSysCache(OPEROID, tup = SearchSysCache(OPEROID,
...@@ -473,7 +500,7 @@ convert_sublink_opers(List *lefthand, List *operOids, ...@@ -473,7 +500,7 @@ convert_sublink_opers(List *lefthand, List *operOids,
* function calls must be inserted for this operator! * function calls must be inserted for this operator!
*/ */
left = make_operand(leftop, exprType(leftop), opform->oprleft); left = make_operand(leftop, exprType(leftop), opform->oprleft);
right = make_operand((Node *) prm, prm->paramtype, opform->oprright); right = make_operand(rightop, te->resdom->restype, opform->oprright);
result = lappend(result, result = lappend(result,
make_opclause(opid, make_opclause(opid,
opform->oprresult, opform->oprresult,
...@@ -564,6 +591,96 @@ subplan_is_hashable(SubLink *slink, SubPlan *node) ...@@ -564,6 +591,96 @@ subplan_is_hashable(SubLink *slink, SubPlan *node)
return true; return true;
} }
/*
* convert_IN_to_join: can we convert an IN SubLink to join style?
*
* The caller has found a SubLink at the top level of WHERE, but has not
* checked the properties of the SubLink at all. Decide whether it is
* appropriate to process this SubLink in join style. If not, return NULL.
* If so, build the qual clause(s) to replace the SubLink, and return them.
*
* Side effects of a successful conversion include adding the SubLink's
* subselect to the query's rangetable and adding an InClauseInfo node to
* its in_info_list.
*/
Node *
convert_IN_to_join(Query *parse, SubLink *sublink)
{
Query *subselect = (Query *) sublink->subselect;
List *left_varnos;
int rtindex;
RangeTblEntry *rte;
RangeTblRef *rtr;
InClauseInfo *ininfo;
List *exprs;
/*
* The sublink type must be "= ANY" --- that is, an IN operator.
* (We require the operator name to be unqualified, which may be
* overly paranoid, or may not be.)
*/
if (sublink->subLinkType != ANY_SUBLINK)
return NULL;
if (length(sublink->operName) != 1 ||
strcmp(strVal(lfirst(sublink->operName)), "=") != 0)
return NULL;
/*
* The sub-select must not refer to any Vars of the parent query.
* (Vars of higher levels should be okay, though.)
*/
if (contain_vars_of_level((Node *) subselect, 1))
return NULL;
/*
* The left-hand expressions must contain some Vars of the current
* query, else it's not gonna be a join.
*/
left_varnos = pull_varnos((Node *) sublink->lefthand);
if (left_varnos == NIL)
return NULL;
/*
* The left-hand expressions mustn't be volatile. (Perhaps we should
* test the combining operators, too? We'd only need to point the
* function directly at the sublink ...)
*/
if (contain_volatile_functions((Node *) sublink->lefthand))
return NULL;
/*
* Okay, pull up the sub-select into top range table and jointree.
*
* We rely here on the assumption that the outer query has no references
* to the inner (necessarily true, other than the Vars that we build
* below). Therefore this is a lot easier than what pull_up_subqueries
* has to go through.
*/
rte = addRangeTableEntryForSubquery(NULL,
subselect,
makeAlias("IN_subquery", NIL),
false);
parse->rtable = lappend(parse->rtable, rte);
rtindex = length(parse->rtable);
rtr = makeNode(RangeTblRef);
rtr->rtindex = rtindex;
parse->jointree->fromlist = lappend(parse->jointree->fromlist, rtr);
/*
* Now build the InClauseInfo node.
*/
ininfo = makeNode(InClauseInfo);
ininfo->lefthand = left_varnos;
ininfo->righthand = makeListi1(rtindex);
parse->in_info_list = lcons(ininfo, parse->in_info_list);
/*
* Build the result qual expressions. As a side effect,
* ininfo->sub_targetlist is filled with a list of the Vars
* representing the subselect outputs.
*/
exprs = convert_sublink_opers(sublink->lefthand,
sublink->operOids,
subselect->targetList,
rtindex,
&ininfo->sub_targetlist);
return (Node *) make_ands_explicit(exprs);
}
/* /*
* Replace correlation vars (uplevel vars) with Params. * Replace correlation vars (uplevel vars) with Params.
*/ */
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
# Makefile for optimizer/prep # Makefile for optimizer/prep
# #
# IDENTIFICATION # IDENTIFICATION
# $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Makefile,v 1.13 2002/06/16 00:09:11 momjian Exp $ # $Header: /cvsroot/pgsql/src/backend/optimizer/prep/Makefile,v 1.14 2003/01/20 18:54:54 tgl Exp $
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
...@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/prep ...@@ -12,7 +12,7 @@ subdir = src/backend/optimizer/prep
top_builddir = ../../../.. top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global include $(top_builddir)/src/Makefile.global
OBJS = prepqual.o preptlist.o prepunion.o OBJS = prepjointree.o prepqual.o preptlist.o prepunion.o
all: SUBSYS.o all: SUBSYS.o
......
This diff is collapsed.
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.87 2003/01/17 02:01:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.88 2003/01/20 18:54:54 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -62,6 +62,7 @@ static List *generate_append_tlist(List *colTypes, bool flag, ...@@ -62,6 +62,7 @@ static List *generate_append_tlist(List *colTypes, bool flag,
List *refnames_tlist); List *refnames_tlist);
static Node *adjust_inherited_attrs_mutator(Node *node, static Node *adjust_inherited_attrs_mutator(Node *node,
adjust_inherited_attrs_context *context); adjust_inherited_attrs_context *context);
static List *adjust_rtindex_list(List *relids, Index oldrelid, Index newrelid);
static List *adjust_inherited_tlist(List *tlist, Oid new_relid); static List *adjust_inherited_tlist(List *tlist, Oid new_relid);
...@@ -239,8 +240,9 @@ generate_union_plan(SetOperationStmt *op, Query *parse, ...@@ -239,8 +240,9 @@ generate_union_plan(SetOperationStmt *op, Query *parse,
tlist = new_unsorted_tlist(tlist); tlist = new_unsorted_tlist(tlist);
sortList = addAllTargetsToSortList(NIL, tlist); sortList = addAllTargetsToSortList(NIL, tlist);
plan = make_sortplan(parse, tlist, plan, sortList); plan = (Plan *) make_sort_from_sortclauses(parse, tlist,
plan = (Plan *) make_unique(tlist, plan, copyObject(sortList)); plan, sortList);
plan = (Plan *) make_unique(tlist, plan, sortList);
} }
return plan; return plan;
} }
...@@ -292,7 +294,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse, ...@@ -292,7 +294,7 @@ generate_nonunion_plan(SetOperationStmt *op, Query *parse,
*/ */
tlist = new_unsorted_tlist(tlist); tlist = new_unsorted_tlist(tlist);
sortList = addAllTargetsToSortList(NIL, tlist); sortList = addAllTargetsToSortList(NIL, tlist);
plan = make_sortplan(parse, tlist, plan, sortList); plan = (Plan *) make_sort_from_sortclauses(parse, tlist, plan, sortList);
switch (op->op) switch (op->op)
{ {
case SETOP_INTERSECT: case SETOP_INTERSECT:
...@@ -830,6 +832,23 @@ adjust_inherited_attrs_mutator(Node *node, ...@@ -830,6 +832,23 @@ adjust_inherited_attrs_mutator(Node *node,
j->rtindex = context->new_rt_index; j->rtindex = context->new_rt_index;
return (Node *) j; return (Node *) j;
} }
if (IsA(node, InClauseInfo))
{
/* Copy the InClauseInfo node with correct mutation of subnodes */
InClauseInfo *ininfo;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
adjust_inherited_attrs_mutator,
(void *) context);
/* now fix InClauseInfo's rtindex lists */
ininfo->lefthand = adjust_rtindex_list(ininfo->lefthand,
context->old_rt_index,
context->new_rt_index);
ininfo->righthand = adjust_rtindex_list(ininfo->righthand,
context->old_rt_index,
context->new_rt_index);
return (Node *) ininfo;
}
/* /*
* We have to process RestrictInfo nodes specially. * We have to process RestrictInfo nodes specially.
...@@ -856,26 +875,12 @@ adjust_inherited_attrs_mutator(Node *node, ...@@ -856,26 +875,12 @@ adjust_inherited_attrs_mutator(Node *node,
/* /*
* Adjust left/right relids lists too. * Adjust left/right relids lists too.
*/ */
if (intMember(context->old_rt_index, oldinfo->left_relids)) newinfo->left_relids = adjust_rtindex_list(oldinfo->left_relids,
{ context->old_rt_index,
newinfo->left_relids = listCopy(oldinfo->left_relids); context->new_rt_index);
newinfo->left_relids = lremovei(context->old_rt_index, newinfo->right_relids = adjust_rtindex_list(oldinfo->right_relids,
newinfo->left_relids); context->old_rt_index,
newinfo->left_relids = lconsi(context->new_rt_index, context->new_rt_index);
newinfo->left_relids);
}
else
newinfo->left_relids = oldinfo->left_relids;
if (intMember(context->old_rt_index, oldinfo->right_relids))
{
newinfo->right_relids = listCopy(oldinfo->right_relids);
newinfo->right_relids = lremovei(context->old_rt_index,
newinfo->right_relids);
newinfo->right_relids = lconsi(context->new_rt_index,
newinfo->right_relids);
}
else
newinfo->right_relids = oldinfo->right_relids;
newinfo->eval_cost.startup = -1; /* reset these too */ newinfo->eval_cost.startup = -1; /* reset these too */
newinfo->this_selec = -1; newinfo->this_selec = -1;
...@@ -922,6 +927,23 @@ adjust_inherited_attrs_mutator(Node *node, ...@@ -922,6 +927,23 @@ adjust_inherited_attrs_mutator(Node *node,
(void *) context); (void *) context);
} }
/*
* Substitute newrelid for oldrelid in a list of RT indexes
*/
static List *
adjust_rtindex_list(List *relids, Index oldrelid, Index newrelid)
{
if (intMember(oldrelid, relids))
{
/* Ensure we have a modifiable copy */
relids = listCopy(relids);
/* Remove old, add new */
relids = lremovei(oldrelid, relids);
relids = lconsi(newrelid, relids);
}
return relids;
}
/* /*
* Adjust the targetlist entries of an inherited UPDATE operation * Adjust the targetlist entries of an inherited UPDATE operation
* *
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.124 2003/01/17 03:25:03 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.125 2003/01/20 18:54:54 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -2200,6 +2200,15 @@ expression_tree_walker(Node *node, ...@@ -2200,6 +2200,15 @@ expression_tree_walker(Node *node,
return true; return true;
} }
break; break;
case T_InClauseInfo:
{
InClauseInfo *ininfo = (InClauseInfo *) node;
if (expression_tree_walker((Node *) ininfo->sub_targetlist,
walker, context))
return true;
}
break;
default: default:
elog(ERROR, "expression_tree_walker: Unexpected node type %d", elog(ERROR, "expression_tree_walker: Unexpected node type %d",
nodeTag(node)); nodeTag(node));
...@@ -2241,6 +2250,8 @@ query_tree_walker(Query *query, ...@@ -2241,6 +2250,8 @@ query_tree_walker(Query *query,
return true; return true;
if (walker(query->havingQual, context)) if (walker(query->havingQual, context))
return true; return true;
if (walker(query->in_info_list, context))
return true;
foreach(rt, query->rtable) foreach(rt, query->rtable)
{ {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
...@@ -2610,6 +2621,16 @@ expression_tree_mutator(Node *node, ...@@ -2610,6 +2621,16 @@ expression_tree_mutator(Node *node,
return (Node *) newnode; return (Node *) newnode;
} }
break; break;
case T_InClauseInfo:
{
InClauseInfo *ininfo = (InClauseInfo *) node;
InClauseInfo *newnode;
FLATCOPY(newnode, ininfo, InClauseInfo);
MUTATE(newnode->sub_targetlist, ininfo->sub_targetlist, List *);
return (Node *) newnode;
}
break;
default: default:
elog(ERROR, "expression_tree_mutator: Unexpected node type %d", elog(ERROR, "expression_tree_mutator: Unexpected node type %d",
nodeTag(node)); nodeTag(node));
...@@ -2662,6 +2683,7 @@ query_tree_mutator(Query *query, ...@@ -2662,6 +2683,7 @@ query_tree_mutator(Query *query,
MUTATE(query->jointree, query->jointree, FromExpr *); MUTATE(query->jointree, query->jointree, FromExpr *);
MUTATE(query->setOperations, query->setOperations, Node *); MUTATE(query->setOperations, query->setOperations, Node *);
MUTATE(query->havingQual, query->havingQual, Node *); MUTATE(query->havingQual, query->havingQual, Node *);
MUTATE(query->in_info_list, query->in_info_list, List *);
foreach(rt, query->rtable) foreach(rt, query->rtable)
{ {
RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt);
......
...@@ -8,37 +8,29 @@ ...@@ -8,37 +8,29 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.31 2002/06/20 20:29:31 momjian Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/joininfo.c,v 1.32 2003/01/20 18:54:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "optimizer/joininfo.h" #include "optimizer/joininfo.h"
static JoinInfo *joininfo_member(List *join_relids, List *joininfo_list);
/* /*
* joininfo_member * find_joininfo_node
* Determines whether a node has already been created for a join * Find the joininfo node within a relation entry corresponding
* between a set of join relations and the relation described by * to a join between 'this_rel' and the relations in 'join_relids'.
* 'joininfo_list'. * If there is no such node, return NULL.
*
* 'join_relids' is a list of relids corresponding to the join relation
* 'joininfo_list' is the list of joininfo nodes against which this is
* checked
*
* Returns the corresponding node in 'joininfo_list' if such a node
* exists.
* *
* Returns a joininfo node, or NULL.
*/ */
static JoinInfo * JoinInfo *
joininfo_member(List *join_relids, List *joininfo_list) find_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
{ {
List *i; List *i;
foreach(i, joininfo_list) foreach(i, this_rel->joininfo)
{ {
JoinInfo *joininfo = (JoinInfo *) lfirst(i); JoinInfo *joininfo = (JoinInfo *) lfirst(i);
...@@ -48,22 +40,19 @@ joininfo_member(List *join_relids, List *joininfo_list) ...@@ -48,22 +40,19 @@ joininfo_member(List *join_relids, List *joininfo_list)
return NULL; return NULL;
} }
/* /*
* find_joininfo_node * make_joininfo_node
* Find the joininfo node within a relation entry corresponding * Find the joininfo node within a relation entry corresponding
* to a join between 'this_rel' and the relations in 'join_relids'. * 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 * A new node is created and added to the relation entry's joininfo
* field if the desired one can't be found. * field if the desired one can't be found.
* *
* Returns a joininfo node. * Returns a joininfo node.
*
*/ */
JoinInfo * JoinInfo *
find_joininfo_node(RelOptInfo *this_rel, Relids join_relids) make_joininfo_node(RelOptInfo *this_rel, Relids join_relids)
{ {
JoinInfo *joininfo = joininfo_member(join_relids, JoinInfo *joininfo = find_joininfo_node(this_rel, join_relids);
this_rel->joininfo);
if (joininfo == NULL) if (joininfo == NULL)
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.83 2002/12/05 15:50:35 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/pathnode.c,v 1.84 2003/01/20 18:54:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -22,6 +22,8 @@ ...@@ -22,6 +22,8 @@
#include "optimizer/pathnode.h" #include "optimizer/pathnode.h"
#include "optimizer/paths.h" #include "optimizer/paths.h"
#include "optimizer/restrictinfo.h" #include "optimizer/restrictinfo.h"
#include "utils/memutils.h"
#include "utils/selfuncs.h"
/***************************************************************************** /*****************************************************************************
...@@ -149,6 +151,7 @@ set_cheapest(RelOptInfo *parent_rel) ...@@ -149,6 +151,7 @@ set_cheapest(RelOptInfo *parent_rel)
parent_rel->cheapest_startup_path = cheapest_startup_path; parent_rel->cheapest_startup_path = cheapest_startup_path;
parent_rel->cheapest_total_path = cheapest_total_path; parent_rel->cheapest_total_path = cheapest_total_path;
parent_rel->cheapest_unique_path = NULL; /* computed only if needed */
} }
/* /*
...@@ -489,6 +492,111 @@ create_material_path(RelOptInfo *rel, Path *subpath) ...@@ -489,6 +492,111 @@ create_material_path(RelOptInfo *rel, Path *subpath)
return pathnode; return pathnode;
} }
/*
* create_unique_path
* Creates a path representing elimination of distinct rows from the
* input data.
*
* If used at all, this is likely to be called repeatedly on the same rel;
* and the input subpath should always be the same (the cheapest_total path
* for the rel). So we cache the result.
*/
UniquePath *
create_unique_path(Query *root, RelOptInfo *rel, Path *subpath)
{
UniquePath *pathnode;
Path sort_path; /* dummy for result of cost_sort */
MemoryContext oldcontext;
List *sub_targetlist;
List *l;
int numCols;
/* Caller made a mistake if subpath isn't cheapest_total */
Assert(subpath == rel->cheapest_total_path);
/* If result already cached, return it */
if (rel->cheapest_unique_path)
return (UniquePath *) rel->cheapest_unique_path;
/*
* We must ensure path struct is allocated in same context as parent
* rel; otherwise GEQO memory management causes trouble. (Compare
* best_inner_indexscan().)
*/
oldcontext = MemoryContextSwitchTo(GetMemoryChunkContext(rel));
pathnode = makeNode(UniquePath);
/* There is no substructure to allocate, so can switch back right away */
MemoryContextSwitchTo(oldcontext);
pathnode->path.pathtype = T_Unique;
pathnode->path.parent = rel;
/*
* Treat the output as always unsorted, since we don't necessarily have
* pathkeys to represent it.
*/
pathnode->path.pathkeys = NIL;
pathnode->subpath = subpath;
/*
* Try to identify the targetlist that will actually be unique-ified.
* In current usage, this routine is only used for sub-selects of IN
* clauses, so we should be able to find the tlist in in_info_list.
*/
sub_targetlist = NIL;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (sameseti(ininfo->righthand, rel->relids))
{
sub_targetlist = ininfo->sub_targetlist;
break;
}
}
/*
* If we know the targetlist, try to estimate number of result rows;
* otherwise punt.
*/
if (sub_targetlist)
{
pathnode->rows = estimate_num_groups(root, sub_targetlist, rel->rows);
numCols = length(sub_targetlist);
}
else
{
pathnode->rows = rel->rows;
numCols = length(rel->targetlist); /* second-best estimate */
}
/*
* Estimate cost for sort+unique implementation
*/
cost_sort(&sort_path, root, NIL,
subpath->total_cost,
rel->rows,
rel->width);
/*
* Charge one cpu_operator_cost per comparison per input tuple. We
* assume all columns get compared at most of the tuples. (XXX probably
* this is an overestimate.) This should agree with make_unique.
*/
sort_path.total_cost += cpu_operator_cost * rel->rows * numCols;
pathnode->use_hash = false; /* for now */
pathnode->path.startup_cost = sort_path.startup_cost;
pathnode->path.total_cost = sort_path.total_cost;
rel->cheapest_unique_path = (Path *) pathnode;
return pathnode;
}
/* /*
* create_subqueryscan_path * create_subqueryscan_path
* Creates a path corresponding to a sequential scan of a subquery, * Creates a path corresponding to a sequential scan of a subquery,
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.43 2003/01/15 19:35:44 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/relnode.c,v 1.44 2003/01/20 18:54:56 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -140,6 +140,7 @@ make_base_rel(Query *root, int relid) ...@@ -140,6 +140,7 @@ make_base_rel(Query *root, int relid)
rel->pathlist = NIL; rel->pathlist = NIL;
rel->cheapest_startup_path = NULL; rel->cheapest_startup_path = NULL;
rel->cheapest_total_path = NULL; rel->cheapest_total_path = NULL;
rel->cheapest_unique_path = NULL;
rel->pruneable = true; rel->pruneable = true;
rel->rtekind = rte->rtekind; rel->rtekind = rte->rtekind;
rel->indexlist = NIL; rel->indexlist = NIL;
...@@ -244,6 +245,7 @@ find_join_rel(Query *root, Relids relids) ...@@ -244,6 +245,7 @@ find_join_rel(Query *root, Relids relids)
* Returns relation entry corresponding to the union of two given rels, * Returns relation entry corresponding to the union of two given rels,
* creating a new relation entry if none already exists. * creating a new relation entry if none already exists.
* *
* 'joinrelids' is the Relids list that uniquely identifies the join
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be * 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
* joined * joined
* 'jointype': type of join (inner/outer) * 'jointype': type of join (inner/outer)
...@@ -256,27 +258,20 @@ find_join_rel(Query *root, Relids relids) ...@@ -256,27 +258,20 @@ find_join_rel(Query *root, Relids relids)
*/ */
RelOptInfo * RelOptInfo *
build_join_rel(Query *root, build_join_rel(Query *root,
List *joinrelids,
RelOptInfo *outer_rel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, RelOptInfo *inner_rel,
JoinType jointype, JoinType jointype,
List **restrictlist_ptr) List **restrictlist_ptr)
{ {
List *joinrelids;
RelOptInfo *joinrel; RelOptInfo *joinrel;
List *restrictlist; List *restrictlist;
List *new_outer_tlist; List *new_outer_tlist;
List *new_inner_tlist; List *new_inner_tlist;
/* We should never try to join two overlapping sets of rels. */
Assert(nonoverlap_setsi(outer_rel->relids, inner_rel->relids));
/* /*
* See if we already have a joinrel for this set of base rels. * See if we already have a joinrel for this set of base rels.
*
* nconc(listCopy(x), y) is an idiom for making a new list without
* changing either input list.
*/ */
joinrelids = nconc(listCopy(outer_rel->relids), inner_rel->relids);
joinrel = find_join_rel(root, joinrelids); joinrel = find_join_rel(root, joinrelids);
if (joinrel) if (joinrel)
...@@ -299,13 +294,14 @@ build_join_rel(Query *root, ...@@ -299,13 +294,14 @@ build_join_rel(Query *root,
*/ */
joinrel = makeNode(RelOptInfo); joinrel = makeNode(RelOptInfo);
joinrel->reloptkind = RELOPT_JOINREL; joinrel->reloptkind = RELOPT_JOINREL;
joinrel->relids = joinrelids; joinrel->relids = listCopy(joinrelids);
joinrel->rows = 0; joinrel->rows = 0;
joinrel->width = 0; joinrel->width = 0;
joinrel->targetlist = NIL; joinrel->targetlist = NIL;
joinrel->pathlist = NIL; joinrel->pathlist = NIL;
joinrel->cheapest_startup_path = NULL; joinrel->cheapest_startup_path = NULL;
joinrel->cheapest_total_path = NULL; joinrel->cheapest_total_path = NULL;
joinrel->cheapest_unique_path = NULL;
joinrel->pruneable = true; joinrel->pruneable = true;
joinrel->rtekind = RTE_JOIN; joinrel->rtekind = RTE_JOIN;
joinrel->indexlist = NIL; joinrel->indexlist = NIL;
...@@ -557,7 +553,7 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel, ...@@ -557,7 +553,7 @@ subbuild_joinrel_joinlist(RelOptInfo *joinrel,
*/ */
JoinInfo *new_joininfo; JoinInfo *new_joininfo;
new_joininfo = find_joininfo_node(joinrel, new_unjoined_relids); new_joininfo = make_joininfo_node(joinrel, new_unjoined_relids);
new_joininfo->jinfo_restrictinfo = new_joininfo->jinfo_restrictinfo =
set_union(new_joininfo->jinfo_restrictinfo, set_union(new_joininfo->jinfo_restrictinfo,
joininfo->jinfo_restrictinfo); joininfo->jinfo_restrictinfo);
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.53 2002/12/12 15:49:32 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.54 2003/01/20 18:54:57 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -255,3 +255,25 @@ get_sortgroupclause_expr(SortClause *sortClause, List *targetList) ...@@ -255,3 +255,25 @@ get_sortgroupclause_expr(SortClause *sortClause, List *targetList)
return (Node *) tle->expr; return (Node *) tle->expr;
} }
/*
* get_sortgrouplist_exprs
* Given a list of SortClauses (or GroupClauses), build a list
* of the referenced targetlist expressions.
*/
List *
get_sortgrouplist_exprs(List *sortClauses, List *targetList)
{
List *result = NIL;
List *l;
foreach(l, sortClauses)
{
SortClause *sortcl = (SortClause *) lfirst(l);
Node *sortexpr;
sortexpr = get_sortgroupclause_expr(sortcl, targetList);
result = lappend(result, sortexpr);
}
return result;
}
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.46 2003/01/17 02:01:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/optimizer/util/var.c,v 1.47 2003/01/20 18:54:58 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "nodes/plannodes.h" #include "nodes/plannodes.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/prep.h"
#include "optimizer/var.h" #include "optimizer/var.h"
#include "parser/parsetree.h" #include "parser/parsetree.h"
...@@ -41,7 +42,7 @@ typedef struct ...@@ -41,7 +42,7 @@ typedef struct
typedef struct typedef struct
{ {
List *rtable; Query *root;
int sublevels_up; int sublevels_up;
} flatten_join_alias_vars_context; } flatten_join_alias_vars_context;
...@@ -50,10 +51,13 @@ static bool pull_varnos_walker(Node *node, ...@@ -50,10 +51,13 @@ static bool pull_varnos_walker(Node *node,
static bool contain_var_reference_walker(Node *node, static bool contain_var_reference_walker(Node *node,
contain_var_reference_context *context); contain_var_reference_context *context);
static bool contain_var_clause_walker(Node *node, void *context); static bool contain_var_clause_walker(Node *node, void *context);
static bool contain_vars_of_level_walker(Node *node, int *sublevels_up);
static bool contain_vars_above_level_walker(Node *node, int *sublevels_up);
static bool pull_var_clause_walker(Node *node, static bool pull_var_clause_walker(Node *node,
pull_var_clause_context *context); pull_var_clause_context *context);
static Node *flatten_join_alias_vars_mutator(Node *node, static Node *flatten_join_alias_vars_mutator(Node *node,
flatten_join_alias_vars_context *context); flatten_join_alias_vars_context *context);
static List *alias_rtindex_list(Query *root, List *rtlist);
/* /*
...@@ -224,6 +228,103 @@ contain_var_clause_walker(Node *node, void *context) ...@@ -224,6 +228,103 @@ contain_var_clause_walker(Node *node, void *context)
return expression_tree_walker(node, contain_var_clause_walker, context); return expression_tree_walker(node, contain_var_clause_walker, context);
} }
/*
* contain_vars_of_level
* Recursively scan a clause to discover whether it contains any Var nodes
* of the specified query level.
*
* Returns true if any such Var found.
*
* Will recurse into sublinks. Also, may be invoked directly on a Query.
*/
bool
contain_vars_of_level(Node *node, int levelsup)
{
int sublevels_up = levelsup;
return query_or_expression_tree_walker(node,
contain_vars_of_level_walker,
(void *) &sublevels_up,
0);
}
static bool
contain_vars_of_level_walker(Node *node, int *sublevels_up)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
if (((Var *) node)->varlevelsup == *sublevels_up)
return true; /* abort tree traversal and return true */
}
if (IsA(node, Query))
{
/* Recurse into subselects */
bool result;
(*sublevels_up)++;
result = query_tree_walker((Query *) node,
contain_vars_of_level_walker,
(void *) sublevels_up,
0);
(*sublevels_up)--;
return result;
}
return expression_tree_walker(node,
contain_vars_of_level_walker,
(void *) sublevels_up);
}
/*
* contain_vars_above_level
* Recursively scan a clause to discover whether it contains any Var nodes
* above the specified query level. (For example, pass zero to detect
* all nonlocal Vars.)
*
* Returns true if any such Var found.
*
* Will recurse into sublinks. Also, may be invoked directly on a Query.
*/
bool
contain_vars_above_level(Node *node, int levelsup)
{
int sublevels_up = levelsup;
return query_or_expression_tree_walker(node,
contain_vars_above_level_walker,
(void *) &sublevels_up,
0);
}
static bool
contain_vars_above_level_walker(Node *node, int *sublevels_up)
{
if (node == NULL)
return false;
if (IsA(node, Var))
{
if (((Var *) node)->varlevelsup > *sublevels_up)
return true; /* abort tree traversal and return true */
}
if (IsA(node, Query))
{
/* Recurse into subselects */
bool result;
(*sublevels_up)++;
result = query_tree_walker((Query *) node,
contain_vars_above_level_walker,
(void *) sublevels_up,
0);
(*sublevels_up)--;
return result;
}
return expression_tree_walker(node,
contain_vars_above_level_walker,
(void *) sublevels_up);
}
/* /*
* pull_var_clause * pull_var_clause
...@@ -277,11 +378,11 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) ...@@ -277,11 +378,11 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context)
* to be applied directly to a Query node. * to be applied directly to a Query node.
*/ */
Node * Node *
flatten_join_alias_vars(Node *node, List *rtable) flatten_join_alias_vars(Query *root, Node *node)
{ {
flatten_join_alias_vars_context context; flatten_join_alias_vars_context context;
context.rtable = rtable; context.root = root;
context.sublevels_up = 0; context.sublevels_up = 0;
return flatten_join_alias_vars_mutator(node, &context); return flatten_join_alias_vars_mutator(node, &context);
...@@ -301,7 +402,7 @@ flatten_join_alias_vars_mutator(Node *node, ...@@ -301,7 +402,7 @@ flatten_join_alias_vars_mutator(Node *node,
if (var->varlevelsup != context->sublevels_up) if (var->varlevelsup != context->sublevels_up)
return node; /* no need to copy, really */ return node; /* no need to copy, really */
rte = rt_fetch(var->varno, context->rtable); rte = rt_fetch(var->varno, context->root->rtable);
if (rte->rtekind != RTE_JOIN) if (rte->rtekind != RTE_JOIN)
return node; return node;
Assert(var->varattno > 0); Assert(var->varattno > 0);
...@@ -309,6 +410,24 @@ flatten_join_alias_vars_mutator(Node *node, ...@@ -309,6 +410,24 @@ flatten_join_alias_vars_mutator(Node *node,
/* expand it; recurse in case join input is itself a join */ /* expand it; recurse in case join input is itself a join */
return flatten_join_alias_vars_mutator(newvar, context); return flatten_join_alias_vars_mutator(newvar, context);
} }
if (IsA(node, InClauseInfo))
{
/* Copy the InClauseInfo node with correct mutation of subnodes */
InClauseInfo *ininfo;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
flatten_join_alias_vars_mutator,
(void *) context);
/* now fix InClauseInfo's rtindex lists */
if (context->sublevels_up == 0)
{
ininfo->lefthand = alias_rtindex_list(context->root,
ininfo->lefthand);
ininfo->righthand = alias_rtindex_list(context->root,
ininfo->righthand);
}
return (Node *) ininfo;
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
...@@ -329,3 +448,27 @@ flatten_join_alias_vars_mutator(Node *node, ...@@ -329,3 +448,27 @@ flatten_join_alias_vars_mutator(Node *node,
return expression_tree_mutator(node, flatten_join_alias_vars_mutator, return expression_tree_mutator(node, flatten_join_alias_vars_mutator,
(void *) context); (void *) context);
} }
/*
* alias_rtindex_list: in a list of RT indexes, replace joins by their
* underlying base relids
*/
static List *
alias_rtindex_list(Query *root, List *rtlist)
{
List *result = NIL;
List *l;
foreach(l, rtlist)
{
int rtindex = lfirsti(l);
RangeTblEntry *rte;
rte = rt_fetch(rtindex, root->rtable);
if (rte->rtekind == RTE_JOIN)
result = nconc(result, get_relids_for_join(root, rtindex));
else
result = lappendi(result, rtindex);
}
return result;
}
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.69 2003/01/17 02:01:16 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.70 2003/01/20 18:54:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -90,8 +90,8 @@ checkExprHasSubLink_walker(Node *node, void *context) ...@@ -90,8 +90,8 @@ checkExprHasSubLink_walker(Node *node, void *context)
* *
* Find all Var nodes in the given tree with varlevelsup == sublevels_up, * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
* and increment their varno fields (rangetable indexes) by 'offset'. * and increment their varno fields (rangetable indexes) by 'offset'.
* The varnoold fields are adjusted similarly. Also, RangeTblRef and * The varnoold fields are adjusted similarly. Also, adjust other nodes
* JoinExpr nodes in join trees and setOp trees are adjusted. * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
* *
* NOTE: although this has the form of a walker, we cheat and modify the * NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. The given expression tree should have been copied * nodes in-place. The given expression tree should have been copied
...@@ -137,6 +137,25 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) ...@@ -137,6 +137,25 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
j->rtindex += context->offset; j->rtindex += context->offset;
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, InClauseInfo))
{
InClauseInfo *ininfo = (InClauseInfo *) node;
if (context->sublevels_up == 0)
{
List *rt;
foreach(rt, ininfo->lefthand)
{
lfirsti(rt) += context->offset;
}
foreach(rt, ininfo->righthand)
{
lfirsti(rt) += context->offset;
}
}
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -196,8 +215,8 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) ...@@ -196,8 +215,8 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up)
* *
* Find all Var nodes in the given tree belonging to a specific relation * Find all Var nodes in the given tree belonging to a specific relation
* (identified by sublevels_up and rt_index), and change their varno fields * (identified by sublevels_up and rt_index), and change their varno fields
* to 'new_index'. The varnoold fields are changed too. Also, RangeTblRef * to 'new_index'. The varnoold fields are changed too. Also, adjust other
* and JoinExpr nodes in join trees and setOp trees are adjusted. * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
* *
* NOTE: although this has the form of a walker, we cheat and modify the * NOTE: although this has the form of a walker, we cheat and modify the
* nodes in-place. The given expression tree should have been copied * nodes in-place. The given expression tree should have been copied
...@@ -247,6 +266,27 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) ...@@ -247,6 +266,27 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
j->rtindex = context->new_index; j->rtindex = context->new_index;
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, InClauseInfo))
{
InClauseInfo *ininfo = (InClauseInfo *) node;
if (context->sublevels_up == 0)
{
List *rt;
foreach(rt, ininfo->lefthand)
{
if (lfirsti(rt) == context->rt_index)
lfirsti(rt) = context->new_index;
}
foreach(rt, ininfo->righthand)
{
if (lfirsti(rt) == context->rt_index)
lfirsti(rt) = context->new_index;
}
}
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
...@@ -423,6 +463,16 @@ rangeTableEntry_used_walker(Node *node, ...@@ -423,6 +463,16 @@ rangeTableEntry_used_walker(Node *node,
return true; return true;
/* fall through to examine children */ /* fall through to examine children */
} }
if (IsA(node, InClauseInfo))
{
InClauseInfo *ininfo = (InClauseInfo *) node;
if (context->sublevels_up == 0 &&
(intMember(context->rt_index, ininfo->lefthand) ||
intMember(context->rt_index, ininfo->righthand)))
return true;
/* fall through to examine children */
}
if (IsA(node, Query)) if (IsA(node, Query))
{ {
/* Recurse into subselects */ /* Recurse into subselects */
......
...@@ -15,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.126 2003/01/15 19:35:44 tgl Exp $ * $Header: /cvsroot/pgsql/src/backend/utils/adt/selfuncs.c,v 1.127 2003/01/20 18:54:59 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -1825,8 +1825,7 @@ mergejoinscansel(Query *root, Node *clause, ...@@ -1825,8 +1825,7 @@ mergejoinscansel(Query *root, Node *clause,
* *
* Inputs: * Inputs:
* root - the query * root - the query
* groupClauses - list of GroupClauses (or SortClauses for the DISTINCT * groupExprs - list of expressions being grouped by
* case, but those are equivalent structs)
* input_rows - number of rows estimated to arrive at the group/unique * input_rows - number of rows estimated to arrive at the group/unique
* filter step * filter step
* *
...@@ -1867,7 +1866,7 @@ mergejoinscansel(Query *root, Node *clause, ...@@ -1867,7 +1866,7 @@ mergejoinscansel(Query *root, Node *clause,
* do better). * do better).
*/ */
double double
estimate_num_groups(Query *root, List *groupClauses, double input_rows) estimate_num_groups(Query *root, List *groupExprs, double input_rows)
{ {
List *allvars = NIL; List *allvars = NIL;
List *varinfos = NIL; List *varinfos = NIL;
...@@ -1879,14 +1878,12 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows) ...@@ -1879,14 +1878,12 @@ estimate_num_groups(Query *root, List *groupClauses, double input_rows)
} MyVarInfo; } MyVarInfo;
/* We should not be called unless query has GROUP BY (or DISTINCT) */ /* We should not be called unless query has GROUP BY (or DISTINCT) */
Assert(groupClauses != NIL); Assert(groupExprs != NIL);
/* Step 1: get the unique Vars used */ /* Step 1: get the unique Vars used */
foreach(l, groupClauses) foreach(l, groupExprs)
{ {
GroupClause *grpcl = (GroupClause *) lfirst(l); Node *groupexpr = (Node *) lfirst(l);
Node *groupexpr = get_sortgroupclause_expr(grpcl,
root->targetList);
List *varshere; List *varshere;
varshere = pull_var_clause(groupexpr, false); varshere = pull_var_clause(groupexpr, false);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: nodes.h,v 1.134 2002/12/16 16:22:46 tgl Exp $ * $Id: nodes.h,v 1.135 2003/01/20 18:55:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -152,10 +152,12 @@ typedef enum NodeTag ...@@ -152,10 +152,12 @@ typedef enum NodeTag
T_AppendPath, T_AppendPath,
T_ResultPath, T_ResultPath,
T_MaterialPath, T_MaterialPath,
T_UniquePath,
T_PathKeyItem, T_PathKeyItem,
T_RestrictInfo, T_RestrictInfo,
T_JoinInfo, T_JoinInfo,
T_InnerIndexscanInfo, T_InnerIndexscanInfo,
T_InClauseInfo,
/* /*
* TAGS FOR MEMORY NODES (memnodes.h) * TAGS FOR MEMORY NODES (memnodes.h)
...@@ -408,11 +410,20 @@ typedef enum JoinType ...@@ -408,11 +410,20 @@ typedef enum JoinType
* join in the executor. (The planner must convert it to an Append * join in the executor. (The planner must convert it to an Append
* plan.) * plan.)
*/ */
JOIN_UNION JOIN_UNION,
/* /*
* Eventually we will have some additional join types for efficient * These are used for queries like WHERE foo IN (SELECT bar FROM ...).
* support of queries like WHERE foo IN (SELECT bar FROM ...). * Only JOIN_IN is actually implemented in the executor; the others
* are defined for internal use in the planner.
*/
JOIN_IN, /* at most one result per outer row */
JOIN_REVERSE_IN, /* at most one result per inner row */
JOIN_UNIQUE_OUTER, /* outer path must be made unique */
JOIN_UNIQUE_INNER /* inner path must be made unique */
/*
* We might need additional join types someday.
*/ */
} JoinType; } JoinType;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: parsenodes.h,v 1.225 2003/01/06 00:31:45 tgl Exp $ * $Id: parsenodes.h,v 1.226 2003/01/20 18:55:00 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -101,6 +101,7 @@ typedef struct Query ...@@ -101,6 +101,7 @@ typedef struct Query
List *join_rel_list; /* list of join-relation RelOptInfos */ List *join_rel_list; /* list of join-relation RelOptInfos */
List *equi_key_list; /* list of lists of equijoined List *equi_key_list; /* list of lists of equijoined
* PathKeyItems */ * PathKeyItems */
List *in_info_list; /* list of InClauseInfos */
List *query_pathkeys; /* desired pathkeys for query_planner() */ List *query_pathkeys; /* desired pathkeys for query_planner() */
bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */ bool hasJoinRTEs; /* true if any RTEs are RTE_JOIN kind */
} Query; } Query;
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pg_list.h,v 1.30 2002/11/24 21:52:15 tgl Exp $ * $Id: pg_list.h,v 1.31 2003/01/20 18:55:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -145,7 +145,8 @@ extern List *set_intersecti(List *list1, List *list2); ...@@ -145,7 +145,8 @@ extern List *set_intersecti(List *list1, List *list2);
extern bool equali(List *list1, List *list2); extern bool equali(List *list1, List *list2);
extern bool sameseti(List *list1, List *list2); extern bool sameseti(List *list1, List *list2);
extern bool nonoverlap_setsi(List *list1, List *list2); extern bool overlap_setsi(List *list1, List *list2);
#define nonoverlap_setsi(list1, list2) (!overlap_setsi(list1, list2))
extern bool is_subseti(List *list1, List *list2); extern bool is_subseti(List *list1, List *list2);
extern void freeList(List *list); extern void freeList(List *list);
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: relation.h,v 1.76 2003/01/15 19:35:44 tgl Exp $ * $Id: relation.h,v 1.77 2003/01/20 18:55:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -97,6 +97,8 @@ typedef struct QualCost ...@@ -97,6 +97,8 @@ typedef struct QualCost
* (regardless of its ordering) * (regardless of its ordering)
* cheapest_total_path - the pathlist member with lowest total cost * cheapest_total_path - the pathlist member with lowest total cost
* (regardless of its ordering) * (regardless of its ordering)
* cheapest_unique_path - for caching cheapest path to produce unique
* (no duplicates) output from relation
* pruneable - flag to let the planner know whether it can prune the * pruneable - flag to let the planner know whether it can prune the
* pathlist of this RelOptInfo or not. * pathlist of this RelOptInfo or not.
* *
...@@ -183,6 +185,7 @@ typedef struct RelOptInfo ...@@ -183,6 +185,7 @@ typedef struct RelOptInfo
List *pathlist; /* Path structures */ List *pathlist; /* Path structures */
struct Path *cheapest_startup_path; struct Path *cheapest_startup_path;
struct Path *cheapest_total_path; struct Path *cheapest_total_path;
struct Path *cheapest_unique_path;
bool pruneable; bool pruneable;
/* information about a base rel (not set for join rels!) */ /* information about a base rel (not set for join rels!) */
...@@ -403,6 +406,23 @@ typedef struct MaterialPath ...@@ -403,6 +406,23 @@ typedef struct MaterialPath
Path *subpath; Path *subpath;
} MaterialPath; } MaterialPath;
/*
* UniquePath represents elimination of distinct rows from the output of
* its subpath.
*
* This is unlike the other Path nodes in that it can actually generate
* two different plans: either hash-based or sort-based implementation.
* The decision is sufficiently localized that it's not worth having two
* separate Path node types.
*/
typedef struct UniquePath
{
Path path;
Path *subpath;
bool use_hash;
double rows; /* estimated number of result tuples */
} UniquePath;
/* /*
* All join-type paths share these fields. * All join-type paths share these fields.
*/ */
...@@ -649,4 +669,25 @@ typedef struct InnerIndexscanInfo ...@@ -649,4 +669,25 @@ typedef struct InnerIndexscanInfo
Path *best_innerpath; /* best inner indexscan, or NULL if none */ Path *best_innerpath; /* best inner indexscan, or NULL if none */
} InnerIndexscanInfo; } InnerIndexscanInfo;
/*
* IN clause info.
*
* When we convert top-level IN quals into join operations, we must restrict
* the order of joining and use special join methods at some join points.
* We record information about each such IN clause in an InClauseInfo struct.
* These structs are kept in the Query node's in_info_list.
*/
typedef struct InClauseInfo
{
NodeTag type;
List *lefthand; /* base relids in lefthand expressions */
List *righthand; /* base relids coming from the subselect */
List *sub_targetlist; /* targetlist of original RHS subquery */
/*
* Note: sub_targetlist is just a list of Vars or expressions;
* it does not contain TargetEntry nodes.
*/
} InClauseInfo;
#endif /* RELATION_H */ #endif /* RELATION_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: joininfo.h,v 1.21 2002/06/20 20:29:51 momjian Exp $ * $Id: joininfo.h,v 1.22 2003/01/20 18:55:04 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,5 +17,6 @@ ...@@ -17,5 +17,6 @@
#include "nodes/relation.h" #include "nodes/relation.h"
extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, List *join_relids); extern JoinInfo *find_joininfo_node(RelOptInfo *this_rel, List *join_relids);
extern JoinInfo *make_joininfo_node(RelOptInfo *this_rel, List *join_relids);
#endif /* JOININFO_H */ #endif /* JOININFO_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: pathnode.h,v 1.47 2003/01/15 19:35:47 tgl Exp $ * $Id: pathnode.h,v 1.48 2003/01/20 18:55:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -38,6 +38,8 @@ extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths); ...@@ -38,6 +38,8 @@ extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath, extern ResultPath *create_result_path(RelOptInfo *rel, Path *subpath,
List *constantqual); List *constantqual);
extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath); extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
extern UniquePath *create_unique_path(Query *root, RelOptInfo *rel,
Path *subpath);
extern Path *create_subqueryscan_path(RelOptInfo *rel); extern Path *create_subqueryscan_path(RelOptInfo *rel);
extern Path *create_functionscan_path(Query *root, RelOptInfo *rel); extern Path *create_functionscan_path(Query *root, RelOptInfo *rel);
...@@ -75,6 +77,7 @@ extern void build_base_rel(Query *root, int relid); ...@@ -75,6 +77,7 @@ extern void build_base_rel(Query *root, int relid);
extern RelOptInfo *build_other_rel(Query *root, int relid); extern RelOptInfo *build_other_rel(Query *root, int relid);
extern RelOptInfo *find_base_rel(Query *root, int relid); extern RelOptInfo *find_base_rel(Query *root, int relid);
extern RelOptInfo *build_join_rel(Query *root, extern RelOptInfo *build_join_rel(Query *root,
List *joinrelids,
RelOptInfo *outer_rel, RelOptInfo *outer_rel,
RelOptInfo *inner_rel, RelOptInfo *inner_rel,
JoinType jointype, JoinType jointype,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planmain.h,v 1.66 2003/01/15 23:10:32 tgl Exp $ * $Id: planmain.h,v 1.67 2003/01/20 18:55:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,6 +32,8 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual, ...@@ -32,6 +32,8 @@ extern SubqueryScan *make_subqueryscan(List *qptlist, List *qpqual,
extern Append *make_append(List *appendplans, bool isTarget, List *tlist); extern Append *make_append(List *appendplans, bool isTarget, List *tlist);
extern Sort *make_sort(Query *root, List *tlist, extern Sort *make_sort(Query *root, List *tlist,
Plan *lefttree, int keycount); Plan *lefttree, int keycount);
extern Sort *make_sort_from_sortclauses(Query *root, List *tlist,
Plan *lefttree, List *sortcls);
extern Agg *make_agg(Query *root, List *tlist, List *qual, extern Agg *make_agg(Query *root, List *tlist, List *qual,
AggStrategy aggstrategy, AggStrategy aggstrategy,
int numGroupCols, AttrNumber *grpColIdx, int numGroupCols, AttrNumber *grpColIdx,
......
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: planner.h,v 1.24 2002/06/20 20:29:51 momjian Exp $ * $Id: planner.h,v 1.25 2003/01/20 18:55:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -21,7 +21,4 @@ ...@@ -21,7 +21,4 @@
extern Plan *planner(Query *parse); extern Plan *planner(Query *parse);
extern Plan *subquery_planner(Query *parse, double tuple_fraction); extern Plan *subquery_planner(Query *parse, double tuple_fraction);
extern Plan *make_sortplan(Query *parse, List *tlist,
Plan *plannode, List *sortcls);
#endif /* PLANNER_H */ #endif /* PLANNER_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: prep.h,v 1.33 2002/08/29 16:03:49 tgl Exp $ * $Id: prep.h,v 1.34 2003/01/20 18:55:05 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -17,6 +17,16 @@ ...@@ -17,6 +17,16 @@
#include "nodes/parsenodes.h" #include "nodes/parsenodes.h"
#include "nodes/plannodes.h" #include "nodes/plannodes.h"
/*
* prototypes for prepjointree.c
*/
extern Node *pull_up_IN_clauses(Query *parse, Node *node);
extern Node *pull_up_subqueries(Query *parse, Node *jtnode,
bool below_outer_join);
extern Node *preprocess_jointree(Query *parse, Node *jtnode);
extern List *get_relids_in_jointree(Node *jtnode);
extern List *get_relids_for_join(Query *parse, int joinrelid);
/* /*
* prototypes for prepqual.c * prototypes for prepqual.c
*/ */
......
...@@ -2,6 +2,11 @@ ...@@ -2,6 +2,11 @@
* *
* subselect.h * subselect.h
* *
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: subselect.h,v 1.17 2003/01/20 18:55:05 tgl Exp $
*
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef SUBSELECT_H #ifndef SUBSELECT_H
...@@ -14,8 +19,9 @@ extern List *PlannerInitPlan; /* init subplans for current query */ ...@@ -14,8 +19,9 @@ extern List *PlannerInitPlan; /* init subplans for current query */
extern List *PlannerParamVar; /* to get Var from Param->paramid */ extern List *PlannerParamVar; /* to get Var from Param->paramid */
extern int PlannerPlanId; /* to assign unique ID to subquery plans */ extern int PlannerPlanId; /* to assign unique ID to subquery plans */
extern List *SS_finalize_plan(Plan *plan, List *rtable); extern Node *convert_IN_to_join(Query *parse, SubLink *sublink);
extern Node *SS_replace_correlation_vars(Node *expr); extern Node *SS_replace_correlation_vars(Node *expr);
extern Node *SS_process_sublinks(Node *expr, bool isQual); extern Node *SS_process_sublinks(Node *expr, bool isQual);
extern List *SS_finalize_plan(Plan *plan, List *rtable);
#endif /* SUBSELECT_H */ #endif /* SUBSELECT_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: tlist.h,v 1.32 2002/06/20 20:29:51 momjian Exp $ * $Id: tlist.h,v 1.33 2003/01/20 18:55:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -32,5 +32,7 @@ extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause, ...@@ -32,5 +32,7 @@ extern TargetEntry *get_sortgroupclause_tle(SortClause *sortClause,
List *targetList); List *targetList);
extern Node *get_sortgroupclause_expr(SortClause *sortClause, extern Node *get_sortgroupclause_expr(SortClause *sortClause,
List *targetList); List *targetList);
extern List *get_sortgrouplist_exprs(List *sortClauses,
List *targetList);
#endif /* TLIST_H */ #endif /* TLIST_H */
...@@ -7,14 +7,14 @@ ...@@ -7,14 +7,14 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: var.h,v 1.24 2003/01/15 19:35:47 tgl Exp $ * $Id: var.h,v 1.25 2003/01/20 18:55:06 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#ifndef VAR_H #ifndef VAR_H
#define VAR_H #define VAR_H
#include "nodes/primnodes.h" #include "nodes/parsenodes.h"
extern List *pull_varnos(Node *node); extern List *pull_varnos(Node *node);
...@@ -22,7 +22,9 @@ extern bool contain_var_reference(Node *node, int varno, int varattno, ...@@ -22,7 +22,9 @@ extern bool contain_var_reference(Node *node, int varno, int varattno,
int levelsup); int levelsup);
extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup); extern bool contain_whole_tuple_var(Node *node, int varno, int levelsup);
extern bool contain_var_clause(Node *node); extern bool contain_var_clause(Node *node);
extern bool contain_vars_of_level(Node *node, int levelsup);
extern bool contain_vars_above_level(Node *node, int levelsup);
extern List *pull_var_clause(Node *node, bool includeUpperVars); extern List *pull_var_clause(Node *node, bool includeUpperVars);
extern Node *flatten_join_alias_vars(Node *node, List *rtable); extern Node *flatten_join_alias_vars(Query *root, Node *node);
#endif /* VAR_H */ #endif /* VAR_H */
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $Id: selfuncs.h,v 1.10 2002/11/19 23:22:00 tgl Exp $ * $Id: selfuncs.h,v 1.11 2003/01/20 18:55:07 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -75,7 +75,7 @@ extern void mergejoinscansel(Query *root, Node *clause, ...@@ -75,7 +75,7 @@ extern void mergejoinscansel(Query *root, Node *clause,
Selectivity *leftscan, Selectivity *leftscan,
Selectivity *rightscan); Selectivity *rightscan);
extern double estimate_num_groups(Query *root, List *groupClauses, extern double estimate_num_groups(Query *root, List *groupExprs,
double input_rows); double input_rows);
extern Datum btcostestimate(PG_FUNCTION_ARGS); extern Datum btcostestimate(PG_FUNCTION_ARGS);
......
...@@ -58,10 +58,10 @@ SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL ...@@ -58,10 +58,10 @@ SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL
six | Uncorrelated Field six | Uncorrelated Field
-----+-------------------- -----+--------------------
| 1 | 1
| 2
| 3
| 1 | 1
| 2 | 2
| 2
| 3
| 3 | 3
(6 rows) (6 rows)
...@@ -71,10 +71,10 @@ SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL ...@@ -71,10 +71,10 @@ SELECT '' AS six, f1 AS "Uncorrelated Field" FROM SUBSELECT_TBL
six | Uncorrelated Field six | Uncorrelated Field
-----+-------------------- -----+--------------------
| 1 | 1
| 2
| 3
| 1 | 1
| 2 | 2
| 2
| 3
| 3 | 3
(6 rows) (6 rows)
...@@ -134,10 +134,10 @@ SELECT '' AS five, f1 AS "Correlated Field" ...@@ -134,10 +134,10 @@ SELECT '' AS five, f1 AS "Correlated Field"
WHERE f3 IS NOT NULL); WHERE f3 IS NOT NULL);
five | Correlated Field five | Correlated Field
------+------------------ ------+------------------
| 2
| 3
| 1 | 1
| 2 | 2
| 2
| 3
| 3 | 3
(5 rows) (5 rows)
......
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