Coverage Report

Created: 2025-06-13 06:06

/src/postgres/src/backend/executor/nodeMergejoin.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * nodeMergejoin.c
4
 *    routines supporting merge joins
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/backend/executor/nodeMergejoin.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
/*
16
 * INTERFACE ROUTINES
17
 *    ExecMergeJoin     mergejoin outer and inner relations.
18
 *    ExecInitMergeJoin   creates and initializes run time states
19
 *    ExecEndMergeJoin    cleans up the node.
20
 *
21
 * NOTES
22
 *
23
 *    Merge-join is done by joining the inner and outer tuples satisfying
24
 *    join clauses of the form ((= outerKey innerKey) ...).
25
 *    The join clause list is provided by the query planner and may contain
26
 *    more than one (= outerKey innerKey) clause (for composite sort key).
27
 *
28
 *    However, the query executor needs to know whether an outer
29
 *    tuple is "greater/smaller" than an inner tuple so that it can
30
 *    "synchronize" the two relations. For example, consider the following
31
 *    relations:
32
 *
33
 *        outer: (0 ^1 1 2 5 5 5 6 6 7) current tuple: 1
34
 *        inner: (1 ^3 5 5 5 5 6)     current tuple: 3
35
 *
36
 *    To continue the merge-join, the executor needs to scan both inner
37
 *    and outer relations till the matching tuples 5. It needs to know
38
 *    that currently inner tuple 3 is "greater" than outer tuple 1 and
39
 *    therefore it should scan the outer relation first to find a
40
 *    matching tuple and so on.
41
 *
42
 *    Therefore, rather than directly executing the merge join clauses,
43
 *    we evaluate the left and right key expressions separately and then
44
 *    compare the columns one at a time (see MJCompare).  The planner
45
 *    passes us enough information about the sort ordering of the inputs
46
 *    to allow us to determine how to make the comparison.  We may use the
47
 *    appropriate btree comparison function, since Postgres' only notion
48
 *    of ordering is specified by btree opfamilies.
49
 *
50
 *
51
 *    Consider the above relations and suppose that the executor has
52
 *    just joined the first outer "5" with the last inner "5". The
53
 *    next step is of course to join the second outer "5" with all
54
 *    the inner "5's". This requires repositioning the inner "cursor"
55
 *    to point at the first inner "5". This is done by "marking" the
56
 *    first inner 5 so we can restore the "cursor" to it before joining
57
 *    with the second outer 5. The access method interface provides
58
 *    routines to mark and restore to a tuple.
59
 *
60
 *
61
 *    Essential operation of the merge join algorithm is as follows:
62
 *
63
 *    Join {
64
 *      get initial outer and inner tuples        INITIALIZE
65
 *      do forever {
66
 *        while (outer != inner) {          SKIP_TEST
67
 *          if (outer < inner)
68
 *            advance outer           SKIPOUTER_ADVANCE
69
 *          else
70
 *            advance inner           SKIPINNER_ADVANCE
71
 *        }
72
 *        mark inner position             SKIP_TEST
73
 *        do forever {
74
 *          while (outer == inner) {
75
 *            join tuples             JOINTUPLES
76
 *            advance inner position        NEXTINNER
77
 *          }
78
 *          advance outer position          NEXTOUTER
79
 *          if (outer == mark)            TESTOUTER
80
 *            restore inner position to mark    TESTOUTER
81
 *          else
82
 *            break // return to top of outer loop
83
 *        }
84
 *      }
85
 *    }
86
 *
87
 *    The merge join operation is coded in the fashion
88
 *    of a state machine.  At each state, we do something and then
89
 *    proceed to another state.  This state is stored in the node's
90
 *    execution state information and is preserved across calls to
91
 *    ExecMergeJoin. -cim 10/31/89
92
 */
93
#include "postgres.h"
94
95
#include "access/nbtree.h"
96
#include "executor/execdebug.h"
97
#include "executor/nodeMergejoin.h"
98
#include "miscadmin.h"
99
#include "utils/lsyscache.h"
100
101
102
/*
103
 * States of the ExecMergeJoin state machine
104
 */
105
0
#define EXEC_MJ_INITIALIZE_OUTER    1
106
0
#define EXEC_MJ_INITIALIZE_INNER    2
107
0
#define EXEC_MJ_JOINTUPLES        3
108
0
#define EXEC_MJ_NEXTOUTER       4
109
0
#define EXEC_MJ_TESTOUTER       5
110
0
#define EXEC_MJ_NEXTINNER       6
111
0
#define EXEC_MJ_SKIP_TEST       7
112
0
#define EXEC_MJ_SKIPOUTER_ADVANCE   8
113
0
#define EXEC_MJ_SKIPINNER_ADVANCE   9
114
0
#define EXEC_MJ_ENDOUTER        10
115
0
#define EXEC_MJ_ENDINNER        11
116
117
/*
118
 * Runtime data for each mergejoin clause
119
 */
120
typedef struct MergeJoinClauseData
121
{
122
  /* Executable expression trees */
123
  ExprState  *lexpr;      /* left-hand (outer) input expression */
124
  ExprState  *rexpr;      /* right-hand (inner) input expression */
125
126
  /*
127
   * If we have a current left or right input tuple, the values of the
128
   * expressions are loaded into these fields:
129
   */
130
  Datum   ldatum;     /* current left-hand value */
131
  Datum   rdatum;     /* current right-hand value */
132
  bool    lisnull;    /* and their isnull flags */
133
  bool    risnull;
134
135
  /*
136
   * Everything we need to know to compare the left and right values is
137
   * stored here.
138
   */
139
  SortSupportData ssup;
140
}     MergeJoinClauseData;
141
142
/* Result type for MJEvalOuterValues and MJEvalInnerValues */
143
typedef enum
144
{
145
  MJEVAL_MATCHABLE,     /* normal, potentially matchable tuple */
146
  MJEVAL_NONMATCHABLE,    /* tuple cannot join because it has a null */
147
  MJEVAL_ENDOFJOIN,     /* end of input (physical or effective) */
148
} MJEvalResult;
149
150
151
#define MarkInnerTuple(innerTupleSlot, mergestate) \
152
0
  ExecCopySlot((mergestate)->mj_MarkedTupleSlot, (innerTupleSlot))
153
154
155
/*
156
 * MJExamineQuals
157
 *
158
 * This deconstructs the list of mergejoinable expressions, which is given
159
 * to us by the planner in the form of a list of "leftexpr = rightexpr"
160
 * expression trees in the order matching the sort columns of the inputs.
161
 * We build an array of MergeJoinClause structs containing the information
162
 * we will need at runtime.  Each struct essentially tells us how to compare
163
 * the two expressions from the original clause.
164
 *
165
 * In addition to the expressions themselves, the planner passes the btree
166
 * opfamily OID, collation OID, btree strategy number (BTLessStrategyNumber or
167
 * BTGreaterStrategyNumber), and nulls-first flag that identify the intended
168
 * sort ordering for each merge key.  The mergejoinable operator is an
169
 * equality operator in the opfamily, and the two inputs are guaranteed to be
170
 * ordered in either increasing or decreasing (respectively) order according
171
 * to the opfamily and collation, with nulls at the indicated end of the range.
172
 * This allows us to obtain the needed comparison function from the opfamily.
173
 */
174
static MergeJoinClause
175
MJExamineQuals(List *mergeclauses,
176
         Oid *mergefamilies,
177
         Oid *mergecollations,
178
         bool *mergereversals,
179
         bool *mergenullsfirst,
180
         PlanState *parent)
