1 /*-------------------------------------------------------------------------
4 * routines for handling execution of Tioga recipes
6 * Copyright (c) 1994, Regents of the University of California
10 * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.25 1998/10/21 16:21:21 momjian Exp $
12 *-------------------------------------------------------------------------
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>
33 /* from tcop/postgres.c */
34 extern CommandDest whereToSendOutput;
39 beginRecipe(RecipeStmt *stmt)
41 elog(NOTICE, "You must compile with TIOGA defined in order to use recipes\n");
46 #include <tioga/tgRecipe.h>
48 #define DEBUG_RECIPE 1
50 /* structure to keep track of the tee node plans */
51 typedef struct _teePlanInfo
58 typedef struct _teeInfo
64 QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2);
65 void OffsetVarAttno(Node *node, int varno, int offset);
67 static void appendTeeQuery(TeeInfo * teeInfo,
71 static Plan *replaceTeeScans(Plan *plan,
74 static void replaceSeqScan(Plan *plan,
79 static void tg_rewriteQuery(TgRecipe * r, TgNode * n,
81 QueryTreeList *inputQlist);
82 static Node *tg_replaceNumberedParam(Node *expression,
86 static Node *tg_rewriteParamsInExpr(Node *expression,
87 QueryTreeList *inputQlist);
88 static QueryTreeList *tg_parseSubQuery(TgRecipe * r,
91 static QueryTreeList *tg_parseTeeNode(TgRecipe * r,
99 The Tioga recipe rewrite algorithm:
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:
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
114 * this is the main function to recipe execution
115 * this function is invoked for EXECUTE RECIPE ... statements
117 * takes in a RecipeStmt structure from the parser
118 * and returns a list of cursor names
122 beginRecipe(RecipeStmt *stmt)
126 QueryTreeList *qList;
127 char portalName[1024];
131 QueryDesc *queryDesc;
138 * retrieveRecipe() reads the recipe from the database and returns a
139 * TgRecipe* structure we can work with
142 r = retrieveRecipe(stmt->recipeName);
147 /* find the number of tees in the recipe */
148 numTees = r->tees->num;
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++)
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;
167 * for each viewer in the recipe, go backwards from each viewer input
168 * and generate a plan. Attach the plan to cursors.
170 for (i = 0; i < r->eyes->num; i++)
175 if (e->inNodes->num > 1)
178 "beginRecipe: Currently eyes cannot have more than one input");
180 if (e->inNodes->num == 0)
182 /* no input to this eye, skip it */
187 elog(NOTICE, "beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
188 #endif /* DEBUG_RECIPE */
190 qList = tg_parseSubQuery(r, e->inNodes->val[0], teeInfo);
194 /* eye is directly connected to a tee node */
195 /* XXX TODO: handle this case */
198 /* now, plan the queries */
201 * should really do everything pg_plan() does, but for now, we
202 * skip the rule rewrite and time qual stuff
205 /* ----------------------------------------------------------
206 * 1) plan the main query, everything from an eye node back to
208 * ---------------------------------------------------------- */
209 parsetree = qList->qtrees[0];
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
216 setheapoverride(true);
217 plan = planner(parsetree);
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 * ---------------------------------------------------------- */
230 for (t = 0; t < teeInfo->num; t++)
232 if (teeInfo->val[t].tpi_plan == NULL)
234 /* plan it in the usual fashion */
235 tplan = planner(teeInfo->val[t].tpi_parsetree);
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;
249 * the range table of the tee is the range table of
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;
259 /* ----------------------------------------------------------
260 * 3) replace the tee table scans in the main plan with
262 * ---------------------------------------------------------- */
264 plan = replaceTeeScans(plan, parsetree, teeInfo);
268 setheapoverride(false);
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);
274 queryDesc = CreateQueryDesc(parsetree,
278 * call ExecStart to prepare the plan for execution
281 attinfo = ExecutorStart(queryDesc, NULL);
283 ProcessPortal(portalName,
288 elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName);
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
305 * this is the main routine for rewriting the recipe queries
306 * the original query tree 'q' is modified
310 tg_rewriteQuery(TgRecipe * r,
313 QueryTreeList *inputQlist)
322 /* orig is the original parse tree of the node */
326 /*-------------------------------------------------------------------
329 form a combined range table from all the range tables in the original
330 query as well as the input nodes
332 form a combined qualification from the qual in the original plus
333 the quals of the input nodes
334 -------------------------------------------------------------------
337 /* start with the original range table */
338 rtable = orig->rtable;
339 rt_length = length(rtable);
341 for (i = 0; i < n->inNodes->num; i++)
343 if (n->inNodes->val[i] != NULL &&
344 n->inNodes->val[i]->nodeType != TG_TEE_NODE)
346 inputQ = inputQlist->qtrees[i];
347 input_rtable = inputQ->rtable;
350 * need to offset the var nodes in the qual and targetlist
351 * because they are indexed off the original rtable
353 OffsetVarNodes((Node *) inputQ->qual, rt_length, 0);
354 OffsetVarNodes((Node *) inputQ->targetList, rt_length, 0);
356 /* append the range tables from the children nodes */
357 rtable = nconc(rtable, input_rtable);
360 * append the qualifications of the child node into the
363 AddQual(orig, inputQ->qual);
366 orig->rtable = rtable;
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
373 if (orig->targetList != NIL)
378 foreach(tl, orig->targetList)
381 if (tle->resdom != NULL)
382 tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
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
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);
399 * at this point, we're done with the rewrite, the querytreelist q has
406 /* tg_replaceNumberedParam:
408 this procedure replaces the specified numbered param with a
409 reference to a range table
411 this procedure recursively calls itself
413 it returns a (possibly modified) 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
423 TargetEntry *param_tle;
428 if (expression == NULL)
431 switch (nodeTag(expression))
437 * the node is a parameter, substitute the entry from the
438 * target list of the child that corresponds to the
441 p = (Param *) expression;
443 /* we only deal with the case of numbered parameters */
444 if (p->paramkind == PARAM_NUM && p->paramid == pnum)
451 * we have a parameter with an attribute like
452 * $N.foo so replace it with a new var node
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;
464 /* we have $N without the .foo */
469 * TODO here, we need to check to see whether the
470 * type of the tee is a complex type (relation) or
475 * if it is a simple type, then we need to get the
476 * "result" attribute from the tee relation
479 isRel = (typeidTypeRelid(p->paramtype) != 0);
482 newVar = makeVar(rt_ind,
483 0, /* the whole tuple */
484 TypeGet(teeRelName, &defined),
489 return (Node *) newVar;
492 newVar = makeVar(rt_ind,
493 1, /* just the first field,
494 * which is 'result' */
495 TypeGet(teeRelName, &defined),
500 return (Node *) newVar;
505 elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
512 * the node is an expression, we need to recursively call
513 * ourselves until we find parameter nodes
516 Expr *expr = (Expr *) expression;
520 * we have to make a new args lists because Params can be
521 * replaced by Var nodes in tg_replaceNumberedParam()
526 * we only care about argument to expressions, it doesn't
527 * matter when the opType is
529 /* recursively rewrite the arguments of this expression */
530 foreach(l, expr->args)
532 newArgs = lappend(newArgs,
533 tg_replaceNumberedParam(lfirst(l),
538 /* change the arguments of the expression */
539 expr->args = newArgs;
544 /* ignore other expr types */
555 /* tg_rewriteParamsInExpr:
557 rewrite the params in expressions by using the targetlist entries
558 from the input parsetrees
560 this procedure recursively calls itself
562 it returns a (possibly modified) Node*.
566 tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist)
569 TargetEntry *param_tle,
575 if (expression == NULL)
578 switch (nodeTag(expression))
584 * the node is a parameter, substitute the entry from the
585 * target list of the child that corresponds to the
588 p = (Param *) expression;
590 /* we only deal with the case of numbered parameters */
591 if (p->paramkind == PARAM_NUM)
593 /* paramid's start from 1 */
594 childno = p->paramid - 1;
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
605 /* param tlist can only have one entry in them! */
606 param_tle = (TargetEntry *) (lfirst(p->param_tlist));
607 resname = param_tle->resdom->resname;
609 if (inputQlist->qtrees[childno])
611 foreach(tl, inputQlist->qtrees[childno]->targetList)
614 if (strcmp(resname, tle->resdom->resname) == 0)
619 elog(ERROR, "tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
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;
633 elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
640 * the node is an expression, we need to recursively call
641 * ourselves until we find parameter nodes
644 Expr *expr = (Expr *) expression;
648 * we have to make a new args lists because Params can be
649 * replaced by Var nodes in tg_rewriteParamsInExpr()
654 * we only care about argument to expressions, it doesn't
655 * matter when the opType is
657 /* recursively rewrite the arguments of this expression */
658 foreach(l, expr->args)
660 newArgs = lappend(newArgs,
661 tg_rewriteParamsInExpr(lfirst(l), inputQlist));
663 /* change the arguments of the expression */
664 expr->args = newArgs;
669 /* ignore other expr types */
680 given an element, finds its parameter types.
681 the typev array argument is set to the parameter types.
682 the parameterCount is returned
684 this code is very similar to ProcedureDefine() in pg_proc.c
687 getParamTypes(TgElement * elem, Oid *typev)
689 /* this code is similar to ProcedureDefine() */
690 int16 parameterCount;
698 for (i = 0; i < 8; i++)
700 for (j = 0; j < elem->inTypes->num; j++)
702 if (parameterCount == 8)
705 "getParamTypes: Ingredients cannot take > 8 arguments");
707 t = elem->inTypes->val[j];
708 if (strcmp(t, "opaque") == 0)
711 "getParamTypes: Ingredient functions cannot take type 'opaque'");
715 toid = TypeGet(elem->inTypes->val[j], &defined);
716 if (!OidIsValid(toid))
717 elog(ERROR, "getParamTypes: arg type '%s' is not defined", t);
719 elog(NOTICE, "getParamTypes: arg type '%s' is only a shell", t);
721 typev[parameterCount++] = toid;
724 return parameterCount;
731 * handles the parsing of the tee node
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,
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
759 * Later, in the glue phase, we replace all instances of TT sequential
760 * scans with the actual Tee node
762 q = tg_parseSubQuery(r, n, teeInfo);
764 /* tt is the name of the tee node table */
768 appendTeeQuery(teeInfo, q, tt);
770 orig = qList->qtrees[0];
771 rt_ind = RangeTablePosn(orig->rtable, tt);
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.
780 orig->rtable = lappend(orig->rtable,
781 addRangeTableEntry(NULL,
786 rt_ind = length(orig->rtable);
789 orig->qual = tg_replaceNumberedParam(orig->qual,
790 i + 1, /* params start at 1 */
799 * go backwards from a node and parse the query
801 * the result parse tree is passed back
803 * could return NULL if trying to parse a teeNode
804 * that's already been processed by another parent
808 static QueryTreeList *
809 tg_parseSubQuery(TgRecipe * r, TgNode * n, TeeInfo * teeInfo)
813 Oid typev[8]; /* eight arguments maximum */
817 QueryTreeList *qList; /* the parse tree of the nodeElement */
818 QueryTreeList *inputQlist; /* the list of parse trees for the inputs
829 if (n->nodeType == TG_INGRED_NODE)
831 /* parse each ingredient node in turn */
834 switch (elem->srcLang)
840 * for SQL ingredients, the SQL query is contained in
845 elog(NOTICE, "calling parser with %s", elem->src);
846 #endif /* DEBUG_RECIPE */
848 parameterCount = getParamTypes(elem, typev);
850 qList = parser(elem->src, typev, parameterCount);
855 "tg_parseSubQuery: parser produced > 1 query tree");
861 /* C ingredients are registered functions in postgres */
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)
872 funcName = elem->src;
873 parameterCount = getParamTypes(elem, typev);
875 if (parameterCount > 0)
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);
885 sprintf(newquery, "select %s()", funcName);
888 elog(NOTICE, "calling parser with %s", newquery);
889 #endif /* DEBUG_RECIPE */
891 qList = parser(newquery, typev, parameterCount);
895 "tg_parseSubQuery: parser produced > 1 query tree");
899 case TG_RECIPE_GRAPH:
900 elog(NOTICE, "tg_parseSubQuery: can't parse recipe graph ingredients yet!");
903 elog(NOTICE, "tg_parseSubQuery: can't parse compiled ingredients yet!");
906 elog(NOTICE, "tg_parseSubQuery: unknown srcLang: %d", elem->srcLang);
909 /* parse each of the subrecipes that are input to this node */
911 if (n->inNodes->num > 0)
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++)
919 inputQlist->qtrees[i] = NULL;
920 if (n->inNodes->val[i])
922 if (n->inNodes->val[i]->nodeType == TG_TEE_NODE)
924 qList = tg_parseTeeNode(r, n->inNodes->val[i],
928 { /* input node is not a Tee */
929 q = tg_parseSubQuery(r, n->inNodes->val[i],
932 inputQlist->qtrees[i] = q->qtrees[0];
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);
942 else if (n->nodeType == TG_EYE_NODE)
946 * if we hit an eye, we need to stop and make what we have into a
947 * subrecipe query block
949 elog(NOTICE, "tg_parseSubQuery: can't handle eye nodes yet");
951 else if (n->nodeType == TG_TEE_NODE)
955 * if we hit a tee, check to see if the parsing has been done for
956 * this tee already by the other parent
959 rel = RelationNameGetRelation(n->nodeName);
960 if (RelationIsValid(rel))
964 * this tee has already been visited, no need to do any
971 /* we need to process the child of the tee first, */
972 child = n->inNodes->val[0];
974 if (child->nodeType == TG_TEE_NODE)
976 /* nested Tee nodes */
977 qList = tg_parseTeeNode(r, child, 0, qList, teeInfo);
981 Assert(child != NULL);
983 /* parse the input node */
984 q = tg_parseSubQuery(r, child, teeInfo);
987 /* add the parsed query to the main list of queries */
988 qList = appendQlist(qList, q);
990 /* need to create the tee table here */
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
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
1004 * NOTE: we are assuming that the child node only has a single
1007 getParamTypes(child->nodeElem, typev);
1010 * the output type is either a complex type, (and is thus a
1011 * relation) or is a simple type
1014 rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);
1016 if (RelationIsValid(rel))
1020 * for complex types, create new relation with the same
1021 * tuple descriptor as the output table type
1023 len = length(q->qtrees[0]->targetList);
1024 tupdesc = rel->rd_att;
1026 relid = heap_create_with_catalog(
1027 child->nodeElem->outTypes->val[0],
1028 tupdesc, RELKIND_RELATION);
1034 * we have to create a relation with one attribute of the
1035 * simple base type. That attribute will have an attr
1038 /* NOTE: ignore array types for the time being */
1041 tupdesc = CreateTemplateTupleDesc(len);
1043 if (!TupleDescInitEntry(tupdesc, 1,
1047 elog(NOTICE, "tg_parseSubQuery: unexpected result from TupleDescInitEntry");
1050 relid = heap_create_with_catalog(
1051 child->nodeElem->outTypes->val[0],
1052 tupdesc, RELKIND_RELATION);
1057 else if (n->nodeType == TG_RECIPE_NODE)
1058 elog(NOTICE, "tg_parseSubQuery: can't handle embedded recipes yet!");
1060 elog(NOTICE, "unknown nodeType: %d", n->nodeType);
1067 * recursively find all the var nodes with the specified varno
1068 * and offset their varattno with the offset
1070 * code is similar to OffsetVarNodes in rewriteManip.c
1074 OffsetVarAttno(Node *node, int varno, int offset)
1078 switch (nodeTag(node))
1082 TargetEntry *tle = (TargetEntry *) node;
1084 OffsetVarAttno(tle->expr, varno, offset);
1089 Expr *expr = (Expr *) node;
1091 OffsetVarAttno((Node *) expr->args, varno, offset);
1096 Var *var = (Var *) node;
1098 if (var->varno == varno)
1099 var->varattno += offset;
1106 foreach(l, (List *) node)
1107 OffsetVarAttno(lfirst(l), varno, offset);
1111 /* ignore the others */
1118 * add the contents of a QueryTreeList q2 to the end of the QueryTreeList
1121 * returns a new querytree list
1125 appendQlist(QueryTreeList *q1, QueryTreeList *q2)
1127 QueryTreeList *newq;
1138 newlen = q1->len + q2->len;
1139 newq = (QueryTreeList *) malloc(sizeof(QueryTreeList));
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];
1152 * modify the query field of the teeInfo list of the particular tee node
1155 appendTeeQuery(TeeInfo * teeInfo, QueryTreeList *q, char *teeNodeName)
1161 for (i = 0; i < teeInfo->num; i++)
1163 if (strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0)
1166 Assert(q->len == 1);
1167 teeInfo->val[i].tpi_parsetree = q->qtrees[0];
1171 elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
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
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
1186 replaceSeqScan(Plan *plan, Plan *parent,
1187 int rt_ind, Plan *tplan)
1196 if (plan->type == T_SeqScan)
1198 snode = (Scan *) plan;
1199 if (snode->scanrelid == rt_ind)
1203 * found the sequential scan that should be replaced with the
1206 /* we replace the plan, but we also need to modify its parent */
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
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;
1224 /* change all the varno's to 1 */
1225 ChangeVarNodes((Node *) newPlan->plan.targetlist,
1226 snode->scanrelid, 1);
1230 teePlan = (Tee *) tplan;
1232 if (parent->lefttree == plan)
1233 parent->lefttree = (Plan *) newPlan;
1235 parent->righttree = (Plan *) newPlan;
1238 if (teePlan->leftParent == NULL)
1239 teePlan->leftParent = (Plan *) newPlan;
1241 teePlan->rightParent = (Plan *) newPlan;
1243 /* comment for now to test out executor-stuff
1244 if (parent->state) {
1245 ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
1255 replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
1256 if (plan->righttree)
1257 replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
1263 * places the sequential scans of the Tee table with
1264 * a connection to the actual tee plan node
1267 replaceTeeScans(Plan *plan, Query *parsetree, TeeInfo * teeInfo)
1277 rtable = parsetree->rtable;
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
1288 while (rtable != NIL)
1290 rte = lfirst(rtable);
1291 rtable = lnext(rtable);
1292 rt_ind++; /* range table references in varno fields
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
1301 if ((strlen(rte->refname) < 4) ||
1302 (strcmp(rte->relname, rte->refname) != 0))
1304 StrNCpy(prefix, rte->refname, 5);
1305 if (strcmp(prefix, "tee_") == 0)
1307 /* okay, we found a tee node entry in the range table */
1309 /* find the appropriate plan in the teeInfo list */
1311 for (i = 0; i < teeInfo->num; i++)
1313 if (strcmp(teeInfo->val[i].tpi_relName,
1315 tplan = teeInfo->val[i].tpi_plan;
1318 elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan");
1321 * replace the sequential scan node with that var number with
1324 replaceSeqScan(plan, NULL, rt_ind, tplan);