/*-------------------------------------------------------------------------
 *
 * recipe.c--
 *    routines for handling execution of Tioga recipes
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.6 1997/08/12 20:15:11 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <postgres.h>

#include <nodes/parsenodes.h>
#include <nodes/plannodes.h>
#include <nodes/execnodes.h>
#include <nodes/makefuncs.h>
#include <catalog/pg_type.h>
#include <commands/recipe.h>
#include <libpq/libpq-be.h>
#include <utils/builtins.h>
#include <utils/relcache.h> /* for RelationNameGetRelation*/
#include <parser/parse_query.h>
#include <rewrite/rewriteHandler.h>
#include <rewrite/rewriteManip.h>
#include <tcop/pquery.h>
#include <tcop/dest.h>
#include <optimizer/planner.h>
#include <executor/executor.h>

/* from tcop/postgres.c */
extern CommandDest whereToSendOutput;

#ifndef TIOGA

void beginRecipe(RecipeStmt *stmt) {
  elog(NOTICE,"You must compile with TIOGA defined in order to use recipes\n");
}
#else

#include <tioga/tgRecipe.h>

#define DEBUG_RECIPE 1

/* structure to keep track of the tee node plans */
typedef struct _teePlanInfo {
    char* tpi_relName;
    Query* tpi_parsetree;
    Plan*  tpi_plan;
} TeePlanInfo;

typedef struct _teeInfo {
    int num;
    TeePlanInfo *val;
} TeeInfo;

QueryTreeList *appendQlist(QueryTreeList *q1, QueryTreeList *q2);
void OffsetVarAttno(Node* node, int varno, int offset);

static void appendTeeQuery(TeeInfo *teeInfo, 
			   QueryTreeList *q, 
			   char* teeNodeName);

static Plan* replaceTeeScans(Plan* plan,
			     Query* parsetree,
			     TeeInfo *teeInfo);
static void replaceSeqScan(Plan* plan, 
			   Plan* parent,
			   int rt_ind, 
			   Plan* tplan);

static void tg_rewriteQuery(TgRecipe* r, TgNode *n, 
			    QueryTreeList *q,
			    QueryTreeList *inputQlist);
static Node *tg_replaceNumberedParam(Node* expression,
				     int pnum,
				     int rt_ind,
				     char *teeRelName);
static Node *tg_rewriteParamsInExpr(Node *expression, 
				    QueryTreeList *inputQlist);
static QueryTreeList *tg_parseSubQuery(TgRecipe* r, 
				       TgNode* n,
				       TeeInfo* teeInfo);
static QueryTreeList* tg_parseTeeNode(TgRecipe *r, 
				      TgNode *n, 
				      int i, 
				      QueryTreeList *qList,
				      TeeInfo* teeInfo);


/*
   The Tioga recipe rewrite algorithm:

   To parse a Tioga recipe, we start from an eye node and go backwards through
   its input nodes.  To rewrite a Tioga node, we do the following:

      1) parse the node we're at in the standard way (calling parser() )
      2) rewrite its input nodes recursively using Tioga rewrite
      3) now, with the rewritten input parse trees and the original parse tree
         of the node,  we rewrite the the node.
	 To do the rewrite, we use the target lists, range tables, and
	 qualifications of the input parse trees
*/

/*
 * beginRecipe:
 *    this is the main function to recipe execution
 *   this function is invoked for EXECUTE RECIPE ...  statements
 *  
 *  takes in a RecipeStmt structure from the parser
 * and returns a list of cursor names
 */

