]> granicus.if.org Git - postgresql/blob - src/backend/commands/recipe.c
The patch does 2 things:
[postgresql] / src / backend / commands / recipe.c
1 /*-------------------------------------------------------------------------
2  *
3  * recipe.c--
4  *        routines for handling execution of Tioga recipes
5  *
6  * Copyright (c) 1994, Regents of the University of California
7  *
8  *
9  * IDENTIFICATION
10  *        $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.25 1998/10/21 16:21:21 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include <postgres.h>
15
16 #include <nodes/parsenodes.h>
17 #include <nodes/plannodes.h>
18 #include <nodes/execnodes.h>
19 #include <nodes/makefuncs.h>
20 #include <catalog/pg_type.h>
21 #include <commands/recipe.h>
22 #include <libpq/libpq-be.h>
23 #include <parser/parse_node.h>
24 #include <utils/builtins.h>
25 #include <utils/relcache.h>             /* for RelationNameGetRelation */
26 #include <rewrite/rewriteHandler.h>
27 #include <rewrite/rewriteManip.h>
28 #include <tcop/pquery.h>
29 #include <tcop/dest.h>
30 #include <optimizer/planner.h>
31 #include <executor/executor.h>
32
33 /* from tcop/postgres.c */
34 extern CommandDest whereToSendOutput;
35
36 #ifndef TIOGA
37
38 void
39 beginRecipe(RecipeStmt *stmt)
40 {
41         elog(NOTICE, "You must compile with TIOGA defined in order to use recipes\n");
42 }
43
44 #else
45
46 #include <tioga/tgRecipe.h>
47
48 #define DEBUG_RECIPE 1
49
50 /* structure to keep track of the tee node plans */
51 typedef struct _teePlanInfo
52 {
53         char       *tpi_relName;
54         Query      *tpi_parsetree;
55         Plan       *tpi_plan;
56 }                       TeePlanInfo;
57
58 typedef struct _teeInfo
59 {
60         int                     num;
61         TeePlanInfo *val;
62 }                       TeeInfo;
63
64 QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2);
65 void            OffsetVarAttno(Node *node, int varno, int offset);
66
67 static void appendTeeQuery(TeeInfo * teeInfo,
68                            QueryTreeList *q,
69                            char *teeNodeName);
70
71 static Plan *replaceTeeScans(Plan *plan,
72                                 Query *parsetree,
73                                 TeeInfo * teeInfo);
74 static void replaceSeqScan(Plan *plan,
75                            Plan *parent,
76                            int rt_ind,
77                            Plan *tplan);
78
79 static void tg_rewriteQuery(TgRecipe * r, TgNode * n,
80                                 QueryTreeList *q,
81                                 QueryTreeList *inputQlist);
82 static Node *tg_replaceNumberedParam(Node *expression,
83                                                 int pnum,
84                                                 int rt_ind,
85                                                 char *teeRelName);
86 static Node *tg_rewriteParamsInExpr(Node *expression,
87                                            QueryTreeList *inputQlist);
88 static QueryTreeList *tg_parseSubQuery(TgRecipe * r,
89                                  TgNode * n,
90                                  TeeInfo * teeInfo);
91 static QueryTreeList *tg_parseTeeNode(TgRecipe * r,
92                                 TgNode * n,
93                                 int i,
94                                 QueryTreeList *qList,
95                                 TeeInfo * teeInfo);
96
97
98 /*
99    The Tioga recipe rewrite algorithm:
100
101    To parse a Tioga recipe, we start from an eye node and go backwards through
102    its input nodes.  To rewrite a Tioga node, we do the following:
103
104           1) parse the node we're at in the standard way (calling parser() )
105           2) rewrite its input nodes recursively using Tioga rewrite
106           3) now, with the rewritten input parse trees and the original parse tree
107                  of the node,  we rewrite the the node.
108                  To do the rewrite, we use the target lists, range tables, and
109                  qualifications of the input parse trees
110 */
111
112 /*
113  * beginRecipe:
114  *        this is the main function to recipe execution
115  *       this function is invoked for EXECUTE RECIPE ...  statements
116  *
117  *      takes in a RecipeStmt structure from the parser
118  * and returns a list of cursor names
119  */
120
121 void
122 beginRecipe(RecipeStmt *stmt)
123 {
124         TgRecipe   *r;
125         int                     i;
126         QueryTreeList *qList;
127         char            portalName[1024];
128
129         Plan       *plan;
130         TupleDesc       attinfo;
131         QueryDesc  *queryDesc;
132         Query      *parsetree;
133
134         int                     numTees;
135         TeeInfo    *teeInfo;
136
137         /*
138          * retrieveRecipe() reads the recipe from the database and returns a
139          * TgRecipe* structure we can work with
140          */
141
142         r = retrieveRecipe(stmt->recipeName);
143
144         if (r == NULL)
145                 return;
146
147         /* find the number of tees in the recipe */
148         numTees = r->tees->num;
149
150         if (numTees > 0)
151         {
152                 /* allocate a teePlan structure */
153                 teeInfo = (TeeInfo *) malloc(sizeof(TeeInfo));
154                 teeInfo->num = numTees;
155                 teeInfo->val = (TeePlanInfo *) malloc(numTees * sizeof(TeePlanInfo));
156                 for (i = 0; i < numTees; i++)
157                 {
158                         teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName;
159                         teeInfo->val[i].tpi_parsetree = NULL;
160                         teeInfo->val[i].tpi_plan = NULL;
161                 }
162         }
163         else
164                 teeInfo = NULL;
165
166         /*
167          * for each viewer in the recipe, go backwards from each viewer input
168          * and generate a plan.  Attach the plan to cursors.
169          */
170         for (i = 0; i < r->eyes->num; i++)
171         {
172                 TgNodePtr       e;
173
174                 e = r->eyes->val[i];
175                 if (e->inNodes->num > 1)
176                 {
177                         elog(NOTICE,
178                                  "beginRecipe: Currently eyes cannot have more than one input");
179                 }
180                 if (e->inNodes->num == 0)
181                 {
182                         /* no input to this eye, skip it */
183                         continue;
184                 }
185
186 #ifdef DEBUG_RECIPE
187                 elog(NOTICE, "beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
188 #endif   /* DEBUG_RECIPE */
189
190                 qList = tg_parseSubQuery(r, e->inNodes->val[0], teeInfo);
191
192                 if (qList == NULL)
193                 {
194                         /* eye is directly connected to a tee node */
195                         /* XXX TODO: handle this case */
196                 }
197
198                 /* now, plan the queries */
199
200                 /*
201                  * should really do everything pg_plan() does, but for now, we
202                  * skip the rule rewrite and time qual stuff
203                  */
204
205                 /* ----------------------------------------------------------
206                  * 1) plan the main query, everything from an eye node back to
207                          a Tee
208                  * ---------------------------------------------------------- */
209                 parsetree = qList->qtrees[0];
210
211                 /*
212                  * before we plan, we want to see all the changes we did, during
213                  * the rewrite phase, such as creating the tee tables,
214                  * setheapoverride() allows us to see the changes
215                  */
216                 setheapoverride(true);
217                 plan = planner(parsetree);
218
219                 /* ----------------------------------------------------------
220                  * 2) plan the tee queries, (subgraphs rooted from a Tee)
221                          by the time the eye is processed, all tees that contribute
222                          to that eye will have been included in the teeInfo list
223                  * ---------------------------------------------------------- */
224                 if (teeInfo)
225                 {
226                         int                     t;
227                         Plan       *tplan;
228                         Tee                *newplan;
229
230                         for (t = 0; t < teeInfo->num; t++)
231                         {
232                                 if (teeInfo->val[t].tpi_plan == NULL)
233                                 {
234                                         /* plan it in the usual fashion */
235                                         tplan = planner(teeInfo->val[t].tpi_parsetree);
236
237                                         /* now add a tee node to the root of the plan */
238                                         elog(NOTICE, "adding tee plan node to the root of the %s\n",
239                                                  teeInfo->val[t].tpi_relName);
240                                         newplan = (Tee *) makeNode(Tee);
241                                         newplan->plan.targetlist = tplan->targetlist;
242                                         newplan->plan.qual = NULL;      /* tplan->qual; */
243                                         newplan->plan.lefttree = tplan;
244                                         newplan->plan.righttree = NULL;
245                                         newplan->leftParent = NULL;
246                                         newplan->rightParent = NULL;
247
248                                         /*
249                                          * the range table of the tee is the range table of
250                                          * the tplan
251                                          */
252                                         newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable;
253                                         strcpy(newplan->teeTableName,
254                                                    teeInfo->val[t].tpi_relName);
255                                         teeInfo->val[t].tpi_plan = (Plan *) newplan;
256                                 }
257                         }
258
259                         /* ----------------------------------------------------------
260                          * 3) replace the tee table scans in the main plan with
261                                   actual tee plannodes
262                          * ---------------------------------------------------------- */
263
264                         plan = replaceTeeScans(plan, parsetree, teeInfo);
265
266                 }                                               /* if (teeInfo) */
267
268                 setheapoverride(false);
269
270                 /* define a portal for this viewer input */
271                 /* for now, eyes can only have one input */
272                 sprintf(portalName, "%s%d", e->nodeName, 0);
273
274                 queryDesc = CreateQueryDesc(parsetree,
275                                                                         plan,
276                                                                         whereToSendOutput);
277                 /* ----------------
278                  *                call ExecStart to prepare the plan for execution
279                  * ----------------
280                  */
281                 attinfo = ExecutorStart(queryDesc, NULL);
282
283                 ProcessPortal(portalName,
284                                           parsetree,
285                                           plan,
286                                           attinfo,
287                                           whereToSendOutput);
288                 elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName);
289         }
290
291 }
292
293
294
295 /*
296  * tg_rewriteQuery -
297  *        r - the recipe being rewritten
298  *        n - the node that we're current at
299  *        q - a QueryTree List containing the parse tree of the node
300  *        inputQlist - the parsetrees of its input nodes,
301  *                                 the size of inputQlist must be the same as the
302  *                                 number of input nodes.  Some elements in the inpuQlist
303  *                                 may be null if the inputs to those nodes are unconnected
304  *
305  *      this is the main routine for rewriting the recipe queries
306  *      the original query tree 'q' is modified
307  */
308
309 static void
310 tg_rewriteQuery(TgRecipe * r,
311                                 TgNode * n,
312                                 QueryTreeList *q,
313                                 QueryTreeList *inputQlist)
314 {
315         Query      *orig;
316         Query      *inputQ;
317         int                     i;
318         List       *rtable;
319         List       *input_rtable;
320         int                     rt_length;
321
322         /* orig is the original parse tree of the node */
323         orig = q->qtrees[0];
324
325
326         /*-------------------------------------------------------------------
327            step 1:
328
329            form a combined range table from all the range tables in the original
330            query as well as the input nodes
331
332            form a combined qualification from the qual in the original plus
333            the quals of the input nodes
334           -------------------------------------------------------------------
335         */
336
337         /* start with the original range table */
338         rtable = orig->rtable;
339         rt_length = length(rtable);
340
341         for (i = 0; i < n->inNodes->num; i++)
342         {
343                 if (n->inNodes->val[i] != NULL &&
344                         n->inNodes->val[i]->nodeType != TG_TEE_NODE)
345                 {
346                         inputQ = inputQlist->qtrees[i];
347                         input_rtable = inputQ->rtable;
348
349                         /*
350                          * need to offset the var nodes in the qual and targetlist
351                          * because they are indexed off the original rtable
352                          */
353                         OffsetVarNodes((Node *) inputQ->qual, rt_length, 0);
354                         OffsetVarNodes((Node *) inputQ->targetList, rt_length, 0);
355
356                         /* append the range tables from the children nodes      */
357                         rtable = nconc(rtable, input_rtable);
358
359                         /*
360                          * append the qualifications of the child node into the
361                          * original qual list
362                          */
363                         AddQual(orig, inputQ->qual);
364                 }
365         }
366         orig->rtable = rtable;
367
368         /*
369          * step 2: rewrite the target list of the original parse tree if there
370          * are any references to params, replace them with the appropriate
371          * target list entry of the children node
372          */
373         if (orig->targetList != NIL)
374         {
375                 List       *tl;
376                 TargetEntry *tle;
377
378                 foreach(tl, orig->targetList)
379                 {
380                         tle = lfirst(tl);
381                         if (tle->resdom != NULL)
382                                 tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
383                 }
384         }
385
386         /*
387          * step 3: rewrite the qual of the original parse tree if there are
388          * any references to params, replace them with the appropriate target
389          * list entry of the children node
390          */
391         if (orig->qual)
392         {
393                 if (nodeTag(orig->qual) == T_List)
394                         elog(ERROR, "tg_rewriteQuery: Whoa! why is my qual a List???");
395                 orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist);
396         }
397
398         /*
399          * at this point, we're done with the rewrite, the querytreelist q has
400          * been modified
401          */
402
403 }
404
405
406 /* tg_replaceNumberedParam:
407
408    this procedure replaces the specified numbered param with a
409    reference to a range table
410
411    this procedure recursively calls itself
412
413    it returns a (possibly modified) Node*.
414
415 */
416 static Node *
417 tg_replaceNumberedParam(Node *expression,
418                                                 int pnum,               /* the number of the parameter */
419                                                 int rt_ind,             /* the range table index */
420                                                 char *teeRelName)               /* the relname of the tee
421                                                                                                  * table */
422 {
423         TargetEntry *param_tle;
424         Param      *p;
425         Var                *newVar,
426                            *oldVar;
427
428         if (expression == NULL)
429                 return NULL;
430
431         switch (nodeTag(expression))
432         {
433                 case T_Param:
434                         {
435
436                                 /*
437                                  * the node is a parameter, substitute the entry from the
438                                  * target list of the child that corresponds to the
439                                  * parameter number
440                                  */
441                                 p = (Param *) expression;
442
443                                 /* we only deal with the case of numbered parameters */
444                                 if (p->paramkind == PARAM_NUM && p->paramid == pnum)
445                                 {
446
447                                         if (p->param_tlist)
448                                         {
449
450                                                 /*
451                                                  * we have a parameter with an attribute like
452                                                  * $N.foo so replace it with a new var node
453                                                  */
454
455                                                 /* param tlist can only have one entry in them! */
456                                                 param_tle = (TargetEntry *) (lfirst(p->param_tlist));
457                                                 oldVar = (Var *) param_tle->expr;
458                                                 oldVar->varno = rt_ind;
459                                                 oldVar->varnoold = rt_ind;
460                                                 return (Node *) oldVar;
461                                         }
462                                         else
463                                         {
464                                                 /* we have $N without the .foo */
465                                                 bool            defined;
466                                                 bool            isRel;
467
468                                                 /*
469                                                  * TODO here, we need to check to see whether the
470                                                  * type of the tee is a complex type (relation) or
471                                                  * a simple type
472                                                  */
473
474                                                 /*
475                                                  * if it is a simple type, then we need to get the
476                                                  * "result" attribute from the tee relation
477                                                  */
478
479                                                 isRel = (typeidTypeRelid(p->paramtype) != 0);
480                                                 if (isRel)
481                                                 {
482                                                         newVar = makeVar(rt_ind,
483                                                                                          0, /* the whole tuple */
484                                                                                    TypeGet(teeRelName, &defined),
485                                                                                          -1,
486                                                                                          0,
487                                                                                          rt_ind,
488                                                                                          0);
489                                                         return (Node *) newVar;
490                                                 }
491                                                 else
492                                                         newVar = makeVar(rt_ind,
493                                                                                          1, /* just the first field,
494                                                                                                  * which is 'result' */
495                                                                                    TypeGet(teeRelName, &defined),
496                                                                                          -1,
497                                                                                          0,
498                                                                                          rt_ind,
499                                                                                          0);
500                                                 return (Node *) newVar;
501
502                                         }
503                                 }
504                                 else
505                                         elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
506                         }
507                         break;
508                 case T_Expr:
509                         {
510
511                                 /*
512                                  * the node is an expression, we need to recursively call
513                                  * ourselves until we find parameter nodes
514                                  */
515                                 List       *l;
516                                 Expr       *expr = (Expr *) expression;
517                                 List       *newArgs;
518
519                                 /*
520                                  * we have to make a new args lists because Params can be
521                                  * replaced by Var nodes in tg_replaceNumberedParam()
522                                  */
523                                 newArgs = NIL;
524
525                                 /*
526                                  * we only care about argument to expressions, it doesn't
527                                  * matter when the opType is
528                                  */
529                                 /* recursively rewrite the arguments of this expression */
530                                 foreach(l, expr->args)
531                                 {
532                                         newArgs = lappend(newArgs,
533                                                                           tg_replaceNumberedParam(lfirst(l),
534                                                                                                                           pnum,
535                                                                                                                           rt_ind,
536                                                                                                                         teeRelName));
537                                 }
538                                 /* change the arguments of the expression */
539                                 expr->args = newArgs;
540                         }
541                         break;
542                 default:
543                         {
544                                 /* ignore other expr types */
545                         }
546         }
547
548         return expression;
549 }
550
551
552
553
554
555 /* tg_rewriteParamsInExpr:
556
557    rewrite the params in expressions by using the targetlist entries
558    from the input parsetrees
559
560    this procedure recursively calls itself
561
562    it returns a (possibly modified) Node*.
563
564 */
565 static Node *
566 tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist)
567 {
568         List       *tl;
569         TargetEntry *param_tle,
570                            *tle;
571         Param      *p;
572         int                     childno;
573         char       *resname;
574
575         if (expression == NULL)
576                 return NULL;
577
578         switch (nodeTag(expression))
579         {
580                 case T_Param:
581                         {
582
583                                 /*
584                                  * the node is a parameter, substitute the entry from the
585                                  * target list of the child that corresponds to the
586                                  * parameter number
587                                  */
588                                 p = (Param *) expression;
589
590                                 /* we only deal with the case of numbered parameters */
591                                 if (p->paramkind == PARAM_NUM)
592                                 {
593                                         /* paramid's start from 1 */
594                                         childno = p->paramid - 1;
595
596                                         if (p->param_tlist)
597                                         {
598
599                                                 /*
600                                                  * we have a parameter with an attribute like
601                                                  * $N.foo so match the resname "foo" against the
602                                                  * target list of the (N-1)th inputQlist
603                                                  */
604
605                                                 /* param tlist can only have one entry in them! */
606                                                 param_tle = (TargetEntry *) (lfirst(p->param_tlist));
607                                                 resname = param_tle->resdom->resname;
608
609                                                 if (inputQlist->qtrees[childno])
610                                                 {
611                                                         foreach(tl, inputQlist->qtrees[childno]->targetList)
612                                                         {
613                                                                 tle = lfirst(tl);
614                                                                 if (strcmp(resname, tle->resdom->resname) == 0)
615                                                                         return tle->expr;
616                                                         }
617                                                 }
618                                                 else
619                                                         elog(ERROR, "tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
620
621                                         }
622                                         else
623                                         {
624                                                 /* we have $N without the .foo */
625                                                 /* use the first resdom in the targetlist of the */
626                                                 /* appropriate child query */
627                                                 tl = inputQlist->qtrees[childno]->targetList;
628                                                 tle = lfirst(tl);
629                                                 return tle->expr;
630                                         }
631                                 }
632                                 else
633                                         elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
634                         }
635                         break;
636                 case T_Expr:
637                         {
638
639                                 /*
640                                  * the node is an expression, we need to recursively call
641                                  * ourselves until we find parameter nodes
642                                  */
643                                 List       *l;
644                                 Expr       *expr = (Expr *) expression;
645                                 List       *newArgs;
646
647                                 /*
648                                  * we have to make a new args lists because Params can be
649                                  * replaced by Var nodes in tg_rewriteParamsInExpr()
650                                  */
651                                 newArgs = NIL;
652
653                                 /*
654                                  * we only care about argument to expressions, it doesn't
655                                  * matter when the opType is
656                                  */
657                                 /* recursively rewrite the arguments of this expression */
658                                 foreach(l, expr->args)
659                                 {
660                                         newArgs = lappend(newArgs,
661                                                   tg_rewriteParamsInExpr(lfirst(l), inputQlist));
662                                 }
663                                 /* change the arguments of the expression */
664                                 expr->args = newArgs;
665                         }
666                         break;
667                 default:
668                         {
669                                 /* ignore other expr types */
670                         }
671         }
672
673         return expression;
674 }
675
676
677
678 /*
679    getParamTypes:
680           given an element, finds its parameter types.
681           the typev array argument is set to the parameter types.
682           the parameterCount is returned
683
684    this code is very similar to ProcedureDefine() in pg_proc.c
685 */
686 static int
687 getParamTypes(TgElement * elem, Oid *typev)
688 {
689         /* this code is similar to ProcedureDefine() */
690         int16           parameterCount;
691         bool            defined;
692         Oid                     toid;
693         char       *t;
694         int                     i,
695                                 j;
696
697         parameterCount = 0;
698         for (i = 0; i < 8; i++)
699                 typev[i] = 0;
700         for (j = 0; j < elem->inTypes->num; j++)
701         {
702                 if (parameterCount == 8)
703                 {
704                         elog(ERROR,
705                                  "getParamTypes: Ingredients cannot take > 8 arguments");
706                 }
707                 t = elem->inTypes->val[j];
708                 if (strcmp(t, "opaque") == 0)
709                 {
710                         elog(ERROR,
711                                  "getParamTypes: Ingredient functions cannot take type 'opaque'");
712                 }
713                 else
714                 {
715                         toid = TypeGet(elem->inTypes->val[j], &defined);
716                         if (!OidIsValid(toid))
717                                 elog(ERROR, "getParamTypes: arg type '%s' is not defined", t);
718                         if (!defined)
719                                 elog(NOTICE, "getParamTypes: arg type '%s' is only a shell", t);
720                 }
721                 typev[parameterCount++] = toid;
722         }
723
724         return parameterCount;
725 }
726
727
728 /*
729  * tg_parseTeeNode
730  *
731  *       handles the parsing of the tee node
732  *
733  *
734  */
735
736 static QueryTreeList *
737 tg_parseTeeNode(TgRecipe * r,
738                                 TgNode * n,             /* the tee node */
739                                 int i,                  /* which input this node is to its parent */
740                                 QueryTreeList *qList,
741                                 TeeInfo * teeInfo)
742
743 {
744         QueryTreeList *q;
745         char       *tt;
746         int                     rt_ind;
747         Query      *orig;
748
749         /*
750          * the input Node is a tee node, so we need to do the following: we
751          * need to parse the child of the tee node, we add that to our query
752          * tree list we need the name of the tee node table the tee node table
753          * is the table into which the tee node may materialize results.  Call
754          * it TT we add a range table to our existing query with TT in it we
755          * need to replace the parameter $i with TT (otherwise the optimizer
756          * won't know to use the table on expression containining $i) After
757          * that rewrite, the optimizer will generate sequential scans of TT
758          *
759          * Later, in the glue phase, we replace all instances of TT sequential
760          * scans with the actual Tee node
761          */
762         q = tg_parseSubQuery(r, n, teeInfo);
763
764         /* tt is the name of the tee node table */
765         tt = n->nodeName;
766
767         if (q)
768                 appendTeeQuery(teeInfo, q, tt);
769
770         orig = qList->qtrees[0];
771         rt_ind = RangeTablePosn(orig->rtable, tt);
772
773         /*
774          * check to see that this table is not part of the range table
775          * already.  This usually only happens if multiple inputs are
776          * connected to the same Tee.
777          */
778         if (rt_ind == 0)
779         {
780                 orig->rtable = lappend(orig->rtable,
781                                                            addRangeTableEntry(NULL,
782                                                                                                   tt,
783                                                                                                   tt,
784                                                                                                   FALSE,
785                                                                                                   FALSE));
786                 rt_ind = length(orig->rtable);
787         }
788
789         orig->qual = tg_replaceNumberedParam(orig->qual,
790                                                                                  i + 1, /* params start at 1 */
791                                                                                  rt_ind,
792                                                                                  tt);
793         return qList;
794 }
795
796
797 /*
798  * tg_parseSubQuery:
799  *        go backwards from a node and parse the query
800  *
801  *       the result parse tree is passed back
802  *
803  * could return NULL if trying to parse a teeNode
804  * that's already been processed by another parent
805  *
806  */
807
808 static QueryTreeList *
809 tg_parseSubQuery(TgRecipe * r, TgNode * n, TeeInfo * teeInfo)
810 {
811         TgElement  *elem;
812         char       *funcName;
813         Oid                     typev[8];               /* eight arguments maximum      */
814         int                     i;
815         int                     parameterCount;
816
817         QueryTreeList *qList;           /* the parse tree of the nodeElement */
818         QueryTreeList *inputQlist;      /* the list of parse trees for the inputs
819                                                                  * to this node */
820         QueryTreeList *q;
821         Oid                     relid;
822         TgNode     *child;
823         Relation        rel;
824         unsigned int len;
825         TupleDesc       tupdesc;
826
827         qList = NULL;
828
829         if (n->nodeType == TG_INGRED_NODE)
830         {
831                 /* parse each ingredient node in turn */
832
833                 elem = n->nodeElem;
834                 switch (elem->srcLang)
835                 {
836                         case TG_SQL:
837                                 {
838
839                                         /*
840                                          * for SQL ingredients, the SQL query is contained in
841                                          * the 'src' field
842                                          */
843
844 #ifdef DEBUG_RECIPE
845                                         elog(NOTICE, "calling parser with %s", elem->src);
846 #endif   /* DEBUG_RECIPE */
847
848                                         parameterCount = getParamTypes(elem, typev);
849
850                                         qList = parser(elem->src, typev, parameterCount);
851
852                                         if (qList->len > 1)
853                                         {
854                                                 elog(NOTICE,
855                                                          "tg_parseSubQuery: parser produced > 1 query tree");
856                                         }
857                                 }
858                                 break;
859                         case TG_C:
860                                 {
861                                         /* C ingredients are registered functions in postgres */
862
863                                         /*
864                                          * we create a new query string by using the function
865                                          * name (found in the 'src' field) and adding
866                                          * parameters to it so if the function was FOOBAR and
867                                          * took in two arguments, we would create a string
868                                          * select FOOBAR($1,$2)
869                                          */
870                                         char            newquery[1000];
871
872                                         funcName = elem->src;
873                                         parameterCount = getParamTypes(elem, typev);
874
875                                         if (parameterCount > 0)
876                                         {
877                                                 int                     i;
878
879                                                 sprintf(newquery, "select %s($1", funcName);
880                                                 for (i = 1; i < parameterCount; i++)
881                                                         sprintf(newquery, "%s,$%d", newquery, i);
882                                                 sprintf(newquery, "%s)", newquery);
883                                         }
884                                         else
885                                                 sprintf(newquery, "select %s()", funcName);
886
887 #ifdef DEBUG_RECIPE
888                                         elog(NOTICE, "calling parser with %s", newquery);
889 #endif   /* DEBUG_RECIPE */
890
891                                         qList = parser(newquery, typev, parameterCount);
892                                         if (qList->len > 1)
893                                         {
894                                                 elog(NOTICE,
895                                                          "tg_parseSubQuery: parser produced > 1 query tree");
896                                         }
897                                 }
898                                 break;
899                         case TG_RECIPE_GRAPH:
900                                 elog(NOTICE, "tg_parseSubQuery: can't parse recipe graph ingredients yet!");
901                                 break;
902                         case TG_COMPILED:
903                                 elog(NOTICE, "tg_parseSubQuery: can't parse compiled ingredients yet!");
904                                 break;
905                         default:
906                                 elog(NOTICE, "tg_parseSubQuery: unknown srcLang: %d", elem->srcLang);
907                 }
908
909                 /* parse each of the subrecipes that are input to this node */
910
911                 if (n->inNodes->num > 0)
912                 {
913                         inputQlist = malloc(sizeof(QueryTreeList));
914                         inputQlist->len = n->inNodes->num + 1;
915                         inputQlist->qtrees = (Query **) malloc(inputQlist->len * sizeof(Query *));
916                         for (i = 0; i < n->inNodes->num; i++)
917                         {
918
919                                 inputQlist->qtrees[i] = NULL;
920                                 if (n->inNodes->val[i])
921                                 {
922                                         if (n->inNodes->val[i]->nodeType == TG_TEE_NODE)
923                                         {
924                                                 qList = tg_parseTeeNode(r, n->inNodes->val[i],
925                                                                                                 i, qList, teeInfo);
926                                         }
927                                         else
928                                         {                       /* input node is not a Tee */
929                                                 q = tg_parseSubQuery(r, n->inNodes->val[i],
930                                                                                          teeInfo);
931                                                 Assert(q->len == 1);
932                                                 inputQlist->qtrees[i] = q->qtrees[0];
933                                         }
934                                 }
935                         }
936
937                         /* now, we have all the query trees from our input nodes */
938                         /* transform the original parse tree appropriately */
939                         tg_rewriteQuery(r, n, qList, inputQlist);
940                 }
941         }
942         else if (n->nodeType == TG_EYE_NODE)
943         {
944
945                 /*
946                  * if we hit an eye, we need to stop and make what we have into a
947                  * subrecipe query block
948                  */
949                 elog(NOTICE, "tg_parseSubQuery: can't handle eye nodes yet");
950         }
951         else if (n->nodeType == TG_TEE_NODE)
952         {
953
954                 /*
955                  * if we hit a tee, check to see if the parsing has been done for
956                  * this tee already by the other parent
957                  */
958
959                 rel = RelationNameGetRelation(n->nodeName);
960                 if (RelationIsValid(rel))
961                 {
962
963                         /*
964                          * this tee has already been visited, no need to do any
965                          * further processing
966                          */
967                         return NULL;
968                 }
969                 else
970                 {
971                         /* we need to process the child of the tee first, */
972                         child = n->inNodes->val[0];
973
974                         if (child->nodeType == TG_TEE_NODE)
975                         {
976                                 /* nested Tee nodes */
977                                 qList = tg_parseTeeNode(r, child, 0, qList, teeInfo);
978                                 return qList;
979                         }
980
981                         Assert(child != NULL);
982
983                         /* parse the input node */
984                         q = tg_parseSubQuery(r, child, teeInfo);
985                         Assert(q->len == 1);
986
987                         /* add the parsed query to the main list of queries */
988                         qList = appendQlist(qList, q);
989
990                         /* need to create the tee table here */
991
992                         /*
993                          * the tee table created is used both for materializing the
994                          * values at the tee node, and for parsing and optimization.
995                          * The optimization needs to have a real table before it will
996                          * consider scans on it
997                          */
998
999                         /*
1000                          * first, find the type of the tuples being produced by the
1001                          * tee.  The type is the same as the output type of the child
1002                          * node.
1003                          *
1004                          * NOTE: we are assuming that the child node only has a single
1005                          * output here!
1006                          */
1007                         getParamTypes(child->nodeElem, typev);
1008
1009                         /*
1010                          * the output type is either a complex type, (and is thus a
1011                          * relation) or is a simple type
1012                          */
1013
1014                         rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);
1015
1016                         if (RelationIsValid(rel))
1017                         {
1018
1019                                 /*
1020                                  * for complex types, create new relation with the same
1021                                  * tuple descriptor as the output table type
1022                                  */
1023                                 len = length(q->qtrees[0]->targetList);
1024                                 tupdesc = rel->rd_att;
1025
1026                                 relid = heap_create_with_catalog(
1027                                                                            child->nodeElem->outTypes->val[0],
1028                                                                                           tupdesc, RELKIND_RELATION);
1029                         }
1030                         else
1031                         {
1032
1033                                 /*
1034                                  * we have to create a relation with one attribute of the
1035                                  * simple base type.  That attribute will have an attr
1036                                  * name of "result"
1037                                  */
1038                                 /* NOTE: ignore array types for the time being */
1039
1040                                 len = 1;
1041                                 tupdesc = CreateTemplateTupleDesc(len);
1042
1043                                 if (!TupleDescInitEntry(tupdesc, 1,
1044                                                                                 "result",
1045                                                                                 InvalidOid,
1046                                                                                 -1, 0, false))
1047                                         elog(NOTICE, "tg_parseSubQuery: unexpected result from TupleDescInitEntry");
1048                                 else
1049                                 {
1050                                         relid = heap_create_with_catalog(
1051                                                                            child->nodeElem->outTypes->val[0],
1052                                                                                           tupdesc, RELKIND_RELATION);
1053                                 }
1054                         }
1055                 }
1056         }
1057         else if (n->nodeType == TG_RECIPE_NODE)
1058                 elog(NOTICE, "tg_parseSubQuery: can't handle embedded recipes yet!");
1059         else
1060                 elog(NOTICE, "unknown nodeType: %d", n->nodeType);
1061
1062         return qList;
1063 }
1064
1065 /*
1066  * OffsetVarAttno -
1067  *        recursively find all the var nodes with the specified varno
1068  * and offset their varattno with the offset
1069  *
1070  *      code is similar to OffsetVarNodes in rewriteManip.c
1071  */
1072
1073 void
1074 OffsetVarAttno(Node *node, int varno, int offset)
1075 {
1076         if (node == NULL)
1077                 return;
1078         switch (nodeTag(node))
1079         {
1080                 case T_TargetEntry:
1081                         {
1082                                 TargetEntry *tle = (TargetEntry *) node;
1083
1084                                 OffsetVarAttno(tle->expr, varno, offset);
1085                         }
1086                         break;
1087                 case T_Expr:
1088                         {
1089                                 Expr       *expr = (Expr *) node;
1090
1091                                 OffsetVarAttno((Node *) expr->args, varno, offset);
1092                         }
1093                         break;
1094                 case T_Var:
1095                         {
1096                                 Var                *var = (Var *) node;
1097
1098                                 if (var->varno == varno)
1099                                         var->varattno += offset;
1100                         }
1101                         break;
1102                 case T_List:
1103                         {
1104                                 List       *l;
1105
1106                                 foreach(l, (List *) node)
1107                                         OffsetVarAttno(lfirst(l), varno, offset);
1108                         }
1109                         break;
1110                 default:
1111                         /* ignore the others */
1112                         break;
1113         }
1114 }
1115
1116 /*
1117  * appendQlist
1118  *        add the contents of a QueryTreeList q2 to the end of the QueryTreeList
1119  *       q1
1120  *
1121  *      returns a new querytree list
1122  */
1123
1124 QueryTreeList *
1125 appendQlist(QueryTreeList *q1, QueryTreeList *q2)
1126 {
1127         QueryTreeList *newq;
1128         int                     i,
1129                                 j;
1130         int                     newlen;
1131
1132         if (q1 == NULL)
1133                 return q2;
1134
1135         if (q2 == NULL)
1136                 return q1;
1137
1138         newlen = q1->len + q2->len;
1139         newq = (QueryTreeList *) malloc(sizeof(QueryTreeList));
1140         newq->len = newlen;
1141         newq->qtrees = (Query **) malloc(newlen * sizeof(Query *));
1142         for (i = 0; i < q1->len; i++)
1143                 newq->qtrees[i] = q1->qtrees[i];
1144         for (j = 0; j < q2->len; j++)
1145                 newq->qtrees[i + j] = q2->qtrees[j];
1146         return newq;
1147 }
1148
1149 /*
1150  * appendTeeQuery
1151  *
1152  *      modify the query field of the teeInfo list of the particular tee node
1153  */
1154 static void
1155 appendTeeQuery(TeeInfo * teeInfo, QueryTreeList *q, char *teeNodeName)
1156 {
1157         int                     i;
1158
1159         Assert(teeInfo);
1160
1161         for (i = 0; i < teeInfo->num; i++)
1162         {
1163                 if (strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0)
1164                 {
1165
1166                         Assert(q->len == 1);
1167                         teeInfo->val[i].tpi_parsetree = q->qtrees[0];
1168                         return;
1169                 }
1170         }
1171         elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
1172 }
1173
1174
1175
1176 /*
1177  * replaceSeqScan
1178  *        replaces sequential scans of a specified relation with the tee plan
1179  *      the relation is specified by its index in the range table,       rt_ind
1180  *
1181  * returns the modified plan
1182  * the offset_attno is the offset that needs to be added to the parent's
1183  * qual or targetlist because the child plan has been replaced with a tee node
1184  */
1185 static void
1186 replaceSeqScan(Plan *plan, Plan *parent,
1187                            int rt_ind, Plan *tplan)
1188 {
1189         Scan       *snode;
1190         Tee                *teePlan;
1191         Result     *newPlan;
1192
1193         if (plan == NULL)
1194                 return;
1195
1196         if (plan->type == T_SeqScan)
1197         {
1198                 snode = (Scan *) plan;
1199                 if (snode->scanrelid == rt_ind)
1200                 {
1201
1202                         /*
1203                          * found the sequential scan that should be replaced with the
1204                          * tplan.
1205                          */
1206                         /* we replace the plan, but we also need to modify its parent */
1207
1208                         /*
1209                          * replace the sequential scan with a Result node the reason
1210                          * we use a result node is so that we get the proper
1211                          * projection behavior.  The Result node is simply (ab)used as
1212                          * a projection node
1213                          */
1214
1215                         newPlan = makeNode(Result);
1216                         newPlan->plan.cost = 0.0;
1217                         newPlan->plan.state = (EState *) NULL;
1218                         newPlan->plan.targetlist = plan->targetlist;
1219                         newPlan->plan.lefttree = tplan;
1220                         newPlan->plan.righttree = NULL;
1221                         newPlan->resconstantqual = NULL;
1222                         newPlan->resstate = NULL;
1223
1224                         /* change all the varno's to 1 */
1225                         ChangeVarNodes((Node *) newPlan->plan.targetlist,
1226                                                    snode->scanrelid, 1);
1227
1228                         if (parent)
1229                         {
1230                                 teePlan = (Tee *) tplan;
1231
1232                                 if (parent->lefttree == plan)
1233                                         parent->lefttree = (Plan *) newPlan;
1234                                 else
1235                                         parent->righttree = (Plan *) newPlan;
1236
1237
1238                                 if (teePlan->leftParent == NULL)
1239                                         teePlan->leftParent = (Plan *) newPlan;
1240                                 else
1241                                         teePlan->rightParent = (Plan *) newPlan;
1242
1243 /* comment for now to test out executor-stuff
1244                                 if (parent->state) {
1245                                         ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
1246                                 }
1247 */
1248                         }
1249                 }
1250
1251         }
1252         else
1253         {
1254                 if (plan->lefttree)
1255                         replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
1256                 if (plan->righttree)
1257                         replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
1258         }
1259 }
1260
1261 /*
1262  * replaceTeeScans
1263  *        places the sequential scans of the Tee table with
1264  * a connection to the actual tee plan node
1265  */
1266 static Plan *
1267 replaceTeeScans(Plan *plan, Query *parsetree, TeeInfo * teeInfo)
1268 {
1269
1270         int                     i;
1271         List       *rtable;
1272         RangeTblEntry *rte;
1273         char            prefix[5];
1274         int                     rt_ind;
1275         Plan       *tplan;
1276
1277         rtable = parsetree->rtable;
1278         if (rtable == NULL)
1279                 return plan;
1280
1281         /*
1282          * look through the range table for the tee relation entry, that will
1283          * give use the varno we need to detect which sequential scans need to
1284          * be replaced with tee nodes
1285          */
1286
1287         rt_ind = 0;
1288         while (rtable != NIL)
1289         {
1290                 rte = lfirst(rtable);
1291                 rtable = lnext(rtable);
1292                 rt_ind++;                               /* range table references in varno fields
1293                                                                  * start w/ 1 */
1294
1295                 /*
1296                  * look for the "tee_" prefix in the refname, also check to see
1297                  * that the relname and the refname are the same this should
1298                  * eliminate any user-specified table and leave us with the tee
1299                  * table entries only
1300                  */
1301                 if ((strlen(rte->refname) < 4) ||
1302                         (strcmp(rte->relname, rte->refname) != 0))
1303                         continue;
1304                 StrNCpy(prefix, rte->refname, 5);
1305                 if (strcmp(prefix, "tee_") == 0)
1306                 {
1307                         /* okay, we found a tee node entry in the range table */
1308
1309                         /* find the appropriate plan in the teeInfo list */
1310                         tplan = NULL;
1311                         for (i = 0; i < teeInfo->num; i++)
1312                         {
1313                                 if (strcmp(teeInfo->val[i].tpi_relName,
1314                                                    rte->refname) == 0)
1315                                         tplan = teeInfo->val[i].tpi_plan;
1316                         }
1317                         if (tplan == NULL)
1318                                 elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan");
1319
1320                         /*
1321                          * replace the sequential scan node with that var number with
1322                          * the tee plan node
1323                          */
1324                         replaceSeqScan(plan, NULL, rt_ind, tplan);
1325                 }
1326         }
1327
1328         return plan;
1329 }
1330
1331
1332
1333 #endif   /* TIOGA */