Re: Performance improvement for joins where outer side is unique - Mailing list pgsql-hackers

From Kyotaro HORIGUCHI
Subject Re: Performance improvement for joins where outer side is unique
Date
Msg-id 20150318.173032.124713381.horiguchi.kyotaro@lab.ntt.co.jp
Whole thread Raw
In response to Re: Performance improvement for joins where outer side is unique  (Kyotaro HORIGUCHI <horiguchi.kyotaro@lab.ntt.co.jp>)
Responses Re: Performance improvement for joins where outer side is unique
List pgsql-hackers
Hello, The attached is non-search version of unique join.

It is not fully examined but looks to work as expected. Many
small changes make the patch larger but affected area is rather
small.

What do you think about this?

> Hello, I don't have enough time for now but made some
> considerations on this.
> 
> It might be a way that marking unique join peer at bottom and
> propagate up, not searching from top of join list.
> Around create_join_clause might be a candidate for it.
> I'll investigate that later.


regards,

-- 
Kyotaro Horiguchi
NTT Open Source Software Center
diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c
index a951c55..b8a68b5 100644
--- a/src/backend/commands/explain.c
+++ b/src/backend/commands/explain.c
@@ -1151,9 +1151,16 @@ ExplainNode(PlanState *planstate, List *ancestors,
appendStringInfo(es->str," %s Join", jointype);                    else if (!IsA(plan, NestLoop))
appendStringInfoString(es->str, " Join");
 
+                    if (((Join *)plan)->inner_unique)
+                        appendStringInfoString(es->str, "(inner unique)");
+                }                else
+                {                    ExplainPropertyText("Join Type", jointype, es);
+                    ExplainPropertyText("Inner unique", 
+                            ((Join *)plan)->inner_unique?"true":"false", es);
+                }            }            break;        case T_SetOp:
diff --git a/src/backend/executor/nodeHashjoin.c b/src/backend/executor/nodeHashjoin.c
index 1d78cdf..d3b14e5 100644
--- a/src/backend/executor/nodeHashjoin.c
+++ b/src/backend/executor/nodeHashjoin.c
@@ -306,10 +306,11 @@ ExecHashJoin(HashJoinState *node)                    }                    /*
-                     * In a semijoin, we'll consider returning the first
-                     * match, but after that we're done with this outer tuple.
+                     * We'll consider returning the first match if the inner
+                     * is unique, but after that we're done with this outer
+                     * tuple.                     */
-                    if (node->js.jointype == JOIN_SEMI)
+                    if (node->js.inner_unique)                        node->hj_JoinState = HJ_NEED_NEW_OUTER;
         if (otherqual == NIL ||
 
@@ -451,6 +452,7 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)    hjstate = makeNode(HashJoinState);
 hjstate->js.ps.plan = (Plan *) node;    hjstate->js.ps.state = estate;
 
+    hjstate->js.inner_unique = node->join.inner_unique;    /*     * Miscellaneous initialization
@@ -498,8 +500,10 @@ ExecInitHashJoin(HashJoin *node, EState *estate, int eflags)    /* set up null tuples for outer
joins,if needed */    switch (node->join.jointype)    {
 
-        case JOIN_INNER:        case JOIN_SEMI:
+            hjstate->js.inner_unique = true;
+            /* fall through */
+        case JOIN_INNER:            break;        case JOIN_LEFT:        case JOIN_ANTI:
diff --git a/src/backend/executor/nodeMergejoin.c b/src/backend/executor/nodeMergejoin.c
index 15742c5..3c21ffe 100644
--- a/src/backend/executor/nodeMergejoin.c
+++ b/src/backend/executor/nodeMergejoin.c
@@ -840,10 +840,11 @@ ExecMergeJoin(MergeJoinState *node)                    }                    /*
-                     * In a semijoin, we'll consider returning the first
-                     * match, but after that we're done with this outer tuple.
+                     * We'll consider returning the first match if the inner
+                     * is unique, but after that we're done with this outer
+                     * tuple.                     */
-                    if (node->js.jointype == JOIN_SEMI)
+                    if (node->js.inner_unique)                        node->mj_JoinState = EXEC_MJ_NEXTOUTER;
         qualResult = (otherqual == NIL ||
 
@@ -1486,6 +1487,8 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)    mergestate->js.ps.plan = (Plan
*)node;    mergestate->js.ps.state = estate;
 
