/*-------------------------------------------------------------------------
 *
 * subselect.c
 *	  Planning routines for subselects and parameters.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/optimizer/plan/subselect.c,v 1.23 1999/08/22 20:14:49 tgl Exp $
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/clauses.h"
#include "optimizer/planner.h"
#include "optimizer/subselect.h"

int			PlannerQueryLevel;	/* level of current query */
List	   *PlannerInitPlan;	/* init subplans for current query */
List	   *PlannerParamVar;	/* to get Var from Param->paramid */
int			PlannerPlanId;		/* to assign unique ID to subquery plans */

/*--------------------
 * PlannerParamVar is a list of Var nodes, wherein the n'th entry
 * (n counts from 0) corresponds to Param->paramid = n.  The Var nodes
 * are ordinary except for one thing: their varlevelsup field does NOT
 * have the usual interpretation of "subplan levels out from current".
 * Instead, it contains the absolute plan level, with the outermost
 * plan being level 1 and nested plans having higher level numbers.
 * This nonstandardness is useful because we don't have to run around
 * and update the list elements when we enter or exit a subplan
 * recursion level.  But we must pay attention not to confuse this
 * meaning with the normal meaning of varlevelsup.
 *--------------------
 */


/*
 * Create a new entry in the PlannerParamVar list, and return its index.
 *
 * var contains the data to be copied, except for varlevelsup which
 * is set from the absolute level value given by varlevel.
 */
static int
_new_param(Var *var, int varlevel)
{
	Var		   *paramVar = (Var *) copyObject(var);

	paramVar->varlevelsup = varlevel;

	PlannerParamVar = lappend(PlannerParamVar, paramVar);

	return length(PlannerParamVar) - 1;
}

/*
 * Generate a Param node to replace the given Var,
 * which is expected to have varlevelsup > 0 (ie, it is not local).
 */
static Param *
_replace_var(Var *var)
{
	List	   *ppv;
	Param	   *retval;
	int			varlevel;
	int			i;

	Assert(var->varlevelsup > 0 && var->varlevelsup < PlannerQueryLevel);
	varlevel = PlannerQueryLevel - var->varlevelsup;

	/*
	 * If there's already a PlannerParamVar entry for this same Var,
	 * just use it.  NOTE: in situations involving UNION or inheritance,
	 * it is possible for the same varno/varlevel to refer to different RTEs
	 * in different parts of the parsetree, so that different fields might
	 * end up sharing the same Param number.  As long as we check the vartype
	 * as well, I believe that this sort of aliasing will cause no trouble.
	 * The correct field should get stored into the Param slot at execution
	 * in each part of the tree.
	 */
	i = 0;
	foreach(ppv, PlannerParamVar)
	{
		Var	   *pvar = lfirst(ppv);

		if (pvar->varno == var->varno &&
			pvar->varattno == var->varattno &&
			pvar->varlevelsup == varlevel &&
			pvar->vartype == var->vartype)
			break;
		i++;
	}

	if (! ppv)
	{
		/* Nope, so make a new one */
		i = _new_param(var, varlevel);
	}

	retval = makeNode(Param);
	retval->paramkind = PARAM_EXEC;
	retval->paramid = (AttrNumber) i;
	retval->paramtype = var->vartype;

	return retval;
}

