diff options
Diffstat (limited to 'src/backend/commands/define.c')
-rw-r--r-- | src/backend/commands/define.c | 1076 |
1 files changed, 24 insertions, 1052 deletions
diff --git a/src/backend/commands/define.c b/src/backend/commands/define.c index 692fc9f957c..889ddd0f440 100644 --- a/src/backend/commands/define.c +++ b/src/backend/commands/define.c @@ -1,16 +1,15 @@ /*------------------------------------------------------------------------- * * define.c + * Support routines for various kinds of object creation. * - * These routines execute some of the CREATE statements. In an earlier - * version of Postgres, these were "define" statements. * * Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.75 2002/04/11 19:59:57 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/define.c,v 1.76 2002/04/15 05:22:03 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -28,8 +27,6 @@ * "create operator": * operators * - * Most of the parse-tree manipulation routines are defined in - * commands/manip.c. * *------------------------------------------------------------------------- */ @@ -38,42 +35,16 @@ #include <ctype.h> #include <math.h> -#include "access/heapam.h" -#include "catalog/catname.h" -#include "catalog/heap.h" -#include "catalog/namespace.h" -#include "catalog/pg_aggregate.h" -#include "catalog/pg_language.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_proc.h" -#include "catalog/pg_type.h" #include "commands/defrem.h" -#include "fmgr.h" -#include "miscadmin.h" -#include "optimizer/cost.h" -#include "parser/parse_func.h" #include "parser/parse_type.h" -#include "utils/acl.h" -#include "utils/builtins.h" -#include "utils/fmgroids.h" -#include "utils/lsyscache.h" -#include "utils/syscache.h" - - -static Oid findTypeIOFunction(List *procname, bool isOutput); -static char *defGetString(DefElem *def); -static double defGetNumeric(DefElem *def); -static List *defGetQualifiedName(DefElem *def); -static TypeName *defGetTypeName(DefElem *def); -static int defGetTypeLength(DefElem *def); - -#define DEFAULT_TYPDELIM ',' /* * Translate the input language name to lower case. + * + * Output buffer should be NAMEDATALEN long. */ -static void +void case_translate_language_name(const char *input, char *output) { int i; @@ -86,1021 +57,9 @@ case_translate_language_name(const char *input, char *output) /* - * Examine the "returns" clause returnType of the CREATE FUNCTION statement - * and return information about it as *prorettype_p and *returnsSet. - * - * This is more complex than the average typename lookup because we want to - * allow a shell type to be used, or even created if the specified return type - * doesn't exist yet. (Without this, there's no way to define the I/O procs - * for a new type.) But SQL function creation won't cope, so error out if - * the target language is SQL. - */ -static void -compute_return_type(TypeName *returnType, Oid languageOid, - Oid *prorettype_p, bool *returnsSet_p) -{ - Oid rettype; - - rettype = LookupTypeName(returnType); - - if (OidIsValid(rettype)) - { - if (!get_typisdefined(rettype)) - { - if (languageOid == SQLlanguageId) - elog(ERROR, "SQL functions cannot return shell types"); - else - elog(WARNING, "Return type \"%s\" is only a shell", - TypeNameToString(returnType)); - } - } - else - { - char *typnam = TypeNameToString(returnType); - - if (strcmp(typnam, "opaque") == 0) - rettype = InvalidOid; - else - { - Oid namespaceId; - char *typname; - - if (languageOid == SQLlanguageId) - elog(ERROR, "Type \"%s\" does not exist", typnam); - elog(WARNING, "ProcedureCreate: type %s is not yet defined", - typnam); - namespaceId = QualifiedNameGetCreationNamespace(returnType->names, - &typname); - rettype = TypeShellMake(typname, namespaceId); - if (!OidIsValid(rettype)) - elog(ERROR, "could not create type %s", typnam); - } - } - - *prorettype_p = rettype; - *returnsSet_p = returnType->setof; -} - -/* - * Interpret the argument-types list of the CREATE FUNCTION statement. - */ -static int -compute_parameter_types(List *argTypes, Oid languageOid, - Oid *parameterTypes) -{ - int parameterCount = 0; - List *x; - - MemSet(parameterTypes, 0, FUNC_MAX_ARGS * sizeof(Oid)); - foreach(x, argTypes) - { - TypeName *t = (TypeName *) lfirst(x); - Oid toid; - - if (parameterCount >= FUNC_MAX_ARGS) - elog(ERROR, "functions cannot have more than %d arguments", - FUNC_MAX_ARGS); - - toid = LookupTypeName(t); - if (OidIsValid(toid)) - { - if (!get_typisdefined(toid)) - elog(WARNING, "Argument type \"%s\" is only a shell", - TypeNameToString(t)); - } - else - { - char *typnam = TypeNameToString(t); - - if (strcmp(typnam, "opaque") == 0) - { - if (languageOid == SQLlanguageId) - elog(ERROR, "SQL functions cannot have arguments of type \"opaque\""); - toid = InvalidOid; - } - else - elog(ERROR, "Type \"%s\" does not exist", typnam); - } - - if (t->setof) - elog(ERROR, "functions cannot accept set arguments"); - - parameterTypes[parameterCount++] = toid; - } - - return parameterCount; -} - -/*------------- - * Interpret the parameters *parameters and return their contents as - * *byte_pct_p, etc. - * - * These parameters supply optional information about a function. - * All have defaults if not specified. - * - * Note: currently, only three of these parameters actually do anything: - * - * * isImplicit means the function may be used as an implicit type - * coercion. - * - * * isStrict means the function should not be called when any NULL - * inputs are present; instead a NULL result value should be assumed. - * - * * volatility tells the optimizer whether the function's result can - * be assumed to be repeatable over multiple evaluations. - * - * The other four parameters are not used anywhere. They used to be - * used in the "expensive functions" optimizer, but that's been dead code - * for a long time. - *------------ - */ -static void -compute_full_attributes(List *parameters, - int32 *byte_pct_p, int32 *perbyte_cpu_p, - int32 *percall_cpu_p, int32 *outin_ratio_p, - bool *isImplicit_p, bool *isStrict_p, - char *volatility_p) -{ - List *pl; - - /* the defaults */ - *byte_pct_p = BYTE_PCT; - *perbyte_cpu_p = PERBYTE_CPU; - *percall_cpu_p = PERCALL_CPU; - *outin_ratio_p = OUTIN_RATIO; - *isImplicit_p = false; - *isStrict_p = false; - *volatility_p = PROVOLATILE_VOLATILE; - - foreach(pl, parameters) - { - DefElem *param = (DefElem *) lfirst(pl); - - if (strcasecmp(param->defname, "implicitcoercion") == 0) - *isImplicit_p = true; - else if (strcasecmp(param->defname, "isstrict") == 0) - *isStrict_p = true; - else if (strcasecmp(param->defname, "isimmutable") == 0) - *volatility_p = PROVOLATILE_IMMUTABLE; - else if (strcasecmp(param->defname, "isstable") == 0) - *volatility_p = PROVOLATILE_STABLE; - else if (strcasecmp(param->defname, "isvolatile") == 0) - *volatility_p = PROVOLATILE_VOLATILE; - else if (strcasecmp(param->defname, "iscachable") == 0) - { - /* obsolete spelling of isImmutable */ - *volatility_p = PROVOLATILE_IMMUTABLE; - } - else if (strcasecmp(param->defname, "trusted") == 0) - { - /* - * we don't have untrusted functions any more. The 4.2 - * implementation is lousy anyway so I took it out. -ay 10/94 - */ - elog(ERROR, "untrusted function has been decommissioned."); - } - else if (strcasecmp(param->defname, "byte_pct") == 0) - *byte_pct_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "perbyte_cpu") == 0) - *perbyte_cpu_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "percall_cpu") == 0) - *percall_cpu_p = (int) defGetNumeric(param); - else if (strcasecmp(param->defname, "outin_ratio") == 0) - *outin_ratio_p = (int) defGetNumeric(param); - else - elog(WARNING, "Unrecognized function attribute '%s' ignored", - param->defname); - } -} - - -/* - * For a dynamically linked C language object, the form of the clause is - * - * AS <object file name> [, <link symbol name> ] - * - * In all other cases - * - * AS <object reference, or sql code> - * - */ - -static void -interpret_AS_clause(Oid languageOid, const char *languageName, const List *as, - char **prosrc_str_p, char **probin_str_p) -{ - Assert(as != NIL); - - if (languageOid == ClanguageId) - { - /* - * For "C" language, store the file name in probin and, when - * given, the link symbol name in prosrc. - */ - *probin_str_p = strVal(lfirst(as)); - if (lnext(as) == NULL) - *prosrc_str_p = "-"; - else - *prosrc_str_p = strVal(lsecond(as)); - } - else - { - /* Everything else wants the given string in prosrc. */ - *prosrc_str_p = strVal(lfirst(as)); - *probin_str_p = "-"; - - if (lnext(as) != NIL) - elog(ERROR, "CREATE FUNCTION: only one AS item needed for %s language", - languageName); - } -} - - - -/* - * CreateFunction - * Execute a CREATE FUNCTION utility statement. - */ -void -CreateFunction(ProcedureStmt *stmt) -{ - char *probin_str; - char *prosrc_str; - Oid prorettype; - bool returnsSet; - char languageName[NAMEDATALEN]; - Oid languageOid; - char *funcname; - Oid namespaceId; - int parameterCount; - Oid parameterTypes[FUNC_MAX_ARGS]; - int32 byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio; - bool isImplicit, - isStrict; - char volatility; - HeapTuple languageTuple; - Form_pg_language languageStruct; - - /* Convert list of names to a name and namespace */ - namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname, - &funcname); - - /* Convert language name to canonical case */ - case_translate_language_name(stmt->language, languageName); - - /* Look up the language and validate permissions */ - languageTuple = SearchSysCache(LANGNAME, - PointerGetDatum(languageName), - 0, 0, 0); - if (!HeapTupleIsValid(languageTuple)) - elog(ERROR, "language \"%s\" does not exist", languageName); - - languageOid = languageTuple->t_data->t_oid; - languageStruct = (Form_pg_language) GETSTRUCT(languageTuple); - - if (!((languageStruct->lanpltrusted - && pg_language_aclcheck(languageOid, GetUserId()) == ACLCHECK_OK) - || superuser())) - elog(ERROR, "permission denied"); - - ReleaseSysCache(languageTuple); - - /* - * Convert remaining parameters of CREATE to form wanted by - * ProcedureCreate. - */ - compute_return_type(stmt->returnType, languageOid, - &prorettype, &returnsSet); - - parameterCount = compute_parameter_types(stmt->argTypes, languageOid, - parameterTypes); - - compute_full_attributes(stmt->withClause, - &byte_pct, &perbyte_cpu, &percall_cpu, - &outin_ratio, &isImplicit, &isStrict, - &volatility); - - interpret_AS_clause(languageOid, languageName, stmt->as, - &prosrc_str, &probin_str); - - /* - * And now that we have all the parameters, and know we're permitted - * to do so, go ahead and create the function. - */ - ProcedureCreate(funcname, - namespaceId, - stmt->replace, - returnsSet, - prorettype, - languageOid, - prosrc_str, /* converted to text later */ - probin_str, /* converted to text later */ - false, /* not an aggregate */ - true, /* (obsolete "trusted") */ - isImplicit, - isStrict, - volatility, - byte_pct, - perbyte_cpu, - percall_cpu, - outin_ratio, - parameterCount, - parameterTypes); -} - - -/* - * 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(List *names, List *parameters) -{ - char *oprName; - Oid oprNamespace; - uint16 precedence = 0; /* operator precedence */ - bool canHash = false; /* operator hashes */ - bool isLeftAssociative = true; /* operator is left - * associative */ - char *functionName = NULL; /* function for operator */ - TypeName *typeName1 = NULL; /* first type name */ - TypeName *typeName2 = NULL; /* second type name */ - Oid typeId1 = InvalidOid; /* types converted to OID */ - Oid typeId2 = InvalidOid; - 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; - - /* Convert list of names to a name and namespace */ - oprNamespace = QualifiedNameGetCreationNamespace(names, &oprName); - - /* - * loop over the definition list and extract the information we need. - */ - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - if (strcasecmp(defel->defname, "leftarg") == 0) - { - typeName1 = defGetTypeName(defel); - if (typeName1->setof) - elog(ERROR, "setof type not implemented for leftarg"); - } - else if (strcasecmp(defel->defname, "rightarg") == 0) - { - typeName2 = defGetTypeName(defel); - if (typeName2->setof) - elog(ERROR, "setof type not implemented for rightarg"); - } - else if (strcasecmp(defel->defname, "procedure") == 0) - functionName = defGetString(defel); - else if (strcasecmp(defel->defname, "precedence") == 0) - { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: precedence not implemented"); - } - else if (strcasecmp(defel->defname, "associativity") == 0) - { - /* NOT IMPLEMENTED (never worked in v4.2) */ - elog(NOTICE, "CREATE OPERATOR: associativity not implemented"); - } - else if (strcasecmp(defel->defname, "commutator") == 0) - commutatorName = defGetString(defel); - else if (strcasecmp(defel->defname, "negator") == 0) - negatorName = defGetString(defel); - else if (strcasecmp(defel->defname, "restrict") == 0) - restrictionName = defGetString(defel); - else if (strcasecmp(defel->defname, "join") == 0) - joinName = defGetString(defel); - else if (strcasecmp(defel->defname, "hashes") == 0) - canHash = TRUE; - else if (strcasecmp(defel->defname, "sort1") == 0) - sortName1 = defGetString(defel); - else if (strcasecmp(defel->defname, "sort2") == 0) - sortName2 = defGetString(defel); - else - { - elog(WARNING, "DefineOperator: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (functionName == NULL) - elog(ERROR, "Define: \"procedure\" unspecified"); - - /* Transform type names to type OIDs */ - if (typeName1) - typeId1 = typenameTypeId(typeName1); - if (typeName2) - typeId2 = typenameTypeId(typeName2); - - /* - * now have OperatorCreate do all the work.. - */ - OperatorCreate(oprName, /* operator name */ - typeId1, /* left type id */ - typeId2, /* right type id */ - 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(List *names, List *parameters) -{ - char *aggName; - Oid aggNamespace; - List *transfuncName = NIL; - List *finalfuncName = NIL; - TypeName *baseType = NULL; - TypeName *transType = NULL; - char *initval = NULL; - Oid baseTypeId; - Oid transTypeId; - List *pl; - - /* Convert list of names to a name and namespace */ - aggNamespace = QualifiedNameGetCreationNamespace(names, &aggName); - - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - /* - * sfunc1, stype1, and initcond1 are accepted as obsolete - * spellings for sfunc, stype, initcond. - */ - if (strcasecmp(defel->defname, "sfunc") == 0) - transfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "sfunc1") == 0) - transfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "finalfunc") == 0) - finalfuncName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "basetype") == 0) - baseType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "stype") == 0) - transType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "stype1") == 0) - transType = defGetTypeName(defel); - else if (strcasecmp(defel->defname, "initcond") == 0) - initval = defGetString(defel); - else if (strcasecmp(defel->defname, "initcond1") == 0) - initval = defGetString(defel); - else - elog(WARNING, "DefineAggregate: attribute \"%s\" not recognized", - defel->defname); - } - - /* - * make sure we have our required definitions - */ - if (baseType == NULL) - elog(ERROR, "Define: \"basetype\" unspecified"); - if (transType == NULL) - elog(ERROR, "Define: \"stype\" unspecified"); - if (transfuncName == NIL) - elog(ERROR, "Define: \"sfunc\" unspecified"); - - /* - * Handle the aggregate's base type (input data type). This can be - * specified as 'ANY' for a data-independent transition function, such - * as COUNT(*). - */ - baseTypeId = LookupTypeName(baseType); - if (OidIsValid(baseTypeId)) - { - /* no need to allow aggregates on as-yet-undefined types */ - if (!get_typisdefined(baseTypeId)) - elog(ERROR, "Type \"%s\" is only a shell", - TypeNameToString(baseType)); - } - else - { - char *typnam = TypeNameToString(baseType); - - if (strcasecmp(typnam, "ANY") != 0) - elog(ERROR, "Type \"%s\" does not exist", typnam); - baseTypeId = InvalidOid; - } - - /* handle transtype --- no special cases here */ - transTypeId = typenameTypeId(transType); - - /* - * Most of the argument-checking is done inside of AggregateCreate - */ - AggregateCreate(aggName, /* aggregate name */ - aggNamespace, /* namespace */ - transfuncName, /* step function name */ - finalfuncName, /* final function name */ - baseTypeId, /* type of data being aggregated */ - transTypeId, /* transition data type */ - initval); /* initial condition */ -} - -/* - * DefineDomain - * Registers a new domain. - */ -void -DefineDomain(CreateDomainStmt *stmt) -{ - char *domainName; - Oid domainNamespace; - int16 internalLength; - int16 externalLength; - Oid inputProcedure; - Oid outputProcedure; - Oid receiveProcedure; - Oid sendProcedure; - bool byValue; - char delimiter; - char alignment; - char storage; - char typtype; - Datum datum; - bool isnull; - char *defaultValue = NULL; - char *defaultValueBin = NULL; - bool typNotNull = false; - Oid basetypelem; - int32 typNDims = length(stmt->typename->arrayBounds); - HeapTuple typeTup; - List *schema = stmt->constraints; - List *listptr; - - /* Convert list of names to a name and namespace */ - domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname, - &domainName); - - /* - * Domainnames, unlike typenames don't need to account for the '_' - * prefix. So they can be one character longer. - */ - if (strlen(domainName) > (NAMEDATALEN - 1)) - elog(ERROR, "CREATE DOMAIN: domain names must be %d characters or less", - NAMEDATALEN - 1); - - /* - * Look up the base type. - */ - typeTup = typenameType(stmt->typename); - - /* - * What we really don't want is domains of domains. This could cause all sorts - * of neat issues if we allow that. - * - * With testing, we may determine complex types should be allowed - */ - typtype = ((Form_pg_type) GETSTRUCT(typeTup))->typtype; - if (typtype != 'b') - elog(ERROR, "DefineDomain: %s is not a basetype", - TypeNameToString(stmt->typename)); - - /* passed by value */ - byValue = ((Form_pg_type) GETSTRUCT(typeTup))->typbyval; - - /* Required Alignment */ - alignment = ((Form_pg_type) GETSTRUCT(typeTup))->typalign; - - /* TOAST Strategy */ - storage = ((Form_pg_type) GETSTRUCT(typeTup))->typstorage; - - /* Storage Length */ - internalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typlen; - - /* External Length (unused) */ - externalLength = ((Form_pg_type) GETSTRUCT(typeTup))->typprtlen; - - /* Array element Delimiter */ - delimiter = ((Form_pg_type) GETSTRUCT(typeTup))->typdelim; - - /* I/O Functions */ - inputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typinput; - outputProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typoutput; - receiveProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typreceive; - sendProcedure = ((Form_pg_type) GETSTRUCT(typeTup))->typsend; - - /* Inherited default value */ - datum = SysCacheGetAttr(TYPEOID, typeTup, - Anum_pg_type_typdefault, &isnull); - if (!isnull) - defaultValue = DatumGetCString(DirectFunctionCall1(textout, datum)); - - /* Inherited default binary value */ - datum = SysCacheGetAttr(TYPEOID, typeTup, - Anum_pg_type_typdefaultbin, &isnull); - if (!isnull) - defaultValueBin = DatumGetCString(DirectFunctionCall1(textout, datum)); - - /* - * Pull out the typelem name of the parent OID. - * - * This is what enables us to make a domain of an array - */ - basetypelem = ((Form_pg_type) GETSTRUCT(typeTup))->typelem; - - /* - * Run through constraints manually to avoid the additional - * processing conducted by DefineRelation() and friends. - * - * Besides, we don't want any constraints to be cooked. We'll - * do that when the table is created via MergeDomainAttributes(). - */ - foreach(listptr, schema) - { - Constraint *colDef = lfirst(listptr); - bool nullDefined = false; - Node *expr; - ParseState *pstate; - - switch (colDef->contype) - { - /* - * The inherited default value may be overridden by the user - * with the DEFAULT <expr> statement. - * - * We have to search the entire constraint tree returned as we - * don't want to cook or fiddle too much. - */ - case CONSTR_DEFAULT: - /* Create a dummy ParseState for transformExpr */ - pstate = make_parsestate(NULL); - /* - * Cook the colDef->raw_expr into an expression. - * Note: Name is strictly for error message - */ - expr = cookDefault(pstate, colDef->raw_expr, - typeTup->t_data->t_oid, - stmt->typename->typmod, - domainName); - /* - * Expression must be stored as a nodeToString result, - * but we also require a valid textual representation - * (mainly to make life easier for pg_dump). - */ - defaultValue = deparse_expression(expr, - deparse_context_for(domainName, - InvalidOid), - false); - defaultValueBin = nodeToString(expr); - break; - - /* - * Find the NULL constraint. - */ - case CONSTR_NOTNULL: - if (nullDefined) { - elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); - } else { - typNotNull = true; - nullDefined = true; - } - break; - - case CONSTR_NULL: - if (nullDefined) { - elog(ERROR, "CREATE DOMAIN has conflicting NULL / NOT NULL constraint"); - } else { - typNotNull = false; - nullDefined = true; - } - break; - - case CONSTR_UNIQUE: - elog(ERROR, "CREATE DOMAIN / UNIQUE indexes not supported"); - break; - - case CONSTR_PRIMARY: - elog(ERROR, "CREATE DOMAIN / PRIMARY KEY indexes not supported"); - break; - - case CONSTR_CHECK: - elog(ERROR, "DefineDomain: CHECK Constraints not supported"); - break; - - case CONSTR_ATTR_DEFERRABLE: - case CONSTR_ATTR_NOT_DEFERRABLE: - case CONSTR_ATTR_DEFERRED: - case CONSTR_ATTR_IMMEDIATE: - elog(ERROR, "DefineDomain: DEFERRABLE, NON DEFERRABLE, DEFERRED and IMMEDIATE not supported"); - break; - - default: - elog(ERROR, "DefineDomain: unrecognized constraint node type"); - break; - } - } - - /* - * Have TypeCreate do all the real work. - */ - TypeCreate(domainName, /* type name */ - domainNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - internalLength, /* internal size */ - externalLength, /* external size */ - 'd', /* type-type (domain type) */ - delimiter, /* array element delimiter */ - inputProcedure, /* input procedure */ - outputProcedure, /* output procedure */ - receiveProcedure, /* receive procedure */ - sendProcedure, /* send procedure */ - basetypelem, /* element type ID */ - typeTup->t_data->t_oid, /* base type ID */ - defaultValue, /* default type value (text) */ - defaultValueBin, /* default type value (binary) */ - byValue, /* passed by value */ - alignment, /* required alignment */ - storage, /* TOAST strategy */ - stmt->typename->typmod, /* typeMod value */ - typNDims, /* Array dimensions for base type */ - typNotNull); /* Type NOT NULL */ - - /* - * Now we can clean up. - */ - ReleaseSysCache(typeTup); -} - -/* - * DefineType - * Registers a new type. + * Extract a string value (otherwise uninterpreted) from a DefElem. */ -void -DefineType(List *names, List *parameters) -{ - char *typeName; - Oid typeNamespace; - int16 internalLength = -1; /* int2 */ - int16 externalLength = -1; /* int2 */ - Oid elemType = InvalidOid; - List *inputName = NIL; - List *outputName = NIL; - List *sendName = NIL; - List *receiveName = NIL; - char *defaultValue = NULL; - bool byValue = false; - char delimiter = DEFAULT_TYPDELIM; - char alignment = 'i'; /* default alignment */ - char storage = 'p'; /* default TOAST storage method */ - Oid inputOid; - Oid outputOid; - Oid sendOid; - Oid receiveOid; - char *shadow_type; - List *pl; - Oid typoid; - - /* Convert list of names to a name and namespace */ - typeNamespace = QualifiedNameGetCreationNamespace(names, &typeName); - - /* - * Type names must be one character shorter than other names, allowing - * room to create the corresponding array type name with prepended - * "_". - */ - if (strlen(typeName) > (NAMEDATALEN - 2)) - elog(ERROR, "DefineType: type names must be %d characters or less", - NAMEDATALEN - 2); - - foreach(pl, parameters) - { - DefElem *defel = (DefElem *) lfirst(pl); - - if (strcasecmp(defel->defname, "internallength") == 0) - internalLength = defGetTypeLength(defel); - else if (strcasecmp(defel->defname, "externallength") == 0) - externalLength = defGetTypeLength(defel); - else if (strcasecmp(defel->defname, "input") == 0) - inputName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "output") == 0) - outputName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "send") == 0) - sendName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "receive") == 0) - receiveName = defGetQualifiedName(defel); - else if (strcasecmp(defel->defname, "delimiter") == 0) - { - char *p = defGetString(defel); - - delimiter = p[0]; - } - else if (strcasecmp(defel->defname, "element") == 0) - elemType = typenameTypeId(defGetTypeName(defel)); - else if (strcasecmp(defel->defname, "default") == 0) - defaultValue = defGetString(defel); - else if (strcasecmp(defel->defname, "passedbyvalue") == 0) - byValue = true; - else if (strcasecmp(defel->defname, "alignment") == 0) - { - char *a = defGetString(defel); - - /* - * Note: if argument was an unquoted identifier, parser will - * have applied xlateSqlType() to it, so be prepared to - * recognize translated type names as well as the nominal - * form. - */ - if (strcasecmp(a, "double") == 0) - alignment = 'd'; - else if (strcasecmp(a, "float8") == 0) - alignment = 'd'; - else if (strcasecmp(a, "int4") == 0) - alignment = 'i'; - else if (strcasecmp(a, "int2") == 0) - alignment = 's'; - else if (strcasecmp(a, "char") == 0) - alignment = 'c'; - else if (strcasecmp(a, "bpchar") == 0) - alignment = 'c'; - else - elog(ERROR, "DefineType: \"%s\" alignment not recognized", - a); - } - else if (strcasecmp(defel->defname, "storage") == 0) - { - char *a = defGetString(defel); - - if (strcasecmp(a, "plain") == 0) - storage = 'p'; - else if (strcasecmp(a, "external") == 0) - storage = 'e'; - else if (strcasecmp(a, "extended") == 0) - storage = 'x'; - else if (strcasecmp(a, "main") == 0) - storage = 'm'; - else - elog(ERROR, "DefineType: \"%s\" storage not recognized", - a); - } - else - { - elog(WARNING, "DefineType: attribute \"%s\" not recognized", - defel->defname); - } - } - - /* - * make sure we have our required definitions - */ - if (inputName == NIL) - elog(ERROR, "Define: \"input\" unspecified"); - if (outputName == NIL) - elog(ERROR, "Define: \"output\" unspecified"); - - /* Convert I/O proc names to OIDs */ - inputOid = findTypeIOFunction(inputName, false); - outputOid = findTypeIOFunction(outputName, true); - if (sendName) - sendOid = findTypeIOFunction(sendName, true); - else - sendOid = outputOid; - if (receiveName) - receiveOid = findTypeIOFunction(receiveName, false); - else - receiveOid = inputOid; - - /* - * now have TypeCreate do all the real work. - */ - typoid = - TypeCreate(typeName, /* type name */ - typeNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - internalLength, /* internal size */ - externalLength, /* external size */ - 'b', /* type-type (base type) */ - delimiter, /* array element delimiter */ - inputOid, /* input procedure */ - outputOid, /* output procedure */ - receiveOid, /* receive procedure */ - sendOid, /* send procedure */ - elemType, /* element type ID */ - InvalidOid, /* base type ID (only for domains) */ - defaultValue, /* default type value */ - NULL, /* no binary form available */ - byValue, /* passed by value */ - alignment, /* required alignment */ - storage, /* TOAST strategy */ - -1, /* typMod (Domains only) */ - 0, /* Array Dimensions of typbasetype */ - false); /* Type NOT NULL */ - - /* - * When we create a base type (as opposed to a complex type) we need - * to have an array entry for it in pg_type as well. - */ - shadow_type = makeArrayTypeName(typeName); - - /* alignment must be 'i' or 'd' for arrays */ - alignment = (alignment == 'd') ? 'd' : 'i'; - - TypeCreate(shadow_type, /* type name */ - typeNamespace, /* namespace */ - InvalidOid, /* preassigned type oid (not done here) */ - InvalidOid, /* relation oid (n/a here) */ - -1, /* internal size */ - -1, /* external size */ - 'b', /* type-type (base type) */ - DEFAULT_TYPDELIM, /* array element delimiter */ - F_ARRAY_IN, /* input procedure */ - F_ARRAY_OUT, /* output procedure */ - F_ARRAY_IN, /* receive procedure */ - F_ARRAY_OUT, /* send procedure */ - typoid, /* element type ID */ - InvalidOid, /* base type ID */ - NULL, /* never a default type value */ - NULL, /* binary default isn't sent either */ - false, /* never passed by value */ - alignment, /* see above */ - 'x', /* ARRAY is always toastable */ - -1, /* typMod (Domains only) */ - 0, /* Array dimensions of typbasetype */ - false); /* Type NOT NULL */ - - pfree(shadow_type); -} - -static Oid -findTypeIOFunction(List *procname, bool isOutput) -{ - Oid argList[FUNC_MAX_ARGS]; - int nargs; - Oid procOid; - - /* - * First look for a 1-argument func with all argtypes 0. This is - * valid for all kinds of procedure. - */ - MemSet(argList, 0, FUNC_MAX_ARGS * sizeof(Oid)); - - procOid = LookupFuncName(procname, 1, argList); - - if (!OidIsValid(procOid)) - { - /* - * Alternatively, input procedures may take 3 args (data - * value, element OID, atttypmod); the pg_proc argtype - * signature is 0,OIDOID,INT4OID. Output procedures may - * take 2 args (data value, element OID). - */ - if (isOutput) - { - /* output proc */ - nargs = 2; - argList[1] = OIDOID; - } - else - { - /* input proc */ - nargs = 3; - argList[1] = OIDOID; - argList[2] = INT4OID; - } - procOid = LookupFuncName(procname, nargs, argList); - - if (!OidIsValid(procOid)) - func_error("TypeCreate", procname, 1, argList, NULL); - } - - return procOid; -} - - -static char * +char * defGetString(DefElem *def) { if (def->arg == NULL) @@ -1133,7 +92,10 @@ defGetString(DefElem *def) return NULL; /* keep compiler quiet */ } -static double +/* + * Extract a numeric value (actually double) from a DefElem. + */ +double defGetNumeric(DefElem *def) { if (def->arg == NULL) @@ -1152,7 +114,10 @@ defGetNumeric(DefElem *def) return 0; /* keep compiler quiet */ } -static List * +/* + * Extract a possibly-qualified name (as a List of Strings) from a DefElem. + */ +List * defGetQualifiedName(DefElem *def) { if (def->arg == NULL) @@ -1172,7 +137,10 @@ defGetQualifiedName(DefElem *def) return NIL; /* keep compiler quiet */ } -static TypeName * +/* + * Extract a TypeName from a DefElem. + */ +TypeName * defGetTypeName(DefElem *def) { if (def->arg == NULL) @@ -1198,7 +166,11 @@ defGetTypeName(DefElem *def) return NULL; /* keep compiler quiet */ } -static int +/* + * Extract a type length indicator (either absolute bytes, or + * -1 for "variable") from a DefElem. + */ +int defGetTypeLength(DefElem *def) { if (def->arg == NULL) |