/*-------------------------------------------------------------------------
 *
 * rewriteManip.c--
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.7 1997/09/08 21:46:43 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include "postgres.h"
#include "nodes/pg_list.h"
#include "utils/elog.h"
#include "nodes/nodes.h"
#include "nodes/relation.h"
#include "nodes/primnodes.h"
#include "parser/parsetree.h"	/* for getrelid() */
#include "utils/lsyscache.h"
#include "utils/builtins.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rewriteSupport.h"
#include "rewrite/locks.h"

#include "nodes/plannodes.h"
#include "optimizer/clauses.h"

static void ResolveNew(RewriteInfo *info, List *targetlist, Node **node);



void
OffsetVarNodes(Node *node, int offset)
{
	if (node == NULL)
		return;
	switch (nodeTag(node))
	{
		case T_TargetEntry:
			{
				TargetEntry *tle = (TargetEntry *) node;

				OffsetVarNodes(tle->expr, offset);
			}
			break;
		case T_Expr:
			{
				Expr	   *expr = (Expr *) node;

				OffsetVarNodes((Node *) expr->args, offset);
			}
			break;
		case T_Var:
			{
				Var		   *var = (Var *) node;

				var->varno += offset;
				var->varnoold += offset;
			}
			break;
		case T_List:
			{
				List	   *l;

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

void
ChangeVarNodes(Node *node, int old_varno, int new_varno)
{
	if (node == NULL)
		return;
	switch (nodeTag(node))
	{
		case T_TargetEntry:
			{
				TargetEntry *tle = (TargetEntry *) node;

				ChangeVarNodes(tle->expr, old_varno, new_varno);
			}
			break;
		case T_Expr:
			{
				Expr	   *expr = (Expr *) node;

				ChangeVarNodes((Node *) expr->args, old_varno, new_varno);
			}
			break;
		case T_Var:
			{
				Var		   *var = (Var *) node;

				if (var->varno == old_varno)
				{
					var->varno = new_varno;
					var->varnoold = new_varno;
				}
			}
			break;
		case T_List:
			{
				List	   *l;

				foreach(l, (List *) node)
				{
					ChangeVarNodes(lfirst(l), old_varno, new_varno);
				}
			}
			break;
		default:
			/* ignore the others */
			break;
	}
}

void
AddQual(Query *parsetree, Node *qual)
{
	Node	   *copy,
			   *old;

	if (qual == NULL)
		return;

	copy = copyObject(qual);
	old = parsetree->qual;
	if (old == NULL)
		parsetree->qual = copy;
	else
		parsetree->qual =
			(Node *) make_andclause(makeList(parsetree->qual, copy, -1));
}

void
AddNotQual(Query *parsetree, Node *qual)
{
	Node	   *copy;

	if (qual == NULL)
		return;

	copy = (Node *) make_notclause(copyObject(qual));

	AddQual(parsetree, copy);
}

static Node *
make_null(Oid type)
{
	Const	   *c = makeNode(Const);

	c->consttype = type;
	c->constlen = get_typlen(type);
	c->constvalue = PointerGetDatum(NULL);
	c->constisnull = true;
	c->constbyval = get_typbyval(type);
	return (Node *) c;
}

void
FixResdomTypes(List *tlist)
{
	List	   *i;

	foreach(i, tlist)
	{
		TargetEntry *tle = lfirst(i);

		if (nodeTag(tle->expr) == T_Var)
		{
			Var		   *var = (Var *) tle->expr;

			tle->resdom->restype = var->vartype;
			tle->resdom->reslen = get_typlen(var->vartype);
		}
	}
}

static Node *
FindMatchingNew(List *tlist, int attno)
{
	List	   *i;

	foreach(i, tlist)
	{
		TargetEntry *tle = lfirst(i);

		if (tle->resdom->resno == attno)
		{
			return (tle->expr);
		}
	}
	return NULL;
}

static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
{
	List	   *i;

	foreach(i, tlist)
	{
		TargetEntry *tle = lfirst(i);
		char	   *resname;

		resname = tle->resdom->resname;
		if (!strcmp(e_attname, resname))
			return (tle->expr);
	}
	return NULL;
}

static void
ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr)
{
	Node	   *node = *nodePtr;

	if (node == NULL)
		return;

	switch (nodeTag(node))
	{
		case T_TargetEntry:
			ResolveNew(info, targetlist, &((TargetEntry *) node)->expr);
			break;
		case T_Expr:
			ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)));
			break;
		case T_Var:
			{
				int			this_varno = (int) ((Var *) node)->varno;
				Node	   *n;

				if (this_varno == info->new_varno)
				{
					n = FindMatchingNew(targetlist,
										((Var *) node)->varattno);
					if (n == NULL)
					{
						if (info->event == CMD_UPDATE)
						{
							((Var *) node)->varno = info->current_varno;
							((Var *) node)->varnoold = info->current_varno;
						}
						else
						{
							*nodePtr = make_null(((Var *) node)->vartype);
						}
					}
					else
					{
						*nodePtr = n;
					}
				}
				break;
			}
		case T_List:
			{
				List	   *l;

				foreach(l, (List *) node)
				{
					ResolveNew(info, targetlist, (Node **) &(lfirst(l)));
				}
				break;
			}
		default:
			/* ignore the others */
			break;
	}
}