static Node *
_make_subplan(SubLink *slink)
{
	SubPlan    *node = makeNode(SubPlan);
	Plan	   *plan;
	List	   *lst;
	Node	   *result;
	List	   *saved_ip = PlannerInitPlan;

	PlannerInitPlan = NULL;

	PlannerQueryLevel++;		/* we becomes child */

	node->plan = plan = union_planner((Query *) slink->subselect);

	/*
	 * Assign subPlan, extParam and locParam to plan nodes. At the moment,
	 * SS_finalize_plan doesn't handle initPlan-s and so we assigne them
	 * to the topmost plan node and take care about its extParam too.
	 */
	(void) SS_finalize_plan(plan);
	plan->initPlan = PlannerInitPlan;

	/* Create extParam list as union of InitPlan-s' lists */
	foreach(lst, PlannerInitPlan)
	{
		List	   *lp;

		foreach(lp, ((SubPlan *) lfirst(lst))->plan->extParam)
		{
			if (!intMember(lfirsti(lp), plan->extParam))
				plan->extParam = lappendi(plan->extParam, lfirsti(lp));
		}
	}

	/* and now we are parent again */
	PlannerInitPlan = saved_ip;
	PlannerQueryLevel--;

	node->plan_id = PlannerPlanId++;
	node->rtable = ((Query *) slink->subselect)->rtable;
	node->sublink = slink;
	slink->subselect = NULL;	/* cool ?! */

	/* make parParam list of params coming from current query level */
	foreach(lst, plan->extParam)
	{
		Var		   *var = nth(lfirsti(lst), PlannerParamVar);

		/* note varlevelsup is absolute level number */
		if (var->varlevelsup == PlannerQueryLevel)
			node->parParam = lappendi(node->parParam, lfirsti(lst));
	}

	/*
	 * Un-correlated or undirect correlated plans of EXISTS or EXPR types
	 * can be used as initPlans...
	 */
	if (node->parParam == NULL && slink->subLinkType == EXPR_SUBLINK)
	{
		int			i = 0;

		/* transform right side of all sublink Oper-s into Param */
		foreach(lst, slink->oper)
		{
			List	   *rside = lnext(((Expr *) lfirst(lst))->args);
			TargetEntry *te = nth(i, plan->targetlist);
			Var		   *var = makeVar(0, 0, te->resdom->restype,
									  te->resdom->restypmod, 0);
			Param	   *prm = makeNode(Param);

			prm->paramkind = PARAM_EXEC;
			prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
			prm->paramtype = var->vartype;
			lfirst(rside) = prm;
			node->setParam = lappendi(node->setParam, prm->paramid);
			pfree(var);
			i++;
		}
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		if (i > 1)
			result = (Node *) ((slink->useor) ? make_orclause(slink->oper) :
							   make_andclause(slink->oper));
		else
			result = (Node *) lfirst(slink->oper);
	}
	else if (node->parParam == NULL && slink->subLinkType == EXISTS_SUBLINK)
	{
		Var		   *var = makeVar(0, 0, BOOLOID, -1, 0);
		Param	   *prm = makeNode(Param);

		prm->paramkind = PARAM_EXEC;
		prm->paramid = (AttrNumber) _new_param(var, PlannerQueryLevel);
		prm->paramtype = var->vartype;
		node->setParam = lappendi(node->setParam, prm->paramid);
		pfree(var);
		PlannerInitPlan = lappend(PlannerInitPlan, node);
		result = (Node *) prm;
	}
	else
	{
		/* make expression of SUBPLAN type */
		Expr	   *expr = makeNode(Expr);
		List	   *args = NIL;
		int			i = 0;

		expr->typeOid = BOOLOID;
		expr->opType = SUBPLAN_EXPR;
		expr->oper = (Node *) node;

		/*
		 * Make expr->args from parParam. Left sides of sublink Oper-s are
		 * handled by optimizer directly... Also, transform right side of
		 * sublink Oper-s into Const.
		 */
		foreach(lst, node->parParam)
		{
			Var		   *var = nth(lfirsti(lst), PlannerParamVar);

			var = (Var *) copyObject(var);
			/* Must fix absolute-level varlevelsup from the
			 * PlannerParamVar entry.  But since var is at current
			 * subplan level, this is easy:
			 */
			var->varlevelsup = 0;
			args = lappend(args, var);
		}
		foreach(lst, slink->oper)
		{
			List	   *rside = lnext(((Expr *) lfirst(lst))->args);
			TargetEntry *te = nth(i, plan->targetlist);
			Const	   *con = makeConst(te->resdom->restype,
										0, 0, true, 0, 0, 0);

			lfirst(rside) = con;
			i++;
		}
		expr->args = args;
		result = (Node *) expr;
	}

	return result;
}

