/***********************************************************************
 ** *	Recursion Planner (recurplanner.c)			    * **
 ** *		Jimmy Bell					    * **
 ** *								    * **
 ***********************************************************************
 */

/* RcsId ("$Header: /usr/local/dev/postgres/mastertree/src/planner/plan/RCS/recurplanner.c,v 1.4 1990/10/17 15:36:00 choi Exp $"); */

#include "planner/recurplanner.h"
#include "parser/parsetree.h"
#include "utils/log.h"

/* ----------------
 *	Recursive creator declaration
 * ----------------
 */
extern Recursive RMakeRecursive();


/* JJJ -DEBUGing routine */
void
Jshow (msg, obj)
char	*msg;
LispValue	obj;
{
printf("\n *** JJJ : %s : ", msg);
lispDisplay(obj,0);
}
/* JJJ */

/* JJJ -Message routine */
void
Jprint (msg)
char  *msg;
{
printf("\n *** JJJ -- %s -- ",msg);
}
/* JJJ */




Recursive		/* Recursive Node containing query plan */
make_recursive (recursiveMethod, command, initPlans, loopPlans,
		cleanupPlans, checkpoints, resultRelationName)
	RecursiveMethod	recursiveMethod;
	LispValue	command;
	List		initPlans;
	List		loopPlans;
	List		cleanupPlans;
	List		checkpoints;
        LispValue	resultRelationName;
{
	Recursive node = RMakeRecursive();

	set_cost(node,0.0);
	set_fragment(node,0);
	set_state(node,(EState)NULL);
	set_qptargetlist(node,(TargetList)NULL);
	set_qpqual(node,LispNil);
	set_lefttree(node,(Plan)NULL);
	set_righttree(node,(Plan)NULL);

	set_recurMethod(node,recursiveMethod);
	set_recurCommand(node,command);
	set_recurInitPlans(node,initPlans);
	set_recurLoopPlans(node,loopPlans);
	set_recurCleanupPlans(node,cleanupPlans);
	set_recurCheckpoints(node,checkpoints);
	set_recurResultRelationName(node,resultRelationName);

	return(node);
}

/****
 *	functions to facilitate conversions between the internal
 *	form of plans and utility requests (like create FOO) and
 *	their list equivalents.
 */

GeneralPlan		/* either a utility request or a plan */
PlanOrUtilityToGeneralPlan (isUtility, planOrUtility)
	bool		isUtility;	/* false indicates 'plan' */
	PlanOrUtility	planOrUtility;
{
	GeneralPlan	generalPlan;

	generalPlan.isUtility = isUtility;
	if (isUtility)
		generalPlan.planOrUtility.utility =
			lispString(planOrUtility.utility);
	else
		generalPlan.planOrUtility.plan = planOrUtility.plan;

	return generalPlan;
}

List			/* List form of General Plan */
GeneralPlanToList (generalPlan)
	GeneralPlan	generalPlan;	/* general plan to be converted */
{
	if (generalPlan.isUtility)
		return lispCons(lispInteger(generalPlan.isUtility),
				lispCons(generalPlan.planOrUtility.utility,
					LispNil));
	else
		return lispCons(lispInteger(generalPlan.isUtility),
                                lispCons(generalPlan.planOrUtility.plan,
                                        LispNil));
}

GeneralPlan		/* General Plan extracted from List form */
ListToGeneralPlan (list)
	List	list;	/* list being examined */
{
	GeneralPlan	generalPlan;

	if ( CInteger(CAR(list)) == 1 ) {
		generalPlan.isUtility = true;
		generalPlan.planOrUtility.utility = 
			(UtilityRequest) CAR(CDR(list));
	}
	else {
		generalPlan.isUtility = false;
                generalPlan.planOrUtility.plan = (Plan) CAR(CDR(list));
	}
	return generalPlan;
}

GeneralPlan		/* containing utility request from string */
StringToGeneralPlan(str)
	String		str;
{
	GeneralPlan	generalPlan;

  	generalPlan.isUtility = true;
	generalPlan.planOrUtility.utility = lispString(str);

	return generalPlan;
}

/*
 *	end of conversion functions
 *******
 */

/*******
 *	functions for checking linearity and verifying recursion
 */

String			/* name of result  relation */
ParseTreeGetResultRelationName(parseTree)
	ParseTree	parseTree;
{
	LispValue	resultRelation;
	RangeTable	rangeTable;
	String		resultName;

	resultRelation = ParseTreeGetResultRelation( parseTree );
	rangeTable = ParseTreeGetRangeTable( parseTree );

	if ( null(resultRelation) )
	  elog(WARN,"No result relation specified");
	else if ( lispIntegerp(resultRelation) )
	  resultName = CString(rt_relname(
			        nth (CInteger(resultRelation)-1,rangeTable)));
	else
	  resultName = CString(CAR(CDR(resultRelation)));

	return resultName;
}

