/* 
 * $Header: /private/postgres/src/rewrite/RCS/RewriteDefine.c,v 2.18 1992/06/28 03:47:58 mao Exp $
 */

#include <stdio.h>
#include "tmp/postgres.h"

/* #include "rules/prs2locks.h"		/* temporarily */
#include "utils/rel.h"			/* for Relation stuff */
#include "access/heapam.h"		/* access methods like amopenr */
#include "utils/log.h"			/* for elog */
#include "nodes/pg_lisp.h"		/* for Lisp support */
#include "parser/parse.h"		/* lisp atom database */
#include "parser/parsetree.h"		/* for parsetree manip defines */
#include "./locks.h"			
ObjectId LastOidProcessed = InvalidObjectId;
bool prs2AttributeIsOfBasicType();

/*
 * This is too small for many rule plans, but it'll have to do for now.
 * Rule plans, etc will eventually have to be large objects.
 * 
 * should this be smaller?
 */

#define RULE_PLAN_SIZE 8192 

#define ShowParseTL(ptree)	lispDisplay(parse_targetlist(ptree))
#define ShowParseQual(ptree)	lispDisplay(parse_qualification(ptree))
#define ShowParseCmd(ptree)	lispDisplay(root_command_type \
					    parse_root(ptree))
#define ShowParseQual(ptree)	lispDisplay(parse_qualification(ptree))

/*
 *	InsertRule
 *	DESC :  takes the arguments and inserts them
 *		as attributes into the system relation "pg_prs2rule"
 *
 *	MODS :	changes the value of LastOidProcessed as a side
 *		effect of inserting the rule tuple
 *
 *	ARGS :	rulname		-	name of the rule
 *		evtype		-	one of RETRIEVE,REPLACE,DELETE,APPEND
 *		evobj		-	name of relation
 *		evslot		-	comma delimited list of slots
 *					if null => multi-attr rule
 *		evinstead	-	is an instead rule
 *		actiontree	-	parsetree(s) of rule action
 */

static char *foo = "reality is for dead birds" ;

void strcpyq(dest, source)
	char *dest, *source;
{
	char *current=source,*destp= dest;	

	for(current=source; *current; current++) {
		if (*current == '\"')  {
			*destp = '\\';
			destp++;
		}
		*destp = *current;
		destp++;
	}
	*destp = '\0';
}

OID
InsertRule ( rulname , evtype , evobj , evslot , evqual, evinstead ,
	     actiontree )
     Name 	rulname;
     int 	evtype;
     Name 	evobj;
     Name 	evslot;
     char	*evqual;
     bool	evinstead;
     char	*actiontree;

{
    static char	rulebuf[RULE_PLAN_SIZE];
	static char actionbuf[RULE_PLAN_SIZE];
	static char qualbuf[RULE_PLAN_SIZE];
    ObjectId eventrel_oid = InvalidObjectId;
    AttributeNumber evslot_index = InvalidAttributeNumber;
    Relation eventrel = NULL;
    char *is_instead = "f";
    extern void eval_as_new_xact();

    eventrel = amopenr( evobj );
    if ( eventrel == NULL ) {
	elog ( WARN, "rules cannot be defined on relations not in schema");
    }
    eventrel_oid = RelationGetRelationId(eventrel);

    /*
     * if the slotname is null, we know that this is a multi-attr
     * rule
     */
    if ( evslot == NULL )
      evslot_index = -1;
    else
      evslot_index = varattno ( eventrel,evslot );
    if ( evinstead )
	is_instead = "t";
    if ( evqual == NULL )
      evqual = "nil";
    amclose(eventrel);
    if (IsDefinedRewriteRule(rulname)) 
	elog(WARN, "Attempt to insert rule '%s' failed: already exists",
	     rulname);
	strcpyq(actionbuf,actiontree);	
	strcpyq(qualbuf, evqual);
    sprintf(rulebuf,
	    "append pg_rewrite (rulename=\"%s\",ev_type=\"%d2\"::char,\
	    ev_class=%d::oid,ev_attr= %d::int2,\
	    action= \"%s\"::text , ev_qual= \"%s\"::text, \
            fril_lb= %f , fril_ub= %f , is_instead=\"%s\"::bool )",
	    rulname,
	    AtomValueGetString(evtype),
	    eventrel_oid,
	    evslot_index,
	    actionbuf,
	    qualbuf,
	    0.0, 0.0,		/* deprecated */
	    is_instead );

    /* fprintf(stdout,"rule is \n%s\n", rulebuf ); */

    pg_eval(rulebuf, (char *) NULL, (ObjectId *) NULL, 0);

    /* elog(NOTICE,"RuleOID is : %d\n", LastOidProcessed ); */

    return ( LastOidProcessed );
}

void
ModifyActionToReplaceCurrent ( retrieve_parsetree )
	List retrieve_parsetree;
{
	List root = parse_root ( retrieve_parsetree );

	CADR(root) = lispAtom("replace");
	CADDR(root) = lispInteger(1); /* CURRENT */
}	
/*
 *	for now, event_object must be a single attribute
 */

