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; char *typname;
ObjectAddress address;
/* *OnlyC-codedfunctionscanbeI/Ofunctions.Weenforcethis *restrictionheremainlytopreventlitteringthecatalogswith *shelltypesduetosimpletyposinuser-definedfunction *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 = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(),
ACL_CREATE); if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
get_namespace_name(namespaceId));
address = TypeShellMake(typname, namespaceId, GetUserId());
rettype = address.objectId;
Assert(OidIsValid(rettype));
}
/* *InterpretthefunctionparameterlistofaCREATEFUNCTION, *CREATEPROCEDURE,orCREATEAGGREGATEstatement. * *Inputparameters: *parameters:listofFunctionParameterstructs *languageOid:OIDoffunctionlanguage(InvalidOidifit'sCREATEAGGREGATE) *objtype:identifiestypeofobjectbeingcreated * *Resultsarestoredintooutputparameters.parameterTypesmustalways *becreated,buttheotherarrays/listscanbeNULLpointersifnotneeded. *variadicArgTypeissettothevariadicarraytypeifthere'saVARIADIC *parameter(therecanbeonlyone);ortoInvalidOidifnot. *requiredResultTypeissettoInvalidOidiftherearenoOUTparameters, *elseitissettotheOIDoftheimpliedresulttype.
*/ void
interpret_function_parameter_list(ParseState *pstate,
List *parameters,
Oid languageOid,
ObjectType objtype,
oidvector **parameterTypes,
List **parameterTypes_list,
ArrayType **allParameterTypes,
ArrayType **parameterModes,
ArrayType **parameterNames,
List **inParameterNames_list,
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;
*variadicArgType = InvalidOid; /* default result */
*requiredResultType = InvalidOid; /* default result */
/* Scan the list and extract data into work arrays */
i = 0;
foreach(x, parameters)
{
FunctionParameter *fp = (FunctionParameter *) lfirst(x); TypeName *t = fp->argType;
FunctionParameterMode fpmode = fp->mode; bool isinput = false;
Oid toid;
Type typtup;
AclResult aclresult;
/* For our purposes here, a defaulted mode spec is identical to IN */ if (fpmode == FUNC_PARAM_DEFAULT)
fpmode = FUNC_PARAM_IN;
typtup = LookupTypeName(pstate, 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)),
parser_errposition(pstate, t->location))); /* We don't allow creating aggregates on shell types either */ elseif (objtype == OBJECT_AGGREGATE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregate cannot accept shell type %s",
TypeNameToString(t)),
parser_errposition(pstate, t->location))); else
ereport(NOTICE,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("argument type %s is only a shell",
TypeNameToString(t)),
parser_errposition(pstate, t->location)));
}
toid = typeTypeId(typtup);
ReleaseSysCache(typtup);
} else
{
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("type %s does not exist",
TypeNameToString(t)),
parser_errposition(pstate, t->location)));
toid = InvalidOid; /* keep compiler quiet */
}
if (t->setof)
{ if (objtype == OBJECT_AGGREGATE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregates cannot accept set arguments"),
parser_errposition(pstate, fp->location))); elseif (objtype == OBJECT_PROCEDURE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("procedures cannot accept set arguments"),
parser_errposition(pstate, fp->location))); else
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("functions cannot accept set arguments"),
parser_errposition(pstate, fp->location)));
}
/* handle input parameters */ if (fpmode != FUNC_PARAM_OUT && fpmode != 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"),
parser_errposition(pstate, fp->location)));
inTypes[inCount++] = toid;
isinput = true; if (parameterTypes_list)
*parameterTypes_list = lappend_oid(*parameterTypes_list, toid);
}
/* handle output parameters */ if (fpmode != FUNC_PARAM_IN && fpmode != FUNC_PARAM_VARIADIC)
{ if (objtype == OBJECT_PROCEDURE)
{ /* *WedisallowOUT-after-VARIADIConlyforprocedures.While *suchacasecausesnoconfusioninordinaryfunctioncalls, *itwouldcauseconfusioninaCALLstatement.
*/ if (varCount > 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("VARIADIC parameter must be the last parameter"),
parser_errposition(pstate, fp->location))); /* Procedures with output parameters always return RECORD */
*requiredResultType = RECORDOID;
} elseif (outCount == 0) /* save first output param's type */
*requiredResultType = toid;
outCount++;
}
if (fpmode == FUNC_PARAM_VARIADIC)
{
*variadicArgType = toid;
varCount++; /* validate variadic parameter type */ switch (toid)
{ case ANYARRAYOID: case ANYCOMPATIBLEARRAYOID: 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"),
parser_errposition(pstate, fp->location))); break;
}
}
if (inParameterNames_list)
*inParameterNames_list = lappend(*inParameterNames_list, makeString(fp->name ? fp->name : pstrdup("")));
if (fp->defexpr)
{
Node *def;
if (!isinput)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only input parameters can have default values"),
parser_errposition(pstate, fp->location)));
*parameterDefaults = lappend(*parameterDefaults, def);
have_defaults = true;
} else
{ if (isinput && have_defaults)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("input parameters after one with a default value must also have defaults"),
parser_errposition(pstate, fp->location)));
/* *Forprocedures,wealsocan'tallowOUTparametersafterone *withadefault,becausethesamesortofconfusionarisesina *CALLstatement.
*/ if (objtype == OBJECT_PROCEDURE && have_defaults)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("procedure OUT parameters cannot appear after one with a default value"),
parser_errposition(pstate, fp->location)));
}
i++;
}
/* Now construct the proper outputs as needed */
*parameterTypes = buildoidvector(inTypes, inCount);
procOid = LookupFuncName(procName, 1, argList, true); if (!OidIsValid(procOid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_FUNCTION),
errmsg("function %s does not exist",
func_signature_string(procName, 1, NIL, argList))));
if (get_func_rettype(procOid) != INTERNALOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("support function %s must return type %s",
NameListToString(procName), "internal")));
/* *SomedaywemightwantanACLcheckhere;butfornow,weinsistthat *youbesuperusertospecifyasupportfunction,soprivilegeonthe *supportfunctionismoot.
*/ if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to specify a support function")));
if (as_item)
*as = (List *) as_item->arg; if (language_item)
*language = strVal(language_item->arg); if (transform_item)
*transform = transform_item->arg; if (windowfunc_item)
*windowfunc_p = boolVal(windowfunc_item->arg); if (volatility_item)
*volatility_p = interpret_func_volatility(volatility_item); if (strict_item)
*strict_p = boolVal(strict_item->arg); if (security_item)
*security_definer = boolVal(security_item->arg); if (leakproof_item)
*leakproof_p = boolVal(leakproof_item->arg); if (set_items)
*proconfig = update_proconfig_value(NULL, set_items); if (cost_item)
{
*procost = defGetNumeric(cost_item); if (*procost <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("COST must be positive")));
} if (rows_item)
{
*prorows = defGetNumeric(rows_item); if (*prorows <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS must be positive")));
} if (support_item)
*prosupport = interpret_func_support(support_item); if (parallel_item)
*parallel_p = interpret_func_parallel(parallel_item);
}
/* *ForadynamicallylinkedClanguageobject,theformoftheclauseis * *AS<objectfilename>[,<linksymbolname>] * *Inallothercases * *AS<objectreference,orsqlcode>
*/ staticvoid
interpret_AS_clause(Oid languageOid, constchar *languageName, char *funcname, List *as, Node *sql_body_in,
List *parameterTypes, List *inParameterNames, char **prosrc_str_p, char **probin_str_p,
Node **sql_body_out, constchar *queryString)
{ if (!sql_body_in && !as)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("no function body specified")));
if (sql_body_in && as)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("duplicate function body specified")));
if (sql_body_in && languageOid != SQLlanguageId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("inline SQL function body only valid for language SQL")));
pinfo->argtypes[i] = list_nth_oid(parameterTypes, i); if (IsPolymorphicType(pinfo->argtypes[i]))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("SQL function with unquoted function body cannot have polymorphic arguments")));
/* But we definitely don't need probin. */
*probin_str_p = NULL;
} else
{ /* Everything else wants the given string in prosrc. */
*prosrc_str_p = strVal(linitial(as));
*probin_str_p = NULL;
if (list_length(as) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only one AS item needed for language \"%s\"",
languageName)));
/* *CreateFunction *ExecuteaCREATEFUNCTION(orCREATEPROCEDURE)utilitystatement.
*/
ObjectAddress
CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
{ char *probin_str; char *prosrc_str;
Node *prosqlbody;
Oid prorettype; bool returnsSet; char *language;
Oid languageOid;
Oid languageValidator;
Node *transformDefElem = NULL; char *funcname;
Oid namespaceId;
AclResult aclresult;
oidvector *parameterTypes;
List *parameterTypes_list = NIL;
ArrayType *allParameterTypes;
ArrayType *parameterModes;
ArrayType *parameterNames;
List *inParameterNames_list = NIL;
List *parameterDefaults;
Oid variadicArgType;
List *trftypes_list = NIL;
List *trfoids_list = NIL;
ArrayType *trftypes;
Oid requiredResultType; bool isWindowFunc,
isStrict,
security,
isLeakProof; char volatility;
ArrayType *proconfig;
float4 procost;
float4 prorows;
Oid prosupport;
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause; char parallel;
/* Convert list of names to a name and namespace */
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
&funcname);
/* Check we have creation rights in target namespace */
aclresult = object_aclcheck(NamespaceRelationId, namespaceId, GetUserId(), ACL_CREATE); if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_SCHEMA,
get_namespace_name(namespaceId));
/* Set default attributes */
as_clause = NIL;
language = NULL;
isWindowFunc = false;
isStrict = false;
security = false;
isLeakProof = false;
volatility = PROVOLATILE_VOLATILE;
proconfig = NULL;
procost = -1; /* indicates not set */
prorows = -1; /* indicates not set */
prosupport = InvalidOid;
parallel = PROPARALLEL_UNSAFE;
if (!language)
{ if (stmt->sql_body)
language = "sql"; else
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("no language specified")));
}
/* Look up the language and validate permissions */
languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language)); if (!HeapTupleIsValid(languageTuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", language),
(extension_file_exists(language) ?
errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
/* *SetdefaultvaluesforCOSTandROWSdependingonotherparameters; *rejectROWSifit'snotreturnsSet.NB:pg_dumpknowsthesedefault *values,keepitinsyncifyouchangethem.
*/ if (procost < 0)
{ /* SQL and PL-language functions are assumed more expensive */ if (languageOid == INTERNALlanguageId ||
languageOid == ClanguageId)
procost = 1; else
procost = 100;
} if (prorows < 0)
{ if (returnsSet)
prorows = 1000; else
prorows = 0; /* dummy value if not returnsSet */
} elseif (!returnsSet)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS is not applicable when function does not return a set")));
/* *Andnowthatwehavealltheparameters,andknowwe'repermittedtodo *so,goaheadandcreatethefunction.
*/ return ProcedureCreate(funcname,
namespaceId,
stmt->replace,
returnsSet,
prorettype,
GetUserId(),
languageOid,
languageValidator,
prosrc_str, /* converted to text later */
probin_str, /* converted to text later */
prosqlbody,
stmt->is_procedure ? PROKIND_PROCEDURE : (isWindowFunc ? PROKIND_WINDOW : PROKIND_FUNCTION),
security,
isLeakProof,
isStrict,
volatility,
parallel,
parameterTypes,
PointerGetDatum(allParameterTypes),
PointerGetDatum(parameterModes),
PointerGetDatum(parameterNames),
parameterDefaults,
PointerGetDatum(trftypes),
trfoids_list,
PointerGetDatum(proconfig),
prosupport,
procost,
prorows);
}
tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid)); if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
tup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(funcOid)); if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid);
tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid)); if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
/* Permission check: must own function */ if (!object_ownercheck(ProcedureRelationId, funcOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, stmt->objtype,
NameListToString(stmt->func->objname));
if (procForm->prokind == PROKIND_AGGREGATE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(stmt->func->objname))));
if (volatility_item)
procForm->provolatile = interpret_func_volatility(volatility_item); if (strict_item)
procForm->proisstrict = boolVal(strict_item->arg); if (security_def_item)
procForm->prosecdef = boolVal(security_def_item->arg); if (leakproof_item)
{
procForm->proleakproof = boolVal(leakproof_item->arg); if (procForm->proleakproof && !superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("only superuser can define a leakproof function")));
} if (cost_item)
{
procForm->procost = defGetNumeric(cost_item); if (procForm->procost <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("COST must be positive")));
} if (rows_item)
{
procForm->prorows = defGetNumeric(rows_item); if (procForm->prorows <= 0)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS must be positive"))); if (!procForm->proretset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("ROWS is not applicable when function does not return a set")));
} if (support_item)
{ /* interpret_func_support handles the privilege check */
Oid newsupport = interpret_func_support(support_item);
/* Add or replace dependency on support function */ if (OidIsValid(procForm->prosupport))
{ if (changeDependencyFor(ProcedureRelationId, funcOid,
ProcedureRelationId, procForm->prosupport,
newsupport) != 1)
elog(ERROR, "could not change support dependency for function %s",
get_func_name(funcOid));
} else
{
ObjectAddress referenced;
tup = heap_modify_tuple(tup, RelationGetDescr(rel),
repl_val, repl_null, repl_repl);
} /* DO NOT put more touches of procForm below here; it's now dangling. */
/* Do the update */
CatalogTupleUpdate(rel, &tup->t_self, tup);
/* No pseudo-types allowed */ if (sourcetyptype == TYPTYPE_PSEUDO)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("source data type %s is a pseudo-type",
TypeNameToString(stmt->sourcetype))));
if (targettyptype == TYPTYPE_PSEUDO)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("target data type %s is a pseudo-type",
TypeNameToString(stmt->targettype))));
/* Permission check */ if (!object_ownercheck(TypeRelationId, sourcetypeid, GetUserId())
&& !object_ownercheck(TypeRelationId, targettypeid, GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of type %s or type %s",
format_type_be(sourcetypeid),
format_type_be(targettypeid))));
/* Domains are allowed for historical reasons, but we warn */ if (sourcetyptype == TYPTYPE_DOMAIN)
ereport(WARNING,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cast will be ignored because the source data type is a domain")));
elseif (targettyptype == TYPTYPE_DOMAIN)
ereport(WARNING,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("cast will be ignored because the target data type is a domain")));
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", funcid);
procstruct = (Form_pg_proc) GETSTRUCT(tuple);
nargs = procstruct->pronargs; if (nargs < 1 || nargs > 3)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must take one to three arguments"))); if (!IsBinaryCoercibleWithCast(sourcetypeid,
procstruct->proargtypes.values[0],
&incastid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("argument of cast function must match or be binary-coercible from source data type"))); if (nargs > 1 && procstruct->proargtypes.values[1] != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("second argument of cast function must be type %s", "integer"))); if (nargs > 2 && procstruct->proargtypes.values[2] != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("third argument of cast function must be type %s", "boolean"))); if (!IsBinaryCoercibleWithCast(procstruct->prorettype,
targettypeid,
&outcastid))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("return data type of cast function must match or be binary-coercible to target data type")));
/* *Restrictingthevolatilityofacastfunctionmayormaynotbea *goodideaintheabstract,butitdefinitelybreaksmanyold *user-definedtypes.Disablethischeck---tgl2/1/03
*/ #ifdef NOT_USED if (procstruct->provolatile == PROVOLATILE_VOLATILE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not be volatile"))); #endif if (procstruct->prokind != PROKIND_FUNCTION)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must be a normal function"))); if (procstruct->proretset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not return a set")));
/* *Mustbesuperusertocreatebinary-compatiblecasts,since *erroneouscastscaneasilycrashthebackend.
*/ if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
/* *Also,insistthatthetypesmatchastosize,alignment,and *pass-by-valueattributes;thisprovidesatleastacrudecheckthat *theyhavesimilarrepresentations.Apairoftypesthatfailthis *testshouldcertainlynotbeequated.
*/
get_typlenbyvalalign(sourcetypeid, &typ1len, &typ1byval, &typ1align);
get_typlenbyvalalign(targettypeid, &typ2len, &typ2byval, &typ2align); if (typ1len != typ2len ||
typ1byval != typ2byval ||
typ1align != typ2align)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("source and target data types are not physically compatible")));
/* *Weknowthatcomposite,array,rangeandenumtypesarenever *binary-compatiblewitheachother.TheyallhaveOIDsembeddedin *them. * *Theoreticallyyoucouldbuildauser-definedbasetypethatis *binary-compatiblewithsuchatype.Butwedisallowitanyway,as *inpracticesuchacastissurelyamistake.Youcanalwayswork *aroundthatbywritingacastfunction. * *NOTE:ifweeverhaveakindofcontainertypethatdoesn'tneedto *berejectedforthisreason,we'dlikelyneedtorecursivelyapply *allofthesesamecheckstothecontainedtype(s).
*/ if (sourcetyptype == TYPTYPE_COMPOSITE ||
targettyptype == TYPTYPE_COMPOSITE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("composite data types are not binary-compatible")));
if (OidIsValid(get_element_type(sourcetypeid)) ||
OidIsValid(get_element_type(targettypeid)))
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("array data types are not binary-compatible")));
if (sourcetyptype == TYPTYPE_RANGE ||
targettyptype == TYPTYPE_RANGE ||
sourcetyptype == TYPTYPE_MULTIRANGE ||
targettyptype == TYPTYPE_MULTIRANGE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("range data types are not binary-compatible")));
if (sourcetyptype == TYPTYPE_ENUM ||
targettyptype == TYPTYPE_ENUM)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("enum data types are not binary-compatible")));
/* *Wealsodisallowcreatingbinary-compatibilitycastsinvolving *domains.Castingfromadomaintoitsbasetypeisalready *allowed,andcastingtheotherwayoughttogothroughdomain *coerciontopermitconstraintchecking.Again,ifyou'reintenton *havingyourownsemanticsforthat,createano-opcastfunction. * *NOTE:ifweweretorelaxthis,theabovechecksforcomposites *etc.wouldhavetobemodifiedtolookthroughdomainstotheir *basetypes.
*/ if (sourcetyptype == TYPTYPE_DOMAIN ||
targettyptype == TYPTYPE_DOMAIN)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("domain data types must not be marked binary-compatible")));
}
/* *Allowsourceandtargettypestobesameonlyforlengthcoercion *functions.Weassumeamulti-argfunctiondoeslengthcoercion.
*/ if (sourcetypeid == targettypeid && nargs < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("source data type and target data type are the same")));
/* convert CoercionContext enum to char value for castcontext */ switch (stmt->context)
{ case COERCION_IMPLICIT:
castcontext = COERCION_CODE_IMPLICIT; break; case COERCION_ASSIGNMENT:
castcontext = COERCION_CODE_ASSIGNMENT; break; /* COERCION_PLPGSQL is intentionally not covered here */ case COERCION_EXPLICIT:
castcontext = COERCION_CODE_EXPLICIT; break; default:
elog(ERROR, "unrecognized CoercionContext: %d", stmt->context);
castcontext = 0; /* keep compiler quiet */ break;
}
staticvoid
check_transform_function(Form_pg_proc procstruct)
{ if (procstruct->provolatile == PROVOLATILE_VOLATILE)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("transform function must not be volatile"))); if (procstruct->prokind != PROKIND_FUNCTION)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("transform function must be a normal function"))); if (procstruct->proretset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("transform function must not return a set"))); if (procstruct->pronargs != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("transform function must take one argument"))); if (procstruct->proargtypes.values[0] != INTERNALOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("first argument of transform function must be type %s", "internal")));
}
/* *CREATETRANSFORM
*/
ObjectAddress
CreateTransform(CreateTransformStmt *stmt)
{
Oid typeid; char typtype;
Oid langid;
Oid fromsqlfuncid;
Oid tosqlfuncid;
AclResult aclresult;
Form_pg_proc procstruct;
Datum values[Natts_pg_transform]; bool nulls[Natts_pg_transform] = {0}; bool replaces[Natts_pg_transform] = {0};
Oid transformid;
HeapTuple tuple;
HeapTuple newtuple;
Relation relation;
ObjectAddress myself,
referenced;
ObjectAddresses *addrs; bool is_replace;
if (typtype == TYPTYPE_PSEUDO)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("data type %s is a pseudo-type",
TypeNameToString(stmt->type_name))));
if (typtype == TYPTYPE_DOMAIN)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("data type %s is a domain",
TypeNameToString(stmt->type_name))));
if (!object_ownercheck(TypeRelationId, typeid, GetUserId()))
aclcheck_error_type(ACLCHECK_NOT_OWNER, typeid);
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(fromsqlfuncid)); if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", fromsqlfuncid);
procstruct = (Form_pg_proc) GETSTRUCT(tuple); if (procstruct->prorettype != INTERNALOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("return data type of FROM SQL function must be %s", "internal")));
check_transform_function(procstruct);
ReleaseSysCache(tuple);
} else
fromsqlfuncid = InvalidOid;
if (stmt->tosql)
{
tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, false);
if (!object_ownercheck(ProcedureRelationId, tosqlfuncid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, OBJECT_FUNCTION, NameListToString(stmt->tosql->objname));
tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(tosqlfuncid)); if (!HeapTupleIsValid(tuple))
elog(ERROR, "cache lookup failed for function %u", tosqlfuncid);
procstruct = (Form_pg_proc) GETSTRUCT(tuple); if (procstruct->prorettype != typeid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("return data type of TO SQL function must be the transform data type")));
check_transform_function(procstruct);
ReleaseSysCache(tuple);
} else
tosqlfuncid = InvalidOid;
tuple = SearchSysCache2(TRFTYPELANG,
ObjectIdGetDatum(typeid),
ObjectIdGetDatum(langid)); if (HeapTupleIsValid(tuple))
{
Form_pg_transform form = (Form_pg_transform) GETSTRUCT(tuple);
if (!stmt->replace)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("transform for type %s language \"%s\" already exists",
format_type_be(typeid),
stmt->lang)));
/* dependency on extension */
recordDependencyOnCurrentExtension(&myself, is_replace);
/* Post creation hook for new transform */
InvokeObjectPostCreateHook(TransformRelationId, transformid, 0);
heap_freetuple(newtuple);
table_close(relation, RowExclusiveLock);
return myself;
}
/* *get_transform_oid-giventypeOIDandlanguageOID,lookupatransformOID * *Ifmissing_okisfalse,throwanerrorifthetransformisnotfound.If *true,justreturnInvalidOid.
*/
Oid
get_transform_oid(Oid type_id, Oid lang_id, bool missing_ok)
{
Oid oid;
oid = GetSysCacheOid2(TRFTYPELANG, Anum_pg_transform_oid,
ObjectIdGetDatum(type_id),
ObjectIdGetDatum(lang_id)); if (!OidIsValid(oid) && !missing_ok)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("transform for type %s language \"%s\" does not exist",
format_type_be(type_id),
get_language_name(lang_id, false)))); return oid;
}
/* *SubroutineforALTERFUNCTION/AGGREGATESETSCHEMA/RENAME * *Isthereafunctionwiththegivennameandsignaturealreadyinthegiven *namespace?Ifso,raiseanappropriateerrormessage.
*/ void
IsThereFunctionInNamespace(constchar *proname, int pronargs,
oidvector *proargtypes, Oid nspOid)
{ /* check for duplicate name (more friendly than unique-index failure) */ if (SearchSysCacheExists3(PROCNAMEARGSNSP,
CStringGetDatum(proname),
PointerGetDatum(proargtypes),
ObjectIdGetDatum(nspOid)))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(proname, pronargs,
NIL, proargtypes->values),
get_namespace_name(nspOid))));
}
/* if LANGUAGE option wasn't specified, use the default */ if (language_item)
language = strVal(language_item->arg); else
language = "plpgsql";
/* Look up the language and validate permissions */
languageTuple = SearchSysCache1(LANGNAME, PointerGetDatum(language)); if (!HeapTupleIsValid(languageTuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", language),
(extension_file_exists(language) ?
errhint("Use CREATE EXTENSION to load the language into the database.") : 0)));
if (languageStruct->lanpltrusted)
{ /* if trusted language, need USAGE privilege */
AclResult aclresult;
aclresult = object_aclcheck(LanguageRelationId, codeblock->langOid, GetUserId(),
ACL_USAGE); if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, OBJECT_LANGUAGE,
NameStr(languageStruct->lanname));
} else
{ /* if untrusted language, must be superuser */ if (!superuser())
aclcheck_error(ACLCHECK_NO_PRIV, OBJECT_LANGUAGE,
NameStr(languageStruct->lanname));
}
/* get the handler function's OID */
laninline = languageStruct->laninline; if (!OidIsValid(laninline))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("language \"%s\" does not support inline code execution",
NameStr(languageStruct->lanname))));
ReleaseSysCache(languageTuple);
/* execute the inline handler */
OidFunctionCall1(laninline, PointerGetDatum(codeblock));
}
/* Prep the context object we'll pass to the procedure */
callcontext = makeNode(CallContext);
callcontext->atomic = atomic;
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(fexpr->funcid)); if (!HeapTupleIsValid(tp))
elog(ERROR, "cache lookup failed for function %u", fexpr->funcid);
/* safety check; see ExecInitFunc() */
nargs = list_length(fexpr->args); if (nargs > FUNC_MAX_ARGS)
ereport(ERROR,
(errcode(ERRCODE_TOO_MANY_ARGUMENTS),
errmsg_plural("cannot pass more than %d argument to a procedure", "cannot pass more than %d arguments to a procedure",
FUNC_MAX_ARGS,
FUNC_MAX_ARGS)));
/* Get rid of temporary snapshot for arguments, if we made one */ if (!atomic)
PopActiveSnapshot();
/* Here we actually call the procedure */
pgstat_init_function_usage(fcinfo, &fcusage);
retval = FunctionCallInvoke(fcinfo);
pgstat_end_function_usage(&fcusage, true);
/* Handle the procedure's outputs */ if (fexpr->funcresulttype == VOIDOID)
{ /* do nothing */
} elseif (fexpr->funcresulttype == RECORDOID)
{ /* send tuple to client */
HeapTupleHeader td;
Oid tupType;
int32 tupTypmod;
TupleDesc retdesc;
HeapTupleData rettupdata;
TupOutputState *tstate;
TupleTableSlot *slot;
if (fcinfo->isnull)
elog(ERROR, "procedure returned null record");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.