181
0
{
182
0
  MergeJoinClause clauses;
183
0
  int     nClauses = list_length(mergeclauses);
184
0
  int     iClause;
185
0
  ListCell   *cl;
186
187
0
  clauses = (MergeJoinClause) palloc0(nClauses * sizeof(MergeJoinClauseData));
188
189
0
  iClause = 0;
190
0
  foreach(cl, mergeclauses)
191
0
  {
192
0
    OpExpr     *qual = (OpExpr *) lfirst(cl);
193
0
    MergeJoinClause clause = &clauses[iClause];
194
0
    Oid     opfamily = mergefamilies[iClause];
195
0
    Oid     collation = mergecollations[iClause];
196
0
    bool    reversed = mergereversals[iClause];
197
0
    bool    nulls_first = mergenullsfirst[iClause];
198
0
    int     op_strategy;
199
0
    Oid     op_lefttype;
200
0
    Oid     op_righttype;
201
0
    Oid     sortfunc;
202
203
0
    if (!IsA(qual, OpExpr))
204
0
      elog(ERROR, "mergejoin clause is not an OpExpr");
205
206
    /*
207
     * Prepare the input expressions for execution.
208
     */
209
0
    clause->lexpr = ExecInitExpr((Expr *) linitial(qual->args), parent);
210
0
    clause->rexpr = ExecInitExpr((Expr *) lsecond(qual->args), parent);
211
212
    /* Set up sort support data */
213
0
    clause->ssup.ssup_cxt = CurrentMemoryContext;
214
0
    clause->ssup.ssup_collation = collation;
215
0
    clause->ssup.ssup_reverse = reversed;
216
0
    clause->ssup.ssup_nulls_first = nulls_first;
217
218
    /* Extract the operator's declared left/right datatypes */
219
0
    get_op_opfamily_properties(qual->opno, opfamily, false,
220
0
                   &op_strategy,
221
0
                   &op_lefttype,
222
0
                   &op_righttype);
223
0
    if (IndexAmTranslateStrategy(op_strategy, get_opfamily_method(opfamily), opfamily, true) != COMPARE_EQ) /* should not happen */
224
0
      elog(ERROR, "cannot merge using non-equality operator %u",
225
0
         qual->opno);
226
227
    /*
228
     * sortsupport routine must know if abbreviation optimization is
229
     * applicable in principle.  It is never applicable for merge joins
230
     * because there is no convenient opportunity to convert to
231
     * alternative representation.
232
     */
233
0
    clause->ssup.abbreviate = false;
234
235
    /* And get the matching support or comparison function */
236
0
    Assert(clause->ssup.comparator == NULL);
237
0
    sortfunc = get_opfamily_proc(opfamily,
238
0
                   op_lefttype,
239
0
                   op_righttype,
240
0
                   BTSORTSUPPORT_PROC);
241
0
    if (OidIsValid(sortfunc))
242
0
    {
243
      /* The sort support function can provide a comparator */
244
0
      OidFunctionCall1(sortfunc, PointerGetDatum(&clause->ssup));
245
0
    }
246
0
    if (clause->ssup.comparator == NULL)
247
0
    {
248
      /* support not available, get comparison func */
249
0
      sortfunc = get_opfamily_proc(opfamily,
250
0
                     op_lefttype,
251
0
                     op_righttype,
252
0
                     BTORDER_PROC);
253
0
      if (!OidIsValid(sortfunc)) /* should not happen */
254
0
        elog(ERROR, "missing support function %d(%u,%u) in opfamily %u",
255
0
           BTORDER_PROC, op_lefttype, op_righttype, opfamily);
256
      /* We'll use a shim to call the old-style btree comparator */
257
0
      PrepareSortSupportComparisonShim(sortfunc, &clause->ssup);
258
0
    }
259
260
0
    iClause++;
261
0
  }
262
263
0
  return clauses;
264
0
}
265
266
/*
267
 * MJEvalOuterValues
268
 *
269
 * Compute the values of the mergejoined expressions for the current
270
 * outer tuple.  We also detect whether it's impossible for the current
271
 * outer tuple to match anything --- this is true if it yields a NULL
272
 * input, since we assume mergejoin operators are strict.  If the NULL
273
 * is in the first join column, and that column sorts nulls last, then
274
 * we can further conclude that no following tuple can match anything
275
 * either, since they must all have nulls in the first column.  However,
276
 * that case is only interesting if we're not in FillOuter mode, else
277
 * we have to visit all the tuples anyway.
278
 *
279
 * For the convenience of callers, we also make this routine responsible
280
 * for testing for end-of-input (null outer tuple), and returning
281
 * MJEVAL_ENDOFJOIN when that's seen.  This allows the same code to be used
282
 * for both real end-of-input and the effective end-of-input represented by
283
 * a first-column NULL.
284
 *
285
 * We evaluate the values in OuterEContext, which can be reset each
286
 * time we move to a new tuple.
287
 */
288
static MJEvalResult
289
MJEvalOuterValues(MergeJoinState *mergestate)
290
0
{
291
0
  ExprContext *econtext = mergestate->mj_OuterEContext;
292
0
  MJEvalResult result = MJEVAL_MATCHABLE;
293
0
  int     i;
294
0
  MemoryContext oldContext;
295
296
  /* Check for end of outer subplan */
297
0
  if (TupIsNull(mergestate->mj_OuterTupleSlot))
298
0
    return MJEVAL_ENDOFJOIN;
299
300
0
  ResetExprContext(econtext);
301
302
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
303
304
0
  econtext->ecxt_outertuple = mergestate->mj_OuterTupleSlot;
305
306
0
  for (i = 0; i < mergestate->mj_NumClauses; i++)
307
0
  {
308
0
    MergeJoinClause clause = &mergestate->mj_Clauses[i];
309
310
0
    clause->ldatum = ExecEvalExpr(clause->lexpr, econtext,
311
0
                    &clause->lisnull);
312
0
    if (clause->lisnull)
313
0
    {
314
      /* match is impossible; can we end the join early? */
315
0
      if (i == 0 && !clause->ssup.ssup_nulls_first &&
316
0
        !mergestate->mj_FillOuter)
317
0
        result = MJEVAL_ENDOFJOIN;
318
0
      else if (result == MJEVAL_MATCHABLE)
319
0
        result = MJEVAL_NONMATCHABLE;
320
0
    }
321
0
  }
322
323
0
  MemoryContextSwitchTo(oldContext);
324
325
0
  return result;
326
0
}
327
328
/*
329
 * MJEvalInnerValues
330
 *
331
 * Same as above, but for the inner tuple.  Here, we have to be prepared
332
 * to load data from either the true current inner, or the marked inner,
333
 * so caller must tell us which slot to load from.
334
 */
335
static MJEvalResult
336
MJEvalInnerValues(MergeJoinState *mergestate, TupleTableSlot *innerslot)
337
0
{
338
0
  ExprContext *econtext = mergestate->mj_InnerEContext;
339
0
  MJEvalResult result = MJEVAL_MATCHABLE;
340
0
  int     i;
341
0
  MemoryContext oldContext;
342
343
  /* Check for end of inner subplan */
344
0
  if (TupIsNull(innerslot))
345
0
    return MJEVAL_ENDOFJOIN;
346
347
0
  ResetExprContext(econtext);
348
349
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
350
351
0
  econtext->ecxt_innertuple = innerslot;
352
353
0
  for (i = 0; i < mergestate->mj_NumClauses; i++)
354
0
  {
355
0
    MergeJoinClause clause = &mergestate->mj_Clauses[i];
356
357
0
    clause->rdatum = ExecEvalExpr(clause->rexpr, econtext,
358
0
                    &clause->risnull);
359
0
    if (clause->risnull)
360
0
    {
361
      /* match is impossible; can we end the join early? */
362
0
      if (i == 0 && !clause->ssup.ssup_nulls_first &&
363
0
        !mergestate->mj_FillInner)
364
0
        result = MJEVAL_ENDOFJOIN;
365
0
      else if (result == MJEVAL_MATCHABLE)
366
0
        result = MJEVAL_NONMATCHABLE;
367
0
    }
368
0
  }
369
370
0
  MemoryContextSwitchTo(oldContext);
371
372
0
  return result;
373
0
}
374
375
/*
376
 * MJCompare
377
 *
378
 * Compare the mergejoinable values of the current two input tuples
379
 * and return 0 if they are equal (ie, the mergejoin equalities all
380
 * succeed), >0 if outer > inner, <0 if outer < inner.
381
 *
382
 * MJEvalOuterValues and MJEvalInnerValues must already have been called
383
 * for the current outer and inner tuples, respectively.
384
 */