static List *
set_unioni(List *l1, List *l2)
{
	if (l1 == NULL)
		return l2;
	if (l2 == NULL)
		return l1;

	return nconc(l1, set_differencei(l2, l1));
}

typedef struct finalize_primnode_results {
	List	*subplans;			/* List of subplans found in expr */
	List	*paramids;			/* List of PARAM_EXEC paramids found */
} finalize_primnode_results;

static bool finalize_primnode_walker(Node *node,
									 finalize_primnode_results *results);

static void
finalize_primnode(Node *expr, finalize_primnode_results *results)
{
	results->subplans = NIL;	/* initialize */
	results->paramids = NIL;
	(void) finalize_primnode_walker(expr, results);
}

static bool
finalize_primnode_walker(Node *node,
						 finalize_primnode_results *results)
{
	if (node == NULL)
		return false;
	if (IsA(node, Param))
	{
		if (((Param *) node)->paramkind == PARAM_EXEC)
		{
			int		paramid = (int) ((Param *) node)->paramid;

			if (! intMember(paramid, results->paramids))
				results->paramids = lconsi(paramid, results->paramids);
		}
		return false;			/* no more to do here */
	}
	if (is_subplan(node))
	{
		SubPlan	   *subplan = (SubPlan *) ((Expr *) node)->oper;
		List	   *lst;

		/* Add subplan to subplans list */
		results->subplans = lappend(results->subplans, subplan);
		/* Check extParam list for params to add to paramids */
		foreach(lst, subplan->plan->extParam)
		{
			int			paramid = lfirsti(lst);
			Var		   *var = nth(paramid, PlannerParamVar);

			/* note varlevelsup is absolute level number */
			if (var->varlevelsup < PlannerQueryLevel &&
				! intMember(paramid, results->paramids))
				results->paramids = lconsi(paramid, results->paramids);
		}
		/* XXX We do NOT allow expression_tree_walker to examine the args
		 * passed to the subplan.  Is that correct???  It's what the
		 * old code did, but it seems mighty bogus...  tgl 7/14/99
		 */
		return false;			/* don't recurse into subplan args */
	}
	return expression_tree_walker(node, finalize_primnode_walker,
								  (void *) results);
}

/* Replace correlation vars (uplevel vars) with Params. */

/* XXX should replace this with use of a generalized tree rebuilder,
 * designed along the same lines as expression_tree_walker.
 * Not done yet.
 */
Node *
SS_replace_correlation_vars(Node *expr)
{
	if (expr == NULL)
		return NULL;
	if (IsA(expr, Var))
	{
		if (((Var *) expr)->varlevelsup > 0)
			expr = (Node *) _replace_var((Var *) expr);
	}
	else if (single_node(expr))
		return expr;
	else if (IsA(expr, List))
	{
		List	   *le;

		foreach(le, (List *) expr)
			lfirst(le) = SS_replace_correlation_vars((Node *) lfirst(le));
	}
	else if (IsA(expr, Expr))
	{
		/* XXX do we need to do anything special with subplans? */
		((Expr *) expr)->args = (List *)
			SS_replace_correlation_vars((Node *) ((Expr *) expr)->args);
	}
	else if (IsA(expr, Aggref))
		((Aggref *) expr)->target = SS_replace_correlation_vars(((Aggref *) expr)->target);
	else if (IsA(expr, Iter))
		((Iter *) expr)->iterexpr = SS_replace_correlation_vars(((Iter *) expr)->iterexpr);
	else if (IsA(expr, ArrayRef))
	{
		((ArrayRef *) expr)->refupperindexpr = (List *)
			SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->refupperindexpr);
		((ArrayRef *) expr)->reflowerindexpr = (List *)
			SS_replace_correlation_vars((Node *) ((ArrayRef *) expr)->reflowerindexpr);
		((ArrayRef *) expr)->refexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refexpr);
		((ArrayRef *) expr)->refassgnexpr = SS_replace_correlation_vars(((ArrayRef *) expr)->refassgnexpr);
	}
	else if (IsA(expr, CaseExpr))
	{
		CaseExpr   *caseexpr = (CaseExpr *) expr;
		List	   *le;

		foreach(le, caseexpr->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(le);
			Assert(IsA(when, CaseWhen));
			when->expr = SS_replace_correlation_vars(when->expr);
			when->result = SS_replace_correlation_vars(when->result);
		}
		/* caseexpr->arg should be null, but we'll check it anyway */
		caseexpr->arg = SS_replace_correlation_vars(caseexpr->arg);
		caseexpr->defresult = SS_replace_correlation_vars(caseexpr->defresult);
	}
	else if (IsA(expr, TargetEntry))
		((TargetEntry *) expr)->expr = SS_replace_correlation_vars(((TargetEntry *) expr)->expr);
	else if (IsA(expr, SubLink))
	{
		List	   *le;

		foreach(le, ((SubLink *) expr)->oper)	/* left sides only */
		{
			List	   *oparg = ((Expr *) lfirst(le))->args;

			lfirst(oparg) = (List *)
				SS_replace_correlation_vars((Node *) lfirst(oparg));
		}
		((SubLink *) expr)->lefthand = (List *)
			SS_replace_correlation_vars((Node *) ((SubLink *) expr)->lefthand);
	}
	else
		elog(ERROR, "SS_replace_correlation_vars: can't handle node %d",
			 nodeTag(expr));

	return expr;
}