void
FixNew(RewriteInfo *info, Query *parsetree)
{
	ResolveNew(info, parsetree->targetList,
			   (Node **) &(info->rule_action->targetList));
	ResolveNew(info, parsetree->targetList, &info->rule_action->qual);
}

static void
nodeHandleRIRAttributeRule(Node **nodePtr,
						   List *rtable,
						   List *targetlist,
						   int rt_index,
						   int attr_num,
						   int *modified,
						   int *badsql)
{
	Node	   *node = *nodePtr;

	if (node == NULL)
		return;
	switch (nodeTag(node))
	{
		case T_List:
			{
				List	   *i;

				foreach(i, (List *) node)
				{
					nodeHandleRIRAttributeRule((Node **) (&(lfirst(i))), rtable,
										  targetlist, rt_index, attr_num,
											   modified, badsql);
				}
			}
			break;
		case T_TargetEntry:
			{
				TargetEntry *tle = (TargetEntry *) node;

				nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist,
								   rt_index, attr_num, modified, badsql);
			}
			break;
		case T_Expr:
			{
				Expr	   *expr = (Expr *) node;

				nodeHandleRIRAttributeRule((Node **) (&(expr->args)), rtable,
										   targetlist, rt_index, attr_num,
										   modified, badsql);
			}
			break;
		case T_Var:
			{
				int			this_varno = (int) ((Var *) node)->varno;
				NameData	name_to_look_for;

				memset(name_to_look_for.data, 0, NAMEDATALEN);

				if (this_varno == rt_index &&
					((Var *) node)->varattno == attr_num)
				{
					if (((Var *) node)->vartype == 32)
					{			/* HACK */
						*nodePtr = make_null(((Var *) node)->vartype);
						*modified = TRUE;
						*badsql = TRUE;
						break;
					}
					else
					{
						namestrcpy(&name_to_look_for,
								(char *) get_attname(getrelid(this_varno,
															  rtable),
													 attr_num));
					}
				}
				if (name_to_look_for.data[0])
				{
					Node	   *n;

					n = FindMatchingTLEntry(targetlist, (char *) &name_to_look_for);
					if (n == NULL)
					{
						*nodePtr = make_null(((Var *) node)->vartype);
					}
					else
					{
						*nodePtr = n;
					}
					*modified = TRUE;
				}
			}
			break;
		default:
			/* ignore the others */
			break;
	}
}

/*
 * Handles 'on retrieve to relation.attribute
 *			do instead retrieve (attribute = expression) w/qual'
 */
void
HandleRIRAttributeRule(Query *parsetree,
					   List *rtable,
					   List *targetlist,
					   int rt_index,
					   int attr_num,
					   int *modified,
					   int *badsql)
{
	nodeHandleRIRAttributeRule((Node **) (&(parsetree->targetList)), rtable,
							   targetlist, rt_index, attr_num,
							   modified, badsql);
	nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
							   rt_index, attr_num, modified, badsql);
}


static void
nodeHandleViewRule(Node **nodePtr,
				   List *rtable,
				   List *targetlist,
				   int rt_index,
				   int *modified)
{
	Node	   *node = *nodePtr;

	if (node == NULL)
		return;

	switch (nodeTag(node))
	{
		case T_List:
			{
				List	   *l;

				foreach(l, (List *) node)
				{
					nodeHandleViewRule((Node **) (&(lfirst(l))),
									   rtable, targetlist,
									   rt_index, modified);
				}
			}
			break;
		case T_TargetEntry:
			{
				TargetEntry *tle = (TargetEntry *) node;

				nodeHandleViewRule(&(tle->expr), rtable, targetlist,
								   rt_index, modified);
			}
			break;
		case T_Expr:
			{
				Expr	   *expr = (Expr *) node;

				nodeHandleViewRule((Node **) (&(expr->args)),
								   rtable, targetlist,
								   rt_index, modified);
			}
			break;
		case T_Var:
			{
				Var		   *var = (Var *) node;
				int			this_varno = var->varno;
				Node	   *n;

				if (this_varno == rt_index)
				{
					n = FindMatchingTLEntry(targetlist,
										 get_attname(getrelid(this_varno,
															  rtable),
													 var->varattno));
					if (n == NULL)
					{
						*nodePtr = make_null(((Var *) node)->vartype);
					}
					else
					{
						*nodePtr = n;
					}
					*modified = TRUE;
				}
				break;
			}
		default:
			/* ignore the others */
			break;
	}
}

void
HandleViewRule(Query *parsetree,
			   List *rtable,
			   List *targetlist,
			   int rt_index,
			   int *modified)
{
	nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
					   modified);
	nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist,
					   rt_index, modified);
}