void
beginRecipe(RecipeStmt* stmt)
{
  TgRecipe* r;
  int i;
  QueryTreeList *qList;
  char portalName[1024];

  Plan *plan;
  TupleDesc attinfo;
  QueryDesc *queryDesc;
  Query *parsetree;

  int numTees;
  TeeInfo* teeInfo;

  /* retrieveRecipe() reads the recipe from the database
     and returns a TgRecipe* structure we can work with */

  r = retrieveRecipe(stmt->recipeName);

  if (r == NULL) return;

  /* find the number of tees in the recipe */
  numTees = r->tees->num;

  if (numTees > 0) {
      /* allocate a teePlan structure */
      teeInfo = (TeeInfo*)malloc(sizeof(TeeInfo));
      teeInfo->num = numTees;
      teeInfo->val = (TeePlanInfo*)malloc(numTees * sizeof(TeePlanInfo));
      for (i=0;i<numTees;i++) {
	  teeInfo->val[i].tpi_relName = r->tees->val[i]->nodeName;
	  teeInfo->val[i].tpi_parsetree = NULL;
	  teeInfo->val[i].tpi_plan = NULL;
      }
  } else
      teeInfo = NULL;

  /* 
   *  for each viewer in the recipe, go backwards from each viewer input
   *  and generate a plan.  Attach the plan to cursors.
   **/
  for (i=0;i<r->eyes->num;i++) {
      TgNodePtr e;

      e = r->eyes->val[i];
      if (e->inNodes->num > 1) {
	  elog(NOTICE,
	       "beginRecipe: Currently eyes cannot have more than one input");
      }
      if (e->inNodes->num == 0) { 
	  /* no input to this eye, skip it */
	  continue;
      }

#ifdef DEBUG_RECIPE
   elog(NOTICE,"beginRecipe: eyes[%d] = %s\n", i, e->nodeName);
#endif  /* DEBUG_RECIPE */

      qList = tg_parseSubQuery(r,e->inNodes->val[0], teeInfo);

      if (qList == NULL) {
	  /* eye is directly connected to a tee node */
	  /* XXX TODO: handle this case */
      }

      /* now, plan the queries */
      /* should really do everything pg_plan() does, but for now,
	 we skip the rule rewrite and time qual stuff */

      /* ---------------------------------------------------------- 
       * 1) plan the main query, everything from an eye node back to
           a Tee 
       * ---------------------------------------------------------- */
      parsetree = qList->qtrees[0];

      /* before we plan, we want to see all the changes
	 we did, during the rewrite phase, such as
	 creating the tee tables,
	 setheapoverride() allows us to see the changes */
      setheapoverride(true);
      plan = planner(parsetree);

      /* ---------------------------------------------------------- 
       * 2) plan the tee queries, (subgraphs rooted from a Tee)
           by the time the eye is processed, all tees that contribute
	   to that eye will have been included in the teeInfo list
       * ---------------------------------------------------------- */
      if (teeInfo) {
	  int t;
	  Plan* tplan;
	  Tee* newplan;

	  for (t=0; t<teeInfo->num;t++) {
	      if (teeInfo->val[t].tpi_plan == NULL) {
		  /* plan it in the usual fashion */
		  tplan =  planner(teeInfo->val[t].tpi_parsetree);

		  /* now add a tee node to the root of the plan */
elog(NOTICE, "adding tee plan node to the root of the %s\n",
     teeInfo->val[t].tpi_relName);
		  newplan = (Tee*)makeNode(Tee);
		  newplan->plan.targetlist = tplan->targetlist;
		  newplan->plan.qual = NULL; /* tplan->qual; */
		  newplan->plan.lefttree = tplan;
		  newplan->plan.righttree = NULL;
		  newplan->leftParent = NULL;
		  newplan->rightParent = NULL;
		  /* the range table of the tee is the range table
		     of the tplan */
		  newplan->rtentries = teeInfo->val[t].tpi_parsetree->rtable;
		  strcpy(newplan->teeTableName,
			 teeInfo->val[t].tpi_relName);
		  teeInfo->val[t].tpi_plan = (Plan*)newplan;
	      }
	  }
	  
	  /* ---------------------------------------------------------- 
	   * 3) replace the tee table scans in the main plan with
	        actual tee plannodes
	   * ---------------------------------------------------------- */

	  plan = replaceTeeScans(plan, parsetree, teeInfo);

      } /* if (teeInfo) */

      setheapoverride(false);
      
      /* define a portal for this viewer input */
      /* for now, eyes can only have one input */
      sprintf(portalName, "%s%d",e->nodeName,0);
	  
      queryDesc = CreateQueryDesc(parsetree,
				  plan,
				  whereToSendOutput);
      /* ----------------
       *	call ExecStart to prepare the plan for execution
       * ----------------
       */
      attinfo = ExecutorStart(queryDesc,NULL);
      
      ProcessPortal(portalName,
		    parsetree,
		    plan,
		    attinfo,
		    whereToSendOutput);
elog(NOTICE, "beginRecipe: cursor named %s is now available", portalName);
      }

}



/*
 * tg_rewriteQuery -
 *    r - the recipe being rewritten
 *    n - the node that we're current at
 *    q - a QueryTree List containing the parse tree of the node
 *    inputQlist - the parsetrees of its input nodes,
 *                 the size of inputQlist must be the same as the
 *                 number of input nodes.  Some elements in the inpuQlist
 *                 may be null if the inputs to those nodes are unconnected
 *
 *  this is the main routine for rewriting the recipe queries
 *  the original query tree 'q' is modified
 */