/*
 *	ParseTreeGetVariablesOnResultRelation --
 *		Take short-cut to approximate the number of tuple
 *		var's used to refer to result relation.  I count
 *		occurrences of result's name string in range table.
 */

int		/* number of tuple variables on result relation */
ParseTreeGetVariablesOnResultRelation(parseTree)
	ParseTree	parseTree;
{
	String		resultName;
	RangeTable	rangeTable;
	LispValue	element;
	int		count;

	rangeTable = ParseTreeGetRangeTable( parseTree );
	resultName = ParseTreeGetResultRelationName( parseTree );
	count = 0;
	foreach(element, rangeTable) {
	  if ( strcmp(CString(rt_relname(CAR(element))),resultName) == 0 )
	    count++;
	}

	return count;
}

/*
 *	RecursiveQueryGetLinearity --
 *		Returns the number of distinct tuple variables in the
 *		query which reference the result relation.  If the user
 *		has not wasted tuple variables, this will be the level
 *		of recursion.  In delete and replace, a tuple variable
 *		is automatically declared for the result relation.
 *		Additional ones for any command are declared with the
 *		"from" clause.
 *
 *	Append* is only recursive if an assignment in the target list or
 *		part of the qualification is based a tuple variable of
 *		the result relation.  If the user references the result
 *		by name or declares tuple variables on it, assume recursive.
 *
 *	Retrieve* is similar.  We assume that the user has referenced
 *		the result relation by name, so if any additional
 *		tuple var's are declared for the result, we assume
 *		non-linearity.
 *
 *	Delete* is recursive only if an aggregate function or rule is
 *		used which is sensitive either to a decrease in the
 *		number of tuples, or to the absence of particular tuples.
 *		The not-in operator is also hole-sensitive.  If one of
 *		these 3 references the result relation, it should be
 *		assumed to be recursive (linear if exactly one tuple var).
 *		We assume that since one provided it is used this way.
 *		In the absence of not-in, this is not supported.
 *
 *	Replace* is assumed to be recursive since mere assignment from
 *		other relations based on a tuples field values can
 *		cause tuples to requalify on the nexxt iteration.  If
 *		additional tuple var's are declared, assumed nonlinear.
 *
 *	Execute* not supported at all.
 */

int			/* 0 if non-recursive, 1 if linear, 2+ if nonlinear */
RecursiveQueryGetLinearity(parseTree)
	ParseTree	parseTree;
{
	int	resultRelationVarCount;

	resultRelationVarCount = 
	  ParseTreeGetVariablesOnResultRelation(parseTree);
	switch ((Command) CInteger(ParseTreeGetCommandType(parseTree))) {
	  case APPEND:
	  case RETRIEVE:
	  case DELETE:
		return resultRelationVarCount;
		break;
	  case REPLACE:
		if ( resultRelationVarCount == 0 ) {
		  return 1;
		}
		return ( resultRelationVarCount );
	  case EXECUTE:
		elog(WARN,
		     "RecursiveQueryGetLinearity: 'execute' not supported");
	  default:
		elog(WARN,
		     "RecursiveQueryGetLinearity: command not recognized");
	}
}

/*
 *	RecursiveMethodChoose -
 *		Determines which evaluation method to use on a recursive
 *		query.  Chooses between Naive and Seminaive based on a
 *		simplified linearity check.  Signals that the query is
 *		not recursive at all by returning RecursiveMethodNone,
 *		in which case the standard planner will take over.
 */

RecursiveMethod		/* the optimal evaluation method to use */
RecursiveMethodChoose(parseTree)
	ParseTree	parseTree;	/* parse tree of query in question */
{
	switch (RecursiveQueryGetLinearity(parseTree)) {
	  case 0:
		return RecursiveMethodNone;
		break;
	  case 1:
		return RecursiveMethodSemiNaive;
		break;
	  default:
		return RecursiveMethodNaive;
		break;
	}
}

/* JJJ - Needed for hacky delete, but don't worry */

/* assume clause has no subparts, since in cnf */
bool		/* true iff clause references given range table entry */
ClauseRefersTo( clause, rangeTableIdx )
	List		clause;
	LispValue	rangeTableIdx;
{
/* *** DONE *** */
	List		variableList;

	foreach(variableList,pull_var_clause(clause)) {
	  if ( equal(rangeTableIdx,get_varno(CAR(variableList))) )
	    return true;
	}

	return false;
}

List		/* list of indexes to RT found in clause, except given one */
ClauseGetOtherRefs ( clause, rangeTableIdx )
	List		clause;
	LispValue	rangeTableIdx;
{
/* *** DONE *** */
	List		indexList;
	LispValue	varNumber;
	List		subList;

	indexList = lispList();

        foreach(subList,pull_var_clause(clause)) {
	  varNumber = (LispValue) get_varno(CAR(subList));
	  if (!equal(varNumber,rangeTableIdx) && !member(varNumber,indexList))
	    indexList = append1(indexList,varNumber);
	}

	return indexList;
}