/* Replace sublinks by subplans in the given expression */

/* XXX should replace this with use of a generalized tree rebuilder,
 * designed along the same lines as expression_tree_walker.
 * Not done yet.
 */
Node *
SS_process_sublinks(Node *expr)
{
	if (expr == NULL)
		return NULL;
	if (IsA(expr, SubLink))
	{
		expr = _make_subplan((SubLink *) expr);
	}
	else if (single_node(expr))
		return expr;
	else if (IsA(expr, List))
	{
		List	   *le;

		foreach(le, (List *) expr)
			lfirst(le) = SS_process_sublinks((Node *) lfirst(le));
	}
	else if (IsA(expr, Expr))
	{
		/* We should never see a subplan node here, since this is the
		 * routine that makes 'em in the first place.  No need to check.
		 */
		((Expr *) expr)->args = (List *)
			SS_process_sublinks((Node *) ((Expr *) expr)->args);
	}
	else if (IsA(expr, Aggref))
		((Aggref *) expr)->target = SS_process_sublinks(((Aggref *) expr)->target);
	else if (IsA(expr, Iter))
		((Iter *) expr)->iterexpr = SS_process_sublinks(((Iter *) expr)->iterexpr);
	else if (IsA(expr, ArrayRef))
	{
		((ArrayRef *) expr)->refupperindexpr = (List *)
			SS_process_sublinks((Node *) ((ArrayRef *) expr)->refupperindexpr);
		((ArrayRef *) expr)->reflowerindexpr = (List *)
			SS_process_sublinks((Node *) ((ArrayRef *) expr)->reflowerindexpr);
		((ArrayRef *) expr)->refexpr = SS_process_sublinks(((ArrayRef *) expr)->refexpr);
		((ArrayRef *) expr)->refassgnexpr = SS_process_sublinks(((ArrayRef *) expr)->refassgnexpr);
	}
	else if (IsA(expr, CaseExpr))
	{
		CaseExpr   *caseexpr = (CaseExpr *) expr;
		List	   *le;

		foreach(le, caseexpr->args)
		{
			CaseWhen   *when = (CaseWhen *) lfirst(le);
			Assert(IsA(when, CaseWhen));
			when->expr = SS_process_sublinks(when->expr);
			when->result = SS_process_sublinks(when->result);
		}
		/* caseexpr->arg should be null, but we'll check it anyway */
		caseexpr->arg = SS_process_sublinks(caseexpr->arg);
		caseexpr->defresult = SS_process_sublinks(caseexpr->defresult);
	}
	else
		elog(ERROR, "SS_process_sublinks: can't handle node %d",
			 nodeTag(expr));

	return expr;
}

