~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/optimizer/plan/subselect.c

  • Committer: Package Import Robot
  • Author(s): Martin Pitt
  • Date: 2013-02-05 18:13:52 UTC
  • mfrom: (1.1.10) (10.1.5 oneiric-proposed)
  • Revision ID: package-import@ubuntu.com-20130205181352-3kw4f94ilqklzm7c
Tags: 9.1.8-0ubuntu11.10
* New upstream security/bug fix release: (LP: #1116336)
  - Prevent execution of enum_recv from SQL
    The function was misdeclared, allowing a simple SQL command to crash the
    server.  In principle an attacker might be able to use it to examine the
    contents of server memory.  Our thanks to Sumit Soni (via Secunia SVCRP)
    for reporting this issue. (CVE-2013-0255)
  - See HISTORY/changelog.gz for the other bug fixes.

Show diffs side-by-side

added added

removed removed

Lines of Context:
55
55
 
56
56
static Node *build_subplan(PlannerInfo *root, Plan *plan,
57
57
                          List *rtable, List *rowmarks,
 
58
                          List *plan_params,
58
59
                          SubLinkType subLinkType, Node *testexpr,
59
60
                          bool adjust_testexpr, bool unknownEqFalse);
60
61
static List *generate_subquery_params(PlannerInfo *root, List *tlist,
83
84
 
84
85
 
85
86
/*
86
 
 * Select a PARAM_EXEC number to identify the given Var.
87
 
 * If the Var already has a param slot, return that one.
 
87
 * Select a PARAM_EXEC number to identify the given Var as a parameter for
 
88
 * the current subquery, or for a nestloop's inner scan.
 
89
 * If the Var already has a param in the current context, return that one.
88
90
 */
89
91
static int
90
92
assign_param_for_var(PlannerInfo *root, Var *var)
91
93
{
92
94
        ListCell   *ppl;
93
95
        PlannerParamItem *pitem;
94
 
        Index           abslevel;
95
 
        int                     i;
96
 
 
97
 
        abslevel = root->query_level - var->varlevelsup;
98
 
 
99
 
        /* If there's already a paramlist entry for this same Var, just use it */
100
 
        i = 0;
101
 
        foreach(ppl, root->glob->paramlist)
 
96
        Index           levelsup;
 
97
 
 
98
        /* Find the query level the Var belongs to */
 
99
        for (levelsup = var->varlevelsup; levelsup > 0; levelsup--)
 
100
                root = root->parent_root;
 
101
 
 
102
        /* If there's already a matching PlannerParamItem there, just use it */
 
103
        foreach(ppl, root->plan_params)
102
104
        {
103
105
                pitem = (PlannerParamItem *) lfirst(ppl);
104
 
                if (pitem->abslevel == abslevel && IsA(pitem->item, Var))
 
106
                if (IsA(pitem->item, Var))
105
107
                {
106
108
                        Var                *pvar = (Var *) pitem->item;
107
109
 
 
110
                        /*
 
111
                         * This comparison must match _equalVar(), except for ignoring
 
112
                         * varlevelsup.  Note that _equalVar() ignores the location.
 
113
                         */
108
114
                        if (pvar->varno == var->varno &&
109
115
                                pvar->varattno == var->varattno &&
110
116
                                pvar->vartype == var->vartype &&
111
 
                                pvar->vartypmod == var->vartypmod)
112
 
                                return i;
 
117
                                pvar->vartypmod == var->vartypmod &&
 
118
                                pvar->varcollid == var->varcollid &&
 
119
                                pvar->varnoold == var->varnoold &&
 
120
                                pvar->varoattno == var->varoattno)
 
121
                                return pitem->paramId;
113
122
                }
114
 
                i++;
115
123
        }
116
124
 
117
125
        /* Nope, so make a new one */
120
128
 
121
129
        pitem = makeNode(PlannerParamItem);
122
130
        pitem->item = (Node *) var;
123
 
        pitem->abslevel = abslevel;
124
 
 
125
 
        root->glob->paramlist = lappend(root->glob->paramlist, pitem);
126
 
 
127
 
        /* i is already the correct list index for the new item */
128
 
        return i;
 
131
        pitem->paramId = root->glob->nParamExec++;
 
132
 
 
133
        root->plan_params = lappend(root->plan_params, pitem);
 
134
 
 
135
        return pitem->paramId;
129
136
}
130
137
 
131
138
/*
140
147
 
141
148
        Assert(var->varlevelsup > 0 && var->varlevelsup < root->query_level);
142
149
 
143
 
        /*
144
 
         * Find the Var in root->glob->paramlist, or add it if not present.
145
 
         *
146
 
         * NOTE: in sufficiently complex querytrees, it is possible for the same
147
 
         * varno/abslevel to refer to different RTEs in different parts of the
148
 
         * parsetree, so that different fields might end up sharing the same Param
149
 
         * number.      As long as we check the vartype/typmod as well, I believe that
150
 
         * this sort of aliasing will cause no trouble.  The correct field should
151
 
         * get stored into the Param slot at execution in each part of the tree.
152
 
         */
 
150
        /* Find the Var in the appropriate plan_params, or add it if not present */
153
151
        i = assign_param_for_var(root, var);
154
152
 
155
153
        retval = makeNode(Param);
158
156
        retval->paramtype = var->vartype;
159
157
        retval->paramtypmod = var->vartypmod;
160
158
        retval->paramcollid = var->varcollid;
161
 
        retval->location = -1;
 
159
        retval->location = var->location;
162
160
 
163
161
        return retval;
164
162
}
167
165
 * Generate a Param node to replace the given Var, which will be supplied