List		/* list if RT indexes which only appear in one disjunct */
BindingListGetSolos ( bindingList, conjunct )
	List		bindingList;
	List		conjunct;
{
/* *** DONE *** */
	LispValue	index;
	List		newBindingList;
	List		list;
	List		varList;

	newBindingList = lispList();
	varList = pull_var_clause(conjunct);

	foreach( list, bindingList ) {
	  index = CAR(list);
	  if ( ! member(index,varList) )
	    newBindingList = append1(newBindingList,index);
	}

	return newBindingList;
}

/* JJJ  remove_duplicates(foo,test);
	LispRemove(foo,bar); value bar;
	LispDelete(val,list);  auto;
	set_difference(source,sub); value;
	push(this,onlist); value;
	integerp(foo);bool;
	findif;
	lispList(); value;
*/
	

/*************
 *	Removes the disjuncts which refer to the given tuple variable,
 *	and recursively removes occurrences of tuple variables
 *	which only have meaning in the presence of the deleted disjunct.
 *
 *	Assumes qualification has already been preprocessed.
 *************
 */
Qualification		/* qual without references to certain variables */
QualificationYankReferences(qual,rangeTableIdx)
	Qualification	qual;
	LispValue	rangeTableIdx;
{
	Qualification	newQual;
	LispValue	list;
	LispValue	subList;
	List		bindingList;
	LispValue	bindingSubList;
	List		andClauseArg;
	List		newAndClauseArgs;
	List		orClauseArg;
	List		newOrClauseArgs;
        LispValue	newRangeTableIdx;
	bool		isANotClause;

	/* Needs to be extended to deal with functions, etc. */

	/* qual is already preprocessed */

	if ( null(qual) )
	  return qual;
	else if ( and_clause(qual) ) {
	  newAndClauseArgs = lispList();
	  foreach(list,get_andclauseargs(qual)) {
	    andClauseArg = CAR(list);
	    if ( not_clause(andClauseArg) ) {
	      isANotClause = true;
	      andClauseArg = get_orclauseargs(andClauseArg);
	    }
	    else
	      isANotClause = false;

/* JJJ *** need another check for NOT clauses */

	    if ( or_clause(andClauseArg) ) {
	      newOrClauseArgs = lispList();
	      foreach(subList,get_orclauseargs(andClauseArg)) {
		orClauseArg = CAR(subList);
		if ( ClauseRefersTo(orClauseArg,rangeTableIdx) ) {
		  /* omit it, and others (if this was necessary instance) */
		  bindingList = ClauseGetOtherRefs(orClauseArg,rangeTableIdx);
		  bindingList = BindingListGetSolos(bindingList,andClauseArg);
		  /* construct newQual */
		  newAndClauseArgs =
		    append( newAndClauseArgs, ((isANotClause) ?
			     make_notclause( make_orclause(append(
					     newOrClauseArgs,subList))) :
			     make_orclause(append(newOrClauseArgs,subList))) );
		  newQual = make_andclause(append(newAndClauseArgs,
						  list));
		  foreach(bindingSubList,bindingList) {
		    newQual = QualificationYankReferences(newQual,
							  CAR(bindingSubList));
		  }
/* JJJ */	  /* to avoid reconstructing my variables ... this is slow */
		  return QualificationYankReferences(newQual,
						      lispInteger(0));
		}
		else
		  newOrClauseArgs = append1( newOrClauseArgs, orClauseArg );
	      }
	      newAndClauseArgs = append( newAndClauseArgs,
		( (isANotClause) ? make_notclause(
					make_orclause(newOrClauseArgs)) :
				   make_orclause(newOrClauseArgs)) );
	    }
	    else if ( is_clause(andClauseArg) ) {
	      /* assume it is a simple qualification */
	      if ( ! ClauseRefersTo(andClauseArg,rangeTableIdx) )
		newAndClauseArgs = append1( newAndClauseArgs,
				            ( (isANotClause) ?
					     make_notclause(andClauseArg ) :
					     andClauseArg) );
	    }
	    else
	      elog(WARN,"Bad And-Clause arg. in prep'd Qualification");
	  }
	  return make_andclause(newAndClauseArgs);
	}
	else if ( or_clause(qual) ) {
	  newOrClauseArgs = lispList();
	  foreach(list,get_orclauseargs(qual)) {
	    orClauseArg = CAR(list);
	    if ( ClauseRefersTo(orClauseArg,rangeTableIdx) ) {
	      /* omit it, and leave others ... */
	    }
	    else
	      newOrClauseArgs = append1( newOrClauseArgs, orClauseArg );
	  }
	  return make_orclause(newOrClauseArgs);
	  
	}
	else if ( is_funcclause(qual) ) {
	  elog(WARN,"Can't trim functions in recursion module");
	}
	else if ( not_clause(qual) ) {
	  return make_notclause(
		      QualificationYankReferences(get_notclausearg(qual),
						  rangeTableIdx));
	}
	else if ( is_clause(qual) ) {
	  if ( ClauseRefersTo(qual,rangeTableIdx) )
	    return LispNil;
	  else
	    return qual;
	}
	else if ( single_node(qual) ) {
	  elog(WARN,"Can't deal with single node in qualification");
	}
	else
	  elog(WARN,"Unknown clause type in qualification");


	return qual;
}

