/*-------------------------------------------------------------------------
 *
 * define.c--
 *    POSTGRES "define" utility code.
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *    /usr/local/devel/pglite/cvs/src/backend/commands/define.c,v 1.9 1995/05/25 07:40:07 andrew Exp
 *
 * DESCRIPTION
 *    The "DefineFoo" routines take the parse tree and pick out the
 *    appropriate arguments/flags, passing the results to the
 *    corresponding "FooDefine" routines (in src/catalog) that do
 *    the actual catalog-munging.
 *
 * NOTES
 *    These things must be defined and committed in the following order:
 *	"define function":
 *		input/output, recv/send procedures
 *	"define type":
 *		type
 *	"define operator":
 *		operators
 *
 *	Most of the parse-tree manipulation routines are defined in
 *	commands/manip.c.
 *
 *-------------------------------------------------------------------------
 */
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "postgres.h"

#include "access/heapam.h"
#include "access/htup.h"
#include "utils/tqual.h"
#include "catalog/catname.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "utils/syscache.h"
#include "nodes/pg_list.h"
#include "nodes/parsenodes.h"
#include "fmgr.h"		/* for fmgr */

#include "utils/builtins.h"	/* prototype for textin() */

#include "utils/elog.h"
#include "utils/palloc.h"
#include "commands/defrem.h"
#include "optimizer/xfunc.h"
#include "tcop/dest.h"

static Name defGetName(DefElem *def);
static char *defGetString(DefElem *def);
static int  defGetInteger(DefElem *def);
static int  defGetTypeLength(DefElem *def);

#define	DEFAULT_TYPDELIM	','

/*
 * DefineFunction --
 *	Registers a new function.
 *
 * Exceptions:
 *	XXX
 */