List *
SS_finalize_plan(Plan *plan)
{
	List	   *extParam = NIL;
	List	   *locParam = NIL;
	finalize_primnode_results results;
	List	   *lst;

	if (plan == NULL)
		return NULL;

	/* Find params in targetlist, make sure there are no subplans there */
	finalize_primnode((Node *) plan->targetlist, &results);
	Assert(results.subplans == NIL);

	/* From here on, we invoke finalize_primnode_walker not finalize_primnode,
	 * so that results.paramids lists are automatically merged together and
	 * we don't have to do it the hard way.  But when recursing to self,
	 * we do have to merge the lists.  Oh well.
	 */
	switch (nodeTag(plan))
	{
		case T_Result:
			finalize_primnode_walker(((Result *) plan)->resconstantqual,
									 &results);
			/* results.subplans is NOT necessarily empty here ... */
			break;

		case T_Append:
			foreach(lst, ((Append *) plan)->appendplans)
				results.paramids = set_unioni(results.paramids,
								SS_finalize_plan((Plan *) lfirst(lst)));
			break;

		case T_IndexScan:
			finalize_primnode_walker((Node *) ((IndexScan *) plan)->indxqual,
									 &results);
			Assert(results.subplans == NIL);
			break;

		case T_MergeJoin:
			finalize_primnode_walker((Node *) ((MergeJoin *) plan)->mergeclauses,
									 &results);
			Assert(results.subplans == NIL);
			break;

		case T_HashJoin:
			finalize_primnode_walker((Node *) ((HashJoin *) plan)->hashclauses,
									 &results);
			Assert(results.subplans == NIL);
			break;

		case T_Hash:
			finalize_primnode_walker((Node *) ((Hash *) plan)->hashkey,
									 &results);
			Assert(results.subplans == NIL);
			break;

		case T_Agg:
			/* XXX Code used to reject subplans in Aggref args; needed?? */
			break;

		case T_SeqScan:
		case T_NestLoop:
		case T_Material:
		case T_Sort:
		case T_Unique:
		case T_Group:
			break;

		default:
			elog(ERROR, "SS_finalize_plan: node %d unsupported",
				 nodeTag(plan));
			return NULL;
	}

	finalize_primnode_walker((Node *) plan->qual, &results);
	/* subplans are OK in the qual... */

	results.paramids = set_unioni(results.paramids,
								  SS_finalize_plan(plan->lefttree));
	results.paramids = set_unioni(results.paramids,
								  SS_finalize_plan(plan->righttree));

	/* Now we have all the paramids and subplans */

	foreach(lst, results.paramids)
	{
		Var		   *var = nth(lfirsti(lst), PlannerParamVar);

		/* note varlevelsup is absolute level number */
		if (var->varlevelsup < PlannerQueryLevel)
			extParam = lappendi(extParam, lfirsti(lst));
		else if (var->varlevelsup > PlannerQueryLevel)
			elog(ERROR, "SS_finalize_plan: plan shouldn't reference subplan's variable");
		else
		{
			Assert(var->varno == 0 && var->varattno == 0);
			locParam = lappendi(locParam, lfirsti(lst));
		}
	}

	plan->extParam = extParam;
	plan->locParam = locParam;
	plan->subPlan = results.subplans;

	return results.paramids;
}

/* Construct a list of all subplans found within the given node tree */

static bool SS_pull_subplan_walker(Node *node, List **listptr);

List *
SS_pull_subplan(Node *expr)
{
	List	   *result = NIL;

	SS_pull_subplan_walker(expr, &result);
	return result;
}

static bool
SS_pull_subplan_walker(Node *node, List **listptr)
{
	if (node == NULL)
		return false;
	if (is_subplan(node))
	{
		*listptr = lappend(*listptr, ((Expr *) node)->oper);
		/* XXX original code did not examine args to subplan, is this right? */
		return false;
	}
	return expression_tree_walker(node, SS_pull_subplan_walker,
								  (void *) listptr);
}