168
166
 * from an upper NestLoop join node.
169
167
 *
170
 
 * Because we allow nestloop and subquery Params to alias each other,
171
 
 * this is effectively the same as replace_outer_var, except that we expect
 
168
 * This is effectively the same as replace_outer_var, except that we expect
172
169
 * the Var to be local to the current query level.
173
170
 */
174
171
Param *
187
184
        retval->paramtype = var->vartype;
188
185
        retval->paramtypmod = var->vartypmod;
189
186
        retval->paramcollid = var->varcollid;
190
 
        retval->location = -1;
 
187
        retval->location = var->location;
191
188
 
192
189
        return retval;
193
190
}
194
191
 
195
192
/*
196
 
 * Select a PARAM_EXEC number to identify the given PlaceHolderVar.
197
 
 * If the PlaceHolderVar already has a param slot, return that one.
 
193
 * Select a PARAM_EXEC number to identify the given PlaceHolderVar as a
 
194
 * parameter for the current subquery, or for a nestloop's inner scan.
 
195
 * If the PHV already has a param in the current context, return that one.
198
196
 *
199
197
 * This is just like assign_param_for_var, except for PlaceHolderVars.
200
198
 */
203
201
{
204
202
        ListCell   *ppl;
205
203
        PlannerParamItem *pitem;
206
 
        Index           abslevel;
207
 
        int                     i;
208
 
 
209
 
        abslevel = root->query_level - phv->phlevelsup;
210
 
 
211
 
        /* If there's already a paramlist entry for this same PHV, just use it */
212
 
        i = 0;
213
 
        foreach(ppl, root->glob->paramlist)
 
204
        Index           levelsup;
 
205
 
 
206
        /* Find the query level the PHV belongs to */
 
207
        for (levelsup = phv->phlevelsup; levelsup > 0; levelsup--)
 
208
                root = root->parent_root;
 
209
 
 
210
        /* If there's already a matching PlannerParamItem there, just use it */
 
211
        foreach(ppl, root->plan_params)
214
212
        {
215
213
                pitem = (PlannerParamItem *) lfirst(ppl);
216
 
                if (pitem->abslevel == abslevel && IsA(pitem->item, PlaceHolderVar))
 
214
                if (IsA(pitem->item, PlaceHolderVar))
217
215
                {
218
216
                        PlaceHolderVar *pphv = (PlaceHolderVar *) pitem->item;
219
217
 
220
218
                        /* We assume comparing the PHIDs is sufficient */
221
219
                        if (pphv->phid == phv->phid)
222
 
                                return i;
 
220
                                return pitem->paramId;
223
221
                }
224
 
                i++;
225
222
        }
226
223
 
227
224
        /* Nope, so make a new one */
234
231
 
235
232
        pitem = makeNode(PlannerParamItem);
236
233
        pitem->item = (Node *) phv;
237
 
        pitem->abslevel = abslevel;
238
 
 
239
 
        root->glob->paramlist = lappend(root->glob->paramlist, pitem);
240
 
 
241
 
        /* i is already the correct list index for the new item */
242
 
        return i;
 
234
        pitem->paramId = root->glob->nParamExec++;
 
235
 
 
236
        root->plan_params = lappend(root->plan_params, pitem);
 
237
 
 
238
        return pitem->paramId;
243
239
}
244
240
 