static void 
tg_rewriteQuery(TgRecipe* r, 
		TgNode *n, 
		QueryTreeList *q,
		QueryTreeList *inputQlist)
{
    Query* orig;
    Query* inputQ;
    int i;
    List *rtable;
    List *input_rtable;
    int rt_length;

    /* orig is the original parse tree of the node */
    orig = q->qtrees[0];


    /*-------------------------------------------------------------------
       step 1:
 
       form a combined range table from all the range tables in the original
       query as well as the input nodes 

       form a combined qualification from the qual in the original plus
       the quals of the input nodes
      -------------------------------------------------------------------
    */

    /* start with the original range table */
    rtable = orig->rtable;
    rt_length = length(rtable);

    for (i=0;i<n->inNodes->num;i++) {
	if (n->inNodes->val[i] != NULL &&
	    n->inNodes->val[i]->nodeType != TG_TEE_NODE) {
	    inputQ = inputQlist->qtrees[i];
	    input_rtable = inputQ->rtable;

	    /* need to offset the var nodes in the qual and targetlist
	       because they are indexed off the original rtable */
	    OffsetVarNodes((Node*)inputQ->qual, rt_length);
	    OffsetVarNodes((Node*)inputQ->targetList, rt_length);

	    /* append the range tables from the children nodes  */
	    rtable = nconc (rtable, input_rtable);

	    /* append the qualifications of the child node into the
	       original qual list */
	    AddQual(orig, inputQ->qual);
	}
    }
    orig->rtable = rtable;

    /* step 2:
       rewrite the target list of the original parse tree
       if there are any references to params, replace them with 
       the appropriate target list entry of the children node
     */
    if (orig->targetList != NIL) {
    	List *tl;
	TargetEntry *tle;

	foreach (tl, orig->targetList) {
	  tle = lfirst(tl);
	  if (tle->resdom != NULL) {
	      tle->expr = tg_rewriteParamsInExpr(tle->expr, inputQlist);
	  }
        }
    }
    
    /* step 3:
       rewrite the qual of the original parse tree
       if there are any references to params, replace them with 
       the appropriate target list entry of the children node
     */
    if (orig->qual) {
	if (nodeTag(orig->qual) == T_List) {
	    elog(WARN, "tg_rewriteQuery: Whoa! why is my qual a List???");
	}
	orig->qual = tg_rewriteParamsInExpr(orig->qual, inputQlist);
    }

    /* at this point, we're done with the rewrite, the querytreelist q
       has been modified */

}


/* tg_replaceNumberedParam:

   this procedure replaces the specified numbered param with a 
   reference to a range table

   this procedure recursively calls itself

   it returns a (possibly modified) Node*.

*/
static Node*
tg_replaceNumberedParam(Node *expression, 
			int pnum,  /* the number of the parameter */
			int rt_ind, /* the range table index */
			char *teeRelName) /* the relname of the tee table */
{
    TargetEntry *param_tle;
    Param* p;
    Var *newVar,*oldVar;

    if (expression == NULL) return NULL;
    
    switch (nodeTag(expression)) {
    case T_Param:
     {
        /* the node is a parameter, 
	   substitute the entry from the target list of the child that
	   corresponds to the parameter number*/
	 p = (Param*)expression;

	 /* we only deal with the case of numbered parameters */
	 if (p->paramkind == PARAM_NUM && p->paramid == pnum) {

	     if (p->param_tlist) {
		 /* we have a parameter with an attribute like $N.foo 
		    so replace it with a new var node */

		 /* param tlist can only have one entry in them! */
		 param_tle = (TargetEntry*)(lfirst(p->param_tlist));
		 oldVar = (Var*)param_tle->expr;
		 oldVar->varno = rt_ind;
		 oldVar->varnoold = rt_ind;
		 return (Node*)oldVar;
	     } else {
		 /* we have $N without the .foo */
		 bool defined;
		 bool isRel;
		 /* TODO here, we need to check to see whether the type of the
		    tee is a complex type (relation) or a simple type */
		 /* if it is a simple type, then we need to get the "result"
		    attribute from the tee relation */
		 
		 isRel = (typeid_get_relid(p->paramtype) != 0);
		 if (isRel) {
		     newVar = makeVar(rt_ind,
				      0, /* the whole tuple */
				      TypeGet(teeRelName,&defined),
				      rt_ind,
				      0);
		     return (Node*)newVar;
		 } else
		     newVar = makeVar(rt_ind,
				      1, /* just the first field, which is 'result' */
				      TypeGet(teeRelName,&defined),
				      rt_ind,
				      0);
		     return (Node*)newVar;

	     }
	 }
	 else {
	     elog(NOTICE, "tg_replaceNumberedParam: unexpected paramkind value of %d", p->paramkind);
	 }
     }
     break;
    case T_Expr: 
     {
	 /* the node is an expression, we need to recursively
	    call ourselves until we find parameter nodes */
	 List *l;
	 Expr *expr = (Expr*)expression;
	 List *newArgs; 

	 /* we have to make a new args lists because Params
	    can be replaced by Var nodes in tg_replaceNumberedParam()*/
	 newArgs = NIL;

	 /* we only care about argument to expressions,
	    it doesn't matter when the opType is */
	 /* recursively rewrite the arguments of this expression */
	 foreach (l, expr->args) {
	     newArgs = lappend(newArgs,
			       tg_replaceNumberedParam(lfirst(l),
						       pnum,
						       rt_ind,
						       teeRelName));
	 }
	 /* change the arguments of the expression */
	 expr->args = newArgs;
     }
    break;
 default:
     {
     /* ignore other expr types */
     }
 }

    return expression;
}