void
DefineFunction(ProcedureStmt *stmt, CommandDest dest)
{
    List   	*parameters = stmt->withClause;
    String	proname_str = stmt->funcname;
    String	probin_str;
    String	prosrc_str;
    char16	pronameData;
    char16	prorettypeData;
    Name	languageName;
    bool	canCache;
    bool        trusted = TRUE;
    List	*argList;
    int32       byte_pct, perbyte_cpu, percall_cpu, outin_ratio;
    bool	returnsSet;
    int		i;
    
    /* ----------------
     * Note:
     * XXX	Checking of "name" validity (16 characters?) is needed.
     * ----------------
     */
    AssertArg(NameIsValid(proname_str));
    
    (void) namestrcpy(&pronameData, proname_str);
    
    /* ----------------
     * figure out the language and convert it to lowercase.
     * ----------------
     */
    languageName = makeName(stmt->language);
    for (i = 0; i < NAMEDATALEN && languageName->data[i]; ++i) {
	languageName->data[i] = tolower(languageName->data[i]);
    }
    
    /* ----------------
     * handle "returntype = X".  The function could return a singleton
     * value or a set of values.  Figure out which.
     * ----------------
     */
    if (nodeTag(stmt->returnType)==T_TypeName) {
	TypeName *setType = (TypeName *)stmt->returnType;
	/* a set of values */
	(void) namestrcpy(&prorettypeData,
			  setType->name);
	returnsSet = true;
    }else {
	/* singleton */
	(void) namestrcpy(&prorettypeData, strVal(stmt->returnType));
	returnsSet = false;
    }
    
    /* Next attributes only definable for C functions */
    if (!namestrcmp(languageName, "c")) {
	List *pl;

	/* the defaults */
	canCache = FALSE;
	byte_pct = BYTE_PCT;
	perbyte_cpu = PERBYTE_CPU;
	percall_cpu = PERCALL_CPU;
	outin_ratio = OUTIN_RATIO;

	foreach(pl, parameters) {
	    int count;
	    char *ptr;
	    ParamString *param = (ParamString*)lfirst(pl);

	    if (!strcasecmp(param->name, "isacachable")) {
		/* ----------------
		 * handle "[ iscachable ]": figure out if Postquel functions 
		 * are cacheable automagically?
		 * ----------------
		 */
		canCache = TRUE;
	    }else if (!strcasecmp(param->name, "trusted")) {
		/*
		 * we don't have untrusted functions any more. The 4.2
		 * implementation is lousy anyway so I took it out.
		 *				   	   -ay 10/94
		 */
		elog(WARN, "untrusted function has been decommissioned.");
	    }else if (!strcasecmp(param->name, "byte_pct")) {
		/*
		 ** handle expensive function parameters
		 */
		byte_pct = atoi(param->val);
	    }else if (!strcasecmp(param->name, "perbyte_cpu")) {
		if (!sscanf(param->val, "%d", &perbyte_cpu)) {
		    for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
			if (*ptr == '!') {
			    count++;
			}
		    }
		    perbyte_cpu = (int) pow(10.0, (double) count);
		}
	    }else if (!strcasecmp(param->name, "percall_cpu")) {
		if (!sscanf(param->val, "%d", &percall_cpu)) {
		    for (count = 0, ptr = param->val; *ptr != '\0'; ptr++) {
			if (*ptr == '!') {
			    count++;
			}
		    }
		    percall_cpu = (int) pow(10.0, (double) count);
		}
	    }else if (!strcasecmp(param->name, "outin_ratio")) {
		outin_ratio = atoi(param->val);
	    }
	}
    } else if (!namestrcmp(languageName, "sql")) {
	canCache = false;
	trusted = true;
	
	/* query optimizer groks postquel, these are meaningless */
	perbyte_cpu = percall_cpu = 0;
	byte_pct = outin_ratio = 100;
    } else {
	elog(WARN, "DefineFunction: language \"-.*s\" is not supported",
	     NAMEDATALEN, languageName->data);
    }
    
    /* ----------------
     * handle "[ arg is (...) ]"
     * XXX fix optional arg handling below
     * ----------------
     */
    argList = stmt->defArgs;
    
    if (!namestrcmp(languageName, "c")) {
	prosrc_str = "-";
	probin_str = stmt->as;
    } else {
	prosrc_str = stmt->as;
	probin_str = "-";
    }
    
    /* C is stored uppercase in pg_language */
    if (!namestrcmp(languageName, "c")) {
	languageName->data[0] = 'C';
    }
    
    /* ----------------
     *	now have ProcedureDefine do all the work..
     * ----------------
     */
    ProcedureDefine(&pronameData,
		    returnsSet,
		    &prorettypeData,
		    languageName,
		    prosrc_str,		/* converted to text later */
		    probin_str,		/* converted to text later */
		    canCache,
		    trusted,
		    byte_pct,
		    perbyte_cpu,
		    percall_cpu, 
		    outin_ratio,
		    argList,
		    dest);
    
    /* XXX free palloc'd memory */
}

/* --------------------------------
 * DefineOperator--
 *
 *	this function extracts all the information from the
 *	parameter list generated by the parser and then has
 *	OperatorDefine() do all the actual work.
 *
 * 'parameters' is a list of DefElem
 * --------------------------------
 */