/*
 *	RetrieveRemoveRecursion --
 *		Removes recursive traits from the qualification of the
 *		query's parsetree.  Does not affect target list, since
 *		the query as written is assumed to work in the absence
 *		of the result relation for the first pass.  This view
 *		of the command may change in the future.
 * 
 *		Does not change the range table, so hopefully the
 *		most efficient plan will still be produced.  And
 *		hopefully no code depend on all the relations in the
 *		range table begin used.
 */

ParseTree		/* parse tree of retrieve without self-reference */
RetrieveRemoveRecursion (parseTree)
	ParseTree	parseTree;
{
	Qualification	qual;
	Qualification	newQual;

	qual = cnfify(ParseTreeGetQualification(parseTree),
		      ParseTreeGetTargetList(parseTree));
       	newQual = cnfify(QualificationYankReferences(qual,0),
			 ParseTreeGetTargetList(parseTree));

	return lispCons(ParseTreeGetRoot(parseTree),
	         lispCons(ParseTreeGetTargetList(parseTree),
		   lispCons(newQual, LispNil)));
}

LispValue		/* new index into range table */
RangeTableGetReplacementIndex(rangeTable,newRelationName,oldIndex)
     RangeTable		rangeTable;
     String		newRelationName;
     LispValue		oldIndex;
{
  String		oldName;
  int			countVarsWithOldName;
  int			currentIndex;
  List			list;
  int			countVarsWithNewName;
  int			i;

  oldName = CString(rt_relname(nth(CInteger(oldIndex)-1,rangeTable)));

  countVarsWithOldName = 0;
  currentIndex = 0;
  foreach( list, rangeTable ) {
    currentIndex += 1;
    if ( strcmp(CString(rt_relname(CAR(list))),oldName) == 0 ) {
      countVarsWithOldName += 1;
    if ( currentIndex == CInteger(oldIndex) )
      break;
    }
  }

  countVarsWithNewName = 0;
  currentIndex = 0;
  foreach( list, rangeTable ) {
    currentIndex += 1;
    if ( strcmp(CString(rt_relname(CAR(list))),newRelationName) == 0 ) {
      countVarsWithNewName += 1;
      if ( countVarsWithNewName == countVarsWithOldName ) {
	return lispInteger(currentIndex);
      }
    }
  }

  /* not found, so create enough new entries */
  for( i = 1; i <= (countVarsWithOldName - countVarsWithNewName); i++ ) {
    nappend1(rangeTable,lispCons(lispString(newRelationName),
			lispCons(lispInteger(0),
			lispCons(lispInteger(0),
			lispCons(LispNil,
			lispCons(LispNil, LispNil))))) );

  }

  return 
    lispInteger( currentIndex + countVarsWithOldName - countVarsWithNewName );
}

void		/* doesn't assume cnf form */
ClauseInstallTemps(clause,oldResultRelation,newChiefResultName,newExtraResultName,rangeTable)
     List		clause;
     LispValue		oldResultRelation;
     String		newChiefResultName;	/* main var for result */
     String		newExtraResultName;	/* others found in parse */
     RangeTable		rangeTable;		/* to be changed, if needed */
{
  int			oldChiefResultIndex;
  String		oldResultName;
  LispValue		newRangeTableIndex;
  List			list;

/* JJJ **** redo */
  oldChiefResultIndex = CInteger(oldResultRelation);
  oldResultName = CString(rt_relname(nth(oldChiefResultIndex-1,rangeTable)));

  if ( null(clause) )
    return;
  else if (IsA (clause,Var)) {
    /* may need refining */
    if ( ( newChiefResultName != NULL ) && 
	( CInteger(get_varno(clause)) == oldChiefResultIndex ) ) {
      newRangeTableIndex = RangeTableGetReplacementIndex(rangeTable,
							 newChiefResultName,
							 get_varno(clause));
      set_varno(clause, newRangeTableIndex);
      CAR(get_varid(clause)) = newRangeTableIndex;
    }
    else if ( ( newExtraResultName != NULL ) &&
	     ( strcmp(CString(rt_relname(nth(CInteger(get_varno(clause))-1,
					     rangeTable))),
		      oldResultName) == 0 ) ) {
      newRangeTableIndex = RangeTableGetReplacementIndex(rangeTable,
							 newExtraResultName,
							 get_varno(clause));
      set_varno(clause, newRangeTableIndex);
      CAR(get_varid(clause)) = newRangeTableIndex;
    }
    else {
      /* leave it alone */
    }
  }
  else if ( single_node (clause) )
    return;
  else if ( and_clause(clause) ) {
    foreach( list, get_andclauseargs(clause) ) {
      ClauseInstallTemps(CAR(list),
			 oldResultRelation,
			 newChiefResultName,
			 newExtraResultName,
			 rangeTable);
    }
    return;
  }
  else if ( or_clause(clause) ) {
    foreach( list, get_orclauseargs(clause) ) {
      ClauseInstallTemps(CAR(list),
			 oldResultRelation,
			 newChiefResultName,
			 newExtraResultName,
			 rangeTable);
    }
    return;
  }
  else if ( is_funcclause(clause) ) {
    foreach( list, get_funcargs(clause) ) {
      ClauseInstallTemps(CAR(list),
			 oldResultRelation,
			 newChiefResultName,
			 newExtraResultName,
			 rangeTable);
    }
    return;
  }
  else if ( not_clause(clause) ) {
    ClauseInstallTemps(get_notclausearg(clause),
		       oldResultRelation,
		       newChiefResultName,
		       newExtraResultName,
		       rangeTable);

    return;
  }
  else if ( is_clause(clause) ) {
    ClauseInstallTemps(get_leftop(clause),
		       oldResultRelation,
		       newChiefResultName,
		       newExtraResultName,
		       rangeTable);
    ClauseInstallTemps(get_rightop(clause),
		       oldResultRelation,
		       newChiefResultName,
		       newExtraResultName,
		       rangeTable);
    return;
  }
  else
    elog(WARN,"Can't install temporaries in this clause");
}