245
241
/*
256
252
 
257
253
        Assert(phv->phlevelsup > 0 && phv->phlevelsup < root->query_level);
258
254
 
259
 
        /*
260
 
         * Find the PlaceHolderVar in root->glob->paramlist, or add it if not
261
 
         * present.
262
 
         */
 
255
        /* Find the PHV in the appropriate plan_params, or add it if not present */
263
256
        i = assign_param_for_placeholdervar(root, phv);
264
257
 
265
258
        retval = makeNode(Param);
309
302
{
310
303
        Param      *retval;
311
304
        PlannerParamItem *pitem;
312
 
        Index           abslevel;
313
 
        int                     i;
 
305
        Index           levelsup;
314
306
 
315
307
        Assert(agg->agglevelsup > 0 && agg->agglevelsup < root->query_level);
316
 
        abslevel = root->query_level - agg->agglevelsup;
 
308
 
 
309
        /* Find the query level the Aggref belongs to */
 
310
        for (levelsup = agg->agglevelsup; levelsup > 0; levelsup--)
 
311
                root = root->parent_root;
317
312
 
318
313
        /*
319
314
         * It does not seem worthwhile to try to match duplicate outer aggs. Just
325
320
 
326
321
        pitem = makeNode(PlannerParamItem);
327
322
        pitem->item = (Node *) agg;
328
 
        pitem->abslevel = abslevel;
 
323
        pitem->paramId = root->glob->nParamExec++;
329
324
 
330
 
        root->glob->paramlist = lappend(root->glob->paramlist, pitem);
331
 
        i = list_length(root->glob->paramlist) - 1;
 
325
        root->plan_params = lappend(root->plan_params, pitem);
332
326
 
333
327
        retval = makeNode(Param);
334
328
        retval->paramkind = PARAM_EXEC;
335
 
        retval->paramid = i;
 
329
        retval->paramid = pitem->paramId;
336
330
        retval->paramtype = agg->aggtype;
337
331
        retval->paramtypmod = -1;
338
332
        retval->paramcollid = agg->aggcollid;
339
 
        retval->location = -1;
 
333
        retval->location = agg->location;
340
334
 
341
335
        return retval;
342
336
}
344
338
/*
345
339
 * Generate a new Param node that will not conflict with any other.
346
340
 *
347
 
 * This is used to allocate PARAM_EXEC slots for subplan outputs.
 
341
 * This is used to create Params representing subplan outputs.
 
342
 * We don't need to build a PlannerParamItem for such a Param, but we do
 
343
 * need to record the PARAM_EXEC slot number as being allocated.
348
344
 */
349
345
static Param *
350
346
generate_new_param(PlannerInfo *root, Oid paramtype, int32 paramtypmod,
351
347
                                   Oid paramcollation)
352
348
{
353
349
        Param      *retval;
354
 
        PlannerParamItem *pitem;
355
350
 
356
351
        retval = makeNode(Param);
357
352
        retval->paramkind = PARAM_EXEC;
358
 
        retval->paramid = list_length(root->glob->paramlist);
 
353
        retval->paramid = root->glob->nParamExec++;
359
354
        retval->paramtype = paramtype;
360
355
        retval->paramtypmod = paramtypmod;
361
356
        retval->paramcollid = paramcollation;
362
357
        retval->location = -1;
363
358
 
364
 
        pitem = makeNode(PlannerParamItem);
365
 
        pitem->item = (Node *) retval;
366
 
        pitem->abslevel = root->query_level;
367
 
 
368
 
        root->glob->paramlist = lappend(root->glob->paramlist, pitem);
369
 
 
370
359
        return retval;
371
360
}
372
361
 
375
364
 * is not actually used to carry a value at runtime).  Such parameters are
376
365
 * used for special runtime signaling purposes, such as connecting a
377
366
 * recursive union node to its worktable scan node or forcing plan
378
 
 * re-evaluation within the EvalPlanQual mechanism.
 