void
DefineOperator(Name name,	/* XXX actually a String */
	       List *parameters)
{
    uint16 	precedence=0; 		/* operator precedence */
    bool	canHash=false; 		/* operator hashes */
    bool	isLeftAssociative=true;	/* operator is left associative */
    char16	oprNameData;		/* operator name */
    Name 	functionName=NULL; 	/* function for operator */
    Name 	typeName1=NULL;	 	/* first type name */
    Name 	typeName2=NULL;	 	/* second type name */
    Name 	commutatorName=NULL; 	/* optional commutator operator name */
    Name 	negatorName=NULL; 	/* optional negator operator name */
    Name 	restrictionName=NULL;	/* optional restrict. sel. procedure */
    Name 	joinName=NULL;	 	/* optional join sel. procedure name */
    Name 	sortName1=NULL;	 	/* optional first sort operator */
    Name 	sortName2=NULL;	 	/* optional second sort operator */
    List	*pl;

    /* ----------------
     *	sanity checks
     *
     * XXX	Checking of operator "name" validity
     *		(16 characters?) is needed.
     * ----------------
     */
    AssertArg(NameIsValid(name));
    
    (void) namestrcpy(&oprNameData, (char *) name);

    /*
     * loop over the definition list and extract the information we need.
     */
    foreach (pl, parameters) {
	DefElem *defel = (DefElem *)lfirst(pl);

	if (!strcasecmp(defel->defname, "leftarg")) {
	    /* see gram.y, must be setof */
	    if (nodeTag(defel->arg)==T_TypeName) 
		elog(WARN, "setof type not implemented for leftarg");

	    if (nodeTag(defel->arg)==T_String) {
		typeName1 = defGetName(defel);
	    }else {
		elog(WARN, "type for leftarg is malformed.");
	    }
	} else if (!strcasecmp(defel->defname, "rightarg")) {
	    /* see gram.y, must be setof */
	    if (nodeTag(defel->arg)==T_TypeName) 
		elog(WARN, "setof type not implemented for rightarg");

	    if (nodeTag(defel->arg)==T_String) {
		typeName2 = defGetName(defel);
	    }else {
		elog(WARN, "type for rightarg is malformed.");
	    }
	} else if (!strcasecmp(defel->defname, "procedure")) {
	    functionName = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "precedence")) {
	    /* NOT IMPLEMENTED (never worked in v4.2) */
	    elog(NOTICE, "CREATE OPERATOR: precedence not implemented");
	} else if (!strcasecmp(defel->defname, "associativity")) {
	    /* NOT IMPLEMENTED (never worked in v4.2) */
	    elog(NOTICE, "CREATE OPERATOR: associativity not implemented");
	} else if (!strcasecmp(defel->defname, "commutator")) {
	    commutatorName = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "negator")) {
	    negatorName = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "restrict")) {
	    restrictionName = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "join")) {
	    joinName = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "hashes")) {
	    canHash = TRUE;
	} else if (!strcasecmp(defel->defname, "sort1")) {
	    /* ----------------
	     * XXX ( ... [ , sort1 = oprname ] [ , sort2 = oprname ] ... )
	     * XXX is undocumented in the reference manual source as of
	     * 89/8/22.
	     * ----------------
	     */
	    sortName1 = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "sort2")) {
	    sortName2 = defGetName(defel);
	} else {
	    elog(NOTICE, "DefineOperator: attribute \"%s\" not recognized",
		 defel->defname);
	}
    }

    /*
     * make sure we have our required definitions
     */
    if (functionName==NULL) {
	elog(WARN, "Define: \"procedure\" unspecified");
    }
    
    /* ----------------
     *	now have OperatorDefine do all the work..
     * ----------------
     */
    OperatorDefine(&oprNameData,	/* operator name */
		   typeName1,		/* first type name */
		   typeName2,		/* second type name */
		   functionName,	/* function for operator */
		   precedence,		/* operator precedence */
		   isLeftAssociative,	/* operator is left associative */
		   commutatorName,	/* optional commutator operator name */
		   negatorName,		/* optional negator operator name */
		   restrictionName,	/* optional restrict. sel. procedure */
		   joinName,		/* optional join sel. procedure name */
		   canHash,		/* operator hashes */
		   sortName1,		/* optional first sort operator */
		   sortName2);		/* optional second sort operator */
    
    /* XXX free palloc'd memory */
}

/* -------------------
 *  DefineAggregate
 * ------------------
 */