void
TargetListInstallTemps(targetList,
		       oldResultRelation,
		       newChiefResultName,
		       newExtraResultName,
		       rangeTable)
     TargetList		targetList;		/* to be modified */
     LispValue		oldResultRelation;
     String		newChiefResultName;	/* main var for result */
     String		newExtraResultName;	/* others found in parse */
     RangeTable		rangeTable;		/* to be changed, if needed */
{
  int			oldChiefResultIndex;
  String		oldResultName;
  LispValue		newRangeTableIndex;
  LispValue		resultNumber;
  List			list;

  oldChiefResultIndex = CInteger(oldResultRelation);
  oldResultName = CString(rt_relname(nth(oldChiefResultIndex-1,rangeTable)));

  foreach( list, targetList ) {
    resultNumber = lispInteger(get_resno(tl_resdom(CAR(list))));
    if ( CInteger(resultNumber) == oldChiefResultIndex ) {
      if ( newChiefResultName != NULL ) {
/*
	newRangeTableIndex = RangeTableGetReplacementIndex(rangeTable,
							   newChiefResultName,
							   resultNumber);
	set_resno(tl_resdom(CAR(list)), newRangeTableIndex);
*/
	/* JJJ -- ask jeff how to recurse down expressions */
      }
    }
    else if ( strcmp(CString(rt_relname(nth(CInteger(resultNumber)-1,
					    rangeTable))),
		     oldResultName) == 0 ) {
      if ( newExtraResultName != NULL ) {
/*
	newRangeTableIndex = RangeTableGetReplacementIndex(rangeTable,
							   newExtraResultName,
							   resultNumber);
	set_resno(tl_resdom(CAR(list)), newRangeTableIndex);
*/
	/* JJJ -- ask jeff how to recurse down expressions */
      }
    }
    else {
      /* leave it alone ??? */
      /* JJJ -- ask jeff how to recurse down expressions */
    }
    
  }
}