/* tg_rewriteParamsInExpr:

   rewrite the params in expressions by using the targetlist entries 
   from the input parsetrees 

   this procedure recursively calls itself

   it returns a (possibly modified) Node*.

*/
static Node*
tg_rewriteParamsInExpr(Node *expression, QueryTreeList *inputQlist)
{
    List *tl;
    TargetEntry *param_tle, *tle;
    Param* p;
    int childno;
    char *resname;

    if (expression == NULL) return NULL;
    
    switch (nodeTag(expression)) {
    case T_Param:
     {
        /* the node is a parameter, 
	   substitute the entry from the target list of the child that
	   corresponds to the parameter number*/
	 p = (Param*)expression;

	 /* we only deal with the case of numbered parameters */
	 if (p->paramkind == PARAM_NUM) {
	     /* paramid's start from 1*/
	     childno = p->paramid - 1;

	     if (p->param_tlist) {
		 /* we have a parameter with an attribute like $N.foo 
		    so match the resname "foo" against the target list
		    of the (N-1)th inputQlist */

		 /* param tlist can only have one entry in them! */
		 param_tle = (TargetEntry*)(lfirst(p->param_tlist));
		 resname = param_tle->resdom->resname;

		 if (inputQlist->qtrees[childno]) {
		     foreach (tl, inputQlist->qtrees[childno]->targetList) {
			 tle = lfirst(tl);
			 if (strcmp(resname, tle->resdom->resname) == 0) {
			     return tle->expr;
			 }
		     }
		 }
		 else {
		     elog(WARN,"tg_rewriteParamsInExpr:can't substitute for parameter %d when that input is unconnected", p->paramid);
		 }
 
	     } else {
		 /* we have $N without the .foo */
		 /* use the first resdom in the targetlist of the */
		 /* appropriate child query */
		 tl = inputQlist->qtrees[childno]->targetList;
		 tle = lfirst(tl);
		 return tle->expr;
	     }
	 }
	 else {
	     elog(NOTICE, "tg_rewriteParamsInExpr: unexpected paramkind value of %d", p->paramkind);
	 }
     }
     break;
    case T_Expr: 
     {
	 /* the node is an expression, we need to recursively
	    call ourselves until we find parameter nodes */
	 List *l;
	 Expr *expr = (Expr*)expression;
	 List *newArgs; 

	 /* we have to make a new args lists because Params
	    can be replaced by Var nodes in tg_rewriteParamsInExpr()*/
	 newArgs = NIL;

	 /* we only care about argument to expressions,
	    it doesn't matter when the opType is */
	 /* recursively rewrite the arguments of this expression */
	 foreach (l, expr->args) {
	     newArgs = lappend(newArgs,
			       tg_rewriteParamsInExpr(lfirst(l), inputQlist));
	 }
	 /* change the arguments of the expression */
	 expr->args = newArgs;
     }
    break;
 default:
     {
     /* ignore other expr types */
     }
 }

    return expression;
}