385
static int
386
MJCompare(MergeJoinState *mergestate)
387
0
{
388
0
  int     result = 0;
389
0
  bool    nulleqnull = false;
390
0
  ExprContext *econtext = mergestate->js.ps.ps_ExprContext;
391
0
  int     i;
392
0
  MemoryContext oldContext;
393
394
  /*
395
   * Call the comparison functions in short-lived context, in case they leak
396
   * memory.
397
   */
398
0
  ResetExprContext(econtext);
399
400
0
  oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
401
402
0
  for (i = 0; i < mergestate->mj_NumClauses; i++)
403
0
  {
404
0
    MergeJoinClause clause = &mergestate->mj_Clauses[i];
405
406
    /*
407
     * Special case for NULL-vs-NULL, else use standard comparison.
408
     */
409
0
    if (clause->lisnull && clause->risnull)
410
0
    {
411
0
      nulleqnull = true;  /* NULL "=" NULL */
412
0
      continue;
413
0
    }
414
415
0
    result = ApplySortComparator(clause->ldatum, clause->lisnull,
416
0
                   clause->rdatum, clause->risnull,
417
0
                   &clause->ssup);
418
419
0
    if (result != 0)
420
0
      break;
421
0
  }
422
423
  /*
424
   * If we had any NULL-vs-NULL inputs, we do not want to report that the
425
   * tuples are equal.  Instead, if result is still 0, change it to +1. This
426
   * will result in advancing the inner side of the join.
427
   *
428
   * Likewise, if there was a constant-false joinqual, do not report
429
   * equality.  We have to check this as part of the mergequals, else the
430
   * rescan logic will do the wrong thing.
431
   */
432
0
  if (result == 0 &&
433
0
    (nulleqnull || mergestate->mj_ConstFalseJoin))
434
0
    result = 1;
435
436
0
  MemoryContextSwitchTo(oldContext);
437
438
0
  return result;
439
0
}
440
441
442
/*
443
 * Generate a fake join tuple with nulls for the inner tuple,
444
 * and return it if it passes the non-join quals.
445
 */
446
static TupleTableSlot *
447
MJFillOuter(MergeJoinState *node)
448
0
{
449
0
  ExprContext *econtext = node->js.ps.ps_ExprContext;
450
0
  ExprState  *otherqual = node->js.ps.qual;
451
452
0
  ResetExprContext(econtext);
453
454
0
  econtext->ecxt_outertuple = node->mj_OuterTupleSlot;
455
0
  econtext->ecxt_innertuple = node->mj_NullInnerTupleSlot;
456
457
0
  if (ExecQual(otherqual, econtext))
458
0
  {
459
    /*
460
     * qualification succeeded.  now form the desired projection tuple and
461
     * return the slot containing it.
462
     */
463
0
    MJ_printf("ExecMergeJoin: returning outer fill tuple\n");
464
465
0
    return ExecProject(node->js.ps.ps_ProjInfo);
466
0
  }
467
0
  else
468
0
    InstrCountFiltered2(node, 1);
469
470
0
  return NULL;
471
0
}
472
473
/*
474
 * Generate a fake join tuple with nulls for the outer tuple,
475
 * and return it if it passes the non-join quals.
476
 */
477
static TupleTableSlot *
478
MJFillInner(MergeJoinState *node)
479
0
{
480
0
  ExprContext *econtext = node->js.ps.ps_ExprContext;
481
0
  ExprState  *otherqual = node->js.ps.qual;
482
483
0
  ResetExprContext(econtext);
484
485
0
  econtext->ecxt_outertuple = node->mj_NullOuterTupleSlot;
486
0
  econtext->ecxt_innertuple = node->mj_InnerTupleSlot;
487
488
0
  if (ExecQual(otherqual, econtext))
489
0
  {
490
    /*
491
     * qualification succeeded.  now form the desired projection tuple and
492
     * return the slot containing it.
493
     */
494
0
    MJ_printf("ExecMergeJoin: returning inner fill tuple\n");
495
496
0
    return ExecProject(node->js.ps.ps_ProjInfo);
497
0
  }
498
0
  else
499
0
    InstrCountFiltered2(node, 1);
500
501
0
  return NULL;
502
0
}
503
504
505
/*
506
 * Check that a qual condition is constant true or constant false.
507
 * If it is constant false (or null), set *is_const_false to true.
508
 *
509
 * Constant true would normally be represented by a NIL list, but we allow an
510
 * actual bool Const as well.  We do expect that the planner will have thrown
511
 * away any non-constant terms that have been ANDed with a constant false.
512
 */
513
static bool
514
check_constant_qual(List *qual, bool *is_const_false)
515
0
{
516
0
  ListCell   *lc;
517
518
0
  foreach(lc, qual)
519
0
  {
520
0
    Const    *con = (Const *) lfirst(lc);
521
522
0
    if (!con || !IsA(con, Const))
523
0
      return false;
524
0
    if (con->constisnull || !DatumGetBool(con->constvalue))
525
0
      *is_const_false = true;
526
0
  }
527
0
  return true;
528
0
}
529
530
531
/* ----------------------------------------------------------------
532
 *    ExecMergeTupleDump
533
 *
534
 *    This function is called through the MJ_dump() macro
535
 *    when EXEC_MERGEJOINDEBUG is defined
536
 * ----------------------------------------------------------------
537
 */
538
#ifdef EXEC_MERGEJOINDEBUG
539
540
static void
541
ExecMergeTupleDumpOuter(MergeJoinState *mergestate)
542
{
543
  TupleTableSlot *outerSlot = mergestate->mj_OuterTupleSlot;
544
545
  printf("==== outer tuple ====\n");
546
  if (TupIsNull(outerSlot))
547
    printf("(nil)\n");
548
  else
549
    MJ_debugtup(outerSlot);
550
}
551
552
static void
553
ExecMergeTupleDumpInner(MergeJoinState *mergestate)
554
{
555
  TupleTableSlot *innerSlot = mergestate->mj_InnerTupleSlot;
556
557
  printf("==== inner tuple ====\n");
558
  if (TupIsNull(innerSlot))
559
    printf("(nil)\n");
560
  else
561
    MJ_debugtup(innerSlot);
562
}
563
564
static void
565
ExecMergeTupleDumpMarked(MergeJoinState *mergestate)
566
{
567
  TupleTableSlot *markedSlot = mergestate->mj_MarkedTupleSlot;
568
569
  printf("==== marked tuple ====\n");
570
  if (TupIsNull(markedSlot))
571
    printf("(nil)\n");
572
  else
573
    MJ_debugtup(markedSlot);
574
}
575
576
static void
577
ExecMergeTupleDump(MergeJoinState *mergestate)
578
{
579
  printf("******** ExecMergeTupleDump ********\n");
580
581
  ExecMergeTupleDumpOuter(mergestate);
582
  ExecMergeTupleDumpInner(mergestate);
583
  ExecMergeTupleDumpMarked(mergestate);
584
585
  printf("********\n");
586
}
587
#endif
588
589
/* ----------------------------------------------------------------
590
 *    ExecMergeJoin
591
 * ----------------------------------------------------------------
592
 */