+    mergestate->js.inner_unique = node->join.inner_unique;
+    /*     * Miscellaneous initialization     *
@@ -1553,8 +1556,10 @@ ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)    switch (node->join.jointype)
{
 
-        case JOIN_INNER:        case JOIN_SEMI:
+            mergestate->js.inner_unique = true;
+            /* fall through */
+        case JOIN_INNER:            mergestate->mj_FillOuter = false;            mergestate->mj_FillInner = false;
      break;
 
diff --git a/src/backend/executor/nodeNestloop.c b/src/backend/executor/nodeNestloop.c
index e66bcda..342c448 100644
--- a/src/backend/executor/nodeNestloop.c
+++ b/src/backend/executor/nodeNestloop.c
@@ -247,10 +247,10 @@ ExecNestLoop(NestLoopState *node)            }            /*
-             * In a semijoin, we'll consider returning the first match, but
-             * after that we're done with this outer tuple.
+             * We'll consider returning the first match if the inner is
+             * unique, but after that we're done with this outer tuple.             */
-            if (node->js.jointype == JOIN_SEMI)
+            if (node->js.inner_unique)                node->nl_NeedNewOuter = true;            if (otherqual == NIL ||
ExecQual(otherqual,econtext, false))
 
@@ -310,6 +310,8 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)    nlstate->js.ps.plan = (Plan *)
node;   nlstate->js.ps.state = estate;
 