/*
   getParamTypes:
      given an element, finds its parameter types.
      the typev array argument is set to the parameter types. 
      the parameterCount is returned
   
   this code is very similar to ProcedureDefine() in pg_proc.c 
*/
static int
getParamTypes (TgElement *elem, Oid typev[])
{
    /* this code is similar to ProcedureDefine() */
    int16 parameterCount;
    bool defined;
    Oid toid;
    char *t;
    int i,j;

    parameterCount = 0;
    for (i=0;i<8;i++) {
	typev[i] = 0;
    }
    for (j=0;j<elem->inTypes->num;j++) {
	if (parameterCount == 8) {
	    elog(WARN, 
		 "getParamTypes: Ingredients cannot take > 8 arguments"); 
	}
	t = elem->inTypes->val[j];
	if (strcmp(t,"opaque") == 0) {
	    elog(WARN, 
		 "getParamTypes: Ingredient functions cannot take type 'opaque'");
	} else {
	    toid = TypeGet(elem->inTypes->val[j], &defined);
	    if (!OidIsValid(toid)) {
		elog(WARN, "getParamTypes: arg type '%s' is not defined",t);
	    }
	    if (!defined) {
		elog(NOTICE, "getParamTypes: arg type '%s' is only a shell",t);
	    }
	}
	typev[parameterCount++] = toid;
    }

    return parameterCount;
}


/*
 * tg_parseTeeNode
 *    
 *   handles the parsing of the tee node 
 *   
 *
 */

static QueryTreeList*
tg_parseTeeNode(TgRecipe *r, 
		TgNode *n,  /* the tee node */
		int i, /* which input this node is to its parent */
		QueryTreeList *qList,
		TeeInfo* teeInfo)

{
    QueryTreeList *q;
    char* tt;
    int rt_ind;
    Query* orig;

    /* the input Node is a tee node, so we need to do the following:
     *  we need to parse the child of the tee node,
     we add that to our query tree list
     *  we need the name of the tee node table 
     the tee node table is the table into which the tee node
     may materialize results.  Call it TT
     *  we add a range table to our existing query with TT in it
     *  we need to replace the parameter $i with TT
     (otherwise the optimizer won't know to use the table
     on expression containining $i)
     After that rewrite, the optimizer will generate
     sequential scans of TT
     
     Later, in the glue phase, we replace all instances of TT
     sequential scans with the actual Tee node
     */
    q = tg_parseSubQuery(r,n, teeInfo);

    /* tt is the name of the tee node table */
    tt = n->nodeName;

    if (q) 
	appendTeeQuery(teeInfo,q,tt);

    orig = qList->qtrees[0];
    rt_ind = RangeTablePosn(orig->rtable,tt);
    /* check to see that this table is not part of 
       the range table already.  This usually only 
       happens if multiple inputs are connected to the
       same Tee. */
    if (rt_ind == 0) {
	orig->rtable = lappend(orig->rtable,
			       addRangeTableEntry(NULL,
						   tt,
						   tt,
						   FALSE,
						   FALSE,
						   NULL));
	rt_ind = length(orig->rtable);
    }
			
    orig->qual = tg_replaceNumberedParam(orig->qual,
					 i+1, /* params start at 1*/
					 rt_ind,
					 tt);
    return qList;
}


/*
 * tg_parseSubQuery:
 *    go backwards from a node and parse the query
 *
 *   the result parse tree is passed back
 * 
 * could return NULL if trying to parse a teeNode 
 * that's already been processed by another parent
 * 
 */

