/*-------------------------------------------------------------------------
 *
 * 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.24 1998/09/01 04:27:56 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 <parser/parse_node.h>
#include <utils/builtins.h>
#include <utils/relcache.h>		/* for RelationNameGetRelation */
#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(ERROR, "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 = (typeidTypeRelid(p->paramtype) != 0);
						if (isRel)
						{
							newVar = makeVar(rt_ind,
											 0, /* the whole tuple */
										   TypeGet(teeRelName, &defined),
											 -1,
											 0,
											 rt_ind,
											 0);
							return (Node *) newVar;
						}
						else
							newVar = makeVar(rt_ind,
											 1, /* just the first field,
												 * which is 'result' */
										   TypeGet(teeRelName, &defined),
											 -1,
											 0,
											 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(ERROR, "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(ERROR,
				 "getParamTypes: Ingredients cannot take > 8 arguments");
		}
		t = elem->inTypes->val[j];
		if (strcmp(t, "opaque") == 0)
		{
			elog(ERROR,
				 "getParamTypes: Ingredient functions cannot take type 'opaque'");
		}
		else
		{
			toid = TypeGet(elem->inTypes->val[j], &defined);
			if (!OidIsValid(toid))
				elog(ERROR, "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));
		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_with_catalog(
									   child->nodeElem->outTypes->val[0],
											  tupdesc, RELKIND_RELATION);
			}
			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",
										InvalidOid,
										-1, 0, false))
					elog(NOTICE, "tg_parseSubQuery: unexpected result from TupleDescInitEntry");
				else
				{
					relid = heap_create_with_catalog(
									   child->nodeElem->outTypes->val[0],
											  tupdesc, RELKIND_RELATION);
				}
			}
		}
	}
	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, 5);
		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 */