void ValidateRule(event_type, eobj_string, eslot_string, event_qual,
		  action, is_instead,event_attype)
     char *eobj_string, *eslot_string;
     int is_instead,event_type;
     List *action,event_qual;
    ObjectId event_attype;
{
    int count;
    char *template = "(((0 retrieve nil nil 0 nil nil nil )((#S(resdom \
:resno 1 :restype %d :reslen %d :resname \"%s\" :reskey 0 :reskeyop 0 \
:resjunk 0)#S(const :consttype %d :constlen 0 :constisnull true \
:constvalue NIL :constbyval nil))) nil))";
    count = length(*action);
    if (((event_type == APPEND) || (event_type == DELETE)) && eslot_string)
	elog(WARN, "'to class.attribute' rules not allowed for this event");
    if (event_qual && !*action && is_instead)
	elog(WARN,
	     "event_quals on 'instead nothing' rules not currently supported");
/*    if (event_type == RETRIEVE && is_instead && count > 1)
	elog(WARN,
	     "multiple rule actions not supported on 'retrieve instead' rules");*/
    /* on retrieve to class.attribute do instead nothing is converted
     * to 'on retrieve to class.attribute do instead
     *        retrieve (attribute = NULL)'
     * --- this is also a terrible hack that works well -- glass*/
    if (is_instead && !*action && eslot_string && event_type == RETRIEVE) {
	char *temp_buffer = (char *) palloc(strlen(template)+80);
	sprintf(temp_buffer, template, event_attype,
		get_typlen(event_attype), eslot_string,
		event_attype);
	*action = (List) StringToPlan(temp_buffer);
	pfree(temp_buffer);
    }
}
     

DefineQueryRewrite ( args ) 
     List args;
{
    List i			= NULL;
    Name rulename 		= (Name)CString ( nth ( 0,args )) ;
    LispValue event_type	= nth ( 1 , args );
    List event_obj		= nth ( 2 , args );
    List event_qual	        = nth ( 3 , args );
    bool is_instead	        = (bool)CInteger ( nth ( 4 , args ));
    List action		= nth ( 5 , args );
    Relation event_relation 	= NULL ;
    ObjectId ruleId;
    ObjectId ev_relid		= 0;
    char locktype;
    char *eobj_string		= NULL;
    char *eslot_string		= NULL;
    int event_attno 		= 0;
    int j			= 0;		/* save index */
    int k			= 0;		/* actual lock placement */
    ObjectId event_attype	= 0;
	char *actionP, *event_qualP;
    
    extern ObjectId att_typeid();

    extern	char		*PlanToString();

    eobj_string = CString ( CAR ( event_obj));
    
    if ( CDR (event_obj) != LispNil )
      eslot_string = CString ( CADR ( event_obj));
    else
      eslot_string = NULL;
    
    event_relation = amopenr ( eobj_string );
    if ( event_relation == NULL ) {
	elog(WARN, "virtual relations not supported yet");
    }
    ev_relid = RelationGetRelationId (event_relation);
    
    if ( eslot_string == NULL ) {
      event_attno = -1;
      event_attype = -1; /* XXX - don't care */
    } else {
	event_attno = varattno ( event_relation, eslot_string );
	event_attype = att_typeid(event_relation,event_attno);
    }
    amclose(event_relation);
    /* fix bug about instead nothing */
    ValidateRule(CAtom(event_type), eobj_string,
		 eslot_string, event_qual, &action,
		 is_instead,event_attype);
    if (action == LispNil) {
	if (!is_instead) return;	/* doesn't do anything */

	actionP = PlanToString(event_qual);

	if (strlen(actionP) > RULE_PLAN_SIZE)
		elog(WARN, "DefineQueryRewrite: rule action too long.");

	ruleId = InsertRule ( rulename, 
				CAtom(event_type),
				(Name)eobj_string,
				(Name)eslot_string,
				actionP,
				1,
				"nil ");
	locktype = PutRelationLocks ( ruleId,
					ev_relid,
					event_attno,
					CAtom(event_type),
					1);
	prs2AddRelationLevelLock(ruleId,locktype,
				 ev_relid,event_attno);
    } else {

	printf("# number of actions = %d\n", length(action));
	/*
	 * I don't use the some of the more interesting LockTypes...so
	 * PutRelationLocks() has suddenly got much dumber -- glass
	 */

	event_qualP = PlanToString(event_qual);
	actionP = PlanToString(action);

	if (strlen(event_qualP) > RULE_PLAN_SIZE)
		elog(WARN, "DefineQueryRewrite: event qual plan string too big.");

	if (strlen(actionP) > RULE_PLAN_SIZE)
		elog(WARN, "DefineQueryRewrite: action plan string too big.");

	ruleId = InsertRule ( rulename, 
				CAtom(event_type),
				(Name)eobj_string,
				(Name)eslot_string,
				event_qualP,
				is_instead,
				actionP);

	locktype = PutRelationLocks ( ruleId,
					ev_relid,
					event_attno,
					CAtom(event_type),
					is_instead);
	/* what is the max size of type text? XXX -- glass */
	j = length(action);
	if ( j > 15 )
	    elog(WARN,"max # of actions exceeded"); 
	prs2AddRelationLevelLock(ruleId,locktype,
				 ev_relid,event_attno);
    }
}

ShowRuleAction(ruleaction)
     LispValue ruleaction;
{
    if ( ! lispNullp (ruleaction) ) {
	if ( atom(CAR(ruleaction)) ) {
	    printf("Utility Actions are not supported yet");
	} else {
	    switch ( root_command_type (parse_root ( ruleaction ) )) {
	      case RETRIEVE:
		printf("\nAction Type: retrieve\n");
		printf("Available attributes :\n");
		break;
	      case REPLACE: 
		printf("\nAction Type: retrieve\n");
		printf("\nAction Targetlist :\n");
	      case DELETE: 
		printf("\nAction Type: delete\n");
		printf("\nAction Targetlist :\n");
	      case APPEND: 
		printf("\nAction Type: append\n");
		printf("\nAction Targetlist :\n");
	      case EXECUTE:
		printf("\nAction Type: execute\n");
		printf("\nAction Targetlist :\n");
	      default:
		printf("unknown action type\n");
	    }
	    ShowParseTL(ruleaction);
	    printf("\nAdditional Qualifications :\n");
	    ShowParseQual(ruleaction);
	}
    } else {
	printf (" NULL command ");
    }
}