593
static TupleTableSlot *
594
ExecMergeJoin(PlanState *pstate)
595
0
{
596
0
  MergeJoinState *node = castNode(MergeJoinState, pstate);
597
0
  ExprState  *joinqual;
598
0
  ExprState  *otherqual;
599
0
  bool    qualResult;
600
0
  int     compareResult;
601
0
  PlanState  *innerPlan;
602
0
  TupleTableSlot *innerTupleSlot;
603
0
  PlanState  *outerPlan;
604
0
  TupleTableSlot *outerTupleSlot;
605
0
  ExprContext *econtext;
606
0
  bool    doFillOuter;
607
0
  bool    doFillInner;
608
609
0
  CHECK_FOR_INTERRUPTS();
610
611
  /*
612
   * get information from node
613
   */
614
0
  innerPlan = innerPlanState(node);
615
0
  outerPlan = outerPlanState(node);
616
0
  econtext = node->js.ps.ps_ExprContext;
617
0
  joinqual = node->js.joinqual;
618
0
  otherqual = node->js.ps.qual;
619
0
  doFillOuter = node->mj_FillOuter;
620
0
  doFillInner = node->mj_FillInner;
621
622
  /*
623
   * Reset per-tuple memory context to free any expression evaluation
624
   * storage allocated in the previous tuple cycle.
625
   */
626
0
  ResetExprContext(econtext);
627
628
  /*
629
   * ok, everything is setup.. let's go to work
630
   */
631
0
  for (;;)
632
0
  {
633
0
    MJ_dump(node);
634
635
    /*
636
     * get the current state of the join and do things accordingly.
637
     */
638
0
    switch (node->mj_JoinState)
639
0
    {
640
        /*
641
         * EXEC_MJ_INITIALIZE_OUTER means that this is the first time
642
         * ExecMergeJoin() has been called and so we have to fetch the
643
         * first matchable tuple for both outer and inner subplans. We
644
         * do the outer side in INITIALIZE_OUTER state, then advance
645
         * to INITIALIZE_INNER state for the inner subplan.
646
         */
647
0
      case EXEC_MJ_INITIALIZE_OUTER:
648
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_OUTER\n");
649
650
0
        outerTupleSlot = ExecProcNode(outerPlan);
651
0
        node->mj_OuterTupleSlot = outerTupleSlot;
652
653
        /* Compute join values and check for unmatchability */
654
0
        switch (MJEvalOuterValues(node))
655
0
        {
656
0
          case MJEVAL_MATCHABLE:
657
            /* OK to go get the first inner tuple */
658
0
            node->mj_JoinState = EXEC_MJ_INITIALIZE_INNER;
659
0
            break;
660
0
          case MJEVAL_NONMATCHABLE:
661
            /* Stay in same state to fetch next outer tuple */
662
0
            if (doFillOuter)
663
0
            {
664
              /*
665
               * Generate a fake join tuple with nulls for the
666
               * inner tuple, and return it if it passes the
667
               * non-join quals.
668
               */
669
0
              TupleTableSlot *result;
670
671
0
              result = MJFillOuter(node);
672
0
              if (result)
673
0
                return result;
674
0
            }
675
0
            break;
676
0
          case MJEVAL_ENDOFJOIN:
677
            /* No more outer tuples */
678
0
            MJ_printf("ExecMergeJoin: nothing in outer subplan\n");
679
0
            if (doFillInner)
680
0
            {
681
              /*
682
               * Need to emit right-join tuples for remaining
683
               * inner tuples. We set MatchedInner = true to
684
               * force the ENDOUTER state to advance inner.
685
               */
686
0
              node->mj_JoinState = EXEC_MJ_ENDOUTER;
687
0
              node->mj_MatchedInner = true;
688
0
              break;
689
0
            }
690
            /* Otherwise we're done. */
691
0
            return NULL;
692
0
        }
693
0
        break;
694
695
0
      case EXEC_MJ_INITIALIZE_INNER:
696
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE_INNER\n");
697
698
0
        innerTupleSlot = ExecProcNode(innerPlan);
699
0
        node->mj_InnerTupleSlot = innerTupleSlot;
700
701
        /* Compute join values and check for unmatchability */
702
0
        switch (MJEvalInnerValues(node, innerTupleSlot))
703
0
        {
704
0
          case MJEVAL_MATCHABLE:
705
706
            /*
707
             * OK, we have the initial tuples.  Begin by skipping
708
             * non-matching tuples.
709
             */
710
0
            node->mj_JoinState = EXEC_MJ_SKIP_TEST;
711
0
            break;
712
0
          case MJEVAL_NONMATCHABLE:
713
            /* Mark before advancing, if wanted */
714
0
            if (node->mj_ExtraMarks)
715
0
              ExecMarkPos(innerPlan);
716
            /* Stay in same state to fetch next inner tuple */
717
0
            if (doFillInner)
718
0
            {
719
              /*
720
               * Generate a fake join tuple with nulls for the
721
               * outer tuple, and return it if it passes the
722
               * non-join quals.
723
               */
724
0
              TupleTableSlot *result;
725
726
0
              result = MJFillInner(node);
727
0
              if (result)
728
0
                return result;
729
0
            }
730
0
            break;
731
0
          case MJEVAL_ENDOFJOIN:
732
            /* No more inner tuples */
733
0
            MJ_printf("ExecMergeJoin: nothing in inner subplan\n");
734
0
            if (doFillOuter)
735
0
            {
736
              /*
737
               * Need to emit left-join tuples for all outer
738
               * tuples, including the one we just fetched.  We
739
               * set MatchedOuter = false to force the ENDINNER
740
               * state to emit first tuple before advancing
741
               * outer.
742
               */
743
0
              node->mj_JoinState = EXEC_MJ_ENDINNER;
744
0
              node->mj_MatchedOuter = false;
745
0
              break;
746
0
            }
747
            /* Otherwise we're done. */
748
0
            return NULL;
749
0
        }
750
0
        break;
751
752
        /*
753
         * EXEC_MJ_JOINTUPLES means we have two tuples which satisfied
754
         * the merge clause so we join them and then proceed to get
755
         * the next inner tuple (EXEC_MJ_NEXTINNER).
756
         */
757
0
      case EXEC_MJ_JOINTUPLES:
758
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
759
760
        /*
761
         * Set the next state machine state.  The right things will
762
         * happen whether we return this join tuple or just fall
763
         * through to continue the state machine execution.
764
         */
765
0
        node->mj_JoinState = EXEC_MJ_NEXTINNER;
766
767
        /*
768
         * Check the extra qual conditions to see if we actually want
769
         * to return this join tuple.  If not, can proceed with merge.
770
         * We must distinguish the additional joinquals (which must
771
         * pass to consider the tuples "matched" for outer-join logic)
772
         * from the otherquals (which must pass before we actually
773
         * return the tuple).
774
         *
775
         * We don't bother with a ResetExprContext here, on the
776
         * assumption that we just did one while checking the merge
777
         * qual.  One per tuple should be sufficient.  We do have to
778
         * set up the econtext links to the tuples for ExecQual to
779
         * use.
780
         */
781
0
        outerTupleSlot = node->mj_OuterTupleSlot;
782
0
        econtext->ecxt_outertuple = outerTupleSlot;
783
0
        innerTupleSlot = node->mj_InnerTupleSlot;
784
0
        econtext->ecxt_innertuple = innerTupleSlot;
785
786
0
        qualResult = (joinqual == NULL ||
787
0
                ExecQual(joinqual, econtext));
788
0
        MJ_DEBUG_QUAL(joinqual, qualResult);
789
790
0
        if (qualResult)
791
0
        {
792
0
          node->mj_MatchedOuter = true;
793
0
          node->mj_MatchedInner = true;
794
795
          /* In an antijoin, we never return a matched tuple */
796
0
          if (node->js.jointype == JOIN_ANTI)
797
0
          {
798
0
            node->mj_JoinState = EXEC_MJ_NEXTOUTER;
799
0
            break;
800
0
          }
801
802
          /*
803
           * If we only need to consider the first matching inner
804
           * tuple, then advance to next outer tuple after we've
805
           * processed this one.
806
           */
807
0
          if (node->js.single_match)
808
0
            node->mj_JoinState = EXEC_MJ_NEXTOUTER;
809
810
          /*
811
           * In a right-antijoin, we never return a matched tuple.
812
           * If it's not an inner_unique join, we need to stay on
813
           * the current outer tuple to continue scanning the inner
814
           * side for matches.
815
           */
816
0
          if (node->js.jointype == JOIN_RIGHT_ANTI)
817
0
            break;
818
819
0
          qualResult = (otherqual == NULL ||
820
0
                  ExecQual(otherqual, econtext));
821
0
          MJ_DEBUG_QUAL(otherqual, qualResult);
822
823
0
          if (qualResult)
824
0
          {
825
            /*
826
             * qualification succeeded.  now form the desired
827
             * projection tuple and return the slot containing it.
828
             */
829
0
            MJ_printf("ExecMergeJoin: returning tuple\n");
830
831
0
            return ExecProject(node->js.ps.ps_ProjInfo);
832
0
          }
833
0
          else
834
0
            InstrCountFiltered2(node, 1);
835
0
        }
836
0
        else
837
0
          InstrCountFiltered1(node, 1);
838
0
        break;
839
840
        /*
841
         * EXEC_MJ_NEXTINNER means advance the inner scan to the next
842
         * tuple. If the tuple is not nil, we then proceed to test it
843
         * against the join qualification.
844
         *
845
         * Before advancing, we check to see if we must emit an
846
         * outer-join fill tuple for this inner tuple.
847
         */
848
0
      case EXEC_MJ_NEXTINNER:
849
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
850
851
0
        if (doFillInner && !node->mj_MatchedInner)
852
0
        {
853
          /*
854
           * Generate a fake join tuple with nulls for the outer
855
           * tuple, and return it if it passes the non-join quals.
856
           */
857
0
          TupleTableSlot *result;
858
859
0
          node->mj_MatchedInner = true; /* do it only once */
860
861
0
          result = MJFillInner(node);
862
0
          if (result)
863
0
            return result;
864
0
        }
865
866
        /*
867
         * now we get the next inner tuple, if any.  If there's none,
868
         * advance to next outer tuple (which may be able to join to
869
         * previously marked tuples).
870
         *
871
         * NB: must NOT do "extraMarks" here, since we may need to
872
         * return to previously marked tuples.
873
         */
874
0
        innerTupleSlot = ExecProcNode(innerPlan);
875
0
        node->mj_InnerTupleSlot = innerTupleSlot;
876
0
        MJ_DEBUG_PROC_NODE(innerTupleSlot);
877
0
        node->mj_MatchedInner = false;
878
879
        /* Compute join values and check for unmatchability */
880
0
        switch (MJEvalInnerValues(node, innerTupleSlot))
881
0
        {
882
0
          case MJEVAL_MATCHABLE:
883
884
            /*
885
             * Test the new inner tuple to see if it matches
886
             * outer.
887
             *
888
             * If they do match, then we join them and move on to
889
             * the next inner tuple (EXEC_MJ_JOINTUPLES).
890
             *
891
             * If they do not match then advance to next outer
892
             * tuple.
893
             */
894
0
            compareResult = MJCompare(node);
895
0
            MJ_DEBUG_COMPARE(compareResult);
896
897
0
            if (compareResult == 0)
898
0
              node->mj_JoinState = EXEC_MJ_JOINTUPLES;
899
0
            else if (compareResult < 0)
900
0
              node->mj_JoinState = EXEC_MJ_NEXTOUTER;
901
0
            else  /* compareResult > 0 should not happen */
902
0
              elog(ERROR, "mergejoin input data is out of order");
903
0
            break;
904
0
          case MJEVAL_NONMATCHABLE:
905
906
            /*
907
             * It contains a NULL and hence can't match any outer
908
             * tuple, so we can skip the comparison and assume the
909
             * new tuple is greater than current outer.
910
             */
911
0
            node->mj_JoinState = EXEC_MJ_NEXTOUTER;
912
0
            break;
913
0
          case MJEVAL_ENDOFJOIN:
914
915
            /*
916
             * No more inner tuples.  However, this might be only
917
             * effective and not physical end of inner plan, so
918
             * force mj_InnerTupleSlot to null to make sure we
919
             * don't fetch more inner tuples.  (We need this hack
920
             * because we are not transiting to a state where the
921
             * inner plan is assumed to be exhausted.)
922
             */
923
0
            node->mj_InnerTupleSlot = NULL;
924
0
            node->mj_JoinState = EXEC_MJ_NEXTOUTER;
925
0
            break;
926
0
        }
927
0
        break;
928
929
        /*-------------------------------------------
930
         * EXEC_MJ_NEXTOUTER means
931
         *
932
         *        outer inner
933
         * outer tuple -  5   5  - marked tuple
934
         *          5   5
935
         *          6   6  - inner tuple
936
         *          7   7
937
         *
938
         * we know we just bumped into the
939
         * first inner tuple > current outer tuple (or possibly
940
         * the end of the inner stream)
941
         * so get a new outer tuple and then
942
         * proceed to test it against the marked tuple
943
         * (EXEC_MJ_TESTOUTER)
944
         *
945
         * Before advancing, we check to see if we must emit an
946
         * outer-join fill tuple for this outer tuple.
947
         *------------------------------------------------
948
         */
949
0
      case EXEC_MJ_NEXTOUTER:
950
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
951
952
0
        if (doFillOuter && !node->mj_MatchedOuter)
953
0
        {
954
          /*
955
           * Generate a fake join tuple with nulls for the inner
956
           * tuple, and return it if it passes the non-join quals.
957
           */
958
0
          TupleTableSlot *result;
959
960
0
          node->mj_MatchedOuter = true; /* do it only once */
961
962
0
          result = MJFillOuter(node);
963
0
          if (result)
964
0
            return result;
965
0
        }
966
967
        /*
968
         * now we get the next outer tuple, if any
969
         */
970
0
        outerTupleSlot = ExecProcNode(outerPlan);
971
0
        node->mj_OuterTupleSlot = outerTupleSlot;
972
0
        MJ_DEBUG_PROC_NODE(outerTupleSlot);
973
0
        node->mj_MatchedOuter = false;
974
975
        /* Compute join values and check for unmatchability */
976
0
        switch (MJEvalOuterValues(node))
977
0
        {
978
0
          case MJEVAL_MATCHABLE:
979
            /* Go test the new tuple against the marked tuple */
980
0
            node->mj_JoinState = EXEC_MJ_TESTOUTER;
981
0
            break;
982
0
          case MJEVAL_NONMATCHABLE:
983
            /* Can't match, so fetch next outer tuple */
984
0
            node->mj_JoinState = EXEC_MJ_NEXTOUTER;
985
0
            break;
986
0
          case MJEVAL_ENDOFJOIN:
987
            /* No more outer tuples */
988
0
            MJ_printf("ExecMergeJoin: end of outer subplan\n");
989
0
            innerTupleSlot = node->mj_InnerTupleSlot;
990
0
            if (doFillInner && !TupIsNull(innerTupleSlot))
991
0
            {
992
              /*
993
               * Need to emit right-join tuples for remaining
994
               * inner tuples.
995
               */
996
0
              node->mj_JoinState = EXEC_MJ_ENDOUTER;
997
0
              break;
998
0
            }
999
            /* Otherwise we're done. */
1000
0
            return NULL;
1001
0
        }
1002
0
        break;
1003
1004
        /*--------------------------------------------------------
1005
         * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
1006
         * tuple satisfy the merge clause then we know we have
1007
         * duplicates in the outer scan so we have to restore the
1008
         * inner scan to the marked tuple and proceed to join the
1009
         * new outer tuple with the inner tuples.
1010
         *
1011
         * This is the case when
1012
         *              outer inner
1013
         *              4   5  - marked tuple
1014
         *       outer tuple -  5   5
1015
         *     new outer tuple -  5   5
1016
         *              6   8  - inner tuple
1017
         *              7  12
1018
         *
1019
         *        new outer tuple == marked tuple
1020
         *
1021
         * If the outer tuple fails the test, then we are done
1022
         * with the marked tuples, and we have to look for a
1023
         * match to the current inner tuple.  So we will
1024
         * proceed to skip outer tuples until outer >= inner
1025
         * (EXEC_MJ_SKIP_TEST).
1026
         *
1027
         *    This is the case when
1028
         *
1029
         *              outer inner
1030
         *              5   5  - marked tuple
1031
         *       outer tuple -  5   5
1032
         *     new outer tuple -  6   8  - inner tuple
1033
         *              7  12
1034
         *
1035
         *        new outer tuple > marked tuple
1036
         *
1037
         *---------------------------------------------------------
1038
         */
1039
0
      case EXEC_MJ_TESTOUTER:
1040
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
1041
1042
        /*
1043
         * Here we must compare the outer tuple with the marked inner
1044
         * tuple.  (We can ignore the result of MJEvalInnerValues,
1045
         * since the marked inner tuple is certainly matchable.)
1046
         */
1047
0
        innerTupleSlot = node->mj_MarkedTupleSlot;
1048
0
        (void) MJEvalInnerValues(node, innerTupleSlot);
1049
1050
0
        compareResult = MJCompare(node);
1051
0
        MJ_DEBUG_COMPARE(compareResult);
1052
1053
0
        if (compareResult == 0)
1054
0
        {
1055
          /*
1056
           * the merge clause matched so now we restore the inner
1057
           * scan position to the first mark, and go join that tuple
1058
           * (and any following ones) to the new outer.
1059
           *
1060
           * If we were able to determine mark and restore are not
1061
           * needed, then we don't have to back up; the current
1062
           * inner is already the first possible match.
1063
           *
1064
           * NOTE: we do not need to worry about the MatchedInner
1065
           * state for the rescanned inner tuples.  We know all of
1066
           * them will match this new outer tuple and therefore
1067
           * won't be emitted as fill tuples.  This works *only*
1068
           * because we require the extra joinquals to be constant
1069
           * when doing a right, right-anti or full join ---
1070
           * otherwise some of the rescanned tuples might fail the
1071
           * extra joinquals.  This obviously won't happen for a
1072
           * constant-true extra joinqual, while the constant-false
1073
           * case is handled by forcing the merge clause to never
1074
           * match, so we never get here.
1075
           */
1076
0
          if (!node->mj_SkipMarkRestore)
1077
0
          {
1078
0
            ExecRestrPos(innerPlan);
1079
1080
            /*
1081
             * ExecRestrPos probably should give us back a new
1082
             * Slot, but since it doesn't, use the marked slot.
1083
             * (The previously returned mj_InnerTupleSlot cannot
1084
             * be assumed to hold the required tuple.)
1085
             */
1086
0
            node->mj_InnerTupleSlot = innerTupleSlot;
1087
            /* we need not do MJEvalInnerValues again */
1088
0
          }
1089
1090
0
          node->mj_JoinState = EXEC_MJ_JOINTUPLES;
1091
0
        }
1092
0
        else if (compareResult > 0)
1093
0
        {
1094
          /* ----------------
1095
           *  if the new outer tuple didn't match the marked inner
1096
           *  tuple then we have a case like:
1097
           *
1098
           *       outer inner
1099
           *         4   4  - marked tuple
1100
           * new outer - 5   4
1101
           *         6   5  - inner tuple
1102
           *         7
1103
           *
1104
           *  which means that all subsequent outer tuples will be
1105
           *  larger than our marked inner tuples.  So we need not
1106
           *  revisit any of the marked tuples but can proceed to
1107
           *  look for a match to the current inner.  If there's
1108
           *  no more inners, no more matches are possible.
1109
           * ----------------
1110
           */
1111
0
          innerTupleSlot = node->mj_InnerTupleSlot;
1112
1113
          /* reload comparison data for current inner */
1114
0
          switch (MJEvalInnerValues(node, innerTupleSlot))
1115
0
          {
1116
0
            case MJEVAL_MATCHABLE:
1117
              /* proceed to compare it to the current outer */
1118
0
              node->mj_JoinState = EXEC_MJ_SKIP_TEST;
1119
0
              break;
1120
0
            case MJEVAL_NONMATCHABLE:
1121
1122
              /*
1123
               * current inner can't possibly match any outer;
1124
               * better to advance the inner scan than the
1125
               * outer.
1126
               */
1127
0
              node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1128
0
              break;
1129
0
            case MJEVAL_ENDOFJOIN:
1130
              /* No more inner tuples */
1131
0
              if (doFillOuter)
1132
0
              {
1133
                /*
1134
                 * Need to emit left-join tuples for remaining
1135
                 * outer tuples.
1136
                 */
1137
0
                node->mj_JoinState = EXEC_MJ_ENDINNER;
1138
0
                break;
1139
0
              }
1140
              /* Otherwise we're done. */
1141
0
              return NULL;
1142
0
          }
1143
0
        }
1144
0
        else      /* compareResult < 0 should not happen */
1145
0
          elog(ERROR, "mergejoin input data is out of order");
1146
0
        break;
1147
1148
        /*----------------------------------------------------------
1149
         * EXEC_MJ_SKIP_TEST means compare tuples and if they do not
1150
         * match, skip whichever is lesser.
1151
         *
1152
         * For example:
1153
         *
1154
         *        outer inner
1155
         *          5   5
1156
         *          5   5
1157
         * outer tuple -  6   8  - inner tuple
1158
         *          7    12
1159
         *          8    14
1160
         *
1161
         * we have to advance the outer scan
1162
         * until we find the outer 8.
1163
         *
1164
         * On the other hand:
1165
         *
1166
         *        outer inner
1167
         *          5   5
1168
         *          5   5
1169
         * outer tuple - 12   8  - inner tuple
1170
         *         14    10
1171
         *         17    12
1172
         *
1173
         * we have to advance the inner scan
1174
         * until we find the inner 12.
1175
         *----------------------------------------------------------
1176
         */
1177
0
      case EXEC_MJ_SKIP_TEST:
1178
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_SKIP_TEST\n");
1179
1180
        /*
1181
         * before we advance, make sure the current tuples do not
1182
         * satisfy the mergeclauses.  If they do, then we update the
1183
         * marked tuple position and go join them.
1184
         */
1185
0
        compareResult = MJCompare(node);
1186
0
        MJ_DEBUG_COMPARE(compareResult);
1187
1188
0
        if (compareResult == 0)
1189
0
        {
1190
0
          if (!node->mj_SkipMarkRestore)
1191
0
            ExecMarkPos(innerPlan);
1192
1193
0
          MarkInnerTuple(node->mj_InnerTupleSlot, node);
1194
1195
0
          node->mj_JoinState = EXEC_MJ_JOINTUPLES;
1196
0
        }
1197
0
        else if (compareResult < 0)
1198
0
          node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
1199
0
        else
1200
          /* compareResult > 0 */
1201
0
          node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1202
0
        break;
1203
1204
        /*
1205
         * EXEC_MJ_SKIPOUTER_ADVANCE: advance over an outer tuple that
1206
         * is known not to join to any inner tuple.
1207
         *
1208
         * Before advancing, we check to see if we must emit an
1209
         * outer-join fill tuple for this outer tuple.
1210
         */
1211
0
      case EXEC_MJ_SKIPOUTER_ADVANCE:
1212
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER_ADVANCE\n");
1213
1214
0
        if (doFillOuter && !node->mj_MatchedOuter)
1215
0
        {
1216
          /*
1217
           * Generate a fake join tuple with nulls for the inner
1218
           * tuple, and return it if it passes the non-join quals.
1219
           */
1220
0
          TupleTableSlot *result;
1221
1222
0
          node->mj_MatchedOuter = true; /* do it only once */
1223
1224
0
          result = MJFillOuter(node);
1225
0
          if (result)
1226
0
            return result;
1227
0
        }
1228
1229
        /*
1230
         * now we get the next outer tuple, if any
1231
         */
1232
0
        outerTupleSlot = ExecProcNode(outerPlan);
1233
0
        node->mj_OuterTupleSlot = outerTupleSlot;
1234
0
        MJ_DEBUG_PROC_NODE(outerTupleSlot);
1235
0
        node->mj_MatchedOuter = false;
1236
1237
        /* Compute join values and check for unmatchability */
1238
0
        switch (MJEvalOuterValues(node))
1239
0
        {
1240
0
          case MJEVAL_MATCHABLE:
1241
            /* Go test the new tuple against the current inner */
1242
0
            node->mj_JoinState = EXEC_MJ_SKIP_TEST;
1243
0
            break;
1244
0
          case MJEVAL_NONMATCHABLE:
1245
            /* Can't match, so fetch next outer tuple */
1246
0
            node->mj_JoinState = EXEC_MJ_SKIPOUTER_ADVANCE;
1247
0
            break;
1248
0
          case MJEVAL_ENDOFJOIN:
1249
            /* No more outer tuples */
1250
0
            MJ_printf("ExecMergeJoin: end of outer subplan\n");
1251
0
            innerTupleSlot = node->mj_InnerTupleSlot;
1252
0
            if (doFillInner && !TupIsNull(innerTupleSlot))
1253
0
            {
1254
              /*
1255
               * Need to emit right-join tuples for remaining
1256
               * inner tuples.
1257
               */
1258
0
              node->mj_JoinState = EXEC_MJ_ENDOUTER;
1259
0
              break;
1260
0
            }
1261
            /* Otherwise we're done. */
1262
0
            return NULL;
1263
0
        }
1264
0
        break;
1265
1266
        /*
1267
         * EXEC_MJ_SKIPINNER_ADVANCE: advance over an inner tuple that
1268
         * is known not to join to any outer tuple.
1269
         *
1270
         * Before advancing, we check to see if we must emit an
1271
         * outer-join fill tuple for this inner tuple.
1272
         */
1273
0
      case EXEC_MJ_SKIPINNER_ADVANCE:
1274
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER_ADVANCE\n");
1275
1276
0
        if (doFillInner && !node->mj_MatchedInner)
1277
0
        {
1278
          /*
1279
           * Generate a fake join tuple with nulls for the outer
1280
           * tuple, and return it if it passes the non-join quals.
1281
           */
1282
0
          TupleTableSlot *result;
1283
1284
0
          node->mj_MatchedInner = true; /* do it only once */
1285
1286
0
          result = MJFillInner(node);
1287
0
          if (result)
1288
0
            return result;
1289
0
        }
1290
1291
        /* Mark before advancing, if wanted */
1292
0
        if (node->mj_ExtraMarks)
1293
0
          ExecMarkPos(innerPlan);
1294
1295
        /*
1296
         * now we get the next inner tuple, if any
1297
         */
1298
0
        innerTupleSlot = ExecProcNode(innerPlan);
1299
0
        node->mj_InnerTupleSlot = innerTupleSlot;
1300
0
        MJ_DEBUG_PROC_NODE(innerTupleSlot);
1301
0
        node->mj_MatchedInner = false;
1302
1303
        /* Compute join values and check for unmatchability */
1304
0
        switch (MJEvalInnerValues(node, innerTupleSlot))
1305
0
        {
1306
0
          case MJEVAL_MATCHABLE:
1307
            /* proceed to compare it to the current outer */
1308
0
            node->mj_JoinState = EXEC_MJ_SKIP_TEST;
1309
0
            break;
1310
0
          case MJEVAL_NONMATCHABLE:
1311
1312
            /*
1313
             * current inner can't possibly match any outer;
1314
             * better to advance the inner scan than the outer.
1315
             */
1316
0
            node->mj_JoinState = EXEC_MJ_SKIPINNER_ADVANCE;
1317
0
            break;
1318
0
          case MJEVAL_ENDOFJOIN:
1319
            /* No more inner tuples */
1320
0
            MJ_printf("ExecMergeJoin: end of inner subplan\n");
1321
0
            outerTupleSlot = node->mj_OuterTupleSlot;
1322
0
            if (doFillOuter && !TupIsNull(outerTupleSlot))
1323
0
            {
1324
              /*
1325
               * Need to emit left-join tuples for remaining
1326
               * outer tuples.
1327
               */
1328
0
              node->mj_JoinState = EXEC_MJ_ENDINNER;
1329
0
              break;
1330
0
            }
1331
            /* Otherwise we're done. */
1332
0
            return NULL;
1333
0
        }
1334
0
        break;
1335
1336
        /*
1337
         * EXEC_MJ_ENDOUTER means we have run out of outer tuples, but
1338
         * are doing a right/right-anti/full join and therefore must
1339
         * null-fill any remaining unmatched inner tuples.
1340
         */
1341
0
      case EXEC_MJ_ENDOUTER:
1342
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_ENDOUTER\n");
1343
1344
0
        Assert(doFillInner);
1345
1346
0
        if (!node->mj_MatchedInner)
1347
0
        {
1348
          /*
1349
           * Generate a fake join tuple with nulls for the outer
1350
           * tuple, and return it if it passes the non-join quals.
1351
           */
1352
0
          TupleTableSlot *result;
1353
1354
0
          node->mj_MatchedInner = true; /* do it only once */
1355
1356
0
          result = MJFillInner(node);
1357
0
          if (result)
1358
0
            return result;
1359
0
        }
1360
1361
        /* Mark before advancing, if wanted */
1362
0
        if (node->mj_ExtraMarks)
1363
0
          ExecMarkPos(innerPlan);
1364
1365
        /*
1366
         * now we get the next inner tuple, if any
1367
         */
1368
0
        innerTupleSlot = ExecProcNode(innerPlan);
1369
0
        node->mj_InnerTupleSlot = innerTupleSlot;
1370
0
        MJ_DEBUG_PROC_NODE(innerTupleSlot);
1371
0
        node->mj_MatchedInner = false;
1372
1373
0
        if (TupIsNull(innerTupleSlot))
1374
0
        {
1375
0
          MJ_printf("ExecMergeJoin: end of inner subplan\n");
1376
0
          return NULL;
1377
0
        }
1378
1379
        /* Else remain in ENDOUTER state and process next tuple. */
1380
0
        break;
1381
1382
        /*
1383
         * EXEC_MJ_ENDINNER means we have run out of inner tuples, but
1384
         * are doing a left/full join and therefore must null- fill
1385
         * any remaining unmatched outer tuples.
1386
         */
1387
0
      case EXEC_MJ_ENDINNER:
1388
0
        MJ_printf("ExecMergeJoin: EXEC_MJ_ENDINNER\n");
1389
1390
0
        Assert(doFillOuter);
1391
1392
0
        if (!node->mj_MatchedOuter)
1393
0
        {
1394
          /*
1395
           * Generate a fake join tuple with nulls for the inner
1396
           * tuple, and return it if it passes the non-join quals.
1397
           */
1398
0
          TupleTableSlot *result;
1399
1400
0
          node->mj_MatchedOuter = true; /* do it only once */
1401
1402
0
          result = MJFillOuter(node);
1403
0
          if (result)
1404
0
            return result;
1405
0
        }
1406
1407
        /*
1408
         * now we get the next outer tuple, if any
1409
         */
1410
0
        outerTupleSlot = ExecProcNode(outerPlan);
1411
0
        node->mj_OuterTupleSlot = outerTupleSlot;
1412
0
        MJ_DEBUG_PROC_NODE(outerTupleSlot);
1413
0
        node->mj_MatchedOuter = false;
1414
1415
0
        if (TupIsNull(outerTupleSlot))
1416
0
        {
1417
0
          MJ_printf("ExecMergeJoin: end of outer subplan\n");
1418
0
          return NULL;
1419
0
        }
1420
1421
        /* Else remain in ENDINNER state and process next tuple. */
1422
0
        break;
1423
1424
        /*
1425
         * broken state value?
1426
         */
1427
0
      default:
1428
0
        elog(ERROR, "unrecognized mergejoin state: %d",
1429
0
           (int) node->mj_JoinState);
1430
0
    }
1431
0
  }
1432
0
}
1433
1434
/* ----------------------------------------------------------------
1435
 *    ExecInitMergeJoin
1436
 * ----------------------------------------------------------------
1437
 */