Plan	/* plan for recursive query, with recursion node at root */
RecursiveQueryPlan (parseTree)
     ParseTree	parseTree;	/* parse tree of query in question */
{
/* JJJ * current */
  ParseTree		preppedParseTree;
  TargetList		preppedTargetList;
  Qualification		preppedQualification;

  ParseTree		scratchParseTree;
  RangeTable		scratchRangeTable;	/* don't change pointer */
  Plan			scratchPlan;

  String		scratchString;

  List			initPlans;
  List			loopPlans;
  List			cleanupPlans;

  List			preppedResultRelation;
  String		preppedResultRelationName;
  LispValue		preppedCommandType;

  const String		tempRelationFakeNameA = "pg_temp_A";
  const String		tempRelationFakeNameB = "pg_temp_B";
/* ** */


	ParseTree	pureRecursiveQuery;
	ParseTree	newParseTree;
	ParseTree	workParseTree;
	LispValue	newCommandType;
	List		newResultRelation;
	List		newRoot;
	Plan		stdPlan;
	Plan	        newPlan;
	Plan	        newPlan2;
	Plan	        newPlan3;
	Plan		pureRecursivePlan;
	RecursiveMethod	recursiveMethod;
	List		checkpoints;
	String		utility;
	String		commandString;
	String		commandString2;

Jprint("RecursiveQueryPlan");

	if ( null(ParseTreeGetResultRelation(parseTree)) ) {
	        elog(WARN,
		     "RecursiveQueryPlan: retrieve* to portal not supported");
	}

 	recursiveMethod = RecursiveMethodChoose(parseTree);
	if ( recursiveMethod == RecursiveMethodNone )
		return (Plan)NULL;

Jprint("passed recur. test");

/* JJJ *** current variables *** */
  preppedParseTree = (ParseTree) lispCopy(parseTree);

Jshow("prepped parse tree",preppedParseTree);

  preppedResultRelation = ParseTreeGetResultRelation( preppedParseTree );
  preppedResultRelationName =
    ParseTreeGetResultRelationName( preppedParseTree );
  preppedTargetList =
    preprocess_targetlist(ParseTreeGetTargetList(preppedParseTree),
			  CInteger(ParseTreeGetCommandType(preppedParseTree)),
			  ParseTreeGetResultRelation(preppedParseTree),
			  ParseTreeGetRangeTable(preppedParseTree));
  ParseTreeGetTargetList(preppedParseTree) = preppedTargetList;

Jshow("prepped parse tree after tl",preppedParseTree);

  preppedQualification = cnfify(ParseTreeGetQualification(preppedParseTree));

Jshow("qual after cnf",preppedQualification);

  ParseTreeGetQualification(preppedParseTree) = preppedQualification;

Jshow("prepped parse tree after q",preppedParseTree);

  preppedCommandType = ParseTreeGetCommandType(preppedParseTree);

/* scratch stuff */
  scratchParseTree = (ParseTree) lispCopy(preppedParseTree);
  scratchRangeTable = ParseTreeGetRangeTable(scratchParseTree);
/* *** */



/* JJJ ****** check setf and == and = */

	switch (recursiveMethod) {
	  case RecursiveMethodNaive:

Jprint("Naive");

   		switch ((Command) CInteger(preppedCommandType)) {
		  case APPEND:
Jprint("Append");
/* JJJ *** DONE *** */
/* -------------------------
 *  Example :  append* ANCESTOR( PARENT.name, PARENT.father )
 *		 where PARENT.name = "Jimmy"
 *		 or ANCESTOR.ancestor = PARENT.name
 *
 *  Goes to :
 *	INIT -	append ANCESTOR( name = PARENT.name, ancestor = PARENT.father )
 *		  where PARENT.name = "Jimmy"
 *		  or ANCESTOR.ancestor = PARENT.name
 *		  
 *	LOOP -	append ANCESTOR( name = PARENT.name, ancestor = PARENT.father )
 *		  where PARENT.name = "Jimmy"
 *		  or ANCESTOR.ancestor = PARENT.name
 *		  
 *    CLEANUP -	(nothing)
 * -------------------------
 */

/* --------------
 * INIT :
 *  "append ANCESTOR( name = PARENT.name, ancestor = PARENT.father )
 *		  where PARENT.name = "Jimmy"
 *		  or ANCESTOR.ancestor = PARENT.name"
 *	requires no changes in result variables, since no temps used.
 *	It is the original query.
 * --------------
 */
			stdPlan = planner(parseTree);
                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
			checkpoints = lispCons(lispInteger(1),LispNil);
			return (Plan) make_recursive(recursiveMethod,
					preppedCommandType,
					LispNil,
					loopPlans,
					LispNil,
					checkpoints,
					lispString(preppedResultRelationName));
			break;
	  	  case REPLACE:
Jprint("replace");
/* JJJ *** DONE *** */
                        stdPlan = planner(parseTree);
                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
                        checkpoints = lispCons(lispInteger(1),LispNil);
                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        LispNil,
                                        loopPlans,
                                        LispNil,
                                        checkpoints,
					lispString(preppedResultRelationName));
                        break;
    	  	  case RETRIEVE:
			/* only 'retrieve* into' supported */
Jprint("retrieve");
			/* same as 'append', but place retrieve in init */

			newParseTree = RetrieveRemoveRecursion(parseTree);
                        stdPlan = planner(newParseTree);

			/* convert to 'append' */
			newCommandType = lispAtom("append");
/* JJJ - first check for actual resultName */
/* JJJ - need dummy id */
			newResultRelation = lispString("pg_temp_result");
			/* hope target list and qual can remain same */

			initPlans = lispCons(UtilityToList(utility),LispNil);

			/* same as APPEND */

                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
                        checkpoints = lispCons(lispInteger(1),LispNil);

			/* same as APPEND */

                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        initPlans,
                                        loopPlans,
                                        LispNil,
                                        checkpoints,
					lispString(preppedResultRelationName));
			break;
		  case DELETE:
Jprint("Delete");
/* JJJ *** DONE *** */
                        stdPlan = planner(parseTree);
                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
                        checkpoints = lispCons(lispInteger(1),LispNil);
                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        LispNil,
                                        loopPlans,
                                        LispNil,
                                        checkpoints,
					lispString(preppedResultRelationName));
			break;
    	  	  case EXECUTE:
			/* JRB -- Not a part of my master's project */
			elog(WARN,
			     "RecursiveQueryPlan: 'execute' not supported");
			break;
    	  	  default:
			elog(WARN,"RecursiveQueryPlan: command not supported");
			break;
		}
  	  case RecursiveMethodSemiNaive:
Jprint("Seminaive");
   		switch ((Command) CInteger(preppedCommandType)) {
    	 	  case APPEND:
Jprint("append");
			/** build initPlans **/
			/* make 'retrieve' */
			newCommandType = lispAtom("retrieve");
			newResultRelation = lispCons(lispAtom("into"),
					    lispCons(lispString("pg_temp_A"),
					    LispNil));
			/* should also do a "not-in <result>" check */
	              newRoot = lispCons(ParseTreeGetNumLevels(parseTree),
				 lispCons(newCommandType,
				  lispCons(newResultRelation,
				           CDR(CDR(CDR(CAR(parseTree)))))));
			newParseTree = lispCons(newRoot,CDR(parseTree));
			newPlan = planner(newParseTree);
                        initPlans = lispCons(PlanToList(newPlan),LispNil);

			/* make 'append' */
			commandString = (String) palloc(strlen(
                                           "append foo ( pg_temp_A.all )")
					   + RELATION_NAME_LENGTH );
			sprintf(commandString,"append %s ( pg_temp_A.all )",
				preppedResultRelationName);
                        initPlans = append1(initPlans,
					    StringToList(commandString));
					       
			/** build loopPlans **/

/* JJJ */		/* convert new plan to refer to tempB and tempA */
			loopPlans = lispCons(PlanToList(newPlan),LispNil);

			/* make 'append' */
			sprintf(commandString,"append %s ( pg_temp_B.all )",
				preppedResultRelationName);
                        loopPlans = append1(loopPlans,
					    StringToList(commandString));

			/* make 'destroy' */
			commandString2 = (String) palloc(strlen(
                                           "destroy pg_temp_A" + 1));
			sprintf(commandString2,"destroy pg_temp_A");
                        loopPlans = append1(loopPlans,
					    StringToList(commandString2));
/* JJJ redo */		checkpoints = lispCons(lispInteger(1),LispNil);

			/** build cleanupPlans **/
			/* make 'destroy' */
			sprintf(commandString2,"destroy pg_temp_B");
                        cleanupPlans = lispCons(StringToList(commandString2),
					     LispNil);

			return (Plan) make_recursive(recursiveMethod,
					preppedCommandType,
					initPlans,
					loopPlans,
					cleanupPlans,
					checkpoints,
					lispString(preppedResultRelationName));
			break;
	  	  case REPLACE:
Jprint("replace");
/*** JJJ - needs work ***/
/* -------------------------
 *  Example :  replace* FOO( name = P.father )
 *		 where FOO.name = P.name
 *
 *  Goes to :
 *	INIT -	retrieve into tempA( name = P.father, ... , x = FOO.x )
 *		  where FOO.name = P.name
 *		delete FOO
 *		  where FOO.name = P.name
 *		append FOO( tempA.all )
 *		  
 *	LOOP -	retrieve into tempB( name = P.father, ... , x = tempA.x )
 *		  where tempA.name = P.name
 *		delete tempA
 *		  where tempA.name = P.name
 *		delete FOO
 *		  where FOO.name = P.name
 *		append FOO( tempA.all )
 *		destroy tempB
 *
 *    CLEANUP -	destroy tempA
 * -------------------------
 */

/* --------------
 * INIT :
 *  "retrieve into tempA( name = P.father, ... , x = FOO.x )
 *	      where FOO.name = P.name"
 *	requires no changes in result variables
 * --------------
 */
			scratchParseTree =
			  (ParseTree) lispCopy(preppedParseTree);

			ParseTreeGetCommandType(scratchParseTree) =
			  lispAtom("retrieve");
			ParseTreeGetResultRelation(scratchParseTree) =
			  lispCons(lispAtom("into"),
				   lispCons(lispString(tempRelationFakeNameA),
					    LispNil));
			/* ---------
			 * We should insure that new tuple is not-in target
			 * ---------
			 */

			scratchPlan = planner(scratchParseTree);
/* JJJ following should reserve new copy */
			initPlans = lispCons(PlanToList(scratchPlan),LispNil);

/* --------------
 *  "delete FOO
 *	    where FOO.name = P.name"
 *	its result variable is same as replace*
 * --------------
 */
			ParseTreeGetCommandType(scratchParseTree) =
			  lispAtom("delete");
			ParseTreeGetResultRelation(scratchParseTree) =
			  preppedResultRelation;

			scratchPlan = planner(scratchParseTree);
/* JJJ following should reserve new copy */
			initPlans = append1(initPlans,PlanToList(scratchPlan));
			
/* --------------
 *  "append FOO ( tempA.all )"
 *	is written as a utility string
 * --------------
 */
			scratchString = (String) palloc(strlen(
                                           "append foo ( pg_temp_A.all )")
					   + RELATION_NAME_LENGTH );
			sprintf(scratchString,"append %s ( pg_temp_A.all )",
				preppedResultRelationName);

                        initPlans = append1(initPlans,
					    StringToList(scratchString));

/* --------------
 * LOOP:
 *  "retrieve into tempB( name = P.father, ... , x = tempA.x )
 *		  where tempA.name = P.name"
 *	requires changing only the extra result var's to tempA,
 *	and the minor change of result relation string to tempB
 * --------------
 */
			ParseTreeGetCommandType(scratchParseTree) = 
			  lispAtom("retrieve");
			ParseTreeGetResultRelation(scratchParseTree) =
			  lispCons(lispAtom("into"),
				   lispCons(lispString(tempRelationFakeNameB),
					    LispNil));
			/* ---------
			 * We should insure that new tuple is not-in target
			 * ---------
			 */

			ClauseInstallTemps(preppedQualification,
					   preppedResultRelation,
					   NULL,
					   tempRelationFakeNameA,
					   scratchRangeTable);

			TargetListInstallTemps(preppedTargetList,
					       preppedResultRelation,
					       NULL,
					       tempRelationFakeNameA,
					       scratchRangeTable);
							 
			scratchPlan = planner(scratchParseTree);
/* JJJ following should reserve new copy */
			loopPlans = lispCons(PlanToList(scratchPlan),LispNil);
			
/* --------------
 *  "delete tempA
 *          where tempA.name = P.name"
 *	change all references to result to be tempA
 * --------------
 */
			ParseTreeGetCommandType(scratchParseTree) =
			  lispAtom("delete");
			ParseTreeGetResultRelation(scratchParseTree) =
			  RangeTableGetReplacementIndex(scratchRangeTable,
						        tempRelationFakeNameA,
							preppedResultRelation);

			ClauseInstallTemps(preppedQualification,
					   preppedResultRelation,
					   NULL,
					   tempRelationFakeNameA,
					   scratchRangeTable);

			TargetListInstallTemps(preppedTargetList,
					       preppedResultRelation,
					       NULL,
					       tempRelationFakeNameA,
					       scratchRangeTable);
							 
			scratchPlan = planner(scratchParseTree);
			loopPlans = append1(loopPlans,PlanToList(scratchPlan));

/* --------------
 *  "delete FOO
 *	    where FOO.name = P.name"
 *	its result variable is same as replace*
 * --------------
 */
			ParseTreeGetCommandType(scratchParseTree) =
			  lispAtom("delete");
			ParseTreeGetResultRelation(scratchParseTree) =
			  preppedResultRelation;

			scratchPlan = planner(scratchParseTree);
/* JJJ following should reserve new copy */
			loopPlans = append1(loopPlans,PlanToList(scratchPlan));
			
/* --------------
 *  "append FOO ( tempA.all )"
 *	is written as a utility string
 * --------------
 */
			scratchString = (String) palloc(strlen(
                                           "append foo ( pg_temp_A.all )")
					   + RELATION_NAME_LENGTH );
			sprintf(scratchString,"append %s ( pg_temp_A.all )",
				preppedResultRelationName);

                        loopPlans = append1(loopPlans,
					    StringToList(scratchString));

/* --------------
 *  "destroy tempB"
 *	is written as a utility string
 * --------------
 */
			loopPlans = append1(loopPlans,
					    StringToList("destroy pg_temp_B"));

/* JJJ *** Remember checkpoints */
			checkpoints = lispCons(lispInteger(1),LispNil);

/* --------------
 * CLEANUP:
 *  "destroy tempA"
 * --------------
 */
			cleanupPlans = lispCons(
					 StringToList("destroy pg_temp_A"));

                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        initPlans,
                                        loopPlans,
                                        cleanupPlans,
                                        checkpoints,
					lispString(preppedResultRelationName));
                        break;
    	  	  case RETRIEVE:
			/* only 'retrieve* into' supported */