static QueryTreeList*
tg_parseSubQuery(TgRecipe* r, TgNode* n, TeeInfo* teeInfo)
{
    TgElement *elem;
    char* funcName;
    Oid typev[8];     /* eight arguments maximum  */
    int i;
    int parameterCount;

    QueryTreeList *qList;      /* the parse tree of the nodeElement */
    QueryTreeList *inputQlist; /* the list of parse trees for the
				  inputs to this node */
    QueryTreeList *q;
    Oid relid;
    TgNode* child;
    Relation rel;
    unsigned int len;
    TupleDesc tupdesc;

    qList = NULL;

    if (n->nodeType == TG_INGRED_NODE) {
	/* parse each ingredient node in turn */

	elem = n->nodeElem;
	switch (elem->srcLang) {
	case TG_SQL:
	    { 
		/* for SQL ingredients, the SQL query is contained in the
		   'src' field  */

#ifdef DEBUG_RECIPE
elog(NOTICE,"calling parser with %s",elem->src);
#endif  /* DEBUG_RECIPE */

		parameterCount = getParamTypes(elem,typev);

		qList = parser(elem->src,typev,parameterCount);

		if (qList->len > 1) {
		    elog(NOTICE,
			 "tg_parseSubQuery: parser produced > 1 query tree");
		}
	    }
	    break;
	case TG_C:
	    {
		/* C ingredients are registered functions in postgres */
		/* we create a new query string by using the function name
		   (found in the 'src' field) and adding parameters to it
		   so if the function was FOOBAR and took in two arguments,
		   we would create a string
		         select FOOBAR($1,$2)
		   */
		char newquery[1000];

		funcName = elem->src;
		parameterCount = getParamTypes(elem,typev);

		if (parameterCount > 0) {
		    int i;
		    sprintf(newquery,"select %s($1",funcName);
		    for (i=1;i<parameterCount;i++) {
			sprintf(newquery,"%s,$%d",newquery,i);
		    }
		    sprintf(newquery,"%s)",newquery);
		} else
		    sprintf(newquery,"select %s()",funcName);

#ifdef DEBUG_RECIPE
elog(NOTICE,"calling parser with %s", newquery);
#endif  /* DEBUG_RECIPE */

		qList = parser(newquery,typev,parameterCount);
		if (qList->len > 1) {
		    elog(NOTICE,
			 "tg_parseSubQuery: parser produced > 1 query tree");
		}
	    }
	    break;
	case TG_RECIPE_GRAPH:
	     elog(NOTICE,"tg_parseSubQuery: can't parse recipe graph ingredients yet!");
	    break;
	case TG_COMPILED:
	     elog(NOTICE,"tg_parseSubQuery: can't parse compiled ingredients yet!");
	    break;
	default:
	    elog(NOTICE,"tg_parseSubQuery: unknown srcLang: %d",elem->srcLang);
	}

	/* parse each of the subrecipes that are input to this node*/

	if (n->inNodes->num > 0) {
	    inputQlist = malloc(sizeof(QueryTreeList));
	    inputQlist->len = n->inNodes->num + 1 ;
	    inputQlist->qtrees = (Query**)malloc(inputQlist->len * sizeof(Query*));
	    for (i=0;i<n->inNodes->num;i++) {

		inputQlist->qtrees[i] = NULL;
		if (n->inNodes->val[i]) {
		    if (n->inNodes->val[i]->nodeType == TG_TEE_NODE) {
			qList = tg_parseTeeNode(r,n->inNodes->val[i],
						i,qList,teeInfo);
		    }
		    else 
			{ /* input node is not a Tee */
			    q = tg_parseSubQuery(r,n->inNodes->val[i],
						 teeInfo);
			    Assert (q->len == 1);
			    inputQlist->qtrees[i] = q->qtrees[0];
			}
		}
	    }

	    /* now, we have all the query trees from our input nodes */
	    /* transform the original parse tree appropriately */
	    tg_rewriteQuery(r,n,qList,inputQlist);
	}
    }
    else if (n->nodeType == TG_EYE_NODE) {
	/* if we hit an eye, we need to stop and make what we have 
	   into a subrecipe query block*/
	elog(NOTICE,"tg_parseSubQuery: can't handle eye nodes yet");
    }
    else if (n->nodeType == TG_TEE_NODE) {
	/* if we hit a tee, check to see if the parsing has been done
	   for this tee already by the other parent */
	
	rel = RelationNameGetRelation(n->nodeName);
	if (RelationIsValid(rel)) {
	    /* this tee has already been visited, 
	       no need to do any further processing */
	    return NULL;
	} else {
	    /* we need to process the child of the tee first, */
	    child = n->inNodes->val[0];

	    if (child->nodeType == TG_TEE_NODE) {
		/* nested Tee nodes */
		qList = tg_parseTeeNode(r,child,0,qList,teeInfo);
		return qList;
	    }

	    Assert (child != NULL);
	    
	    /* parse the input node */
	    q = tg_parseSubQuery(r,child, teeInfo);
	    Assert (q->len == 1);

	    /* add the parsed query to the main list of queries */
	    qList = appendQlist(qList,q);

	    /* need to create the tee table here */
	    /* the tee table created is used both for materializing the values
	       at the tee node, and for parsing and optimization.
	       The optimization needs to have a real table before it will
	       consider scans on it */

	    /* first, find the type of the tuples being produced by the 
	       tee.  The type is the same as the output type of 
	       the child node.  

	       NOTE: we are assuming that the child node only has a single
	       output here! */
	    getParamTypes(child->nodeElem,typev);

	    /* the output type is either a complex type, 
	       (and is thus a relation) or is a simple type */
	    
	    rel = RelationNameGetRelation(child->nodeElem->outTypes->val[0]);

	    if (RelationIsValid(rel)) {
		/* for complex types, create new relation with the same
		   tuple descriptor as the output table type*/
                len = length(q->qtrees[0]->targetList);
		tupdesc = rel->rd_att;
		
		relid = heap_create(child->nodeElem->outTypes->val[0],
				    NULL, /* XXX */
				    'n',
				    DEFAULT_SMGR,
				    tupdesc);
	    }
	    else {
		/* we have to create a relation with one attribute of
		   the simple base type.  That attribute will have
                   an attr name of "result" */
		/*NOTE: ignore array types for the time being */

		len = 1;
		tupdesc = CreateTemplateTupleDesc(len);

		if ( !TupleDescInitEntry(tupdesc,1,
                                         "result",
					 NULL,
					 0, false)) {
                      elog(NOTICE,"tg_parseSubQuery: unexpected result from TupleDescInitEntry");
                } else {
		     relid =  heap_create(child->nodeElem->outTypes->val[0],
					  NULL, /* XXX */
					  'n',
					  DEFAULT_SMGR,
					  tupdesc);
		  }
	    }
	}
    }
    else if (n->nodeType == TG_RECIPE_NODE) {
	elog(NOTICE,"tg_parseSubQuery: can't handle embedded recipes yet!");
    } else
	elog (NOTICE, "unknown nodeType: %d", n->nodeType);

    return qList;
}