367
 * re-evaluation within the EvalPlanQual mechanism.  No actual Param node
 
368
 * exists with this ID, however.
379
369
 */
380
370
int
381
371
SS_assign_special_param(PlannerInfo *root)
382
372
{
383
 
        Param      *param;
384
 
 
385
 
        /* We generate a Param of datatype INTERNAL */
386
 
        param = generate_new_param(root, INTERNALOID, -1, InvalidOid);
387
 
        /* ... but the caller only cares about its ID */
388
 
        return param->paramid;
 
373
        return root->glob->nParamExec++;
389
374
}
390
375
 
391
376
/*
446
431
        double          tuple_fraction;
447
432
        Plan       *plan;
448
433
        PlannerInfo *subroot;
 
434
        List       *plan_params;
449
435
        Node       *result;
450
436
 
451
437
        /*
489
475
        else
490
476
                tuple_fraction = 0.0;   /* default behavior */
491
477
 
 
478
        /* plan_params should not be in use in current query level */
 
479
        Assert(root->plan_params == NIL);
 
480
 
492
481
        /*
493
482
         * Generate the plan for the subquery.
494
483
         */
497
486
                                                        false, tuple_fraction,
498
487
                                                        &subroot);
499
488
 
 
489
        /* Isolate the params needed by this specific subplan */
 
490
        plan_params = root->plan_params;
 
491
        root->plan_params = NIL;
 
492
 
500
493
        /* And convert to SubPlan or InitPlan format. */
501
494
        result = build_subplan(root, plan,
502
495
                                                   subroot->parse->rtable, subroot->rowMarks,
 
496
                                                   plan_params,
503
497
                                                   subLinkType, testexpr, true, isTopQual);
504
498
 
505
499
        /*
532
526
                                                                        false, 0.0,
533
527
                                                                        &subroot);
534
528
 
 
529
                        /* Isolate the params needed by this specific subplan */
 
530
                        plan_params = root->plan_params;
 
531
                        root->plan_params = NIL;
 
532
 
535
533
                        /* Now we can check if it'll fit in work_mem */
536
534
                        if (subplan_is_hashable(plan))