Jprint("retrieve");
			sprintf(utility,"create %s",preppedResultRelationName);

			newParseTree = RetrieveRemoveRecursion(parseTree);


/* just create, and do append */

                        stdPlan = planner(newParseTree);
			initPlans = lispCons(StringToList(utility),LispNil);
                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
                        checkpoints = lispCons(lispInteger(1),LispNil);
                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        initPlans,
                                        loopPlans,
                                        LispNil,
                                        checkpoints,
					lispString(preppedResultRelationName));
			break;
    	  	  case DELETE:
Jprint("delete");
/* change to retrieve into */

/* simple deletes */

                        stdPlan = planner(parseTree);
                        loopPlans = lispCons(PlanToList(stdPlan),LispNil);
                        checkpoints = lispCons(lispInteger(1),LispNil);
                        return (Plan) make_recursive(recursiveMethod,
                                        preppedCommandType,
                                        LispNil,
                                        loopPlans,
                                        LispNil,
                                        checkpoints,
					lispString(preppedResultRelationName));
			break;
    	  	  case EXECUTE:
			/* JRB -- Not a part of my master's project */
			elog(WARN,
			     "RecursiveQueryPlan: 'execute' not supported");
			break;
    	  	  default:
			elog(WARN,"RecursiveQueryPlan: command not supported");
			break;
            }
      }
}