/*
 * OffsetVarAttno -
 *    recursively find all the var nodes with the specified varno
 * and offset their varattno with the offset
 * 
 *  code is similar to OffsetVarNodes in rewriteManip.c
 */

void
OffsetVarAttno(Node* node, int varno, int offset)
{
    if (node == NULL) return;
    switch (nodeTag(node)) {
    case T_TargetEntry:
	{
	    TargetEntry *tle = (TargetEntry *)node;
	    OffsetVarAttno(tle->expr, varno, offset);
	}
	break;
    case T_Expr:
	{
	    Expr *expr = (Expr*)node;
	    OffsetVarAttno((Node*)expr->args, varno, offset);
	}
	break;
    case T_Var:
	{
	    Var *var = (Var*)node;
	    if (var->varno == varno)
		var->varattno += offset;
	}
	break;
    case T_List:
	{
	    List *l;

	    foreach(l, (List*)node) {
		OffsetVarAttno(lfirst(l), varno, offset);
	    }
	}
	break;
    default:
	/* ignore the others */
	break;
    }
}

/*
 * appendQlist 
 *    add the contents of a QueryTreeList q2 to the end of the QueryTreeList
 *   q1
 *
 *  returns a new querytree list
 */

QueryTreeList*
appendQlist(QueryTreeList *q1, QueryTreeList *q2)
{
    QueryTreeList* newq;
    int i,j;
    int newlen;
    
    if (q1 == NULL)
	return q2;

    if (q2 == NULL)
	return q1;

    newlen = q1->len + q2->len;
    newq = (QueryTreeList*)malloc(sizeof(QueryTreeList));
    newq->len = newlen;
    newq->qtrees = (Query**)malloc(newlen * sizeof(Query*));
    for (i=0;i<q1->len;i++)
	newq->qtrees[i] = q1->qtrees[i];
    for (j=0;j<q2->len;j++) {
	newq->qtrees[i + j] = q2->qtrees[j];
    }
    return newq;
}

/*
 * appendTeeQuery 
 *    
 *  modify the query field of the teeInfo list of the particular tee node
 */
static void
appendTeeQuery(TeeInfo *teeInfo, QueryTreeList *q, char* teeNodeName)
{
    int i;
    
    Assert(teeInfo);

    for (i=0;i<teeInfo->num;i++) {
	if ( strcmp(teeInfo->val[i].tpi_relName, teeNodeName) == 0) {

	    Assert(q->len == 1);
	    teeInfo->val[i].tpi_parsetree = q->qtrees[0];
	    return;
	}
    }
    elog(NOTICE, "appendTeeQuery: teeNodeName '%s' not found in teeInfo");
}



