diff options
Diffstat (limited to 'src/backend/commands/define.c')
-rw-r--r-- | src/backend/commands/define.c | 564 |
1 files changed, 564 insertions, 0 deletions
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c new file mode 100644 index 00000000000..4ba38c793c7 --- /dev/null +++ b/src/backend/commands/define.c @@ -0,0 +1,564 @@ +/*------------------------------------------------------------------------- + * + * define.c-- + * POSTGRES "define" utility code. + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.1.1.1 1996/07/09 06:21:20 scrappy 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 char *defGetString(DefElem *def); +static int defGetTypeLength(DefElem *def); + +#define DEFAULT_TYPDELIM ',' + +/* + * DefineFunction -- + * Registers a new function. + * + */ +void +DefineFunction(ProcedureStmt *stmt, CommandDest dest) +{ + List *parameters = stmt->withClause; + char *proname = stmt->funcname; + char* probin_str; + char* prosrc_str; + char *prorettype; + char *languageName; + bool canCache; + bool trusted = TRUE; + List *argList; + int32 byte_pct, perbyte_cpu, percall_cpu, outin_ratio; + bool returnsSet; + int i; + + /* ---------------- + * figure out the language and convert it to lowercase. + * ---------------- + */ + languageName = stmt->language; + for (i = 0; i < NAMEDATALEN && languageName[i]; ++i) { + languageName[i] = tolower(languageName[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 */ + prorettype = setType->name, + returnsSet = true; + }else { + /* singleton */ + prorettype = strVal(stmt->returnType); + returnsSet = false; + } + + /* Next attributes are only defined for C functions */ + if ( strcmp(languageName, "c") == 0 || + strcmp(languageName, "internal") == 0 ) { + 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 (!strcmp(languageName, "sql")) { + canCache = false; + trusted = true; + + /* query optimizer groks sql, these are meaningless */ + perbyte_cpu = percall_cpu = 0; + byte_pct = outin_ratio = 100; + } else { + elog(WARN, "DefineFunction: language '%s' is not supported", + languageName); + } + + /* ---------------- + * handle "[ arg is (...) ]" + * XXX fix optional arg handling below + * ---------------- + */ + argList = stmt->defArgs; + + if ( strcmp(languageName, "c") == 0 || + strcmp(languageName, "internal") == 0 ) { + prosrc_str = "-"; + probin_str = stmt->as; + } else { + prosrc_str = stmt->as; + probin_str = "-"; + } + + /* C is stored uppercase in pg_language */ + if (!strcmp(languageName, "c")) { + languageName[0] = 'C'; + } + + /* ---------------- + * now have ProcedureDefine do all the work.. + * ---------------- + */ + ProcedureCreate(proname, + returnsSet, + prorettype, + 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); + +} + +/* -------------------------------- + * DefineOperator-- + * + * this function extracts all the information from the + * parameter list generated by the parser and then has + * OperatorCreate() do all the actual work. + * + * 'parameters' is a list of DefElem + * -------------------------------- + */ +void +DefineOperator(char *oprName, + List *parameters) +{ + uint16 precedence=0; /* operator precedence */ + bool canHash=false; /* operator hashes */ + bool isLeftAssociative=true; /* operator is left associative */ + char *functionName=NULL; /* function for operator */ + char *typeName1=NULL; /* first type name */ + char *typeName2=NULL; /* second type name */ + char *commutatorName=NULL; /* optional commutator operator name */ + char *negatorName=NULL; /* optional negator operator name */ + char *restrictionName=NULL; /* optional restrict. sel. procedure */ + char *joinName=NULL; /* optional join sel. procedure name */ + char *sortName1=NULL; /* optional first sort operator */ + char *sortName2=NULL; /* optional second sort operator */ + List *pl; + + /* + * 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 = defGetString(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 = defGetString(defel); + }else { + elog(WARN, "type for rightarg is malformed."); + } + } else if (!strcasecmp(defel->defname, "procedure")) { + functionName = defGetString(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 = defGetString(defel); + } else if (!strcasecmp(defel->defname, "negator")) { + negatorName = defGetString(defel); + } else if (!strcasecmp(defel->defname, "restrict")) { + restrictionName = defGetString(defel); + } else if (!strcasecmp(defel->defname, "join")) { + joinName = defGetString(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 = defGetString(defel); + } else if (!strcasecmp(defel->defname, "sort2")) { + sortName2 = defGetString(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 OperatorCreate do all the work.. + * ---------------- + */ + OperatorCreate(oprName, /* 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 */ + +} + +/* ------------------- + * DefineAggregate + * ------------------ + */ +void +DefineAggregate(char *aggName, List *parameters) + +{ + char *stepfunc1Name = NULL; + char *stepfunc2Name = NULL; + char *finalfuncName = NULL; + char *baseType = NULL; + char *stepfunc1Type = NULL; + char *stepfunc2Type = NULL; + char *init1 = NULL; + char *init2 = NULL; + List *pl; + + foreach (pl, parameters) { + DefElem *defel = (DefElem *)lfirst(pl); + + /* + * sfunc1 + */ + if (!strcasecmp(defel->defname, "sfunc1")) { + stepfunc1Name = defGetString(defel); + } else if (!strcasecmp(defel->defname, "basetype")) { + baseType = defGetString(defel); + } else if (!strcasecmp(defel->defname, "stype1")) { + stepfunc1Type = defGetString(defel); + + /* + * sfunc2 + */ + } else if (!strcasecmp(defel->defname, "sfunc2")) { + stepfunc2Name = defGetString(defel); + } else if (!strcasecmp(defel->defname, "stype2")) { + stepfunc2Type = defGetString(defel); + /* + * final + */ + } else if (!strcasecmp(defel->defname, "finalfunc")) { + finalfuncName = defGetString(defel); + /* + * initial conditions + */ + } else if (!strcasecmp(defel->defname, "initcond1")) { + init1 = defGetString(defel); + } else if (!strcasecmp(defel->defname, "initcond2")) { + init2 = defGetString(defel); + } else { + elog(NOTICE, "DefineAggregate: attribute \"%s\" not recognized", + defel->defname); + } + } + + /* + * make sure we have our required definitions + */ + if (baseType==NULL) + elog(WARN, "Define: \"basetype\" unspecified"); + if (stepfunc1Name!=NULL) { + 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 AggregateCreate + */ + AggregateCreate(aggName, /* 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 */ + init1, /* first initial condition */ + init2); /* second initial condition */ + + /* XXX free palloc'd memory */ +} + +/* + * DefineType -- + * Registers a new type. + * + */ +void +DefineType(char *typeName, List *parameters) +{ + int16 internalLength= 0; /* int2 */ + int16 externalLength= 0; /* int2 */ + char *elemName = NULL; + char *inputName = NULL; + char *outputName = NULL; + char *sendName = NULL; + char *receiveName = NULL; + char *defaultValue = NULL; /* Datum */ + bool byValue = false; + char delimiter = DEFAULT_TYPDELIM; + char *shadow_type; + List *pl; + char alignment = 'i'; /* default alignment */ + + /* + * Type names can only be 15 characters long, so that the shadow type + * can be created using the 16th character as necessary. + */ + if (strlen(typeName) >= (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 = defGetString(defel); + }else if (!strcasecmp(defel->defname, "output")) { + outputName = defGetString(defel); + }else if (!strcasecmp(defel->defname, "send")) { + sendName = defGetString(defel); + }else if (!strcasecmp(defel->defname, "delimiter")) { + char *p = defGetString(defel); + delimiter = p[0]; + }else if (!strcasecmp(defel->defname, "receive")) { + receiveName = defGetString(defel); + }else if (!strcasecmp(defel->defname, "element")) { + elemName = defGetString(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 TypeCreate do all the real work. + * ---------------- + */ + (void) TypeCreate(typeName, /* 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. + * ---------------- + */ + shadow_type = makeArrayTypeName(typeName); + + (void) TypeCreate(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 */ + "array_in", /* input procedure */ + "array_out", /* output procedure */ + "array_out", /* send procedure */ + "array_in", /* receive procedure */ + typeName, /* element type name */ + defaultValue, /* default type value */ + false, /* never passed by value */ + alignment); + + pfree(shadow_type); +} + +static char * +defGetString(DefElem *def) +{ + if (nodeTag(def->arg)!=T_String) + elog(WARN, "Define: \"%s\" = what?", def->defname); + return (strVal(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 */ + + elog(WARN, "Define: \"%s\" = what?", def->defname); + return -1; +} |