1438
MergeJoinState *
1439
ExecInitMergeJoin(MergeJoin *node, EState *estate, int eflags)
1440
0
{
1441
0
  MergeJoinState *mergestate;
1442
0
  TupleDesc outerDesc,
1443
0
        innerDesc;
1444
0
  const TupleTableSlotOps *innerOps;
1445
1446
  /* check for unsupported flags */
1447
0
  Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)));
1448
1449
0
  MJ1_printf("ExecInitMergeJoin: %s\n",
1450
0
         "initializing node");
1451
1452
  /*
1453
   * create state structure
1454
   */
1455
0
  mergestate = makeNode(MergeJoinState);
1456
0
  mergestate->js.ps.plan = (Plan *) node;
1457
0
  mergestate->js.ps.state = estate;
1458
0
  mergestate->js.ps.ExecProcNode = ExecMergeJoin;
1459
0
  mergestate->js.jointype = node->join.jointype;
1460
0
  mergestate->mj_ConstFalseJoin = false;
1461
1462
  /*
1463
   * Miscellaneous initialization
1464
   *
1465
   * create expression context for node
1466
   */
1467
0
  ExecAssignExprContext(estate, &mergestate->js.ps);
1468
1469
  /*
1470
   * we need two additional econtexts in which we can compute the join
1471
   * expressions from the left and right input tuples.  The node's regular
1472
   * econtext won't do because it gets reset too often.
1473
   */
1474
0
  mergestate->mj_OuterEContext = CreateExprContext(estate);
1475
0
  mergestate->mj_InnerEContext = CreateExprContext(estate);
1476
1477
  /*
1478
   * initialize child nodes
1479
   *
1480
   * inner child must support MARK/RESTORE, unless we have detected that we
1481
   * don't need that.  Note that skip_mark_restore must never be set if
1482
   * there are non-mergeclause joinquals, since the logic wouldn't work.
1483
   */
1484
0
  Assert(node->join.joinqual == NIL || !node->skip_mark_restore);
1485
0
  mergestate->mj_SkipMarkRestore = node->skip_mark_restore;
1486
1487
0
  outerPlanState(mergestate) = ExecInitNode(outerPlan(node), estate, eflags);
1488
0
  outerDesc = ExecGetResultType(outerPlanState(mergestate));
1489
0
  innerPlanState(mergestate) = ExecInitNode(innerPlan(node), estate,
1490
0
                        mergestate->mj_SkipMarkRestore ?
1491
0
                        eflags :
1492
0
                        (eflags | EXEC_FLAG_MARK));
1493
0
  innerDesc = ExecGetResultType(innerPlanState(mergestate));
1494
1495
  /*
1496
   * For certain types of inner child nodes, it is advantageous to issue
1497
   * MARK every time we advance past an inner tuple we will never return to.
1498
   * For other types, MARK on a tuple we cannot return to is a waste of
1499
   * cycles.  Detect which case applies and set mj_ExtraMarks if we want to
1500
   * issue "unnecessary" MARK calls.
1501
   *
1502
   * Currently, only Material wants the extra MARKs, and it will be helpful
1503
   * only if eflags doesn't specify REWIND.
1504
   *
1505
   * Note that for IndexScan and IndexOnlyScan, it is *necessary* that we
1506
   * not set mj_ExtraMarks; otherwise we might attempt to set a mark before
1507
   * the first inner tuple, which they do not support.
1508
   */
1509
0
  if (IsA(innerPlan(node), Material) &&
1510
0
    (eflags & EXEC_FLAG_REWIND) == 0 &&
1511
0
    !mergestate->mj_SkipMarkRestore)
1512
0
    mergestate->mj_ExtraMarks = true;
1513
0
  else
1514
0
    mergestate->mj_ExtraMarks = false;
1515
1516
  /*
1517
   * Initialize result slot, type and projection.
1518
   */
1519
0
  ExecInitResultTupleSlotTL(&mergestate->js.ps, &TTSOpsVirtual);
1520
0
  ExecAssignProjectionInfo(&mergestate->js.ps, NULL);
1521
1522
  /*
1523
   * tuple table initialization
1524
   */
1525
0
  innerOps = ExecGetResultSlotOps(innerPlanState(mergestate), NULL);
1526
0
  mergestate->mj_MarkedTupleSlot = ExecInitExtraTupleSlot(estate, innerDesc,
1527
0
                              innerOps);
1528
1529
  /*
1530
   * initialize child expressions
1531
   */
1532
0
  mergestate->js.ps.qual =
1533
0
    ExecInitQual(node->join.plan.qual, (PlanState *) mergestate);
1534
0
  mergestate->js.joinqual =
1535
0
    ExecInitQual(node->join.joinqual, (PlanState *) mergestate);