/*
 * replaceSeqScan 
 *    replaces sequential scans of a specified relation with the tee plan
 *  the relation is specified by its index in the range table,   rt_ind
 *
 * returns the modified plan
 * the offset_attno is the offset that needs to be added to the parent's
 * qual or targetlist because the child plan has been replaced with a tee node
 */
static void
replaceSeqScan(Plan* plan, Plan* parent,
	       int rt_ind, Plan* tplan)
{
    Scan* snode;
    Tee* teePlan;
    Result* newPlan;

    if (plan == NULL) {
	return;
    }

    if (plan->type == T_SeqScan) {
	snode = (Scan*)plan;
	if (snode->scanrelid == rt_ind) {
	    /* found the sequential scan that should be replaced
	       with the tplan. */
	    /* we replace the plan, but we also need to modify its parent*/

	    /* replace the sequential scan with a Result node
	       the reason we use a result node is so that we get the proper
	       projection behavior.  The Result node is simply (ab)used as
	       a projection node */

	    newPlan = makeNode(Result);
	    newPlan->plan.cost = 0.0;
	    newPlan->plan.state = (EState*)NULL;
	    newPlan->plan.targetlist = plan->targetlist;
	    newPlan->plan.lefttree = tplan;
	    newPlan->plan.righttree = NULL;
	    newPlan->resconstantqual = NULL;
	    newPlan->resstate = NULL;

	    /* change all the varno's to 1*/
	    ChangeVarNodes((Node*)newPlan->plan.targetlist,
			   snode->scanrelid, 1);

	    if (parent) {
		teePlan = (Tee*)tplan;

		if (parent->lefttree == plan)
		    parent->lefttree = (Plan*)newPlan;
		else
		    parent->righttree = (Plan*)newPlan;
		

		if (teePlan->leftParent == NULL)
		    teePlan->leftParent = (Plan*)newPlan;
		else
		    teePlan->rightParent = (Plan*)newPlan;

/* comment for now to test out executor-stuff
		if (parent->state) {
		    ExecInitNode((Plan*)newPlan, parent->state, (Plan*)newPlan);
		}
*/
	    }
	}

    } else {
	if (plan->lefttree) {
	    replaceSeqScan(plan->lefttree, plan, rt_ind, tplan);
	}
	if (plan->righttree) {
	    replaceSeqScan(plan->righttree, plan, rt_ind, tplan);
	}
    }
}

/*
 * replaceTeeScans 
 *    places the sequential scans of the Tee table with 
 * a connection to the actual tee plan node
 */
static Plan* 
replaceTeeScans(Plan* plan, Query* parsetree, TeeInfo *teeInfo)
{

    int i;
    List* rtable;
    RangeTblEntry *rte;
    char prefix[5];
    int rt_ind;
    Plan* tplan;

    rtable = parsetree->rtable;
    if (rtable == NULL)
	return plan;

    /* look through the range table for the tee relation entry,
       that will give use the varno we need to detect which
       sequential scans need to be replaced with tee nodes*/

    rt_ind = 0;
    while (rtable != NIL) {
	rte = lfirst(rtable);
	rtable = lnext(rtable);
	rt_ind++; /* range table references in varno fields start w/ 1 */

	/* look for the "tee_" prefix in the refname,
	   also check to see that the relname and the refname are the same
	   this should eliminate any user-specified table and leave
	   us with the tee table entries only*/
	if  ((strlen(rte->refname) < 4) ||
	     (strcmp (rte->relname, rte->refname) != 0))
	    continue;
	strNcpy(prefix,rte->refname,4);
	if (strcmp(prefix,"tee_") == 0) {
	    /* okay, we found a tee node entry in the range table */

	    /* find the appropriate plan in the teeInfo list */
	    tplan = NULL;
	    for (i=0;i<teeInfo->num;i++) {
		if (strcmp(teeInfo->val[i].tpi_relName,
			   rte->refname) == 0) {
		    tplan = teeInfo->val[i].tpi_plan;
		}
	    }
	    if (tplan == NULL) {
		elog(NOTICE, "replaceTeeScans didn't find the corresponding tee plan"); }
		
	    /* replace the sequential scan node with that var number 
	       with the tee plan node */
	    replaceSeqScan(plan, NULL, rt_ind, tplan);
	}
    }

    return plan;
}



#endif /* TIOGA */
