/* * Extract a string value (otherwise uninterpreted) from a DefElem. */ char * defGetString(DefElem *def) { if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a parameter", def->defname))); switch (nodeTag(def->arg)) { case T_Integer: { char *str = palloc(32); snprintf(str, 32, "%ld", (long) intVal(def->arg)); return str; } case T_Float: /* * T_Float values are kept in string form, so this type cheat * works (and doesn't risk losing precision) */ return strVal(def->arg); case T_String: return strVal(def->arg); case T_TypeName: return TypeNameToString((TypeName *) def->arg); case T_List: return NameListToString((List *) def->arg); default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg)); } return NULL; /* keep compiler quiet */ }
/* * Extract a type length indicator (either absolute bytes, or * -1 for "variable") from a DefElem. */ int defGetTypeLength(DefElem *def) { if (def->arg == NULL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires a parameter", def->defname))); switch (nodeTag(def->arg)) { case T_Integer: return intVal(def->arg); case T_Float: ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s requires an integer value", def->defname))); break; case T_String: if (pg_strcasecmp(strVal(def->arg), "variable") == 0) return -1; /* variable length */ break; case T_TypeName: /* cope if grammar chooses to believe "variable" is a typename */ if (pg_strcasecmp(TypeNameToString((TypeName *) def->arg), "variable") == 0) return -1; /* variable length */ break; case T_List: /* must be an operator name */ break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(def->arg)); } ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid argument for %s: \"%s\"", def->defname, defGetString(def)))); return 0; /* keep compiler quiet */ }
/* * Interpret the function parameter list of a CREATE FUNCTION or * CREATE AGGREGATE statement. * * Input parameters: * parameters: list of FunctionParameter structs * languageOid: OID of function language (InvalidOid if it's CREATE AGGREGATE) * is_aggregate: needed only to determine error handling * queryString: likewise, needed only for error handling * * Results are stored into output parameters. parameterTypes must always * be created, but the other arrays are set to NULL if not needed. * variadicArgType is set to the variadic array type if there's a VARIADIC * parameter (there can be only one); or to InvalidOid if not. * requiredResultType is set to InvalidOid if there are no OUT parameters, * else it is set to the OID of the implied result type. */ void interpret_function_parameter_list(List *parameters, Oid languageOid, bool is_aggregate, const char *queryString, oidvector **parameterTypes, ArrayType **allParameterTypes, ArrayType **parameterModes, ArrayType **parameterNames, List **parameterDefaults, Oid *variadicArgType, Oid *requiredResultType) { int parameterCount = list_length(parameters); Oid *inTypes; int inCount = 0; Datum *allTypes; Datum *paramModes; Datum *paramNames; int outCount = 0; int varCount = 0; bool have_names = false; bool have_defaults = false; ListCell *x; int i; ParseState *pstate; *variadicArgType = InvalidOid; /* default result */ *requiredResultType = InvalidOid; /* default result */ inTypes = (Oid *) palloc(parameterCount * sizeof(Oid)); allTypes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramModes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum)); *parameterDefaults = NIL; /* may need a pstate for parse analysis of default exprs */ pstate = make_parsestate(NULL); pstate->p_sourcetext = queryString; /* Scan the list and extract data into work arrays */ i = 0; foreach(x, parameters) { FunctionParameter *fp = (FunctionParameter *) lfirst(x); TypeName *t = fp->argType; bool isinput = false; Oid toid; Type typtup; AclResult aclresult; typtup = LookupTypeName(NULL, t, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) { /* As above, hard error if language is SQL */ if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot accept shell type %s", TypeNameToString(t)))); /* We don't allow creating aggregates on shell types either */ else if (is_aggregate) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate cannot accept shell type %s", TypeNameToString(t)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("argument type %s is only a shell", TypeNameToString(t)))); } toid = typeTypeId(typtup); ReleaseSysCache(typtup); } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s does not exist", TypeNameToString(t)))); toid = InvalidOid; /* keep compiler quiet */ } aclresult = pg_type_aclcheck(toid, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, toid); if (t->setof) { if (is_aggregate) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregates cannot accept set arguments"))); else ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("functions cannot accept set arguments"))); } /* handle input parameters */ if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE) { /* other input parameters can't follow a VARIADIC parameter */ if (varCount > 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("VARIADIC parameter must be the last input parameter"))); inTypes[inCount++] = toid; isinput = true; } /* handle output parameters */ if (fp->mode != FUNC_PARAM_IN && fp->mode != FUNC_PARAM_VARIADIC) { if (outCount == 0) /* save first output param's type */ *requiredResultType = toid; outCount++; } if (fp->mode == FUNC_PARAM_VARIADIC) { *variadicArgType = toid; varCount++; /* validate variadic parameter type */ switch (toid) { case ANYARRAYOID: case ANYOID: /* okay */ break; default: if (!OidIsValid(get_element_type(toid))) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("VARIADIC parameter must be an array"))); break; } } allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); if (fp->name && fp->name[0]) { ListCell *px; /* * As of Postgres 9.0 we disallow using the same name for two * input or two output function parameters. Depending on the * function's language, conflicting input and output names might * be bad too, but we leave it to the PL to complain if so. */ foreach(px, parameters) { FunctionParameter *prevfp = (FunctionParameter *) lfirst(px); if (prevfp == fp) break; /* pure in doesn't conflict with pure out */ if ((fp->mode == FUNC_PARAM_IN || fp->mode == FUNC_PARAM_VARIADIC) && (prevfp->mode == FUNC_PARAM_OUT || prevfp->mode == FUNC_PARAM_TABLE)) continue; if ((prevfp->mode == FUNC_PARAM_IN || prevfp->mode == FUNC_PARAM_VARIADIC) && (fp->mode == FUNC_PARAM_OUT || fp->mode == FUNC_PARAM_TABLE)) continue; if (prevfp->name && prevfp->name[0] && strcmp(prevfp->name, fp->name) == 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("parameter name \"%s\" used more than once", fp->name))); }
/* * Examine the RETURNS clause 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. (We do this here, not in the SQL-function * validator, so as not to produce a NOTICE and then an ERROR for the same * condition.) */ static void compute_return_type(TypeName *returnType, Oid languageOid, Oid *prorettype_p, bool *returnsSet_p) { Oid rettype; Type typtup; AclResult aclresult; typtup = LookupTypeName(NULL, returnType, NULL, false); if (typtup) { if (!((Form_pg_type) GETSTRUCT(typtup))->typisdefined) { if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot return shell type %s", TypeNameToString(returnType)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("return type %s is only a shell", TypeNameToString(returnType)))); } rettype = typeTypeId(typtup); ReleaseSysCache(typtup); } else { char *typnam = TypeNameToString(returnType); Oid namespaceId; AclResult aclresult; char *typname; /* * Only C-coded functions can be I/O functions. We enforce this * restriction here mainly to prevent littering the catalogs with * shell types due to simple typos in user-defined function * definitions. */ if (languageOid != INTERNALlanguageId && languageOid != ClanguageId) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" does not exist", typnam))); /* Reject if there's typmod decoration, too */ if (returnType->typmods != NIL) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("type modifier cannot be specified for shell type \"%s\"", typnam))); /* Otherwise, go ahead and make a shell type */ ereport(NOTICE, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type \"%s\" is not yet defined", typnam), errdetail("Creating a shell type definition."))); namespaceId = QualifiedNameGetCreationNamespace(returnType->names, &typname); aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK) aclcheck_error(aclresult, ACL_KIND_NAMESPACE, get_namespace_name(namespaceId)); rettype = TypeShellMake(typname, namespaceId, GetUserId()); Assert(OidIsValid(rettype)); } aclresult = pg_type_aclcheck(rettype, GetUserId(), ACL_USAGE); if (aclresult != ACLCHECK_OK) aclcheck_error_type(aclresult, rettype); *prorettype_p = rettype; *returnsSet_p = returnType->setof; }
/* * Interpret the parameter list of the CREATE FUNCTION statement. * * Results are stored into output parameters. parameterTypes must always * be created, but the other arrays are set to NULL if not needed. * requiredResultType is set to InvalidOid if there are no OUT parameters, * else it is set to the OID of the implied result type. */ static void examine_parameter_list(List *parameters, Oid languageOid, oidvector **parameterTypes, ArrayType **allParameterTypes, ArrayType **parameterModes, ArrayType **parameterNames, Oid *requiredResultType) { int parameterCount = list_length(parameters); Oid *inTypes; int inCount = 0; Datum *allTypes; Datum *paramModes; Datum *paramNames; int outCount = 0; bool have_names = false; ListCell *x; int i; *requiredResultType = InvalidOid; /* default result */ inTypes = (Oid *) palloc(parameterCount * sizeof(Oid)); allTypes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramModes = (Datum *) palloc(parameterCount * sizeof(Datum)); paramNames = (Datum *) palloc0(parameterCount * sizeof(Datum)); /* Scan the list and extract data into work arrays */ i = 0; foreach(x, parameters) { FunctionParameter *fp = (FunctionParameter *) lfirst(x); TypeName *t = fp->argType; Oid toid; toid = LookupTypeName(NULL, t); if (OidIsValid(toid)) { if (!get_typisdefined(toid)) { /* As above, hard error if language is SQL */ if (languageOid == SQLlanguageId) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("SQL function cannot accept shell type %s", TypeNameToString(t)))); else ereport(NOTICE, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("argument type %s is only a shell", TypeNameToString(t)))); } } else { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("type %s does not exist", TypeNameToString(t)))); } if (t->setof) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("functions cannot accept set arguments"))); if (fp->mode != FUNC_PARAM_OUT) inTypes[inCount++] = toid; if (fp->mode != FUNC_PARAM_IN) { if (outCount == 0) /* save first OUT param's type */ *requiredResultType = toid; outCount++; } allTypes[i] = ObjectIdGetDatum(toid); paramModes[i] = CharGetDatum(fp->mode); if (fp->name && fp->name[0]) { paramNames[i] = DirectFunctionCall1(textin, CStringGetDatum(fp->name)); have_names = true; } i++; }