void
DefineAggregate(Name name,		/* XXX actually a String */
		List *parameters)
{
    char16		aggNameData;
    Name		stepfunc1Name = (Name) NULL;
    Name		stepfunc2Name = (Name) NULL;
    Name		finalfuncName = (Name) NULL;
    Name		baseType = (Name) NULL;
    Name		stepfunc1Type = (Name) NULL;
    Name		stepfunc2Type = (Name) NULL;
    struct varlena	*primVal = (struct varlena *) NULL;
    struct varlena	*secVal = (struct varlena *) NULL;
    List		*pl;
    
    /* sanity checks...name validity */
    
    AssertArg(NameIsValid(name));
    
    (void) namestrcpy(&aggNameData, (char *) name);

    foreach (pl, parameters) {
	DefElem *defel = (DefElem *)lfirst(pl);

	/*
	 * sfunc1
	 */
	if (!strcasecmp(defel->defname, "sfunc1")) {
	    stepfunc1Name = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "basetype")) {
	    baseType = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "stype1")) {
	    stepfunc1Type = defGetName(defel);

	/*
	 * sfunc2
	 */
	} else if (!strcasecmp(defel->defname, "sfunc2")) {
	    stepfunc2Name = defGetName(defel);
	} else if (!strcasecmp(defel->defname, "stype2")) {
	    stepfunc2Type = defGetName(defel);
	/*
	 * final
	 */
	} else if (!strcasecmp(defel->defname, "finalfunc")) {
	    finalfuncName = defGetName(defel);
	/*
	 * initial conditions
	 */
	} else if (!strcasecmp(defel->defname, "initcond1")) {
	    primVal = textin(defGetString(defel));
	} else if (!strcasecmp(defel->defname, "initcond2")) {
	    secVal = textin(defGetString(defel));
	} else {
	    elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized",
		 defel->defname);
	}
    }

    /*
     * make sure we have our required definitions
     */
    if (stepfunc1Name!=NULL) {
	if (baseType==NULL) 
	    elog(WARN, "Define: \"basetype\" unspecified");
	if (stepfunc1Type==NULL) 
	    elog(WARN, "Define: \"stype1\" unspecified");
    }
    if (stepfunc2Name!=NULL) {
	if (stepfunc2Type==NULL)
	    elog(WARN, "Define: \"stype2\" unspecified");
    }

    /*
     * Most of the argument-checking is done inside of AggregateDefine
     */
    AggregateDefine(&aggNameData, 	/* aggregate name */
		    stepfunc1Name,	/* first step function name */
		    stepfunc2Name,	/* second step function name */
		    finalfuncName,	/* final function name */
		    baseType,		/* type of object being aggregated */
		    stepfunc1Type,	/* return type of first function */
		    stepfunc2Type,	/* return type of second function */
		    (char *) primVal,	/* first initial condition */
		    (char *) secVal);	/* second initial condition */
    
    /* XXX free palloc'd memory */
}

/*
 * DefineType --
 *	Registers a new type.
 *
 * Exceptions:
 *	XXX
 */
