Commit e006a24a authored by Tom Lane's avatar Tom Lane

Implement SEMI and ANTI joins in the planner and executor. (Semijoins replace

the old JOIN_IN code, but antijoins are new functionality.)  Teach the planner
to convert appropriate EXISTS and NOT EXISTS subqueries into semi and anti
joins respectively.  Also, LEFT JOINs with suitable upper-level IS NULL
filters are recognized as being anti joins.  Unify the InClauseInfo and
OuterJoinInfo infrastructure into "SpecialJoinInfo".  With that change,
it becomes possible to associate a SpecialJoinInfo with every join attempt,
which permits some cleanup of join selectivity estimation.  That needs to be
taken much further than this patch does, but the next step is to change the
API for oprjoin selectivity functions, which seems like material for a
separate patch.  So for the moment the output size estimates for semi and
especially anti joins are quite bogus.
parent ef1c807c
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.26 2008/04/14 17:05:32 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/indexam.sgml,v 2.27 2008/08/14 18:47:58 tgl Exp $ -->
<chapter id="indexam">
<title>Index Access Method Interface Definition</title>
......@@ -879,7 +879,8 @@ amcostestimate (PlannerInfo *root,
<programlisting>
*indexSelectivity = clauselist_selectivity(root, indexQuals,
index-&gt;rel-&gt;relid, JOIN_INNER);
index-&gt;rel-&gt;relid,
JOIN_INNER, NULL);
</programlisting>
</para>
</step>
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1994-5, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.176 2008/08/07 03:04:03 tgl Exp $
* $PostgreSQL: pgsql/src/backend/commands/explain.c,v 1.177 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -450,8 +450,11 @@ explain_outNode(StringInfo str,
case JOIN_RIGHT:
pname = "Nested Loop Right Join";
break;
case JOIN_IN:
pname = "Nested Loop IN Join";
case JOIN_SEMI:
pname = "Nested Loop Semi Join";
break;
case JOIN_ANTI:
pname = "Nested Loop Anti Join";
break;
default:
pname = "Nested Loop ??? Join";
......@@ -473,8 +476,11 @@ explain_outNode(StringInfo str,
case JOIN_RIGHT:
pname = "Merge Right Join";
break;
case JOIN_IN:
pname = "Merge IN Join";
case JOIN_SEMI:
pname = "Merge Semi Join";
break;
case JOIN_ANTI:
pname = "Merge Anti Join";
break;
default:
pname = "Merge ??? Join";
......@@ -496,8 +502,11 @@ explain_outNode(StringInfo str,
case JOIN_RIGHT:
pname = "Hash Right Join";
break;
case JOIN_IN:
pname = "Hash IN Join";
case JOIN_SEMI:
pname = "Hash Semi Join";
break;
case JOIN_ANTI:
pname = "Hash Anti Join";
break;
default:
pname = "Hash ??? Join";
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.93 2008/01/01 19:45:49 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeHashjoin.c,v 1.94 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -22,6 +22,9 @@
#include "utils/memutils.h"
/* Returns true for JOIN_LEFT and JOIN_ANTI jointypes */
#define HASHJOIN_IS_OUTER(hjstate) ((hjstate)->hj_NullInnerTupleSlot != NULL)
static TupleTableSlot *ExecHashJoinOuterGetTuple(PlanState *outerNode,
HashJoinState *hjstate,
uint32 *hashvalue);
......@@ -89,14 +92,6 @@ ExecHashJoin(HashJoinState *node)
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
* storage allocated in the previous tuple cycle. Note this can't happen
......@@ -129,7 +124,7 @@ ExecHashJoin(HashJoinState *node)
* outer plan node. If we succeed, we have to stash it away for later
* consumption by ExecHashJoinOuterGetTuple.
*/
if (node->js.jointype == JOIN_LEFT ||
if (HASHJOIN_IS_OUTER(node) ||
(outerNode->plan->startup_cost < hashNode->ps.plan->total_cost &&
!node->hj_OuterNotEmpty))
{
......@@ -162,7 +157,7 @@ ExecHashJoin(HashJoinState *node)
* If the inner relation is completely empty, and we're not doing an
* outer join, we can quit without scanning the outer relation.
*/
if (hashtable->totalTuples == 0 && node->js.jointype != JOIN_LEFT)
if (hashtable->totalTuples == 0 && !HASHJOIN_IS_OUTER(node))
return NULL;
/*
......@@ -263,27 +258,41 @@ ExecHashJoin(HashJoinState *node)
{
node->hj_MatchedOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
/* In an antijoin, we never return a matched tuple */
if (node->js.jointype == JOIN_ANTI)
{
node->hj_NeedNewOuter = true;
break; /* out of loop over hash bucket */
}
else
{
TupleTableSlot *result;
/*
* In a semijoin, we'll consider returning the first match,
* but after that we're done with this outer tuple.
*/
if (node->js.jointype == JOIN_SEMI)
node->hj_NeedNewOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
{
TupleTableSlot *result;
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
}
/*
* If we didn't return a tuple, may need to set NeedNewOuter
*/
if (node->js.jointype == JOIN_IN)
{
node->hj_NeedNewOuter = true;
break; /* out of loop over hash bucket */
/*
* If semijoin and we didn't return the tuple, we're still
* done with this outer tuple.
*/
if (node->js.jointype == JOIN_SEMI)
break; /* out of loop over hash bucket */
}
}
}
......@@ -296,7 +305,7 @@ ExecHashJoin(HashJoinState *node)
node->hj_NeedNewOuter = true;
if (!node->hj_MatchedOuter &&
node->js.jointype == JOIN_LEFT)
HASHJOIN_IS_OUTER(node))
{
/*
* We are doing an outer join and there were no join matches for
......@@ -305,7 +314,7 @@ ExecHashJoin(HashJoinState *node)
*/
econtext->ecxt_innertuple = node->hj_NullInnerTupleSlot;
if (ExecQual(otherqual, econtext, false))
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
{
/*
* qualification was satisfied so we project and return the
......@@ -398,12 +407,14 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)
ExecInitResultTupleSlot(estate, &hjstate->js.ps);
hjstate->hj_OuterTupleSlot = ExecInitExtraTupleSlot(estate);
/* note: HASHJOIN_IS_OUTER macro depends on this initialization */
switch (node->join.jointype)
{
case JOIN_INNER:
case JOIN_IN:
case JOIN_SEMI:
break;
case JOIN_LEFT:
case JOIN_ANTI:
hjstate->hj_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(innerPlanState(hjstate)));
......@@ -570,7 +581,7 @@ ExecHashJoinOuterGetTuple(PlanState *outerNode,
if (ExecHashGetHashValue(hashtable, econtext,
hjstate->hj_OuterHashKeys,
true, /* outer tuple */
(hjstate->js.jointype == JOIN_LEFT),
HASHJOIN_IS_OUTER(hjstate),
hashvalue))
{
/* remember outer relation is not empty for possible rescan */
......@@ -650,7 +661,7 @@ start_over:
* sides. We can sometimes skip over batches that are empty on only one
* side, but there are exceptions:
*
* 1. In a LEFT JOIN, we have to process outer batches even if the inner
* 1. In an outer join, we have to process outer batches even if the inner
* batch is empty.
*
* 2. If we have increased nbatch since the initial estimate, we have to
......@@ -667,7 +678,7 @@ start_over:
hashtable->innerBatchFile[curbatch] == NULL))
{
if (hashtable->outerBatchFile[curbatch] &&
hjstate->js.jointype == JOIN_LEFT)
HASHJOIN_IS_OUTER(hjstate))
break; /* must process due to rule 1 */
if (hashtable->innerBatchFile[curbatch] &&
nbatch != hashtable->nbatch_original)
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.91 2008/04/13 20:51:20 tgl Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeMergejoin.c,v 1.92 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -757,7 +757,7 @@ ExecMergeJoin(MergeJoinState *node)
innerTupleSlot = node->mj_InnerTupleSlot;
econtext->ecxt_innertuple = innerTupleSlot;
if (node->js.jointype == JOIN_IN &&
if (node->js.jointype == JOIN_SEMI &&
node->mj_MatchedOuter)
qualResult = false;
else
......@@ -772,6 +772,10 @@ ExecMergeJoin(MergeJoinState *node)
node->mj_MatchedOuter = true;
node->mj_MatchedInner = true;
/* In an antijoin, we never return a matched tuple */
if (node->js.jointype == JOIN_ANTI)
break;
qualResult = (otherqual == NIL ||
ExecQual(otherqual, econtext, false));
MJ_DEBUG_QUAL(otherqual, qualResult);
......@@ -1472,11 +1476,12 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
switch (node->join.jointype)
{
case JOIN_INNER:
case JOIN_IN:
case JOIN_SEMI:
mergestate->mj_FillOuter = false;
mergestate->mj_FillInner = false;
break;
case JOIN_LEFT:
case JOIN_ANTI:
mergestate->mj_FillOuter = true;
mergestate->mj_FillInner = false;
mergestate->mj_NullInnerTupleSlot =
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.46 2008/01/01 19:45:49 momjian Exp $
* $PostgreSQL: pgsql/src/backend/executor/nodeNestloop.c,v 1.47 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -101,15 +101,6 @@ ExecNestLoop(NestLoopState *node)
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
* storage allocated in the previous tuple cycle. Note this can't happen
......@@ -177,7 +168,8 @@ ExecNestLoop(NestLoopState *node)
node->nl_NeedNewOuter = true;
if (!node->nl_MatchedOuter &&
node->js.jointype == JOIN_LEFT)
(node->js.jointype == JOIN_LEFT ||
node->js.jointype == JOIN_ANTI))
{
/*
* We are doing an outer join and there were no join matches
......@@ -189,7 +181,7 @@ ExecNestLoop(NestLoopState *node)
ENL1_printf("testing qualification for outer-join tuple");
if (ExecQual(otherqual, econtext, false))
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
{
/*
* qualification was satisfied so we project and return
......@@ -232,30 +224,39 @@ ExecNestLoop(NestLoopState *node)
{
node->nl_MatchedOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
/* In an antijoin, we never return a matched tuple */
if (node->js.jointype == JOIN_ANTI)
node->nl_NeedNewOuter = true;
else
{
/*
* qualification was satisfied so we project and return the
* slot containing the result tuple using ExecProject().
* In a semijoin, we'll consider returning the first match,
* but after that we're done with this outer tuple.
*/
TupleTableSlot *result;
ExprDoneCond isDone;
if (node->js.jointype == JOIN_SEMI)
node->nl_NeedNewOuter = true;
if (otherqual == NIL || ExecQual(otherqual, econtext, false))
{
/*
* qualification was satisfied so we project and return
* the slot containing the result tuple using
* ExecProject().
*/
TupleTableSlot *result;
ExprDoneCond isDone;
ENL1_printf("qualification succeeded, projecting tuple");
ENL1_printf("qualification succeeded, projecting tuple");
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
result = ExecProject(node->js.ps.ps_ProjInfo, &isDone);
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
if (isDone != ExprEndResult)
{
node->js.ps.ps_TupFromTlist =
(isDone == ExprMultipleResult);
return result;
}
}
}
/* If we didn't return a tuple, may need to set NeedNewOuter */
if (node->js.jointype == JOIN_IN)
node->nl_NeedNewOuter = true;
}
/*
......@@ -333,9 +334,10 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)
switch (node->join.jointype)
{
case JOIN_INNER:
case JOIN_IN:
case JOIN_SEMI:
break;
case JOIN_LEFT:
case JOIN_ANTI:
nlstate->nl_NullInnerTupleSlot =
ExecInitNullTupleSlot(estate,
ExecGetResultType(innerPlanState(nlstate)));
......
......@@ -15,7 +15,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.399 2008/08/07 19:35:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.400 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1444,36 +1444,37 @@ _copyRestrictInfo(RestrictInfo *from)
}
/*
* _copyOuterJoinInfo
* _copyFlattenedSubLink
*/
static OuterJoinInfo *
_copyOuterJoinInfo(OuterJoinInfo *from)
static FlattenedSubLink *
_copyFlattenedSubLink(FlattenedSubLink *from)
{
OuterJoinInfo *newnode = makeNode(OuterJoinInfo);
FlattenedSubLink *newnode = makeNode(FlattenedSubLink);
COPY_BITMAPSET_FIELD(min_lefthand);
COPY_BITMAPSET_FIELD(min_righthand);
COPY_BITMAPSET_FIELD(syn_lefthand);
COPY_BITMAPSET_FIELD(syn_righthand);
COPY_SCALAR_FIELD(is_full_join);
COPY_SCALAR_FIELD(lhs_strict);
COPY_SCALAR_FIELD(delay_upper_joins);
COPY_SCALAR_FIELD(jointype);
COPY_BITMAPSET_FIELD(lefthand);
COPY_BITMAPSET_FIELD(righthand);
COPY_NODE_FIELD(quals);
return newnode;
}
/*
* _copyInClauseInfo
* _copySpecialJoinInfo
*/
static InClauseInfo *
_copyInClauseInfo(InClauseInfo *from)
static SpecialJoinInfo *
_copySpecialJoinInfo(SpecialJoinInfo *from)
{
InClauseInfo *newnode = makeNode(InClauseInfo);
SpecialJoinInfo *newnode = makeNode(SpecialJoinInfo);
COPY_BITMAPSET_FIELD(lefthand);
COPY_BITMAPSET_FIELD(righthand);
COPY_NODE_FIELD(sub_targetlist);
COPY_NODE_FIELD(in_operators);
COPY_BITMAPSET_FIELD(min_lefthand);
COPY_BITMAPSET_FIELD(min_righthand);
COPY_BITMAPSET_FIELD(syn_lefthand);
COPY_BITMAPSET_FIELD(syn_righthand);
COPY_SCALAR_FIELD(jointype);
COPY_SCALAR_FIELD(lhs_strict);
COPY_SCALAR_FIELD(delay_upper_joins);
COPY_NODE_FIELD(join_quals);
return newnode;
}
......@@ -3233,11 +3234,11 @@ copyObject(void *from)
case T_RestrictInfo:
retval = _copyRestrictInfo(from);
break;
case T_OuterJoinInfo:
retval = _copyOuterJoinInfo(from);
case T_FlattenedSubLink:
retval = _copyFlattenedSubLink(from);
break;
case T_InClauseInfo:
retval = _copyInClauseInfo(from);
case T_SpecialJoinInfo:
retval = _copySpecialJoinInfo(from);
break;
case T_AppendRelInfo:
retval = _copyAppendRelInfo(from);
......
......@@ -18,7 +18,7 @@
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.326 2008/08/07 01:11:47 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.327 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -702,26 +702,27 @@ _equalRestrictInfo(RestrictInfo *a, RestrictInfo *b)
}
static bool
_equalOuterJoinInfo(OuterJoinInfo *a, OuterJoinInfo *b)
_equalFlattenedSubLink(FlattenedSubLink *a, FlattenedSubLink *b)
{
COMPARE_BITMAPSET_FIELD(min_lefthand);
COMPARE_BITMAPSET_FIELD(min_righthand);
COMPARE_BITMAPSET_FIELD(syn_lefthand);
COMPARE_BITMAPSET_FIELD(syn_righthand);
COMPARE_SCALAR_FIELD(is_full_join);
COMPARE_SCALAR_FIELD(lhs_strict);
COMPARE_SCALAR_FIELD(delay_upper_joins);
COMPARE_SCALAR_FIELD(jointype);
COMPARE_BITMAPSET_FIELD(lefthand);
COMPARE_BITMAPSET_FIELD(righthand);
COMPARE_NODE_FIELD(quals);
return true;
}
static bool
_equalInClauseInfo(InClauseInfo *a, InClauseInfo *b)
_equalSpecialJoinInfo(SpecialJoinInfo *a, SpecialJoinInfo *b)
{
COMPARE_BITMAPSET_FIELD(lefthand);
COMPARE_BITMAPSET_FIELD(righthand);
COMPARE_NODE_FIELD(sub_targetlist);
COMPARE_NODE_FIELD(in_operators);
COMPARE_BITMAPSET_FIELD(min_lefthand);
COMPARE_BITMAPSET_FIELD(min_righthand);
COMPARE_BITMAPSET_FIELD(syn_lefthand);
COMPARE_BITMAPSET_FIELD(syn_righthand);
COMPARE_SCALAR_FIELD(jointype);
COMPARE_SCALAR_FIELD(lhs_strict);
COMPARE_SCALAR_FIELD(delay_upper_joins);
COMPARE_NODE_FIELD(join_quals);
return true;
}
......@@ -2185,11 +2186,11 @@ equal(void *a, void *b)
case T_RestrictInfo:
retval = _equalRestrictInfo(a, b);
break;
case T_OuterJoinInfo:
retval = _equalOuterJoinInfo(a, b);
case T_FlattenedSubLink:
retval = _equalFlattenedSubLink(a, b);
break;
case T_InClauseInfo:
retval = _equalInClauseInfo(a, b);
case T_SpecialJoinInfo:
retval = _equalSpecialJoinInfo(a, b);
break;
case T_AppendRelInfo:
retval = _equalAppendRelInfo(a, b);
......
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/list.c,v 1.69 2008/01/01 19:45:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/nodes/list.c,v 1.70 2008/08/14 18:47:58 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -783,6 +783,42 @@ list_union_oid(List *list1, List *list2)
return result;
}
/*
* Return a list that contains all the cells that are in both list1 and
* list2. The returned list is freshly allocated via palloc(), but the
* cells themselves point to the same objects as the cells of the
* input lists.
*
* Duplicate entries in list1 will not be suppressed, so it's only a true
* "intersection" if list1 is known unique beforehand.
*
* This variant works on lists of pointers, and determines list
* membership via equal(). Note that the list1 member will be pointed
* to in the result.
*/
List *
list_intersection(List *list1, List *list2)
{
List *result;
ListCell *cell;
if (list1 == NIL || list2 == NIL)
return NIL;
Assert(IsPointerList(list1));
Assert(IsPointerList(list2));
result = NIL;
foreach(cell, list1)
{
if (list_member(list2, lfirst(cell)))
result = lappend(result, lfirst(cell));
}
check_list_invariants(result);
return result;
}
/*
* Return a list that contains all the cells in list1 that are not in
* list2. The returned list is freshly allocated via palloc(), but the
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.333 2008/08/07 19:35:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.334 2008/08/14 18:47:58 tgl Exp $
*
* NOTES
* Every node type that can appear in stored rules' parsetrees *must*
......@@ -1267,6 +1267,8 @@ _outUniquePath(StringInfo str, UniquePath *node)
WRITE_NODE_FIELD(subpath);
WRITE_ENUM_FIELD(umethod, UniquePathMethod);
WRITE_NODE_FIELD(in_operators);
WRITE_NODE_FIELD(uniq_exprs);
WRITE_FLOAT_FIELD(rows, "%.0f");
}
......@@ -1332,8 +1334,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_NODE_FIELD(left_join_clauses);
WRITE_NODE_FIELD(right_join_clauses);
WRITE_NODE_FIELD(full_join_clauses);
WRITE_NODE_FIELD(oj_info_list);
WRITE_NODE_FIELD(in_info_list);
WRITE_NODE_FIELD(join_info_list);
WRITE_NODE_FIELD(append_rel_list);
WRITE_NODE_FIELD(query_pathkeys);
WRITE_NODE_FIELD(group_pathkeys);
......@@ -1342,7 +1343,6 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node)
WRITE_FLOAT_FIELD(total_table_pages, "%.0f");
WRITE_FLOAT_FIELD(tuple_fraction, "%.4f");
WRITE_BOOL_FIELD(hasJoinRTEs);
WRITE_BOOL_FIELD(hasOuterJoins);
WRITE_BOOL_FIELD(hasHavingQual);
WRITE_BOOL_FIELD(hasPseudoConstantQuals);
}
......@@ -1479,28 +1479,29 @@ _outInnerIndexscanInfo(StringInfo str, InnerIndexscanInfo *node)
}
static void
_outOuterJoinInfo(StringInfo str, OuterJoinInfo *node)
_outFlattenedSubLink(StringInfo str, FlattenedSubLink *node)
{
WRITE_NODE_TYPE("FLATTENEDSUBLINK");
WRITE_ENUM_FIELD(jointype, JoinType);
WRITE_BITMAPSET_FIELD(lefthand);
WRITE_BITMAPSET_FIELD(righthand);
WRITE_NODE_FIELD(quals);
}
static void
_outSpecialJoinInfo(StringInfo str, SpecialJoinInfo *node)
{
WRITE_NODE_TYPE("OUTERJOININFO");
WRITE_NODE_TYPE("SPECIALJOININFO");
WRITE_BITMAPSET_FIELD(min_lefthand);
WRITE_BITMAPSET_FIELD(min_righthand);
WRITE_BITMAPSET_FIELD(syn_lefthand);
WRITE_BITMAPSET_FIELD(syn_righthand);
WRITE_BOOL_FIELD(is_full_join);
WRITE_ENUM_FIELD(jointype, JoinType);
WRITE_BOOL_FIELD(lhs_strict);
WRITE_BOOL_FIELD(delay_upper_joins);
}
static void
_outInClauseInfo(StringInfo str, InClauseInfo *node)
{
WRITE_NODE_TYPE("INCLAUSEINFO");
WRITE_BITMAPSET_FIELD(lefthand);
WRITE_BITMAPSET_FIELD(righthand);
WRITE_NODE_FIELD(sub_targetlist);
WRITE_NODE_FIELD(in_operators);
WRITE_NODE_FIELD(join_quals);
}
static void
......@@ -2352,11 +2353,11 @@ _outNode(StringInfo str, void *obj)
case T_InnerIndexscanInfo:
_outInnerIndexscanInfo(str, obj);
break;
case T_OuterJoinInfo:
_outOuterJoinInfo(str, obj);
case T_FlattenedSubLink:
_outFlattenedSubLink(str, obj);
break;
case T_InClauseInfo:
_outInClauseInfo(str, obj);
case T_SpecialJoinInfo:
_outSpecialJoinInfo(str, obj);
break;
case T_AppendRelInfo:
_outAppendRelInfo(str, obj);
......
$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.47 2008/08/02 21:31:59 tgl Exp $
$PostgreSQL: pgsql/src/backend/optimizer/README,v 1.48 2008/08/14 18:47:59 tgl Exp $
Optimizer
=========
......@@ -114,9 +114,8 @@ no choice but to generate a clauseless Cartesian-product join; so we
consider joining that rel to each other available rel. But in the presence
of join clauses we will only consider joins that use available join
clauses. Note that join-order restrictions induced by outer joins and
IN clauses are treated as if they were real join clauses, to ensure that
we find a workable join order in cases where those restrictions force a
clauseless join to be done.)
IN/EXISTS clauses are also checked, to ensure that we find a workable join
order in cases where those restrictions force a clauseless join to be done.)
If we only had two relations in the list, we are done: we just pick
the cheapest path for the join RelOptInfo. If we had more than two, we now
......@@ -174,9 +173,9 @@ for it or the cheapest path with the desired ordering (if that's cheaper
than applying a sort to the cheapest other path).
If the query contains one-sided outer joins (LEFT or RIGHT joins), or
"IN (sub-select)" WHERE clauses that were converted to joins, then some of
IN or EXISTS WHERE clauses that were converted to joins, then some of
the possible join orders may be illegal. These are excluded by having
join_is_legal consult side lists of outer joins and IN joins to see
join_is_legal consult a side list of such "special" joins to see
whether a proposed join is illegal. (The same consultation allows it
to see which join style should be applied for a valid join, ie,
JOIN_INNER, JOIN_LEFT, etc.)
......@@ -219,10 +218,10 @@ FULL JOIN ordering is enforced by not collapsing FULL JOIN nodes when
translating the jointree to "joinlist" representation. LEFT and RIGHT
JOIN nodes are normally collapsed so that they participate fully in the
join order search. To avoid generating illegal join orders, the planner
creates an OuterJoinInfo node for each outer join, and join_is_legal
creates a SpecialJoinInfo node for each outer join, and join_is_legal
checks this list to decide if a proposed join is legal.
What we store in OuterJoinInfo nodes are the minimum sets of Relids
What we store in SpecialJoinInfo nodes are the minimum sets of Relids
required on each side of the join to form the outer join. Note that
these are minimums; there's no explicit maximum, since joining other
rels to the OJ's syntactic rels may be legal. Per identities 1 and 2,
......@@ -273,7 +272,7 @@ planner()
set up for recursive handling of subqueries
do final cleanup after planning
-subquery_planner()
pull up subqueries from rangetable, if possible
pull up sublinks and subqueries from rangetable, if possible
canonicalize qual
Attempt to simplify WHERE clause to the most useful form; this includes
flattening nested AND/ORs and detecting clauses that are duplicated in
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.90 2008/01/11 17:00:45 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.91 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -94,7 +94,8 @@ Selectivity
clauselist_selectivity(PlannerInfo *root,
List *clauses,
int varRelid,
JoinType jointype)
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
Selectivity s1 = 1.0;
RangeQueryClause *rqlist = NULL;
......@@ -106,7 +107,7 @@ clauselist_selectivity(PlannerInfo *root,
*/
if (list_length(clauses) == 1)
return clause_selectivity(root, (Node *) linitial(clauses),
varRelid, jointype);
varRelid, jointype, sjinfo);
/*
* Initial scan over clauses. Anything that doesn't look like a potential
......@@ -120,7 +121,7 @@ clauselist_selectivity(PlannerInfo *root,
Selectivity s2;
/* Always compute the selectivity using clause_selectivity */
s2 = clause_selectivity(root, clause, varRelid, jointype);
s2 = clause_selectivity(root, clause, varRelid, jointype, sjinfo);
/*
* Check for being passed a RestrictInfo.
......@@ -227,9 +228,8 @@ clauselist_selectivity(PlannerInfo *root,
s2 = rqlist->hibound + rqlist->lobound - 1.0;
/* Adjust for double-exclusion of NULLs */
/* HACK: disable nulltestsel's special outer-join logic */
s2 += nulltestsel(root, IS_NULL, rqlist->var,
varRelid, JOIN_INNER);
varRelid, jointype, sjinfo);
/*
* A zero or slightly negative s2 should be converted into a
......@@ -420,13 +420,32 @@ bms_is_subset_singleton(const Bitmapset *s, int x)
* is appropriate for ordinary join clauses and restriction clauses.
*
* jointype is the join type, if the clause is a join clause. Pass JOIN_INNER
* if the clause isn't a join clause or the context is uncertain.
* if the clause isn't a join clause.
*
* sjinfo is NULL for a non-join clause, otherwise it provides additional
* context information about the join being performed. There are some
* special cases:
* 1. For a special (not INNER) join, sjinfo is always a member of
* root->join_info_list.
* 2. For an INNER join, sjinfo is just a transient struct, and only the
* relids and jointype fields in it can be trusted.
* 3. XXX sjinfo might be NULL even though it really is a join. This case
* will go away soon, but fixing it requires API changes for oprjoin and
* amcostestimate functions.
* It is possible for jointype to be different from sjinfo->jointype.
* This indicates we are considering a variant join: either with
* the LHS and RHS switched, or with one input unique-ified.
*
* Note: when passing nonzero varRelid, it's normally appropriate to set
* jointype == JOIN_INNER, sjinfo == NULL, even if the clause is really a
* join clause; because we aren't treating it as a join clause.
*/
Selectivity
clause_selectivity(PlannerInfo *root,
Node *clause,
int varRelid,
JoinType jointype)
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
Selectivity s1 = 0.5; /* default for any unhandled clause type */
RestrictInfo *rinfo = NULL;
......@@ -457,36 +476,15 @@ clause_selectivity(PlannerInfo *root,
* If possible, cache the result of the selectivity calculation for
* the clause. We can cache if varRelid is zero or the clause
* contains only vars of that relid --- otherwise varRelid will affect
* the result, so mustn't cache. We also have to be careful about the
* jointype. It's OK to cache when jointype is JOIN_INNER or one of
* the outer join types (any given outer-join clause should always be
* examined with the same jointype, so result won't change). It's not
* OK to cache when jointype is one of the special types associated
* with IN processing, because the same clause may be examined with
* different jointypes and the result should vary.
* the result, so mustn't cache.
*/
if (varRelid == 0 ||
bms_is_subset_singleton(rinfo->clause_relids, varRelid))
{
switch (jointype)
{
case JOIN_INNER:
case JOIN_LEFT:
case JOIN_FULL:
case JOIN_RIGHT:
/* Cacheable --- do we already have the result? */
if (rinfo->this_selec >= 0)
return rinfo->this_selec;
cacheable = true;
break;
case JOIN_IN:
case JOIN_REVERSE_IN:
case JOIN_UNIQUE_OUTER:
case JOIN_UNIQUE_INNER:
/* unsafe to cache */
break;
}
/* Cacheable --- do we already have the result? */
if (rinfo->this_selec >= 0)
return rinfo->this_selec;
cacheable = true;
}
/*
......@@ -568,7 +566,8 @@ clause_selectivity(PlannerInfo *root,
s1 = 1.0 - clause_selectivity(root,
(Node *) get_notclausearg((Expr *) clause),
varRelid,
jointype);
jointype,
sjinfo);
}
else if (and_clause(clause))
{
......@@ -576,7 +575,8 @@ clause_selectivity(PlannerInfo *root,
s1 = clauselist_selectivity(root,
((BoolExpr *) clause)->args,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (or_clause(clause))
{
......@@ -594,7 +594,8 @@ clause_selectivity(PlannerInfo *root,
Selectivity s2 = clause_selectivity(root,
(Node *) lfirst(arg),
varRelid,
jointype);
jointype,
sjinfo);
s1 = s1 + s2 - s1 * s2;
}
......@@ -700,7 +701,8 @@ clause_selectivity(PlannerInfo *root,
(ScalarArrayOpExpr *) clause,
is_join_clause,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (IsA(clause, RowCompareExpr))
{
......@@ -708,7 +710,8 @@ clause_selectivity(PlannerInfo *root,
s1 = rowcomparesel(root,
(RowCompareExpr *) clause,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (IsA(clause, NullTest))
{
......@@ -717,7 +720,8 @@ clause_selectivity(PlannerInfo *root,
((NullTest *) clause)->nulltesttype,
(Node *) ((NullTest *) clause)->arg,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (IsA(clause, BooleanTest))
{
......@@ -726,7 +730,8 @@ clause_selectivity(PlannerInfo *root,
((BooleanTest *) clause)->booltesttype,
(Node *) ((BooleanTest *) clause)->arg,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (IsA(clause, CurrentOfExpr))
{
......@@ -743,7 +748,8 @@ clause_selectivity(PlannerInfo *root,
s1 = clause_selectivity(root,
(Node *) ((RelabelType *) clause)->arg,
varRelid,
jointype);
jointype,
sjinfo);
}
else if (IsA(clause, CoerceToDomain))
{
......@@ -751,7 +757,8 @@ clause_selectivity(PlannerInfo *root,
s1 = clause_selectivity(root,
(Node *) ((CoerceToDomain *) clause)->arg,
varRelid,
jointype);
jointype,
sjinfo);
}
/* Cache the result if possible */
......
This diff is collapsed.
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.231 2008/05/27 00:13:09 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/indxpath.c,v 1.232 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1631,16 +1631,16 @@ best_inner_indexscan(PlannerInfo *root, RelOptInfo *rel,
*cheapest_startup = *cheapest_total = NULL;
/*
* Nestloop only supports inner, left, and IN joins.
* Nestloop only supports inner, left, semi, and anti joins.
*/
switch (jointype)
{
case JOIN_INNER:
case JOIN_IN:
case JOIN_UNIQUE_OUTER:
isouterjoin = false;
break;
case JOIN_LEFT:
case JOIN_SEMI:
case JOIN_ANTI:
isouterjoin = true;
break;
default:
......
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.84 2008/01/09 20:42:27 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/path/orindxpath.c,v 1.85 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -169,11 +169,11 @@ create_or_index_quals(PlannerInfo *root, RelOptInfo *rel)
* selectivity will stay cached ...)
*/
or_selec = clause_selectivity(root, (Node *) or_rinfo,
0, JOIN_INNER);
0, JOIN_INNER, NULL);
if (or_selec > 0 && or_selec < 1)
{
orig_selec = clause_selectivity(root, (Node *) bestrinfo,
0, JOIN_INNER);
0, JOIN_INNER, NULL);
bestrinfo->this_selec = orig_selec / or_selec;
/* clamp result to sane range */
if (bestrinfo->this_selec > 1)
......
......@@ -10,7 +10,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.244 2008/08/07 19:35:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.245 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -595,8 +595,8 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
{
Plan *plan;
Plan *subplan;
List *uniq_exprs;
List *in_operators;
List *uniq_exprs;
List *newtlist;
int nextresno;
bool newitems;
......@@ -611,7 +611,7 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
if (best_path->umethod == UNIQUE_PATH_NOOP)
return subplan;
/*----------
/*
* As constructed, the subplan has a "flat" tlist containing just the
* Vars needed here and at upper levels. The values we are supposed
* to unique-ify may be expressions in these variables. We have to
......@@ -626,29 +626,9 @@ create_unique_plan(PlannerInfo *root, UniquePath *best_path)
* Therefore newtlist starts from build_relation_tlist() not just a
* copy of the subplan's tlist; and we don't install it into the subplan
* unless we are sorting or stuff has to be added.
*
* To find the correct list of values to unique-ify, we look in the
* information saved for IN expressions. If this code is ever used in
* other scenarios, some other way of finding what to unique-ify will
* be needed. The IN clause's operators are needed too, since they
* determine what the meaning of "unique" is in this context.
*----------
*/
uniq_exprs = NIL; /* just to keep compiler quiet */
in_operators = NIL;
foreach(l, root->in_info_list)
{
InClauseInfo *ininfo = (InClauseInfo *) lfirst(l);
if (bms_equal(ininfo->righthand, best_path->path.parent->relids))
{
uniq_exprs = ininfo->sub_targetlist;
in_operators = ininfo->in_operators;
break;
}
}
if (l == NULL) /* fell out of loop? */
elog(ERROR, "could not find UniquePath in in_info_list");
in_operators = best_path->in_operators;
uniq_exprs = best_path->uniq_exprs;
/* initialize modified subplan tlist as just the "required" vars */
newtlist = build_relation_tlist(best_path->path.parent);
......
This diff is collapsed.
......@@ -14,7 +14,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.109 2008/08/05 02:43:17 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.110 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -131,8 +131,8 @@ query_planner(PlannerInfo *root, List *tlist,
* Init planner lists to empty, and set up the array to hold RelOptInfos
* for "simple" rels.
*
* NOTE: in_info_list and append_rel_list were set up by subquery_planner,
* do not touch here; eq_classes may contain data already, too.
* NOTE: append_rel_list was set up by subquery_planner, so do not touch
* here; eq_classes may contain data already, too.
*/
root->simple_rel_array_size = list_length(parse->rtable) + 1;
root->simple_rel_array = (RelOptInfo **)
......@@ -143,7 +143,7 @@ query_planner(PlannerInfo *root, List *tlist,
root->left_join_clauses = NIL;
root->right_join_clauses = NIL;
root->full_join_clauses = NIL;
root->oj_info_list = NIL;
root->join_info_list = NIL;
root->initial_rels = NIL;
/*
......@@ -215,13 +215,6 @@ query_planner(PlannerInfo *root, List *tlist,
joinlist = deconstruct_jointree(root);
/*
* Vars mentioned in InClauseInfo items also have to be added to baserel
* targetlists. Nearly always, they'd have got there from the original
* WHERE qual, but in corner cases maybe not.
*/
add_IN_vars_to_tlists(root);
/*
* Reconsider any postponed outer-join quals now that we have built up
* equivalence classes. (This could result in further additions or
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.240 2008/08/07 01:11:50 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.241 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -55,8 +55,7 @@ planner_hook_type planner_hook = NULL;
#define EXPRKIND_RTFUNC 2
#define EXPRKIND_VALUES 3
#define EXPRKIND_LIMIT 4
#define EXPRKIND_ININFO 5
#define EXPRKIND_APPINFO 6
#define EXPRKIND_APPINFO 5
static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind);
......@@ -255,6 +254,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
PlannerInfo *root;
Plan *plan;
List *newHaving;
bool hasOuterJoins;
ListCell *l;
/* Create a PlannerInfo data structure for this subquery */
......@@ -265,23 +265,22 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->planner_cxt = CurrentMemoryContext;
root->init_plans = NIL;
root->eq_classes = NIL;
root->in_info_list = NIL;
root->append_rel_list = NIL;
/*
* Look for IN clauses at the top level of WHERE, and transform them into
* joins. Note that this step only handles IN clauses originally at top
* level of WHERE; if we pull up any subqueries below, their INs are
* processed just before pulling them up.
* Look for ANY and EXISTS SubLinks at the top level of WHERE, and try to
* transform them into joins. Note that this step only handles SubLinks
* originally at top level of WHERE; if we pull up any subqueries below,
* their SubLinks are processed just before pulling them up.
*/
if (parse->hasSubLinks)
parse->jointree->quals = pull_up_IN_clauses(root,
parse->jointree->quals);
parse->jointree->quals = pull_up_sublinks(root,
parse->jointree->quals);
/*
* Scan the rangetable for set-returning functions, and inline them
* if possible (producing subqueries that might get pulled up next).
* Recursion issues here are handled in the same way as for IN clauses.
* Recursion issues here are handled in the same way as for SubLinks.
*/
inline_set_returning_functions(root);
......@@ -295,16 +294,11 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
/*
* Detect whether any rangetable entries are RTE_JOIN kind; if not, we can
* avoid the expense of doing flatten_join_alias_vars(). Also check for
* outer joins --- if none, we can skip reduce_outer_joins() and some
* other processing. This must be done after we have done
* pull_up_subqueries, of course.
*
* Note: if reduce_outer_joins manages to eliminate all outer joins,
* root->hasOuterJoins is not reset currently. This is OK since its
* purpose is merely to suppress unnecessary processing in simple cases.
* outer joins --- if none, we can skip reduce_outer_joins().
* This must be done after we have done pull_up_subqueries, of course.
*/
root->hasJoinRTEs = false;
root->hasOuterJoins = false;
hasOuterJoins = false;
foreach(l, parse->rtable)
{
RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
......@@ -314,7 +308,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
root->hasJoinRTEs = true;
if (IS_OUTER_JOIN(rte->jointype))
{
root->hasOuterJoins = true;
hasOuterJoins = true;
/* Can quit scanning once we find an outer join */
break;
}
......@@ -362,9 +356,6 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
parse->limitCount = preprocess_expression(root, parse->limitCount,
EXPRKIND_LIMIT);
root->in_info_list = (List *)
preprocess_expression(root, (Node *) root->in_info_list,
EXPRKIND_ININFO);
root->append_rel_list = (List *)
preprocess_expression(root, (Node *) root->append_rel_list,
EXPRKIND_APPINFO);
......@@ -442,7 +433,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse,
* This step is most easily done after we've done expression
* preprocessing.
*/
if (root->hasOuterJoins)
if (hasOuterJoins)
reduce_outer_joins(root);
/*
......@@ -639,20 +630,15 @@ inheritance_planner(PlannerInfo *root)
continue;
/*
* Generate modified query with this rel as target. We have to be
* prepared to translate varnos in in_info_list as well as in the
* Query proper.
* Generate modified query with this rel as target.
*/
memcpy(&subroot, root, sizeof(PlannerInfo));
subroot.parse = (Query *)
adjust_appendrel_attrs((Node *) parse,
appinfo);
subroot.in_info_list = (List *)
adjust_appendrel_attrs((Node *) root->in_info_list,
appinfo);
subroot.init_plans = NIL;
/* There shouldn't be any OJ info to translate, as yet */
Assert(subroot.oj_info_list == NIL);
Assert(subroot.join_info_list == NIL);
/* Generate plan */
subplan = grouping_planner(&subroot, 0.0 /* retrieve all tuples */ );
......
This diff is collapsed.
This diff is collapsed.
......@@ -22,7 +22,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.152 2008/08/07 19:35:02 tgl Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.153 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1465,25 +1465,25 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context)
j->rtindex = context->child_relid;
return (Node *) j;
}
if (IsA(node, InClauseInfo))
if (IsA(node, FlattenedSubLink))
{
/* Copy the InClauseInfo node with correct mutation of subnodes */
InClauseInfo *ininfo;
/* Copy the FlattenedSubLink node with correct mutation of subnodes */
FlattenedSubLink *fslink;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
fslink = (FlattenedSubLink *) expression_tree_mutator(node,
adjust_appendrel_attrs_mutator,
(void *) context);
/* now fix InClauseInfo's relid sets */
ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
(void *) context);
/* now fix FlattenedSubLink's relid sets */
fslink->lefthand = adjust_relid_set(fslink->lefthand,
context->parent_relid,
context->child_relid);
ininfo->righthand = adjust_relid_set(ininfo->righthand,
fslink->righthand = adjust_relid_set(fslink->righthand,
context->parent_relid,
context->child_relid);
return (Node *) ininfo;
return (Node *) fslink;
}
/* Shouldn't need to handle OuterJoinInfo or AppendRelInfo here */
Assert(!IsA(node, OuterJoinInfo));
/* Shouldn't need to handle SpecialJoinInfo or AppendRelInfo here */
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
/*
......
This diff is collapsed.
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.89 2008/01/01 19:45:50 momjian Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.90 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -273,7 +273,7 @@ find_join_rel(PlannerInfo *root, Relids relids)
* 'joinrelids' is the Relids set that uniquely identifies the join
* 'outer_rel' and 'inner_rel' are relation nodes for the relations to be
* joined
* 'jointype': type of join (inner/outer)
* 'sjinfo': join context info
* 'restrictlist_ptr': result variable. If not NULL, *restrictlist_ptr
* receives the list of RestrictInfo nodes that apply to this
* particular pair of joinable relations.
......@@ -286,7 +286,7 @@ build_join_rel(PlannerInfo *root,
Relids joinrelids,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
List **restrictlist_ptr)
{
RelOptInfo *joinrel;
......@@ -375,7 +375,7 @@ build_join_rel(PlannerInfo *root,
* Set estimates of the joinrel's size.
*/
set_joinrel_size_estimates(root, joinrel, outer_rel, inner_rel,
jointype, restrictlist);
sjinfo, restrictlist);
/*
* Add the joinrel to the query's joinrel list, and store it into the
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.74 2008/05/12 00:00:49 alvherre Exp $
* $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.75 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -644,23 +644,23 @@ flatten_join_alias_vars_mutator(Node *node,
/* Recurse in case join input is itself a join */
return flatten_join_alias_vars_mutator(newvar, context);
}
if (IsA(node, InClauseInfo))
if (IsA(node, FlattenedSubLink))
{
/* Copy the InClauseInfo node with correct mutation of subnodes */
InClauseInfo *ininfo;
/* Copy the FlattenedSubLink node with correct mutation of subnodes */
FlattenedSubLink *fslink;
ininfo = (InClauseInfo *) expression_tree_mutator(node,
fslink = (FlattenedSubLink *) expression_tree_mutator(node,
flatten_join_alias_vars_mutator,
(void *) context);
/* now fix InClauseInfo's relid sets */
(void *) context);
/* now fix FlattenedSubLink's relid sets */
if (context->sublevels_up == 0)
{
ininfo->lefthand = alias_relid_set(context->root,
ininfo->lefthand);
ininfo->righthand = alias_relid_set(context->root,
ininfo->righthand);
fslink->lefthand = alias_relid_set(context->root,
fslink->lefthand);
fslink->righthand = alias_relid_set(context->root,
fslink->righthand);
}
return (Node *) ininfo;
return (Node *) fslink;
}
if (IsA(node, Query))
......
......@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.107 2008/01/01 19:45:51 momjian Exp $
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.108 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -176,15 +176,15 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
j->rtindex += context->offset;
/* fall through to examine children */
}
if (IsA(node, InClauseInfo))
if (IsA(node, FlattenedSubLink))
{
InClauseInfo *ininfo = (InClauseInfo *) node;
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
if (context->sublevels_up == 0)
{
ininfo->lefthand = offset_relid_set(ininfo->lefthand,
fslink->lefthand = offset_relid_set(fslink->lefthand,
context->offset);
ininfo->righthand = offset_relid_set(ininfo->righthand,
fslink->righthand = offset_relid_set(fslink->righthand,
context->offset);
}
/* fall through to examine children */
......@@ -338,16 +338,16 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
j->rtindex = context->new_index;
/* fall through to examine children */
}
if (IsA(node, InClauseInfo))
if (IsA(node, FlattenedSubLink))
{
InClauseInfo *ininfo = (InClauseInfo *) node;
FlattenedSubLink *fslink = (FlattenedSubLink *) node;
if (context->sublevels_up == 0)
{
ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
fslink->lefthand = adjust_relid_set(fslink->lefthand,
context->rt_index,
context->new_index);
ininfo->righthand = adjust_relid_set(ininfo->righthand,
fslink->righthand = adjust_relid_set(fslink->righthand,
context->rt_index,
context->new_index);
}
......@@ -589,8 +589,8 @@ rangeTableEntry_used_walker(Node *node,
/* fall through to examine children */
}
/* Shouldn't need to handle planner auxiliary nodes here */
Assert(!IsA(node, OuterJoinInfo));
Assert(!IsA(node, InClauseInfo));
Assert(!IsA(node, FlattenedSubLink));
Assert(!IsA(node, SpecialJoinInfo));
Assert(!IsA(node, AppendRelInfo));
if (IsA(node, Query))
......
......@@ -15,7 +15,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.250 2008/07/07 20:24:55 tgl Exp $
* $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.251 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -1300,7 +1300,7 @@ icnlikesel(PG_FUNCTION_ARGS)
*/
Selectivity
booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg,
int varRelid, JoinType jointype)
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
VariableStatData vardata;
double selec;
......@@ -1436,12 +1436,14 @@ booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg,
case IS_TRUE:
case IS_NOT_FALSE:
selec = (double) clause_selectivity(root, arg,
varRelid, jointype);
varRelid,
jointype, sjinfo);
break;
case IS_FALSE:
case IS_NOT_TRUE:
selec = 1.0 - (double) clause_selectivity(root, arg,
varRelid, jointype);
varRelid,
jointype, sjinfo);
break;
default:
elog(ERROR, "unrecognized booltesttype: %d",
......@@ -1463,25 +1465,12 @@ booltestsel(PlannerInfo *root, BoolTestType booltesttype, Node *arg,
* nulltestsel - Selectivity of NullTest Node.
*/
Selectivity
nulltestsel(PlannerInfo *root, NullTestType nulltesttype,
Node *arg, int varRelid, JoinType jointype)
nulltestsel(PlannerInfo *root, NullTestType nulltesttype, Node *arg,
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
VariableStatData vardata;
double selec;
/*
* Special hack: an IS NULL test being applied at an outer join should not
* be taken at face value, since it's very likely being used to select the
* outer-side rows that don't have a match, and thus its selectivity has
* nothing whatever to do with the statistics of the original table
* column. We do not have nearly enough context here to determine its
* true selectivity, so for the moment punt and guess at 0.5. Eventually
* the planner should be made to provide enough info about the clause's
* context to let us do better.
*/
if (IS_OUTER_JOIN(jointype) && nulltesttype == IS_NULL)
return (Selectivity) 0.5;
examine_variable(root, arg, varRelid, &vardata);
if (HeapTupleIsValid(vardata.statsTuple))
......@@ -1579,7 +1568,9 @@ Selectivity
scalararraysel(PlannerInfo *root,
ScalarArrayOpExpr *clause,
bool is_join_clause,
int varRelid, JoinType jointype)
int varRelid,
JoinType jointype,
SpecialJoinInfo *sjinfo)
{
Oid operator = clause->opno;
bool useOr = clause->useOr;
......@@ -1802,7 +1793,7 @@ estimate_array_length(Node *arrayexpr)
Selectivity
rowcomparesel(PlannerInfo *root,
RowCompareExpr *clause,
int varRelid, JoinType jointype)
int varRelid, JoinType jointype, SpecialJoinInfo *sjinfo)
{
Selectivity s1;
Oid opno = linitial_oid(clause->opnos);
......@@ -1942,25 +1933,16 @@ eqjoinsel(PG_FUNCTION_ARGS)
hasmatch2 = (bool *) palloc0(nvalues2 * sizeof(bool));
/*
* If we are doing any variant of JOIN_IN, pretend all the values of
* If we are doing any variant of JOIN_SEMI, pretend all the values of
* the righthand relation are unique (ie, act as if it's been
* DISTINCT'd).
*
* NOTE: it might seem that we should unique-ify the lefthand input
* when considering JOIN_REVERSE_IN. But this is not so, because the
* join clause we've been handed has not been commuted from the way
* the parser originally wrote it. We know that the unique side of
* the IN clause is *always* on the right.
*
* NOTE: it would be dangerous to try to be smart about JOIN_LEFT or
* JOIN_RIGHT here, because we do not have enough information to
* determine which var is really on which side of the join. Perhaps
* someday we should pass in more information.
*/
if (jointype == JOIN_IN ||
jointype == JOIN_REVERSE_IN ||
jointype == JOIN_UNIQUE_INNER ||
jointype == JOIN_UNIQUE_OUTER)
if (jointype == JOIN_SEMI)
{
float4 oneovern = 1.0 / nd2;
......@@ -5144,7 +5126,8 @@ genericcostestimate(PlannerInfo *root,
/* Estimate the fraction of main-table tuples that will be visited */
*indexSelectivity = clauselist_selectivity(root, selectivityQuals,
index->rel->relid,
JOIN_INNER);
JOIN_INNER,
NULL);
/*
* If caller didn't give us an estimate, estimate the number of index
......@@ -5483,7 +5466,8 @@ btcostestimate(PG_FUNCTION_ARGS)
btreeSelectivity = clauselist_selectivity(root, indexBoundQuals,
index->rel->relid,
JOIN_INNER);
JOIN_INNER,
NULL);
numIndexTuples = btreeSelectivity * index->rel->tuples;
/*
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.207 2008/08/02 21:32:00 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.208 2008/08/14 18:48:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -202,8 +202,8 @@ typedef enum NodeTag
T_PathKey,
T_RestrictInfo,
T_InnerIndexscanInfo,
T_OuterJoinInfo,
T_InClauseInfo,
T_FlattenedSubLink,
T_SpecialJoinInfo,
T_AppendRelInfo,
T_PlannerParamItem,
......@@ -474,31 +474,49 @@ typedef enum CmdType
typedef enum JoinType
{
/*
* The canonical kinds of joins
* The canonical kinds of joins according to the SQL JOIN syntax.
* Only these codes can appear in parser output (e.g., JoinExpr nodes).
*/
JOIN_INNER, /* matching tuple pairs only */
JOIN_LEFT, /* pairs + unmatched outer tuples */
JOIN_FULL, /* pairs + unmatched outer + unmatched inner */
JOIN_RIGHT, /* pairs + unmatched inner tuples */
JOIN_LEFT, /* pairs + unmatched LHS tuples */
JOIN_FULL, /* pairs + unmatched LHS + unmatched RHS */
JOIN_RIGHT, /* pairs + unmatched RHS tuples */
/*
* These are used for 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.
* Semijoins and anti-semijoins (as defined in relational theory) do
* not appear in the SQL JOIN syntax, but there are standard idioms for
* representing them (e.g., using EXISTS). The planner recognizes these
* cases and converts them to joins. So the planner and executor must
* support these codes. NOTE: in JOIN_SEMI output, it is unspecified
* which matching RHS row is joined to. In JOIN_ANTI output, the row
* is guaranteed to be null-extended.
*/
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 */
JOIN_SEMI, /* 1 copy of each LHS row that has match(es) */
JOIN_ANTI, /* 1 copy of each LHS row that has no match */
/*
* These codes are used internally in the planner, but are not supported
* by the executor (nor, indeed, by most of the planner).
*/
JOIN_UNIQUE_OUTER, /* LHS path must be made unique */
JOIN_UNIQUE_INNER /* RHS path must be made unique */
/*
* We might need additional join types someday.
*/
} JoinType;
/*
* OUTER joins are those for which pushed-down quals must behave differently
* from the join's own quals. This is in fact everything except INNER joins.
* However, this macro must also exclude the JOIN_UNIQUE symbols since those
* are temporary proxies for what will eventually be an INNER join.
*
* Note: in some places it is preferable to treat JOIN_SEMI as not being
* an outer join, since it doesn't produce null-extended rows. Be aware
* of that distinction when deciding whether to use this macro.
*/
#define IS_OUTER_JOIN(jointype) \
((jointype) == JOIN_LEFT || \
(jointype) == JOIN_FULL || \
(jointype) == JOIN_RIGHT)
((jointype) > JOIN_INNER && (jointype) < JOIN_UNIQUE_OUTER)
#endif /* NODES_H */
......@@ -30,7 +30,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.58 2008/03/17 02:18:55 tgl Exp $
* $PostgreSQL: pgsql/src/include/nodes/pg_list.h,v 1.59 2008/08/14 18:48:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -218,6 +218,9 @@ extern List *list_union_ptr(List *list1, List *list2);
extern List *list_union_int(List *list1, List *list2);
extern List *list_union_oid(List *list1, List *list2);
extern List *list_intersection(List *list1, List *list2);
/* currently, there's no need for list_intersection_int etc */
extern List *list_difference(List *list1, List *list2);
extern List *list_difference_ptr(List *list1, List *list2);
extern List *list_difference_int(List *list1, List *list2);
......
This diff is collapsed.
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.91 2008/08/02 21:32:01 tgl Exp $
* $PostgreSQL: pgsql/src/include/optimizer/clauses.h,v 1.92 2008/08/14 18:48:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -59,6 +59,9 @@ extern bool contain_mutable_functions(Node *clause);
extern bool contain_volatile_functions(Node *clause);
extern bool contain_nonstrict_functions(Node *clause);
extern Relids find_nonnullable_rels(Node *clause);
extern List *find_nonnullable_vars(Node *clause);
extern List *find_forced_null_vars(Node *clause);
extern Var *find_forced_null_var(Node *clause);
extern bool is_pseudo_constant_clause(Node *clause);
extern bool is_pseudo_constant_clause_relids(Node *clause, Relids relids);
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.90 2008/01/01 19:45:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/cost.h,v 1.91 2008/08/14 18:48:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -87,9 +87,12 @@ extern void cost_group(Path *path, PlannerInfo *root,
int numGroupCols, double numGroups,
Cost input_startup_cost, Cost input_total_cost,
double input_tuples);
extern void cost_nestloop(NestPath *path, PlannerInfo *root);
extern void cost_mergejoin(MergePath *path, PlannerInfo *root);
extern void cost_hashjoin(HashPath *path, PlannerInfo *root);
extern void cost_nestloop(NestPath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
extern void cost_mergejoin(MergePath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
extern void cost_hashjoin(HashPath *path, PlannerInfo *root,
SpecialJoinInfo *sjinfo);
extern void cost_qual_eval(QualCost *cost, List *quals, PlannerInfo *root);
extern void cost_qual_eval_node(QualCost *cost, Node *qual, PlannerInfo *root);
extern Cost get_initplan_cost(PlannerInfo *root, SubPlan *subplan);
......@@ -97,7 +100,7 @@ extern void set_baserel_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern void set_joinrel_size_estimates(PlannerInfo *root, RelOptInfo *rel,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
List *restrictlist);
extern void set_function_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
......@@ -109,10 +112,12 @@ extern void set_values_size_estimates(PlannerInfo *root, RelOptInfo *rel);
extern Selectivity clauselist_selectivity(PlannerInfo *root,
List *clauses,
int varRelid,
JoinType jointype);
JoinType jointype,
SpecialJoinInfo *sjinfo);
extern Selectivity clause_selectivity(PlannerInfo *root,
Node *clause,
int varRelid,
JoinType jointype);
JoinType jointype,
SpecialJoinInfo *sjinfo);
#endif /* COST_H */
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.77 2008/01/01 19:45:58 momjian Exp $
* $PostgreSQL: pgsql/src/include/optimizer/pathnode.h,v 1.78 2008/08/14 18:48:00 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -50,7 +50,7 @@ extern AppendPath *create_append_path(RelOptInfo *rel, List *subpaths);
extern ResultPath *create_result_path(List *quals);
extern MaterialPath *create_material_path(RelOptInfo *rel, Path *subpath);
extern UniquePath *create_unique_path(PlannerInfo *root, RelOptInfo *rel,
Path *subpath);
Path *subpath, SpecialJoinInfo *sjinfo);
extern Path *create_subqueryscan_path(RelOptInfo *rel, List *pathkeys);
extern Path *create_functionscan_path(PlannerInfo *root, RelOptInfo *rel);
extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
......@@ -58,6 +58,7 @@ extern Path *create_valuesscan_path(PlannerInfo *root, RelOptInfo *rel);
extern NestPath *create_nestloop_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
......@@ -66,6 +67,7 @@ extern NestPath *create_nestloop_path(PlannerInfo *root,
extern MergePath *create_mergejoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
......@@ -77,6 +79,7 @@ extern MergePath *create_mergejoin_path(PlannerInfo *root,
extern HashPath *create_hashjoin_path(PlannerInfo *root,
RelOptInfo *joinrel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
Path *outer_path,
Path *inner_path,
List *restrict_clauses,
......@@ -93,7 +96,7 @@ extern RelOptInfo *build_join_rel(PlannerInfo *root,
Relids joinrelids,
RelOptInfo *outer_rel,
RelOptInfo *inner_rel,
JoinType jointype,
SpecialJoinInfo *sjinfo,
List **restrictlist_ptr);
#endif /* PATHNODE_H */
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment