]> granicus.if.org Git - postgresql/blob - src/backend/executor/nodeMergejoin.c
SQL-language functions are now callable in ordinary fmgr contexts ...
[postgresql] / src / backend / executor / nodeMergejoin.c
1 /*-------------------------------------------------------------------------
2  *
3  * nodeMergejoin.c
4  *        routines supporting merge joins
5  *
6  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/executor/nodeMergejoin.c,v 1.37 2000/08/24 03:29:03 tgl Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15 /*
16  * INTERFACE ROUTINES
17  *              ExecMergeJoin                   mergejoin outer and inner relations.
18  *              ExecInitMergeJoin               creates and initializes run time states
19  *              ExecEndMergeJoin                cleand up the node.
20  *
21  * NOTES
22  *              Essential operation of the merge join algorithm is as follows:
23  *              (** indicates the tuples satisfy the merge clause).
24  *
25  *              Join {                                                                                             -
26  *                      get initial outer and inner tuples                              INITIALIZE
27  *                      Skip Inner                                                                              SKIPINNER
28  *                      mark inner position                                                             JOINMARK
29  *                      do forever {                                                                       -
30  *                              while (outer == inner) {                                        JOINTEST
31  *                                      join tuples                                                             JOINTUPLES
32  *                                      advance inner position                                  NEXTINNER
33  *                              }                                                                                          -
34  *                              advance outer position                                          NEXTOUTER
35  *                              if (outer == mark) {                                            TESTOUTER
36  *                                      restore inner position to mark                  TESTOUTER
37  *                                      continue                                                                   -
38  *                              } else {                                                                           -
39  *                                      Skip Outer                                                              SKIPOUTER
40  *                                      mark inner position                                             JOINMARK
41  *                              }                                                                                          -
42  *                      }                                                                                                  -
43  *              }                                                                                                          -
44  *
45  *              Skip Outer {                                                                            SKIPOUTER
46  *                      if (inner == outer) Join Tuples                                 JOINTUPLES
47  *                      while (outer < inner)                                                   SKIPOUTER
48  *                              advance outer                                                           SKIPOUTER
49  *                      if (outer > inner)                                                              SKIPOUTER
50  *                              Skip Inner                                                                      SKIPINNER
51  *              }                                                                                                          -
52  *
53  *              Skip Inner {                                                                            SKIPINNER
54  *                      if (inner == outer) Join Tuples                                 JOINTUPLES
55  *                      while (outer > inner)                                                   SKIPINNER
56  *                              advance inner                                                           SKIPINNER
57  *                      if (outer < inner)                                                              SKIPINNER
58  *                              Skip Outer                                                                      SKIPOUTER
59  *              }                                                                                                          -
60  *
61  *              The merge join operation is coded in the fashion
62  *              of a state machine.  At each state, we do something and then
63  *              proceed to another state.  This state is stored in the node's
64  *              execution state information and is preserved across calls to
65  *              ExecMergeJoin. -cim 10/31/89
66  *
67  */
68 #include "postgres.h"
69
70 #include "access/heapam.h"
71 #include "catalog/pg_operator.h"
72 #include "executor/execdebug.h"
73 #include "executor/execdefs.h"
74 #include "executor/nodeMergejoin.h"
75 #include "utils/lsyscache.h"
76 #include "utils/syscache.h"
77
78
79 static bool MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext);
80
81 #define MarkInnerTuple(innerTupleSlot, mergestate) \
82 ( \
83         ExecStoreTuple(heap_copytuple((innerTupleSlot)->val), \
84                                    (mergestate)->mj_MarkedTupleSlot, \
85                                    InvalidBuffer, \
86                                    true) \
87 )
88
89
90 /* ----------------------------------------------------------------
91  *              MJFormSkipQual
92  *
93  *              This takes the mergeclause which is a qualification of the
94  *              form ((= expr expr) (= expr expr) ...) and forms a new
95  *              qualification like ((> expr expr) (> expr expr) ...) which
96  *              is used by ExecMergeJoin() in order to determine if we should
97  *              skip tuples.  The replacement operators are named either ">"
98  *              or "<" according to the replaceopname parameter, and have the
99  *              same operand data types as the "=" operators they replace.
100  *              (We expect there to be such operators because the "=" operators
101  *              were marked mergejoinable; however, there might be a different
102  *              one needed in each qual clause.)
103  * ----------------------------------------------------------------
104  */
105 static List *
106 MJFormSkipQual(List *qualList, char *replaceopname)
107 {
108         List       *qualCopy;
109         List       *qualcdr;
110         Expr       *qual;
111         Oper       *op;
112         HeapTuple       optup;
113         Form_pg_operator opform;
114         Oid                     oprleft,
115                                 oprright;
116
117         /* ----------------
118          *      qualList is a list: ((op .. ..) ...)
119          *      first we make a copy of it.  copyObject() makes a deep copy
120          *      so let's use it instead of the old fashoned lispCopy()...
121          * ----------------
122          */
123         qualCopy = (List *) copyObject((Node *) qualList);
124
125         foreach(qualcdr, qualCopy)
126         {
127                 /* ----------------
128                  *       first get the current (op .. ..) list
129                  * ----------------
130                  */
131                 qual = lfirst(qualcdr);
132
133                 /* ----------------
134                  *       now get at the op
135                  * ----------------
136                  */
137                 op = (Oper *) qual->oper;
138                 if (!IsA(op, Oper))
139                         elog(ERROR, "MJFormSkipQual: op not an Oper!");
140
141                 /* ----------------
142                  *       Get the declared left and right operand types of the operator.
143                  *       Note we do *not* use the actual operand types, since those might
144                  *       be different in scenarios with binary-compatible data types.
145                  *       There should be "<" and ">" operators matching a mergejoinable
146                  *       "=" operator's declared operand types, but we might not find them
147                  *       if we search with the actual operand types.
148                  * ----------------
149                  */
150                 optup = get_operator_tuple(op->opno);
151                 if (!HeapTupleIsValid(optup))   /* shouldn't happen */
152                         elog(ERROR, "MJFormSkipQual: operator %u not found", op->opno);
153                 opform = (Form_pg_operator) GETSTRUCT(optup);
154                 oprleft = opform->oprleft;
155                 oprright = opform->oprright;
156
157                 /* ----------------
158                  *       Now look up the matching "<" or ">" operator.  If there isn't one,
159                  *       whoever marked the "=" operator mergejoinable was a loser.
160                  * ----------------
161                  */
162                 optup = SearchSysCacheTuple(OPERNAME,
163                                                                         PointerGetDatum(replaceopname),
164                                                                         ObjectIdGetDatum(oprleft),
165                                                                         ObjectIdGetDatum(oprright),
166                                                                         CharGetDatum('b'));
167                 if (!HeapTupleIsValid(optup))
168                         elog(ERROR,
169                         "MJFormSkipQual: mergejoin operator %u has no matching %s op",
170                                  op->opno, replaceopname);
171                 opform = (Form_pg_operator) GETSTRUCT(optup);
172
173                 /* ----------------
174                  *       And replace the data in the copied operator node.
175                  * ----------------
176                  */
177                 op->opno = optup->t_data->t_oid;
178                 op->opid = opform->oprcode;
179                 op->op_fcache = NULL;
180         }
181
182         return qualCopy;
183 }
184
185 /* ----------------------------------------------------------------
186  *              MergeCompare
187  *
188  *              Compare the keys according to 'compareQual' which is of the
189  *              form: { (key1a > key2a) (key1b > key2b) ... }.
190  *
191  *              (actually, it could also be of the form (key1a < key2a)...)
192  *
193  *              This is different from calling ExecQual because ExecQual returns
194  *              true only if ALL the comparison clauses are satisfied.
195  *              However, there is an order of significance among the keys with
196  *              the first keys being most significant. Therefore, the clauses
197  *              are evaluated in order and the 'compareQual' is satisfied
198  *              if (key1i > key2i) is true and (key1j = key2j) for 0 < j < i.
199  *              We use the original mergeclause items to detect equality.
200  * ----------------------------------------------------------------
201  */
202 static bool
203 MergeCompare(List *eqQual, List *compareQual, ExprContext *econtext)
204 {
205         bool            result;
206         MemoryContext oldContext;
207         List       *clause;
208         List       *eqclause;
209
210         /*
211          * Do expression eval in short-lived context.
212          */
213         oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory);
214
215         /* ----------------
216          *      for each pair of clauses, test them until
217          *      our compare conditions are satisfied.
218          *      if we reach the end of the list, none of our key greater-than
219          *      conditions were satisfied so we return false.
220          * ----------------
221          */
222         result = false;                         /* assume 'false' result */
223
224         eqclause = eqQual;
225         foreach(clause, compareQual)
226         {
227                 Datum           const_value;
228                 bool            isNull;
229
230                 /* ----------------
231                  *       first test if our compare clause is satisfied.
232                  *       if so then return true.
233                  *
234                  *       A NULL result is considered false.
235                  * ----------------
236                  */
237                 const_value = ExecEvalExpr((Node *) lfirst(clause), econtext,
238                                                                    &isNull, NULL);
239
240                 if (DatumGetBool(const_value) && !isNull)
241                 {
242                         result = true;
243                         break;
244                 }
245
246                 /* ----------------
247                  *       ok, the compare clause failed so we test if the keys
248                  *       are equal... if key1 != key2, we return false.
249                  *       otherwise key1 = key2 so we move on to the next pair of keys.
250                  * ----------------
251                  */
252                 const_value = ExecEvalExpr((Node *) lfirst(eqclause),
253                                                                    econtext,
254                                                                    &isNull,
255                                                                    NULL);
256
257                 if (! DatumGetBool(const_value) || isNull)
258                         break;                          /* return false */
259
260                 eqclause = lnext(eqclause);
261         }
262
263         MemoryContextSwitchTo(oldContext);
264
265         return result;
266 }
267
268 /* ----------------------------------------------------------------
269  *              ExecMergeTupleDump
270  *
271  *              This function is called through the MJ_dump() macro
272  *              when EXEC_MERGEJOINDEBUG is defined
273  * ----------------------------------------------------------------
274  */
275 #ifdef EXEC_MERGEJOINDEBUG
276 void
277                         ExecMergeTupleDumpInner(ExprContext *econtext);
278
279 void
280 ExecMergeTupleDumpInner(ExprContext *econtext)
281 {
282         TupleTableSlot *innerSlot;
283
284         printf("==== inner tuple ====\n");
285         innerSlot = econtext->ecxt_innertuple;
286         if (TupIsNull(innerSlot))
287                 printf("(nil)\n");
288         else
289                 MJ_debugtup(innerSlot->val,
290                                         innerSlot->ttc_tupleDescriptor);
291 }
292
293 void
294                         ExecMergeTupleDumpOuter(ExprContext *econtext);
295
296 void
297 ExecMergeTupleDumpOuter(ExprContext *econtext)
298 {
299         TupleTableSlot *outerSlot;
300
301         printf("==== outer tuple ====\n");
302         outerSlot = econtext->ecxt_outertuple;
303         if (TupIsNull(outerSlot))
304                 printf("(nil)\n");
305         else
306                 MJ_debugtup(outerSlot->val,
307                                         outerSlot->ttc_tupleDescriptor);
308 }
309
310 void ExecMergeTupleDumpMarked(ExprContext *econtext,
311                                                  MergeJoinState *mergestate);
312
313 void
314 ExecMergeTupleDumpMarked(ExprContext *econtext,
315                                                  MergeJoinState *mergestate)
316 {
317         TupleTableSlot *markedSlot;
318
319         printf("==== marked tuple ====\n");
320         markedSlot = mergestate->mj_MarkedTupleSlot;
321
322         if (TupIsNull(markedSlot))
323                 printf("(nil)\n");
324         else
325                 MJ_debugtup(markedSlot->val,
326                                         markedSlot->ttc_tupleDescriptor);
327 }
328
329 void
330                         ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate);
331
332 void
333 ExecMergeTupleDump(ExprContext *econtext, MergeJoinState *mergestate)
334 {
335         printf("******** ExecMergeTupleDump ********\n");
336
337         ExecMergeTupleDumpInner(econtext);
338         ExecMergeTupleDumpOuter(econtext);
339         ExecMergeTupleDumpMarked(econtext, mergestate);
340
341         printf("******** \n");
342 }
343
344 #endif
345
346 /* ----------------------------------------------------------------
347  *              ExecMergeJoin
348  *
349  * old comments
350  *              Details of the merge-join routines:
351  *
352  *              (1) ">" and "<" operators
353  *
354  *              Merge-join is done by joining the inner and outer tuples satisfying
355  *              the join clauses of the form ((= outerKey innerKey) ...).
356  *              The join clauses is provided by the query planner and may contain
357  *              more than one (= outerKey innerKey) clauses (for composite key).
358  *
359  *              However, the query executor needs to know whether an outer
360  *              tuple is "greater/smaller" than an inner tuple so that it can
361  *              "synchronize" the two relations. For e.g., consider the following
362  *              relations:
363  *
364  *                              outer: (0 ^1 1 2 5 5 5 6 6 7)   current tuple: 1
365  *                              inner: (1 ^3 5 5 5 5 6)                 current tuple: 3
366  *
367  *              To continue the merge-join, the executor needs to scan both inner
368  *              and outer relations till the matching tuples 5. It needs to know
369  *              that currently inner tuple 3 is "greater" than outer tuple 1 and
370  *              therefore it should scan the outer relation first to find a
371  *              matching tuple and so on.
372  *
373  *              Therefore, when initializing the merge-join node, the executor
374  *              creates the "greater/smaller" clause by substituting the "="
375  *              operator in the join clauses with the corresponding ">" operator.
376  *              The opposite "smaller/greater" clause is formed by substituting "<".
377  *
378  *              Note: prior to v6.5, the relational clauses were formed using the
379  *              sort op used to sort the inner relation, which of course would fail
380  *              if the outer and inner keys were of different data types.
381  *              In the current code, we instead assume that operators named "<" and ">"
382  *              will do the right thing.  This should be true since the mergejoin "="
383  *              operator's pg_operator entry will have told the planner to sort by
384  *              "<" for each of the left and right sides.
385  *
386  *              (2) repositioning inner "cursor"
387  *
388  *              Consider the above relations and suppose that the executor has
389  *              just joined the first outer "5" with the last inner "5". The
390  *              next step is of course to join the second outer "5" with all
391  *              the inner "5's". This requires repositioning the inner "cursor"
392  *              to point at the first inner "5". This is done by "marking" the
393  *              first inner 5 and restore the "cursor" to it before joining
394  *              with the second outer 5. The access method interface provides
395  *              routines to mark and restore to a tuple.
396  * ----------------------------------------------------------------
397  */
398 TupleTableSlot *
399 ExecMergeJoin(MergeJoin *node)
400 {
401         EState     *estate;
402         MergeJoinState *mergestate;
403         ScanDirection direction;
404         List       *innerSkipQual;
405         List       *outerSkipQual;
406         List       *mergeclauses;
407         List       *qual;
408         bool            qualResult;
409         bool            compareResult;
410         Plan       *innerPlan;
411         TupleTableSlot *innerTupleSlot;
412         Plan       *outerPlan;
413         TupleTableSlot *outerTupleSlot;
414         ExprContext *econtext;
415 #ifdef ENABLE_OUTER_JOINS
416         /*
417          * These should be set from the expression context! - thomas
418          * 1999-02-20
419          */
420         static bool isLeftJoin = true;
421         static bool isRightJoin = false;
422 #endif
423
424         /* ----------------
425          *      get information from node
426          * ----------------
427          */
428         mergestate = node->mergestate;
429         estate = node->join.state;
430         direction = estate->es_direction;
431         innerPlan = innerPlan((Plan *) node);
432         outerPlan = outerPlan((Plan *) node);
433         econtext = mergestate->jstate.cs_ExprContext;
434         mergeclauses = node->mergeclauses;
435         qual = node->join.qual;
436
437         if (ScanDirectionIsForward(direction))
438         {
439                 outerSkipQual = mergestate->mj_OuterSkipQual;
440                 innerSkipQual = mergestate->mj_InnerSkipQual;
441         }
442         else
443         {
444                 outerSkipQual = mergestate->mj_InnerSkipQual;
445                 innerSkipQual = mergestate->mj_OuterSkipQual;
446         }
447
448         /* ----------------
449          *      Check to see if we're still projecting out tuples from a previous
450          *      join tuple (because there is a function-returning-set in the
451          *      projection expressions).  If so, try to project another one.
452          * ----------------
453          */
454         if (mergestate->jstate.cs_TupFromTlist)
455         {
456                 TupleTableSlot *result;
457                 ExprDoneCond    isDone;
458
459                 result = ExecProject(mergestate->jstate.cs_ProjInfo, &isDone);
460                 if (isDone == ExprMultipleResult)
461                         return result;
462                 /* Done with that source tuple... */
463                 mergestate->jstate.cs_TupFromTlist = false;
464         }
465
466         /* ----------------
467          *      Reset per-tuple memory context to free any expression evaluation
468          *      storage allocated in the previous tuple cycle.  Note this can't
469          *      happen until we're done projecting out tuples from a join tuple.
470          * ----------------
471          */
472         ResetExprContext(econtext);
473
474         /* ----------------
475          *      ok, everything is setup.. let's go to work
476          * ----------------
477          */
478         for (;;)
479         {
480                 /* ----------------
481                  *      get the current state of the join and do things accordingly.
482                  *      Note: The join states are highlighted with 32-* comments for
483                  *                improved readability.
484                  * ----------------
485                  */
486                 MJ_dump(econtext, mergestate);
487
488                 switch (mergestate->mj_JoinState)
489                 {
490
491                                 /*
492                                  * EXEC_MJ_INITIALIZE means that this is the first time
493                                  * ExecMergeJoin() has been called and so we have to
494                                  * initialize the inner, outer and marked tuples as well
495                                  * as various stuff in the expression context.
496                                  */
497                         case EXEC_MJ_INITIALIZE:
498                                 MJ_printf("ExecMergeJoin: EXEC_MJ_INITIALIZE\n");
499
500                                 /*
501                                  * Note: at this point, if either of our inner or outer
502                                  * tuples are nil, then the join ends immediately because
503                                  * we know one of the subplans is empty.
504                                  */
505                                 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
506                                 if (TupIsNull(innerTupleSlot))
507                                 {
508                                         MJ_printf("ExecMergeJoin: **** inner tuple is nil ****\n");
509                                         return NULL;
510                                 }
511
512                                 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
513                                 if (TupIsNull(outerTupleSlot))
514                                 {
515                                         MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
516                                         return NULL;
517                                 }
518
519                                 /* ----------------
520                                  *       store the inner and outer tuple in the merge state
521                                  * ----------------
522                                  */
523                                 econtext->ecxt_innertuple = innerTupleSlot;
524                                 econtext->ecxt_outertuple = outerTupleSlot;
525
526                                 mergestate->mj_MarkedTupleSlot->ttc_tupleDescriptor =
527                                         innerTupleSlot->ttc_tupleDescriptor;
528
529                                 /* ----------------
530                                  *      initialize merge join state to skip inner tuples.
531                                  * ----------------
532                                  */
533                                 mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
534                                 break;
535
536                                 /*
537                                  * EXEC_MJ_JOINMARK means we have just found a new outer
538                                  * tuple and a possible matching inner tuple. This is the
539                                  * case after the INITIALIZE, SKIPOUTER or SKIPINNER
540                                  * states.
541                                  */
542                         case EXEC_MJ_JOINMARK:
543                                 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINMARK\n");
544                                 ExecMarkPos(innerPlan);
545
546                                 MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
547
548                                 mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
549                                 break;
550
551                                 /*
552                                  * EXEC_MJ_JOINTEST means we have two tuples which might
553                                  * satisfy the merge clause, so we test them.
554                                  *
555                                  * If they do satisfy, then we join them and move on to the
556                                  * next inner tuple (EXEC_MJ_JOINTUPLES).
557                                  *
558                                  * If they do not satisfy then advance to next outer tuple.
559                                  */
560                         case EXEC_MJ_JOINTEST:
561                                 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTEST\n");
562
563                                 ResetExprContext(econtext);
564
565                                 qualResult = ExecQual((List *) mergeclauses, econtext, false);
566                                 MJ_DEBUG_QUAL(mergeclauses, qualResult);
567
568                                 if (qualResult)
569                                         mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
570                                 else
571                                         mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
572                                 break;
573
574                                 /*
575                                  * EXEC_MJ_JOINTUPLES means we have two tuples which
576                                  * satisfied the merge clause so we join them and then
577                                  * proceed to get the next inner tuple (EXEC_NEXT_INNER).
578                                  */
579                         case EXEC_MJ_JOINTUPLES:
580                                 MJ_printf("ExecMergeJoin: EXEC_MJ_JOINTUPLES\n");
581                                 mergestate->mj_JoinState = EXEC_MJ_NEXTINNER;
582
583                                 /*
584                                  * Check the qpqual to see if we actually want to return
585                                  * this join tuple.  If not, can proceed with merge.
586                                  *
587                                  * (We don't bother with a ResetExprContext here, on the
588                                  * assumption that we just did one before checking the merge
589                                  * qual.  One per tuple should be sufficient.)
590                                  */
591                                 qualResult = ExecQual((List *) qual, econtext, false);
592                                 MJ_DEBUG_QUAL(qual, qualResult);
593
594                                 if (qualResult)
595                                 {
596                                         /* ----------------
597                                          *      qualification succeeded.  now form the desired
598                                          *      projection tuple and return the slot containing it.
599                                          * ----------------
600                                          */
601                                         TupleTableSlot *result;
602                                         ExprDoneCond isDone;
603
604                                         MJ_printf("ExecMergeJoin: **** returning tuple ****\n");
605
606                                         result = ExecProject(mergestate->jstate.cs_ProjInfo,
607                                                                                  &isDone);
608
609                                         if (isDone != ExprEndResult)
610                                         {
611                                                 mergestate->jstate.cs_TupFromTlist = (isDone == ExprMultipleResult);
612                                                 return result;
613                                         }
614                                 }
615                                 break;
616
617                                 /*
618                                  * EXEC_MJ_NEXTINNER means advance the inner scan to the
619                                  * next tuple. If the tuple is not nil, we then proceed to
620                                  * test it against the join qualification.
621                                  */
622                         case EXEC_MJ_NEXTINNER:
623                                 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTINNER\n");
624
625                                 /* ----------------
626                                  *      now we get the next inner tuple, if any
627                                  * ----------------
628                                  */
629                                 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
630                                 MJ_DEBUG_PROC_NODE(innerTupleSlot);
631                                 econtext->ecxt_innertuple = innerTupleSlot;
632
633                                 if (TupIsNull(innerTupleSlot))
634                                         mergestate->mj_JoinState = EXEC_MJ_NEXTOUTER;
635                                 else
636                                         mergestate->mj_JoinState = EXEC_MJ_JOINTEST;
637                                 break;
638
639                                 /*-------------------------------------------
640                                  * EXEC_MJ_NEXTOUTER means
641                                  *
642                                  *                              outer inner
643                                  * outer tuple -  5             5  - marked tuple
644                                  *                                5             5
645                                  *                                6             6  - inner tuple
646                                  *                                7             7
647                                  *
648                                  * we know we just bumped into the
649                                  * first inner tuple > current outer tuple
650                                  * so get a new outer tuple and then
651                                  * proceed to test it against the marked tuple
652                                  * (EXEC_MJ_TESTOUTER)
653                                  *------------------------------------------------
654                                  */
655                         case EXEC_MJ_NEXTOUTER:
656                                 MJ_printf("ExecMergeJoin: EXEC_MJ_NEXTOUTER\n");
657
658                                 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
659                                 MJ_DEBUG_PROC_NODE(outerTupleSlot);
660                                 econtext->ecxt_outertuple = outerTupleSlot;
661
662                                 /* ----------------
663                                  *      if the outer tuple is null then we know
664                                  *      we are done with the join
665                                  * ----------------
666                                  */
667                                 if (TupIsNull(outerTupleSlot))
668                                 {
669                                         MJ_printf("ExecMergeJoin: **** outer tuple is nil ****\n");
670                                         return NULL;
671                                 }
672
673                                 mergestate->mj_JoinState = EXEC_MJ_TESTOUTER;
674                                 break;
675
676                                 /*--------------------------------------------------------
677                                  * EXEC_MJ_TESTOUTER If the new outer tuple and the marked
678                                  * tuple satisfy the merge clause then we know we have
679                                  * duplicates in the outer scan so we have to restore the
680                                  * inner scan to the marked tuple and proceed to join the
681                                  * new outer tuples with the inner tuples (EXEC_MJ_JOINTEST)
682                                  *
683                                  * This is the case when
684                                  *                                                outer inner
685                                  *                                                      4         5  - marked tuple
686                                  *                       outer tuple -  5         5
687                                  *               new outer tuple -      5         5
688                                  *                                                      6         8  - inner tuple
689                                  *                                                      7        12
690                                  *
691                                  *                              new outer tuple = marked tuple
692                                  *
693                                  *              If the outer tuple fails the test, then we know we have
694                                  *              to proceed to skip outer tuples until outer >= inner
695                                  *              (EXEC_MJ_SKIPOUTER).
696                                  *
697                                  *              This is the case when
698                                  *
699                                  *                                                outer inner
700                                  *                                                      5         5  - marked tuple
701                                  *                       outer tuple -  5         5
702                                  *               new outer tuple -      6         8  - inner tuple
703                                  *                                                      7        12
704                                  *
705                                  *
706                                  *               new outer tuple > marked tuple
707                                  *
708                                  *---------------------------------------------------------
709                                  */
710                         case EXEC_MJ_TESTOUTER:
711                                 MJ_printf("ExecMergeJoin: EXEC_MJ_TESTOUTER\n");
712
713                                 /* ----------------
714                                  *      here we compare the outer tuple with the marked inner tuple
715                                  *      by using the marked tuple in place of the inner tuple.
716                                  * ----------------
717                                  */
718                                 innerTupleSlot = econtext->ecxt_innertuple;
719                                 econtext->ecxt_innertuple = mergestate->mj_MarkedTupleSlot;
720
721                                 ResetExprContext(econtext);
722
723                                 qualResult = ExecQual((List *) mergeclauses, econtext, false);
724                                 MJ_DEBUG_QUAL(mergeclauses, qualResult);
725
726                                 if (qualResult)
727                                 {
728
729                                         /*
730                                          * the merge clause matched so now we juggle the slots
731                                          * back the way they were and proceed to JOINTEST.
732                                          *
733                                          * I can't understand why we have to go to JOINTEST and
734                                          * compare outer tuple with the same inner one again
735                                          * -> go to JOINTUPLES...        - vadim 02/27/98
736                                          */
737
738                                         ExecRestrPos(innerPlan);
739                                         mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
740                                 }
741                                 else
742                                 {
743                                         econtext->ecxt_innertuple = innerTupleSlot;
744                                         /* ----------------
745                                          *      if the inner tuple was nil and the new outer
746                                          *      tuple didn't match the marked outer tuple then
747                                          *      we may have the case:
748                                          *
749                                          *                       outer inner
750                                          *                         4     4      - marked tuple
751                                          * new outer - 5         4
752                                          *                         6    nil - inner tuple
753                                          *                         7
754                                          *
755                                          *      which means that all subsequent outer tuples will be
756                                          *      larger than our inner tuples.
757                                          * ----------------
758                                          */
759                                         if (TupIsNull(innerTupleSlot))
760                                         {
761 #ifdef ENABLE_OUTER_JOINS
762                                                 if (isLeftJoin)
763                                                 {
764                                                         /* continue on to null fill outer tuples */
765                                                         mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
766                                                         break;
767                                                 }
768 #endif
769                                                 MJ_printf("ExecMergeJoin: **** weird case 1 ****\n");
770                                                 return NULL;
771                                         }
772
773                                         /* continue on to skip outer tuples */
774                                         mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
775                                 }
776                                 break;
777
778                                 /*----------------------------------------------------------
779                                  * EXEC_MJ_SKIPOUTER means skip over tuples in the outer plan
780                                  * until we find an outer tuple > current inner tuple.
781                                  *
782                                  * For example:
783                                  *
784                                  *                              outer inner
785                                  *                                5             5
786                                  *                                5             5
787                                  * outer tuple -  6             8  - inner tuple
788                                  *                                7    12
789                                  *                                8    14
790                                  *
791                                  * we have to advance the outer scan
792                                  * until we find the outer 8.
793                                  *----------------------------------------------------------
794                                  */
795                         case EXEC_MJ_SKIPOUTER:
796                                 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPOUTER\n");
797                                 /* ----------------
798                                  *      before we advance, make sure the current tuples
799                                  *      do not satisfy the mergeclauses.  If they do, then
800                                  *      we update the marked tuple and go join them.
801                                  * ----------------
802                                  */
803                                 ResetExprContext(econtext);
804
805                                 qualResult = ExecQual((List *) mergeclauses, econtext, false);
806                                 MJ_DEBUG_QUAL(mergeclauses, qualResult);
807
808                                 if (qualResult)
809                                 {
810                                         ExecMarkPos(innerPlan);
811
812                                         MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
813
814                                         mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
815                                         break;
816                                 }
817
818                                 /* ----------------
819                                  *      ok, now test the skip qualification
820                                  * ----------------
821                                  */
822                                 compareResult = MergeCompare(mergeclauses,
823                                                                                          outerSkipQual,
824                                                                                          econtext);
825
826                                 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
827
828                                 /* ----------------
829                                  *      compareResult is true as long as we should
830                                  *      continue skipping tuples.
831                                  * ----------------
832                                  */
833                                 if (compareResult)
834                                 {
835 #ifdef ENABLE_OUTER_JOINS
836                                         /* ----------------
837                                          *      if this is a left or full outer join, then fill
838                                          * ----------------
839                                          */
840                                         if (isLeftJoin)
841                                         {
842                                                 mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
843                                                 break;
844                                         }
845 #endif
846
847                                         outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
848                                         MJ_DEBUG_PROC_NODE(outerTupleSlot);
849                                         econtext->ecxt_outertuple = outerTupleSlot;
850
851                                         /* ----------------
852                                          *      if the outer tuple is null then we know
853                                          *      we are done with the join
854                                          * ----------------
855                                          */
856                                         if (TupIsNull(outerTupleSlot))
857                                         {
858                                                 MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
859                                                 return NULL;
860                                         }
861                                         /* ----------------
862                                          *      otherwise test the new tuple against the skip qual.
863                                          *      (we remain in the EXEC_MJ_SKIPOUTER state)
864                                          * ----------------
865                                          */
866                                         break;
867                                 }
868
869                                 /* ----------------
870                                  *      now check the inner skip qual to see if we
871                                  *      should now skip inner tuples... if we fail the
872                                  *      inner skip qual, then we know we have a new pair
873                                  *      of matching tuples.
874                                  * ----------------
875                                  */
876                                 compareResult = MergeCompare(mergeclauses,
877                                                                                          innerSkipQual,
878                                                                                          econtext);
879
880                                 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
881
882                                 if (compareResult)
883                                         mergestate->mj_JoinState = EXEC_MJ_SKIPINNER;
884                                 else
885                                         mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
886                                 break;
887
888                                 /*-----------------------------------------------------------
889                                  * EXEC_MJ_SKIPINNER means skip over tuples in the inner plan
890                                  * until we find an inner tuple > current outer tuple.
891                                  *
892                                  * For example:
893                                  *
894                                  *                              outer inner
895                                  *                                5             5
896                                  *                                5             5
897                                  * outer tuple - 12             8  - inner tuple
898                                  *                               14    10
899                                  *                               17    12
900                                  *
901                                  * we have to advance the inner scan
902                                  * until we find the inner 12.
903                                  *
904                                  *-------------------------------------------------------
905                                  */
906                         case EXEC_MJ_SKIPINNER:
907                                 MJ_printf("ExecMergeJoin: EXEC_MJ_SKIPINNER\n");
908                                 /* ----------------
909                                  *      before we advance, make sure the current tuples
910                                  *      do not satisfy the mergeclauses.  If they do, then
911                                  *      we update the marked tuple and go join them.
912                                  * ----------------
913                                  */
914                                 ResetExprContext(econtext);
915
916                                 qualResult = ExecQual((List *) mergeclauses, econtext, false);
917                                 MJ_DEBUG_QUAL(mergeclauses, qualResult);
918
919                                 if (qualResult)
920                                 {
921                                         ExecMarkPos(innerPlan);
922
923                                         MarkInnerTuple(econtext->ecxt_innertuple, mergestate);
924
925                                         mergestate->mj_JoinState = EXEC_MJ_JOINTUPLES;
926                                         break;
927                                 }
928
929                                 /* ----------------
930                                  *      ok, now test the skip qualification
931                                  * ----------------
932                                  */
933                                 compareResult = MergeCompare(mergeclauses,
934                                                                                          innerSkipQual,
935                                                                                          econtext);
936
937                                 MJ_DEBUG_MERGE_COMPARE(innerSkipQual, compareResult);
938
939                                 /* ----------------
940                                  *      compareResult is true as long as we should
941                                  *      continue skipping tuples.
942                                  * ----------------
943                                  */
944                                 if (compareResult)
945                                 {
946 #ifdef ENABLE_OUTER_JOINS
947                                         /* ----------------
948                                          *      if this is a right or full outer join, then fill
949                                          * ----------------
950                                          */
951                                         if (isRightJoin)
952                                         {
953                                                 mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
954                                                 break;
955                                         }
956 #endif
957
958                                         /* ----------------
959                                          *      now try and get a new inner tuple
960                                          * ----------------
961                                          */
962                                         innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
963                                         MJ_DEBUG_PROC_NODE(innerTupleSlot);
964                                         econtext->ecxt_innertuple = innerTupleSlot;
965
966                                         /* ----------------
967                                          *      if the inner tuple is null then we know
968                                          *      we have to restore the inner scan
969                                          *      and advance to the next outer tuple
970                                          * ----------------
971                                          */
972                                         if (TupIsNull(innerTupleSlot))
973                                         {
974                                                 /* ----------------
975                                                  *      this is an interesting case.. all our
976                                                  *      inner tuples are smaller then our outer
977                                                  *      tuples so we never found an inner tuple
978                                                  *      to mark.
979                                                  *
980                                                  *                        outer inner
981                                                  *       outer tuple -  5         4
982                                                  *                                      5         4
983                                                  *                                      6        nil  - inner tuple
984                                                  *                                      7
985                                                  *
986                                                  *      This means the join should end.
987                                                  * ----------------
988                                                  */
989                                                 MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
990                                                 return NULL;
991                                         }
992
993                                         /* ----------------
994                                          *      otherwise test the new tuple against the skip qual.
995                                          *      (we remain in the EXEC_MJ_SKIPINNER state)
996                                          * ----------------
997                                          */
998                                         break;
999                                 }
1000
1001                                 /* ----------------
1002                                  *      compare finally failed and we have stopped skipping
1003                                  *      inner tuples so now check the outer skip qual
1004                                  *      to see if we should now skip outer tuples...
1005                                  * ----------------
1006                                  */
1007                                 compareResult = MergeCompare(mergeclauses,
1008                                                                                          outerSkipQual,
1009                                                                                          econtext);
1010
1011                                 MJ_DEBUG_MERGE_COMPARE(outerSkipQual, compareResult);
1012
1013                                 if (compareResult)
1014                                         mergestate->mj_JoinState = EXEC_MJ_SKIPOUTER;
1015                                 else
1016                                         mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1017
1018                                 break;
1019
1020 #ifdef ENABLE_OUTER_JOINS
1021
1022                                 /*
1023                                  * EXEC_MJ_FILLINNER means we have an unmatched inner
1024                                  * tuple which must be null-expanded into the projection
1025                                  * tuple. get the next inner tuple and reset markers
1026                                  * (EXEC_MJ_JOINMARK).
1027                                  */
1028                         case EXEC_MJ_FILLINNER:
1029                                 MJ_printf("ExecMergeJoin: EXEC_MJ_FILLINNER\n");
1030                                 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1031
1032                                 /* ----------------
1033                                  *      project the inner tuple into the result
1034                                  * ----------------
1035                                  */
1036                                 MJ_printf("ExecMergeJoin: project inner tuple into the result (not yet implemented)\n");
1037
1038                                 /* ----------------
1039                                  *      now skip this inner tuple
1040                                  * ----------------
1041                                  */
1042                                 innerTupleSlot = ExecProcNode(innerPlan, (Plan *) node);
1043                                 MJ_DEBUG_PROC_NODE(innerTupleSlot);
1044                                 econtext->ecxt_innertuple = innerTupleSlot;
1045
1046                                 /* ----------------
1047                                  *      if the inner tuple is null then we know
1048                                  *      we have to restore the inner scan
1049                                  *      and advance to the next outer tuple
1050                                  * ----------------
1051                                  */
1052                                 if (TupIsNull(innerTupleSlot))
1053                                 {
1054                                         if (isLeftJoin && !TupIsNull(outerTupleSlot))
1055                                         {
1056                                                 mergestate->mj_JoinState = EXEC_MJ_FILLOUTER;
1057                                                 MJ_printf("ExecMergeJoin: try to complete outer fill\n");
1058                                                 break;
1059                                         }
1060
1061                                         MJ_printf("ExecMergeJoin: **** weird case 2 ****\n");
1062                                         return NULL;
1063                                 }
1064
1065                                 /* ----------------
1066                                  *      otherwise test the new tuple against the skip qual.
1067                                  *      (we move to the EXEC_MJ_JOINMARK state)
1068                                  * ----------------
1069                                  */
1070                                 break;
1071
1072                                 /*
1073                                  * EXEC_MJ_FILLOUTER means we have an unmatched outer
1074                                  * tuple which must be null-expanded into the projection
1075                                  * tuple. get the next outer tuple and reset markers
1076                                  * (EXEC_MJ_JOINMARK).
1077                                  */
1078                         case EXEC_MJ_FILLOUTER:
1079                                 MJ_printf("ExecMergeJoin: EXEC_MJ_FILLOUTER\n");
1080                                 mergestate->mj_JoinState = EXEC_MJ_JOINMARK;
1081
1082                                 /* ----------------
1083                                  *      project the outer tuple into the result
1084                                  * ----------------
1085                                  */
1086                                 MJ_printf("ExecMergeJoin: project outer tuple into the result (not yet implemented)\n");
1087
1088                                 /* ----------------
1089                                  *      now skip this outer tuple
1090                                  * ----------------
1091                                  */
1092                                 outerTupleSlot = ExecProcNode(outerPlan, (Plan *) node);
1093                                 MJ_DEBUG_PROC_NODE(outerTupleSlot);
1094                                 econtext->ecxt_outertuple = outerTupleSlot;
1095
1096                                 /* ----------------
1097                                  *      if the outer tuple is null then we know
1098                                  *      we are done with the left half of the join
1099                                  * ----------------
1100                                  */
1101                                 if (TupIsNull(outerTupleSlot))
1102                                 {
1103                                         if (isRightJoin && !TupIsNull(innerTupleSlot))
1104                                         {
1105                                                 mergestate->mj_JoinState = EXEC_MJ_FILLINNER;
1106                                                 MJ_printf("ExecMergeJoin: try to complete inner fill\n");
1107                                                 break;
1108                                         }
1109
1110                                         MJ_printf("ExecMergeJoin: **** outerTuple is nil ****\n");
1111                                         return NULL;
1112                                 }
1113
1114                                 /* ----------------
1115                                  *      otherwise test the new tuple against the skip qual.
1116                                  *      (we move to the EXEC_MJ_JOINMARK state)
1117                                  * ----------------
1118                                  */
1119                                 break;
1120 #endif
1121
1122                                 /*
1123                                  * if we get here it means our code is fouled up and so we
1124                                  * just end the join prematurely.
1125                                  */
1126                         default:
1127                                 elog(NOTICE, "ExecMergeJoin: invalid join state. aborting");
1128                                 return NULL;
1129                 }
1130         }
1131 }
1132
1133 /* ----------------------------------------------------------------
1134  *              ExecInitMergeJoin
1135  *
1136  * old comments
1137  *              Creates the run-time state information for the node and
1138  *              sets the relation id to contain relevant decriptors.
1139  * ----------------------------------------------------------------
1140  */
1141 bool
1142 ExecInitMergeJoin(MergeJoin *node, EState *estate, Plan *parent)
1143 {
1144         MergeJoinState *mergestate;
1145         List       *joinclauses;
1146         TupleTableSlot *mjSlot;
1147
1148         MJ1_printf("ExecInitMergeJoin: %s\n",
1149                            "initializing node");
1150
1151         /* ----------------
1152          *      assign the node's execution state and
1153          *      get the range table and direction from it
1154          * ----------------
1155          */
1156         node->join.state = estate;
1157
1158         /* ----------------
1159          *      create new merge state for node
1160          * ----------------
1161          */
1162         mergestate = makeNode(MergeJoinState);
1163         mergestate->mj_OuterSkipQual = NIL;
1164         mergestate->mj_InnerSkipQual = NIL;
1165         mergestate->mj_JoinState = 0;
1166         mergestate->mj_MarkedTupleSlot = NULL;
1167         node->mergestate = mergestate;
1168
1169         /* ----------------
1170          *      Miscellaneous initialization
1171          *
1172          *               +      create expression context for node
1173          * ----------------
1174          */
1175         ExecAssignExprContext(estate, &mergestate->jstate);
1176
1177 #define MERGEJOIN_NSLOTS 2
1178         /* ----------------
1179          *      tuple table initialization
1180          *
1181          *      XXX why aren't we getting a tuple table slot in the normal way?
1182          * ----------------
1183          */
1184         ExecInitResultTupleSlot(estate, &mergestate->jstate);
1185         mjSlot = makeNode(TupleTableSlot);
1186         mjSlot->val = NULL;
1187         mjSlot->ttc_shouldFree = true;
1188         mjSlot->ttc_descIsNew = true;
1189         mjSlot->ttc_tupleDescriptor = NULL;
1190         mjSlot->ttc_buffer = InvalidBuffer;
1191         mjSlot->ttc_whichplan = -1;
1192         mergestate->mj_MarkedTupleSlot = mjSlot;
1193
1194         /* ----------------
1195          *      form merge skip qualifications
1196          * ----------------
1197          */
1198         joinclauses = node->mergeclauses;
1199         mergestate->mj_OuterSkipQual = MJFormSkipQual(joinclauses, "<");
1200         mergestate->mj_InnerSkipQual = MJFormSkipQual(joinclauses, ">");
1201
1202         MJ_printf("\nExecInitMergeJoin: OuterSkipQual is ");
1203         MJ_nodeDisplay(mergestate->mj_OuterSkipQual);
1204         MJ_printf("\nExecInitMergeJoin: InnerSkipQual is ");
1205         MJ_nodeDisplay(mergestate->mj_InnerSkipQual);
1206         MJ_printf("\n");
1207
1208         /* ----------------
1209          *      initialize join state
1210          * ----------------
1211          */
1212         mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1213
1214         /* ----------------
1215          *      initialize subplans
1216          * ----------------
1217          */
1218         ExecInitNode(outerPlan((Plan *) node), estate, (Plan *) node);
1219         ExecInitNode(innerPlan((Plan *) node), estate, (Plan *) node);
1220
1221         /* ----------------
1222          *      initialize tuple type and projection info
1223          * ----------------
1224          */
1225         ExecAssignResultTypeFromTL((Plan *) node, &mergestate->jstate);
1226         ExecAssignProjectionInfo((Plan *) node, &mergestate->jstate);
1227
1228         mergestate->jstate.cs_TupFromTlist = false;
1229         /* ----------------
1230          *      initialization successful
1231          * ----------------
1232          */
1233         MJ1_printf("ExecInitMergeJoin: %s\n",
1234                            "node initialized");
1235
1236         return TRUE;
1237 }
1238
1239 int
1240 ExecCountSlotsMergeJoin(MergeJoin *node)
1241 {
1242         return ExecCountSlotsNode(outerPlan((Plan *) node)) +
1243         ExecCountSlotsNode(innerPlan((Plan *) node)) +
1244         MERGEJOIN_NSLOTS;
1245 }
1246
1247 /* ----------------------------------------------------------------
1248  *              ExecEndMergeJoin
1249  *
1250  * old comments
1251  *              frees storage allocated through C routines.
1252  * ----------------------------------------------------------------
1253  */
1254 void
1255 ExecEndMergeJoin(MergeJoin *node)
1256 {
1257         MergeJoinState *mergestate;
1258
1259         MJ1_printf("ExecEndMergeJoin: %s\n",
1260                            "ending node processing");
1261
1262         /* ----------------
1263          *      get state information from the node
1264          * ----------------
1265          */
1266         mergestate = node->mergestate;
1267
1268         /* ----------------
1269          *      Free the projection info and the scan attribute info
1270          *
1271          *      Note: we don't ExecFreeResultType(mergestate)
1272          *                because the rule manager depends on the tupType
1273          *                returned by ExecMain().  So for now, this
1274          *                is freed at end-transaction time.  -cim 6/2/91
1275          * ----------------
1276          */
1277         ExecFreeProjectionInfo(&mergestate->jstate);
1278         ExecFreeExprContext(&mergestate->jstate);
1279
1280         /* ----------------
1281          *      shut down the subplans
1282          * ----------------
1283          */
1284         ExecEndNode((Plan *) innerPlan((Plan *) node), (Plan *) node);
1285         ExecEndNode((Plan *) outerPlan((Plan *) node), (Plan *) node);
1286
1287         /* ----------------
1288          *      clean out the tuple table so that we don't try and
1289          *      pfree the marked tuples..  see HACK ALERT at the top of
1290          *      this file.
1291          * ----------------
1292          */
1293         ExecClearTuple(mergestate->jstate.cs_ResultTupleSlot);
1294         ExecClearTuple(mergestate->mj_MarkedTupleSlot);
1295         pfree(mergestate->mj_MarkedTupleSlot);
1296         mergestate->mj_MarkedTupleSlot = NULL;
1297
1298         MJ1_printf("ExecEndMergeJoin: %s\n",
1299                            "node processing ended");
1300 }
1301
1302 void
1303 ExecReScanMergeJoin(MergeJoin *node, ExprContext *exprCtxt, Plan *parent)
1304 {
1305         MergeJoinState *mergestate = node->mergestate;
1306         TupleTableSlot *mjSlot = mergestate->mj_MarkedTupleSlot;
1307
1308         ExecClearTuple(mjSlot);
1309         mjSlot->ttc_tupleDescriptor = NULL;
1310         mjSlot->ttc_descIsNew = true;
1311         mjSlot->ttc_whichplan = -1;
1312
1313         mergestate->mj_JoinState = EXEC_MJ_INITIALIZE;
1314
1315         /*
1316          * if chgParam of subnodes is not null then plans will be re-scanned
1317          * by first ExecProcNode.
1318          */
1319         if (((Plan *) node)->lefttree->chgParam == NULL)
1320                 ExecReScan(((Plan *) node)->lefttree, exprCtxt, (Plan *) node);
1321         if (((Plan *) node)->righttree->chgParam == NULL)
1322                 ExecReScan(((Plan *) node)->righttree, exprCtxt, (Plan *) node);
1323
1324 }