1536
  /* mergeclauses are handled below */
1537
1538
  /*
1539
   * detect whether we need only consider the first matching inner tuple
1540
   */
1541
0
  mergestate->js.single_match = (node->join.inner_unique ||
1542
0
                   node->join.jointype == JOIN_SEMI);
1543
1544
  /* set up null tuples for outer joins, if needed */
1545
0
  switch (node->join.jointype)
1546
0
  {
1547
0
    case JOIN_INNER:
1548
0
    case JOIN_SEMI:
1549
0
      mergestate->mj_FillOuter = false;
1550
0
      mergestate->mj_FillInner = false;
1551
0
      break;
1552
0
    case JOIN_LEFT:
1553
0
    case JOIN_ANTI:
1554
0
      mergestate->mj_FillOuter = true;
1555
0
      mergestate->mj_FillInner = false;
1556
0
      mergestate->mj_NullInnerTupleSlot =
1557
0
        ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual);
1558
0
      break;
1559
0
    case JOIN_RIGHT:
1560
0
    case JOIN_RIGHT_ANTI:
1561
0
      mergestate->mj_FillOuter = false;
1562
0
      mergestate->mj_FillInner = true;
1563
0
      mergestate->mj_NullOuterTupleSlot =
1564
0
        ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual);
1565
1566
      /*
1567
       * Can't handle right, right-anti or full join with non-constant
1568
       * extra joinclauses.  This should have been caught by planner.
1569
       */
1570
0
      if (!check_constant_qual(node->join.joinqual,
1571
0
                   &mergestate->mj_ConstFalseJoin))
1572
0
        ereport(ERROR,
1573
0
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1574
0
             errmsg("RIGHT JOIN is only supported with merge-joinable join conditions")));
1575
0
      break;
1576
0
    case JOIN_FULL:
1577
0
      mergestate->mj_FillOuter = true;
1578
0
      mergestate->mj_FillInner = true;
1579
0
      mergestate->mj_NullOuterTupleSlot =
1580
0
        ExecInitNullTupleSlot(estate, outerDesc, &TTSOpsVirtual);
1581
0
      mergestate->mj_NullInnerTupleSlot =
1582
0
        ExecInitNullTupleSlot(estate, innerDesc, &TTSOpsVirtual);
1583
1584
      /*
1585
       * Can't handle right, right-anti or full join with non-constant
1586
       * extra joinclauses.  This should have been caught by planner.
1587
       */
1588
0
      if (!check_constant_qual(node->join.joinqual,
1589
0
                   &mergestate->mj_ConstFalseJoin))
1590
0
        ereport(ERROR,
1591
0
            (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
1592
0
             errmsg("FULL JOIN is only supported with merge-joinable join conditions")));
1593
0
      break;
1594
0
    default:
1595
0
      elog(ERROR, "unrecognized join type: %d",
1596
0
         (int) node->join.jointype);
1597
0
  }
1598
1599
  /*
1600
   * preprocess the merge clauses
1601
   */
1602
0
  mergestate->mj_NumClauses = list_length(node->mergeclauses);
1603
0
  mergestate->mj_Clauses = MJExamineQuals(node->mergeclauses,
1604
0
                      node->mergeFamilies,
1605
0
                      node->mergeCollations,
1606
0
                      node->mergeReversals,
1607
0
                      node->mergeNullsFirst,
1608
0
                      (PlanState *) mergestate);
1609
1610
  /*
1611
   * initialize join state
1612
   */
1613
0
  mergestate->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
1614
0
  mergestate->mj_MatchedOuter = false;
1615
0
  mergestate->mj_MatchedInner = false;
1616
0
  mergestate->mj_OuterTupleSlot = NULL;
1617
0
  mergestate->mj_InnerTupleSlot = NULL;
1618
1619
  /*
1620
   * initialization successful
1621
   */
1622
0
  MJ1_printf("ExecInitMergeJoin: %s\n",
1623
0
         "node initialized");
1624
1625
0
  return mergestate;
1626
0
}
1627
1628
/* ----------------------------------------------------------------
1629
 *    ExecEndMergeJoin
1630
 *
1631
 * old comments
1632
 *    frees storage allocated through C routines.
1633
 * ----------------------------------------------------------------
1634
 */
1635
void
1636
ExecEndMergeJoin(MergeJoinState *node)
1637
0
{
1638
0
  MJ1_printf("ExecEndMergeJoin: %s\n",
1639
0
         "ending node processing");
1640
1641
  /*
1642
   * shut down the subplans
1643
   */
1644
0
  ExecEndNode(innerPlanState(node));
1645
0
  ExecEndNode(outerPlanState(node));
1646
1647
0
  MJ1_printf("ExecEndMergeJoin: %s\n",
1648
0
         "node processing ended");
1649
0
}
1650
1651
void
1652
ExecReScanMergeJoin(MergeJoinState *node)
1653
0
{
1654
0
  PlanState  *outerPlan = outerPlanState(node);
1655
0
  PlanState  *innerPlan = innerPlanState(node);
1656
1657
0
  ExecClearTuple(node->mj_MarkedTupleSlot);
1658
1659
0
  node->mj_JoinState = EXEC_MJ_INITIALIZE_OUTER;
1660
0
  node->mj_MatchedOuter = false;
1661
0
  node->mj_MatchedInner = false;
1662
0
  node->mj_OuterTupleSlot = NULL;
1663
0
  node->mj_InnerTupleSlot = NULL;
1664
1665
  /*
1666
   * if chgParam of subnodes is not null then plans will be re-scanned by
1667
   * first ExecProcNode.
1668
   */
1669
0
  if (outerPlan->chgParam == NULL)
1670
0
    ExecReScan(outerPlan);
1671
0
  if (innerPlan->chgParam == NULL)
1672
0
    ExecReScan(innerPlan);
1673
0
}