537
535
                        {
542
540
                                hashplan = (SubPlan *) build_subplan(root, plan,
543
541
                                                                                                         subroot->parse->rtable,
544
542
                                                                                                         subroot->rowMarks,
 
543
                                                                                                         plan_params,
545
544
                                                                                                         ANY_SUBLINK, newtestexpr,
546
545
                                                                                                         false, true);
547
546
                                /* Check we got what we expected */
570
569
 */
571
570
static Node *
572
571
build_subplan(PlannerInfo *root, Plan *plan, List *rtable, List *rowmarks,
 
572
                          List *plan_params,
573
573
                          SubLinkType subLinkType, Node *testexpr,
574
574
                          bool adjust_testexpr, bool unknownEqFalse)
575
575
{
576
576
        Node       *result;
577
577
        SubPlan    *splan;
578
578
        bool            isInitPlan;
579
 
        Bitmapset  *tmpset;
580
 
        int                     paramid;
 
579
        ListCell   *lc;
581
580
 
582
581
        /*
583
582
         * Initialize the SubPlan node.  Note plan_id, plan_name, and cost fields
599
598
         * Make parParam and args lists of param IDs and expressions that current
600
599
         * query level will pass to this child plan.
601
600
         */
602
 
        tmpset = bms_copy(plan->extParam);
603
 
        while ((paramid = bms_first_member(tmpset)) >= 0)
 
601
        foreach(lc, plan_params)
604
602
        {
605
 
                PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
606
 
 
607
 
                if (pitem->abslevel == root->query_level)
608
 
                {
609
 
                        Node       *arg;
610
 
 
611
 
                        /*
612
 
                         * The Var, PlaceHolderVar, or Aggref has already been adjusted to
613
 
                         * have the correct varlevelsup, phlevelsup, or agglevelsup.  We
614
 
                         * probably don't even need to copy it again, but be safe.
615
 
                         */
616
 
                        arg = copyObject(pitem->item);
617
 
 
618
 
                        /*
619
 
                         * If it's a PlaceHolderVar or Aggref, its arguments might contain
620
 
                         * SubLinks, which have not yet been processed (see the comments
621
 
                         * for SS_replace_correlation_vars).  Do that now.
622
 
                         */
623
 
                        if (IsA(arg, PlaceHolderVar) ||
624
 
                                IsA(arg, Aggref))
625
 
                                arg = SS_process_sublinks(root, arg, false);
626
 
 
627
 
                        splan->parParam = lappend_int(splan->parParam, paramid);
628
 
                        splan->args = lappend(splan->args, arg);
629
 
                }
 
603
                PlannerParamItem *pitem = (PlannerParamItem *) lfirst(lc);
 
604
                Node       *arg = pitem->item;
 
605
 
 
606
                /*
 
607
                 * The Var, PlaceHolderVar, or Aggref has already been adjusted to
 
608
                 * have the correct varlevelsup, phlevelsup, or agglevelsup.
 
609
                 *
 
610
                 * If it's a PlaceHolderVar or Aggref, its arguments might contain
 
611
                 * SubLinks, which have not yet been processed (see the comments for
 
612
                 * SS_replace_correlation_vars).  Do that now.
 
613
                 */
 
614
                if (IsA(arg, PlaceHolderVar) ||
 
615
                        IsA(arg, Aggref))
 
616
                        arg = SS_process_sublinks(root, arg, false);
 
617
 
 
618
                splan->parParam = lappend_int(splan->parParam, pitem->paramId);
 
619
                splan->args = lappend(splan->args, arg);
630
620
        }
631
 
        bms_free(tmpset);
632
621
 
633
622
        /*
634
623
         * Un-correlated or undirect correlated plans of EXISTS, EXPR, ARRAY, or
1050
1039
                Plan       *plan;
1051
1040
                PlannerInfo *subroot;
1052
1041
                SubPlan    *splan;
1053
 
                Bitmapset  *tmpset;
1054
1042
                int                     paramid;
1055
 
                Param      *prm;
1056
1043
 
1057
1044
                /*
1058
1045
                 * Ignore SELECT CTEs that are not actually referenced anywhere.
1070
1057
                 */
1071
1058
                subquery = (Query *) copyObject(cte->ctequery);
1072
1059
 
 
1060
                /* plan_params should not be in use in current query level */
 
1061
                Assert(root->plan_params == NIL);
 
1062
 
1073
1063
                /*
1074
1064
                 * Generate the plan for the CTE query.  Always plan for full
1075
1065
                 * retrieval --- we don't have enough info to predict otherwise.
1080
1070
                                                                &subroot);
1081
1071
 
1082
1072
                /*
 
1073
                 * Since the current query level doesn't yet contain any RTEs, it
 
1074
                 * should not be possible for the CTE to have requested parameters of
 
1075
                 * this level.
 
1076
                 */
 
1077
                if (root->plan_params)
 
1078
                        elog(ERROR, "unexpected outer reference in CTE query");
 
1079
 
 
1080
                /*
1083
1081
                 * Make a SubPlan node for it.  This is just enough unlike
1084
1082
                 * build_subplan that we can't share code.
1085
1083
                 *
1098
1096
                splan->args = NIL;
1099
1097
 
1100
1098
                /*
1101
 
                 * Make parParam and args lists of param IDs and expressions that
1102
 
                 * current query level will pass to this child plan.  Even though this
1103
 
                 * is an initplan, there could be side-references to earlier
1104
 
                 * initplan's outputs, specifically their CTE output parameters.
 
1099
                 * The node can't have any inputs (since it's an initplan), so the
 
1100
                 * parParam and args lists remain empty.  (It could contain references
 
1101
                 * to earlier CTEs' output param IDs, but CTE outputs are not
 
1102
                 * propagated via the args list.)
1105
1103
                 */
1106
 
                tmpset = bms_copy(plan->extParam);
1107
 
                while ((paramid = bms_first_member(tmpset)) >= 0)
1108
 
                {
1109
 
                        PlannerParamItem *pitem = list_nth(root->glob->paramlist, paramid);
1110
 
 
1111
 
                        if (pitem->abslevel == root->query_level)
1112
 
                        {
1113
 
                                prm = (Param *) pitem->item;
1114
 
                                if (!IsA(prm, Param) ||
1115
 
                                        prm->paramtype != INTERNALOID)
1116
 
                                        elog(ERROR, "bogus local parameter passed to WITH query");
1117
 
 
1118
 
                                splan->parParam = lappend_int(splan->parParam, paramid);
1119
 
                                splan->args = lappend(splan->args, copyObject(prm));
1120
 
                        }
1121
 
                }
1122
 
                bms_free(tmpset);
1123
1104
 
1124
1105
                /*
1125
 
                 * Assign a param to represent the query output.  We only really care
1126
 
                 * about reserving a parameter ID number.
 
1106
                 * Assign a param ID to represent the CTE's output.  No ordinary
 
1107
                 * "evaluation" of this param slot ever happens, but we use the param
 
1108
                 * ID for setParam/chgParam signaling just as if the CTE plan were
 
1109
                 * returning a simple scalar output.  (Also, the executor abuses the
 
1110
                 * ParamExecData slot for this param ID for communication among
 
1111
                 * multiple CteScan nodes that might be scanning this CTE.)
1127
1112
                 */
1128
 
                prm = generate_new_param(root, INTERNALOID, -1, InvalidOid);
1129
 
                splan->setParam = list_make1_int(prm->paramid);
 
1113
                paramid = SS_assign_special_param(root);
 
1114
                splan->setParam = list_make1_int(paramid);
1130
1115
 
1131
1116
                /*
1132
1117
                 * Add the subplan and its rtable to the global lists.
1940
1925
                           *initExtParam,
1941
1926
                           *initSetParam;
1942
1927
        Cost            initplan_cost;
1943
 
        int                     paramid;
 
1928
        PlannerInfo *proot;
1944
1929
        ListCell   *l;
1945
1930
 
1946
1931
        /*
1972
1957
        /*
1973
1958
         * Now determine the set of params that are validly referenceable in this
1974
1959
         * query level; to wit, those available from outer query levels plus the
1975
 
         * output parameters of any initPlans.  (We do not include output
 
1960
         * output parameters of any local initPlans.  (We do not include output
1976
1961
         * parameters of regular subplans.      Those should only appear within the
1977
1962
         * testexpr of SubPlan nodes, and are taken care of locally within
1978
1963
         * finalize_primnode.  Likewise, special parameters that are generated by
1979
1964
         * nodes such as ModifyTable are handled within finalize_plan.)
1980
 
         *
1981
 
         * Note: this is a bit overly generous since some parameters of upper
1982
 
         * query levels might belong to query subtrees that don't include this
1983
 
         * query, or might be nestloop params that won't be passed down at all.
1984
 
         * However, valid_params is only a debugging crosscheck, so it doesn't
1985
 
         * seem worth expending lots of cycles to try to be exact.
1986
1965
         */
1987
1966
        valid_params = bms_copy(initSetParam);
1988
 
        paramid = 0;
1989
 
        foreach(l, root->glob->paramlist)
 
1967
        for (proot = root->parent_root; proot != NULL; proot = proot->parent_root)
1990
1968
        {
1991
 
                PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
1992
 
 
1993
 
                if (pitem->abslevel < root->query_level)
1994
 
                {
1995
 
                        /* valid outer-level parameter */
1996
 
                        valid_params = bms_add_member(valid_params, paramid);
1997
 
                }
1998
 
 
1999
 
                paramid++;
 
1969
                /* Include ordinary Var/PHV/Aggref params */
 
1970
                foreach(l, proot->plan_params)
 
1971
                {
 
1972
                        PlannerParamItem *pitem = (PlannerParamItem *) lfirst(l);
 
1973
 
 
1974
                        valid_params = bms_add_member(valid_params, pitem->paramId);
 
1975
                }
 
1976
                /* Include any outputs of outer-level initPlans */
 
1977
                foreach(l, proot->init_plans)
 
1978
                {
 
1979
                        SubPlan    *initsubplan = (SubPlan *) lfirst(l);
 
1980
                        ListCell   *l2;
 
1981
 
 
1982
                        foreach(l2, initsubplan->setParam)
 
1983
                        {
 
1984
                                valid_params = bms_add_member(valid_params, lfirst_int(l2));
 
1985
                        }
 
1986
                }
 
1987
                /* Include worktable ID, if a recursive query is being planned */
 
1988
                if (proot->wt_param_id >= 0)
 
1989
                        valid_params = bms_add_member(valid_params, proot->wt_param_id);
2000
1990
        }
2001
1991
 
2002
1992
        /*