void
DefineType(Name name,		/* XXX actually a String */
	   List *parameters)
{
    int16		internalLength= 0;	/* int2 */
    int16		externalLength= 0;	/* int2 */
    char16		typeNameData;
    Name		elemName = NULL;
    Name		inputName = NULL;
    Name		outputName = NULL;
    Name		sendName = NULL;
    Name		receiveName = NULL;
    char		*defaultValue = NULL;	/* Datum */
    bool		byValue = false;
    char		delimiter = DEFAULT_TYPDELIM;
    char16		shadow_type;
    static char16	ain = { "array_in" };
    static char16	aout = { "array_out" };
    List		*pl;
    char		alignment = 'i';	/* default alignment */

    /* ----------------
     *	sanity checks
     *
     * XXX	Checking of operator "name" validity
     *		(16 characters?) is needed.
     * ----------------
     */
    AssertArg(NameIsValid(name));
    
    (void) namestrcpy(&typeNameData, (char *) name);
    
    /*
     * Type names can only be 15 characters long, so that the shadow type
     * can be created using the 16th character as necessary.
     */
    if (NameComputeLength(&typeNameData) >= (NAMEDATALEN - 1)) {
	elog(WARN, "DefineType: type names must be %d characters or less",
	     NAMEDATALEN - 1);
    }

    foreach(pl, parameters) {
	DefElem *defel = (DefElem*)lfirst(pl);

	if (!strcasecmp(defel->defname, "internallength")) {
	    internalLength = defGetTypeLength(defel);
	}else if (!strcasecmp(defel->defname, "externallength")) {
	    externalLength = defGetTypeLength(defel);
	}else if (!strcasecmp(defel->defname, "input")) {
	    inputName = defGetName(defel);
	}else if (!strcasecmp(defel->defname, "output")) {
	    outputName = defGetName(defel);
	}else if (!strcasecmp(defel->defname, "send")) {
	    sendName = defGetName(defel);
	}else if (!strcasecmp(defel->defname, "delimiter")) {
	    char *p = defGetString(defel);
	    delimiter = p[0];
	}else if (!strcasecmp(defel->defname, "receive")) {
	    receiveName = defGetName(defel);
	}else if (!strcasecmp(defel->defname, "element")) {
	    elemName = defGetName(defel);
	}else if (!strcasecmp(defel->defname, "default")) {
	    defaultValue = defGetString(defel);
	}else if (!strcasecmp(defel->defname, "passedbyvalue")) {
	    byValue = true;
	}else if (!strcasecmp(defel->defname, "alignment")) {
	    char *a = defGetString(defel);
	    if (!strcasecmp(a, "double")) {
		alignment = 'd';
	    } else if (!strcasecmp(a, "int")) {
		alignment = 'i';
	    } else {
		elog(WARN, "DefineType: \"%s\" alignment  not recognized",
		     a);
	    }
	}else {
	    elog(NOTICE, "DefineType: attribute \"%s\" not recognized",
		 defel->defname);
	}
    }

    /*
     * make sure we have our required definitions
     */
    if (inputName==NULL)
	elog(WARN, "Define: \"input\" unspecified");
    if (outputName==NULL)
	elog(WARN, "Define: \"output\" unspecified");
    
    /* ----------------
     *	now have TypeDefine do all the real work.
     * ----------------
     */
    (void) TypeDefine(&typeNameData,	/* type name */
		      InvalidOid,  /* relation oid (n/a here) */
		      internalLength,	/* internal size */
		      externalLength,	/* external size */
		      'b',		/* type-type (base type) */
		      delimiter,	/* array element delimiter */
		      inputName,	/* input procedure */
		      outputName,	/* output procedure */
		      sendName,		/* send procedure */
		      receiveName,	/* receive procedure */
		      elemName,		/* element type name */
		      defaultValue,	/* default type value */
		      byValue,		/* passed by value */
		      alignment);
    
    /* ----------------
     *  When we create a true type (as opposed to a complex type)
     *  we need to have an shadow array entry for it in pg_type as well.
     * ----------------
     */
    namestrcpy(&shadow_type, "_");
    namestrcat(&shadow_type, (char *) name);
    
    (void) TypeDefine(&shadow_type,	/* type name */
		      InvalidOid,  /* relation oid (n/a here) */
		      -1,		/* internal size */
		      -1,		/* external size */
		      'b',		/* type-type (base type) */
		      DEFAULT_TYPDELIM,	/* array element delimiter */
		      &ain,		/* input procedure */
		      &aout,		/* output procedure */
		      &aout,		/* send procedure */
		      &ain,		/* receive procedure */
		      &typeNameData,	/* element type name */
		      defaultValue,	/* default type value */
		      false,		/* never passed by value */
		      alignment);
    
    /* XXX free palloc'd memory */
}

static Name
defGetName(DefElem *def)
{
    Name name;

    if (nodeTag(def->arg)!=T_String)
	elog(WARN, "Define: \"%s\" = what?", def->defname);
    if (strlen((char*)strVal(def->arg)) > NAMEDATALEN) {
	elog(WARN, "Define: \"%s\" is too long to be an identifier",
	     strVal(def->arg));
    }
    if (!(name = (Name) palloc(sizeof(char16) + 1))) {
	elog(WARN, "Define: palloc failed!");
    }
    memset(name->data,0,NAMEDATALEN);
    (void) namestrcpy(name, strVal(def->arg));
    
    return (name);
}

static char *
defGetString(DefElem *def)
{
    if (nodeTag(def->arg)!=T_String)
	elog(WARN, "Define: \"%s\" = what?", def->defname);
    return (strVal(def->arg));
}

static int 
defGetInteger(DefElem *def)
{
    if (nodeTag(def->arg)!=T_Integer)
	elog(WARN, "Define: \"%s\" = what?", def->defname);
    return (intVal(def->arg));
}

static int 
defGetTypeLength(DefElem *def)
{
    if (nodeTag(def->arg)==T_Integer)
	return (intVal(def->arg));
    else if (nodeTag(def->arg)==T_String &&
	     !strcasecmp(strVal(def->arg),"variable"))
	return -1;	/* variable length */
    else
	elog(WARN, "Define: \"%s\" = what?", def->defname);
}