+    nlstate->js.inner_unique = node->join.inner_unique;
+    /*     * Miscellaneous initialization     *
@@ -354,8 +356,10 @@ ExecInitNestLoop(NestLoop *node, EState *estate, int eflags)    switch (node->join.jointype)    {
-        case JOIN_INNER:        case JOIN_SEMI:
+            nlstate->js.inner_unique = true;
+            /* fall through */
+        case JOIN_INNER:            break;        case JOIN_LEFT:        case JOIN_ANTI:
diff --git a/src/backend/optimizer/path/joinpath.c b/src/backend/optimizer/path/joinpath.c
index 1da953f..8363216 100644
--- a/src/backend/optimizer/path/joinpath.c
+++ b/src/backend/optimizer/path/joinpath.c
@@ -26,18 +26,21 @@    ((path)->param_info && bms_overlap(PATH_REQ_OUTER(path), (rel)->relids))static void
sort_inner_and_outer(PlannerInfo*root, RelOptInfo *joinrel,
 
-                     RelOptInfo *outerrel, RelOptInfo *innerrel,
+                     RelOptInfo *outerrel,
+                     RelOptInfo *innerrel, bool inner_unique,                     List *restrictlist, List
*mergeclause_list,                    JoinType jointype, SpecialJoinInfo *sjinfo,                     Relids
param_source_rels,Relids extra_lateral_rels);static void match_unsorted_outer(PlannerInfo *root, RelOptInfo *joinrel,
 
-                     RelOptInfo *outerrel, RelOptInfo *innerrel,
+                     RelOptInfo *outerrel,
+                     RelOptInfo *innerrel, bool inner_unique,                     List *restrictlist, List
*mergeclause_list,                    JoinType jointype, SpecialJoinInfo *sjinfo,
SemiAntiJoinFactors*semifactors,                     Relids param_source_rels, Relids extra_lateral_rels);static void
hash_inner_and_outer(PlannerInfo*root, RelOptInfo *joinrel,
 
-                     RelOptInfo *outerrel, RelOptInfo *innerrel,
+                     RelOptInfo *outerrel,
+                     RelOptInfo *innerrel, bool inner_unique,                     List *restrictlist,
  JoinType jointype, SpecialJoinInfo *sjinfo,                     SemiAntiJoinFactors *semifactors,
 
@@ -49,7 +52,8 @@ static List *select_mergejoin_clauses(PlannerInfo *root,                         List *restrictlist,
                      JoinType jointype,                         bool *mergejoin_allowed);
 
-
+static inline bool clause_sides_match_join(RestrictInfo *rinfo, RelOptInfo *outerrel,
+                                           RelOptInfo *innerrel);/* * add_paths_to_joinrel
@@ -89,6 +93,7 @@ add_paths_to_joinrel(PlannerInfo *root,    Relids        param_source_rels = NULL;    Relids
extra_lateral_rels= NULL;    ListCell   *lc;
 
+    bool        inner_unique = false;    /*     * Find potential mergejoin clauses.  We can skip this if we are not
@@ -115,6 +120,31 @@ add_paths_to_joinrel(PlannerInfo *root,                                       &semifactors);
/*
+     * We can optimize inner loop execution for joins on which the inner rel
+     * is unique on the restrictlist.
+     */
+    if (jointype == JOIN_INNER &&
+         innerrel->rtekind == RTE_RELATION &&
+        restrictlist)
+    {
+        /* relation_has_unique_index_for adds some restrictions */
+        int org_len = list_length(restrictlist);
+        ListCell *lc;
+
+        foreach (lc, restrictlist)
+        {
+            clause_sides_match_join((RestrictInfo *) lfirst(lc),
+                                    outerrel, innerrel);
+        }
+        if (relation_has_unique_index_for(root, innerrel, restrictlist,
+                                          NIL, NIL))
+            inner_unique = true;
+
+        /* Remove restirictions added by the function */
+        list_truncate(restrictlist, org_len);
+    }
+
+    /*     * Decide whether it's sensible to generate parameterized paths for this     * joinrel, and if so, which
relationssuch paths should require.  There     * is usually no need to create a parameterized result path unless there
 
@@ -212,7 +242,7 @@ add_paths_to_joinrel(PlannerInfo *root,     * sorted.  Skip this if we can't mergejoin.     */
if(mergejoin_allowed)
 
-        sort_inner_and_outer(root, joinrel, outerrel, innerrel,
+        sort_inner_and_outer(root, joinrel, outerrel, innerrel, inner_unique,
restrictlist,mergeclause_list, jointype,                             sjinfo,
param_source_rels,extra_lateral_rels); 
@@ -225,7 +255,7 @@ add_paths_to_joinrel(PlannerInfo *root,     * joins at all, so it wouldn't work in the prohibited
caseseither.)     */    if (mergejoin_allowed)
 
-        match_unsorted_outer(root, joinrel, outerrel, innerrel,
+        match_unsorted_outer(root, joinrel, outerrel, innerrel, inner_unique,
restrictlist,mergeclause_list, jointype,                             sjinfo, &semifactors,
param_source_rels,extra_lateral_rels);
 
@@ -256,7 +286,7 @@ add_paths_to_joinrel(PlannerInfo *root,     * joins, because there may be no other alternative.
*/   if (enable_hashjoin || jointype == JOIN_FULL)
 
-        hash_inner_and_outer(root, joinrel, outerrel, innerrel,
+        hash_inner_and_outer(root, joinrel, outerrel, innerrel, inner_unique,
restrictlist,jointype,                             sjinfo, &semifactors,                             param_source_rels,
extra_lateral_rels);
@@ -277,6 +307,7 @@ try_nestloop_path(PlannerInfo *root,                  Relids extra_lateral_rels,
Path*outer_path,                  Path *inner_path,
 
+                  bool  inner_unique,                  List *restrict_clauses,                  List *pathkeys){
@@ -349,6 +380,7 @@ try_nestloop_path(PlannerInfo *root,                                      semifactors,
                       outer_path,                                      inner_path,
 
+                                      inner_unique,                                      restrict_clauses,
                        pathkeys,                                      required_outer));
 
@@ -374,6 +406,7 @@ try_mergejoin_path(PlannerInfo *root,                   Relids extra_lateral_rels,
Path *outer_path,                   Path *inner_path,
 
+                   bool     inner_unique,                   List *restrict_clauses,                   List *pathkeys,
                List *mergeclauses,
 
@@ -434,6 +467,7 @@ try_mergejoin_path(PlannerInfo *root,                                       sjinfo,
                     outer_path,                                       inner_path,
 
+                                       inner_unique,                                       restrict_clauses,
                           pathkeys,                                       required_outer,
 
@@ -463,6 +497,7 @@ try_hashjoin_path(PlannerInfo *root,                  Relids extra_lateral_rels,
Path*outer_path,                  Path *inner_path,
 
+                  bool  inner_unique,                  List *restrict_clauses,                  List *hashclauses){
@@ -510,6 +545,7 @@ try_hashjoin_path(PlannerInfo *root,                                      semifactors,
                       outer_path,                                      inner_path,
 
+                                      inner_unique,                                      restrict_clauses,
                        required_outer,                                      hashclauses));
 
@@ -574,6 +610,7 @@ sort_inner_and_outer(PlannerInfo *root,                     RelOptInfo *joinrel,
RelOptInfo *outerrel,                     RelOptInfo *innerrel,
 
+                     bool inner_unique,                     List *restrictlist,                     List
*mergeclause_list,                    JoinType jointype,
 
@@ -629,6 +666,7 @@ sort_inner_and_outer(PlannerInfo *root,                                                 inner_path,
sjinfo);       Assert(inner_path);        jointype = JOIN_INNER;
 
+        inner_unique = true;    }    /*
@@ -712,6 +750,7 @@ sort_inner_and_outer(PlannerInfo *root,                           extra_lateral_rels,
           outer_path,                           inner_path,
 
+                           inner_unique,                           restrictlist,
merge_pathkeys,                          cur_mergeclauses,
 
@@ -762,6 +801,7 @@ match_unsorted_outer(PlannerInfo *root,                     RelOptInfo *joinrel,
RelOptInfo *outerrel,                     RelOptInfo *innerrel,
 
+                     bool  inner_unique,                     List *restrictlist,                     List
*mergeclause_list,                    JoinType jointype,
 
@@ -832,6 +872,7 @@ match_unsorted_outer(PlannerInfo *root,        inner_cheapest_total = (Path *)
create_unique_path(root,innerrel, inner_cheapest_total, sjinfo);        Assert(inner_cheapest_total);
 
+        inner_unique = true;    }    else if (nestjoinOK)    {
@@ -901,6 +942,7 @@ match_unsorted_outer(PlannerInfo *root,                              extra_lateral_rels,
                 outerpath,                              inner_cheapest_total,
 
+                              inner_unique,                              restrictlist,
merge_pathkeys);       }
 
@@ -927,6 +969,7 @@ match_unsorted_outer(PlannerInfo *root,                                  extra_lateral_rels,
                         outerpath,                                  innerpath,
 
+                                  inner_unique,                                  restrictlist,
        merge_pathkeys);            }
 
@@ -942,6 +985,7 @@ match_unsorted_outer(PlannerInfo *root,                                  extra_lateral_rels,
                         outerpath,                                  matpath,
 
+                                  inner_unique,                                  restrictlist,
        merge_pathkeys);        }
 
@@ -998,6 +1042,7 @@ match_unsorted_outer(PlannerInfo *root,                           extra_lateral_rels,
            outerpath,                           inner_cheapest_total,
 
+                           inner_unique,                           restrictlist,
merge_pathkeys,                          mergeclauses,
 
@@ -1097,6 +1142,7 @@ match_unsorted_outer(PlannerInfo *root,                                   extra_lateral_rels,
                             outerpath,                                   innerpath,
 
+                                   inner_unique,                                   restrictlist,
           merge_pathkeys,                                   newclauses,
 
@@ -1143,6 +1189,7 @@ match_unsorted_outer(PlannerInfo *root,                                       extra_lateral_rels,
                                     outerpath,                                       innerpath,
 
+                                       inner_unique,                                       restrictlist,
                       merge_pathkeys,                                       newclauses,
 
@@ -1182,6 +1229,7 @@ hash_inner_and_outer(PlannerInfo *root,                     RelOptInfo *joinrel,
  RelOptInfo *outerrel,                     RelOptInfo *innerrel,
 
+                     bool inner_unique,                     List *restrictlist,                     JoinType jointype,
                   SpecialJoinInfo *sjinfo,
 
@@ -1264,6 +1312,7 @@ hash_inner_and_outer(PlannerInfo *root,                              extra_lateral_rels,
                   cheapest_total_outer,                              cheapest_total_inner,
 
+                              inner_unique,                              restrictlist,
hashclauses);           /* no possibility of cheap startup here */
 
@@ -1284,6 +1333,7 @@ hash_inner_and_outer(PlannerInfo *root,                              extra_lateral_rels,
                   cheapest_total_outer,                              cheapest_total_inner,
 
+                              true,                              restrictlist,
hashclauses);           if (cheapest_startup_outer != NULL &&
 
@@ -1297,6 +1347,7 @@ hash_inner_and_outer(PlannerInfo *root,                                  extra_lateral_rels,
                           cheapest_startup_outer,                                  cheapest_total_inner,
 
+                                  true,                                  restrictlist,
hashclauses);        }
 
@@ -1322,6 +1373,7 @@ hash_inner_and_outer(PlannerInfo *root,                                  extra_lateral_rels,
                           cheapest_startup_outer,                                  cheapest_total_inner,
 
+                                  inner_unique,                                  restrictlist,
        hashclauses);
 
@@ -1360,6 +1412,7 @@ hash_inner_and_outer(PlannerInfo *root,                                      extra_lateral_rels,
                                   outerpath,                                      innerpath,
 
+                                      inner_unique,                                      restrictlist,
                    hashclauses);                }
 
diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c
index cb69c03..448c556 100644
--- a/src/backend/optimizer/plan/createplan.c
+++ b/src/backend/optimizer/plan/createplan.c
@@ -131,12 +131,12 @@ static BitmapAnd *make_bitmap_and(List *bitmapplans);static BitmapOr *make_bitmap_or(List
*bitmapplans);staticNestLoop *make_nestloop(List *tlist,              List *joinclauses, List *otherclauses, List
*nestParams,
-              Plan *lefttree, Plan *righttree,
+              Plan *lefttree, Plan *righttree, bool inner_unique,              JoinType jointype);static HashJoin
*make_hashjoin(List*tlist,              List *joinclauses, List *otherclauses,              List *hashclauses,
 
-              Plan *lefttree, Plan *righttree,
+              Plan *lefttree, Plan *righttree, bool inner_unique,              JoinType jointype);static Hash
*make_hash(Plan*lefttree,          Oid skewTable,
 
@@ -151,7 +151,7 @@ static MergeJoin *make_mergejoin(List *tlist,               Oid *mergecollations,               int
*mergestrategies,              bool *mergenullsfirst,
 
-               Plan *lefttree, Plan *righttree,
+               Plan *lefttree, Plan *righttree, bool inner_unique,               JoinType jointype);static Sort
*make_sort(PlannerInfo*root, Plan *lefttree, int numCols,          AttrNumber *sortColIdx, Oid *sortOperators,
 
@@ -2192,6 +2192,7 @@ create_nestloop_plan(PlannerInfo *root,                              nestParams,
           outer_plan,                              inner_plan,
 
+                              best_path->inner_unique,                              best_path->jointype);
copy_path_costsize(&join_plan->join.plan,&best_path->path);
 
@@ -2486,6 +2487,7 @@ create_mergejoin_plan(PlannerInfo *root,                               mergenullsfirst,
                   outer_plan,                               inner_plan,
 
+                               best_path->jpath.inner_unique,
best_path->jpath.jointype);   /* Costs of sort and material steps are included in path cost already */
 
@@ -2612,6 +2614,7 @@ create_hashjoin_plan(PlannerInfo *root,                              hashclauses,
            outer_plan,                              (Plan *) hash_plan,
 
+                              best_path->jpath.inner_unique,                              best_path->jpath.jointype);
 copy_path_costsize(&join_plan->join.plan, &best_path->jpath.path);
 
@@ -3717,6 +3720,7 @@ make_nestloop(List *tlist,              List *nestParams,              Plan *lefttree,
 Plan *righttree,
 
+              bool  inner_unique,              JoinType jointype){    NestLoop   *node = makeNode(NestLoop);
@@ -3729,6 +3733,7 @@ make_nestloop(List *tlist,    plan->righttree = righttree;    node->join.jointype = jointype;
node->join.joinqual= joinclauses;
 
+    node->join.inner_unique = inner_unique;    node->nestParams = nestParams;    return node;
@@ -3741,6 +3746,7 @@ make_hashjoin(List *tlist,              List *hashclauses,              Plan *lefttree,
  Plan *righttree,
 
+              bool  inner_unique,              JoinType jointype){    HashJoin   *node = makeNode(HashJoin);
@@ -3754,6 +3760,7 @@ make_hashjoin(List *tlist,    node->hashclauses = hashclauses;    node->join.jointype = jointype;
  node->join.joinqual = joinclauses;
 
+    node->join.inner_unique = inner_unique;    return node;}
@@ -3801,6 +3808,7 @@ make_mergejoin(List *tlist,               bool *mergenullsfirst,               Plan *lefttree,
          Plan *righttree,
 
+               bool inner_unique,               JoinType jointype){    MergeJoin  *node = makeNode(MergeJoin);
@@ -3818,6 +3826,7 @@ make_mergejoin(List *tlist,    node->mergeNullsFirst = mergenullsfirst;    node->join.jointype =
jointype;   node->join.joinqual = joinclauses;
 
+    node->join.inner_unique = inner_unique;    return node;}
@@ -4586,7 +4595,6 @@ make_unique(Plan *lefttree, List *distinctList)    node->numCols = numCols;    node->uniqColIdx =
uniqColIdx;   node->uniqOperators = uniqOperators;
 
-    return node;}
diff --git a/src/backend/optimizer/util/pathnode.c b/src/backend/optimizer/util/pathnode.c
index faca30b..299a51d 100644
--- a/src/backend/optimizer/util/pathnode.c
+++ b/src/backend/optimizer/util/pathnode.c
@@ -1135,8 +1135,8 @@ create_unique_path(PlannerInfo *root, RelOptInfo *rel, Path *subpath,     */    if (rel->rtekind
==RTE_RELATION && sjinfo->semi_can_btree &&        relation_has_unique_index_for(root, rel, NIL,
 
-                                      sjinfo->semi_rhs_exprs,
-                                      sjinfo->semi_operators))
+                                       sjinfo->semi_rhs_exprs,
+                                       sjinfo->semi_operators))    {        pathnode->umethod = UNIQUE_PATH_NOOP;
 pathnode->path.rows = rel->rows;
 
@@ -1534,6 +1534,7 @@ create_nestloop_path(PlannerInfo *root,                     SemiAntiJoinFactors *semifactors,
               Path *outer_path,                     Path *inner_path,
 
+                     bool  inner_unique,                     List *restrict_clauses,                     List
*pathkeys,                    Relids required_outer)
 
@@ -1581,6 +1582,7 @@ create_nestloop_path(PlannerInfo *root,    pathnode->jointype = jointype;
pathnode->outerjoinpath= outer_path;    pathnode->innerjoinpath = inner_path;
 
+    pathnode->inner_unique = inner_unique;    pathnode->joinrestrictinfo = restrict_clauses;
final_cost_nestloop(root,pathnode, workspace, sjinfo, semifactors);
 
@@ -1615,6 +1617,7 @@ create_mergejoin_path(PlannerInfo *root,                      SpecialJoinInfo *sjinfo,
         Path *outer_path,                      Path *inner_path,
 
+                      bool  inner_unique,                      List *restrict_clauses,                      List
*pathkeys,                     Relids required_outer,
 
@@ -1638,6 +1641,7 @@ create_mergejoin_path(PlannerInfo *root,    pathnode->jpath.jointype = jointype;
pathnode->jpath.outerjoinpath= outer_path;    pathnode->jpath.innerjoinpath = inner_path;
 
+    pathnode->jpath.inner_unique = inner_unique;    pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->path_mergeclauses= mergeclauses;    pathnode->outersortkeys = outersortkeys;
 
@@ -1674,6 +1678,7 @@ create_hashjoin_path(PlannerInfo *root,                     SemiAntiJoinFactors *semifactors,
               Path *outer_path,                     Path *inner_path,
 
+                     bool  inner_unique,                     List *restrict_clauses,                     Relids
required_outer,                    List *hashclauses)
 
@@ -1706,6 +1711,7 @@ create_hashjoin_path(PlannerInfo *root,    pathnode->jpath.jointype = jointype;
pathnode->jpath.outerjoinpath= outer_path;    pathnode->jpath.innerjoinpath = inner_path;
 
+    pathnode->jpath.inner_unique = inner_unique;    pathnode->jpath.joinrestrictinfo = restrict_clauses;
pathnode->path_hashclauses= hashclauses;    /* final_cost_hashjoin will fill in pathnode->num_batches */
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 59b17f3..f86f806 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -1562,6 +1562,7 @@ typedef struct JoinState    PlanState    ps;    JoinType    jointype;    List       *joinqual;
   /* JOIN quals (in addition to ps.qual) */
 
+    bool        inner_unique;} JoinState;/* ----------------
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index 21cbfa8..122f2f4 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -543,6 +543,7 @@ typedef struct Join    Plan        plan;    JoinType    jointype;    List       *joinqual;
/*JOIN quals (in addition to plan.qual) */
 
+    bool        inner_unique;} Join;/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index 334cf51..c1ebfdb 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -1030,6 +1030,7 @@ typedef struct JoinPath    Path       *outerjoinpath;    /* path for the outer side of the join
*/   Path       *innerjoinpath;    /* path for the inner side of the join */
 
+    bool        inner_unique;    List       *joinrestrictinfo;        /* RestrictInfos to apply to join */
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 9923f0e..cefcecc 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -94,6 +94,7 @@ extern NestPath *create_nestloop_path(PlannerInfo *root,                     SemiAntiJoinFactors
*semifactors,                    Path *outer_path,                     Path *inner_path,
 
+                     bool  inner_unique,                     List *restrict_clauses,                     List
*pathkeys,                    Relids required_outer);
 
@@ -105,6 +106,7 @@ extern MergePath *create_mergejoin_path(PlannerInfo *root,                      SpecialJoinInfo
*sjinfo,                     Path *outer_path,                      Path *inner_path,
 
+                      bool  inner_unique,                      List *restrict_clauses,                      List
*pathkeys,                     Relids required_outer,
 
@@ -120,6 +122,7 @@ extern HashPath *create_hashjoin_path(PlannerInfo *root,                     SemiAntiJoinFactors
*semifactors,                    Path *outer_path,                     Path *inner_path,
 
+                     bool  inner_unique,                     List *restrict_clauses,                     Relids
required_outer,                    List *hashclauses);
 
diff --git a/src/test/regress/expected/equivclass.out b/src/test/regress/expected/equivclass.out
index dfae84e..ad1d673 100644
--- a/src/test/regress/expected/equivclass.out
+++ b/src/test/regress/expected/equivclass.out
@@ -186,7 +186,7 @@ explain (costs off)  select * from ec1, ec2 where ff = x1 and x1 = '42'::int8alias2;
QUERYPLAN                -----------------------------------------
 
- Nested Loop
+ Nested Loop(inner unique)   ->  Seq Scan on ec2         Filter: (x1 = '42'::int8alias2)   ->  Index Scan using
ec1_pkeyon ec1
 
@@ -310,7 +310,7 @@ explain (costs off)         ->  Index Scan using ec1_expr3 on ec1 ec1_5         ->  Index Scan
usingec1_expr4 on ec1 ec1_6   ->  Materialize
 
-         ->  Merge Join
+         ->  Merge Join(inner unique)               Merge Cond: ((((ec1_1.ff + 2) + 1)) = ec1.f1)               ->
MergeAppend                     Sort Key: (((ec1_1.ff + 2) + 1))
 
@@ -365,7 +365,7 @@ explain (costs off)  where ss1.x = ec1.f1 and ec1.ff = 42::int8;                     QUERY PLAN
                ----------------------------------------------------- 
- Merge Join
+ Merge Join(inner unique)   Merge Cond: ((((ec1_1.ff + 2) + 1)) = ec1.f1)   ->  Merge Append         Sort Key:
(((ec1_1.ff+ 2) + 1))
 
diff --git a/src/test/regress/expected/join.out b/src/test/regress/expected/join.out
index 57fc910..b6e3024 100644
--- a/src/test/regress/expected/join.out
+++ b/src/test/regress/expected/join.out
@@ -2614,8 +2614,8 @@ from nt3 as nt3where nt3.id = 1 and ss2.b3;                  QUERY PLAN
-----------------------------------------------
- Nested Loop
-   ->  Nested Loop
+ Nested Loop(inner unique)
+   ->  Nested Loop(inner unique)         ->  Index Scan using nt3_pkey on nt3               Index Cond: (id = 1)
 ->  Index Scan using nt2_pkey on nt2
 
diff --git a/src/test/regress/expected/rowsecurity.out b/src/test/regress/expected/rowsecurity.out
index f41bef1..aaf585d 100644
--- a/src/test/regress/expected/rowsecurity.out
+++ b/src/test/regress/expected/rowsecurity.out
@@ -248,7 +248,7 @@ EXPLAIN (COSTS OFF) SELECT * FROM document WHERE f_leak(dtitle);EXPLAIN (COSTS OFF) SELECT * FROM
documentNATURAL JOIN category WHERE f_leak(dtitle);                     QUERY PLAN
----------------------------------------------------
- Nested Loop
+ Nested Loop(inner unique)   ->  Subquery Scan on document         Filter: f_leak(document.dtitle)         ->  Seq
Scanon document document_1 

pgsql-hackers by date:

Previous
From: Andres Freund
Date:
Subject: Re: Using 128-bit integers for sum, avg and statistics aggregates
Next
From: Abhijit Menon-Sen
Date:
Subject: Re: MD5 authentication needs help -SCRAM