/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames, and proconfig * are either arrays of the proper types or NULL. We declare them Datum, * not "ArrayType *", to avoid importing array.h into pg_proc_fn.h. * ---------------------------------------------------------------- */ Oid ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid proowner, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, bool isAgg, bool isWindowFunc, bool security_definer, bool isLeakProof, bool isStrict, char volatility, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames, List *parameterDefaults, Datum proconfig, float4 procost, float4 prorows) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; char *paramModes = NULL; bool genericInParam = false; bool genericOutParam = false; bool anyrangeInParam = false; bool anyrangeOutParam = false; bool internalInParam = false; bool internalOutParam = false; Oid variadicType = InvalidOid; Acl *proacl = NULL; Relation rel; HeapTuple tup; HeapTuple oldtup; bool nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; bool replaces[Natts_pg_proc]; Oid relid; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; /* * sanity checks */ Assert(PointerIsValid(prosrc)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg_plural("functions cannot have more than %d argument", "functions cannot have more than %d arguments", FUNC_MAX_ARGS, FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ /* Deconstruct array inputs */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ ArrayType *allParamArray = (ArrayType *) DatumGetPointer(allParameterTypes); allParamCount = ARR_DIMS(allParamArray)[0]; if (ARR_NDIM(allParamArray) != 1 || allParamCount <= 0 || ARR_HASNULL(allParamArray) || ARR_ELEMTYPE(allParamArray) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(allParamArray); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } if (parameterModes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D CHAR array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of char values. */ ArrayType *modesArray = (ArrayType *) DatumGetPointer(parameterModes); if (ARR_NDIM(modesArray) != 1 || ARR_DIMS(modesArray)[0] != allParamCount || ARR_HASNULL(modesArray) || ARR_ELEMTYPE(modesArray) != CHAROID) elog(ERROR, "parameterModes is not a 1-D char array"); paramModes = (char *) ARR_DATA_PTR(modesArray); } /* * Detect whether we have polymorphic or INTERNAL arguments. The first * loop checks input arguments, the second output arguments. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericInParam = true; break; case ANYRANGEOID: genericInParam = true; anyrangeInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { if (paramModes == NULL || paramModes[i] == PROARGMODE_IN || paramModes[i] == PROARGMODE_VARIADIC) continue; /* ignore input-only params */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: case ANYNONARRAYOID: case ANYENUMOID: genericOutParam = true; break; case ANYRANGEOID: genericOutParam = true; anyrangeOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } /* * Do not allow polymorphic return type unless at least one input argument * is polymorphic. ANYRANGE return type is even stricter: must have an * ANYRANGE input (since we can't deduce the specific range type from * ANYELEMENT). Also, do not allow return type INTERNAL unless at least * one input argument is INTERNAL. */ if ((IsPolymorphicType(returnType) || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == ANYRANGEOID || anyrangeOutParam) && !anyrangeInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyrange\" must have at least one \"anyrange\" argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OidIsValid(parameterTypes->values[0]) && (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid && get_attnum(relid, procedureName) != InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); if (paramModes != NULL) { /* * Only the last input parameter can be variadic; if it is, save its * element type. Errors here are just elog since caller should have * checked this already. */ for (i = 0; i < allParamCount; i++) { switch (paramModes[i]) { case PROARGMODE_IN: case PROARGMODE_INOUT: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); break; case PROARGMODE_OUT: case PROARGMODE_TABLE: /* okay */ break; case PROARGMODE_VARIADIC: if (OidIsValid(variadicType)) elog(ERROR, "variadic parameter must be last"); switch (allParams[i]) { case ANYOID: variadicType = ANYOID; break; case ANYARRAYOID: variadicType = ANYELEMENTOID; break; default: variadicType = get_element_type(allParams[i]); if (!OidIsValid(variadicType)) elog(ERROR, "variadic parameter is not an array"); break; } break; default: elog(ERROR, "invalid parameter mode '%c'", paramModes[i]); break; } } } /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = false; values[i] = (Datum) 0; replaces[i] = true; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(proowner); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_procost - 1] = Float4GetDatum(procost); values[Anum_pg_proc_prorows - 1] = Float4GetDatum(prorows); values[Anum_pg_proc_provariadic - 1] = ObjectIdGetDatum(variadicType); values[Anum_pg_proc_protransform - 1] = ObjectIdGetDatum(InvalidOid); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_proiswindow - 1] = BoolGetDatum(isWindowFunc); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proleakproof - 1] = BoolGetDatum(isLeakProof); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_pronargdefaults - 1] = UInt16GetDatum(list_length(parameterDefaults)); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = true; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = true; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = true; if (parameterDefaults != NIL) values[Anum_pg_proc_proargdefaults - 1] = CStringGetTextDatum(nodeToString(parameterDefaults)); else nulls[Anum_pg_proc_proargdefaults - 1] = true; values[Anum_pg_proc_prosrc - 1] = CStringGetTextDatum(prosrc); if (probin) values[Anum_pg_proc_probin - 1] = CStringGetTextDatum(probin); else nulls[Anum_pg_proc_probin - 1] = true; if (proconfig != PointerGetDatum(NULL)) values[Anum_pg_proc_proconfig - 1] = proconfig; else nulls[Anum_pg_proc_proconfig - 1] = true; /* proacl will be determined later */ rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache3(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace)); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); Datum proargnames; bool isnull; if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), proowner)) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION %s first.", format_procedure(HeapTupleGetOid(oldtup))))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION %s first.", format_procedure(HeapTupleGetOid(oldtup))))); } /* * If there were any named input parameters, check to make sure the * names have not been changed, as this could break existing calls. We * allow adding names to formerly unnamed parameters, though. */ proargnames = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargnames, &isnull); if (!isnull) { Datum proargmodes; char **old_arg_names; char **new_arg_names; int n_old_arg_names; int n_new_arg_names; int j; proargmodes = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargmodes, &isnull); if (isnull) proargmodes = PointerGetDatum(NULL); /* just to be sure */ n_old_arg_names = get_func_input_arg_names(proargnames, proargmodes, &old_arg_names); n_new_arg_names = get_func_input_arg_names(parameterNames, parameterModes, &new_arg_names); for (j = 0; j < n_old_arg_names; j++) { if (old_arg_names[j] == NULL) continue; if (j >= n_new_arg_names || new_arg_names[j] == NULL || strcmp(old_arg_names[j], new_arg_names[j]) != 0) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change name of input parameter \"%s\"", old_arg_names[j]), errhint("Use DROP FUNCTION %s first.", format_procedure(HeapTupleGetOid(oldtup))))); } } /* * If there are existing defaults, check compatibility: redefinition * must not remove any defaults nor change their types. (Removing a * default might cause a function to fail to satisfy an existing call. * Changing type would only be possible if the associated parameter is * polymorphic, and in such cases a change of default type might alter * the resolved output type of existing calls.) */ if (oldproc->pronargdefaults != 0) { Datum proargdefaults; List *oldDefaults; ListCell *oldlc; ListCell *newlc; if (list_length(parameterDefaults) < oldproc->pronargdefaults) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot remove parameter defaults from existing function"), errhint("Use DROP FUNCTION %s first.", format_procedure(HeapTupleGetOid(oldtup))))); proargdefaults = SysCacheGetAttr(PROCNAMEARGSNSP, oldtup, Anum_pg_proc_proargdefaults, &isnull); Assert(!isnull); oldDefaults = (List *) stringToNode(TextDatumGetCString(proargdefaults)); Assert(IsA(oldDefaults, List)); Assert(list_length(oldDefaults) == oldproc->pronargdefaults); /* new list can have more defaults than old, advance over 'em */ newlc = list_head(parameterDefaults); for (i = list_length(parameterDefaults) - oldproc->pronargdefaults; i > 0; i--) newlc = lnext(newlc); foreach(oldlc, oldDefaults) { Node *oldDef = (Node *) lfirst(oldlc); Node *newDef = (Node *) lfirst(newlc); if (exprType(oldDef) != exprType(newDef)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change data type of existing parameter default value"), errhint("Use DROP FUNCTION %s first.", format_procedure(HeapTupleGetOid(oldtup))))); newlc = lnext(newlc); } }
void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->out.r.natts != desc->natts) { if (arg->out.r.atts) PLy_free(arg->out.r.atts); arg->out.r.natts = desc->natts; arg->out.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create output functions for an * anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the output */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->out.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_output_datum_func2(&(arg->out.r.atts[i]), typeTup); ReleaseSysCache(typeTup); } }
/* ---------------------------------------------------------------- * ProcedureCreate * * Note: allParameterTypes, parameterModes, parameterNames are either arrays * of the proper types or NULL. We declare them Datum, not "ArrayType *", * to avoid importing array.h into pg_proc.h. * ---------------------------------------------------------------- */ Oid ProcedureCreate(const char *procedureName, Oid procNamespace, bool replace, bool returnsSet, Oid returnType, Oid languageObjectId, Oid languageValidator, const char *prosrc, const char *probin, bool isAgg, bool security_definer, bool isStrict, char volatility, oidvector *parameterTypes, Datum allParameterTypes, Datum parameterModes, Datum parameterNames) { Oid retval; int parameterCount; int allParamCount; Oid *allParams; bool genericInParam = false; bool genericOutParam = false; bool internalInParam = false; bool internalOutParam = false; Relation rel; HeapTuple tup; HeapTuple oldtup; char nulls[Natts_pg_proc]; Datum values[Natts_pg_proc]; char replaces[Natts_pg_proc]; Oid relid; NameData procname; TupleDesc tupDesc; bool is_update; ObjectAddress myself, referenced; int i; /* * sanity checks */ Assert(PointerIsValid(prosrc)); Assert(PointerIsValid(probin)); parameterCount = parameterTypes->dim1; if (parameterCount < 0 || parameterCount > FUNC_MAX_ARGS) ereport(ERROR, (errcode(ERRCODE_TOO_MANY_ARGUMENTS), errmsg("functions cannot have more than %d arguments", FUNC_MAX_ARGS))); /* note: the above is correct, we do NOT count output arguments */ if (allParameterTypes != PointerGetDatum(NULL)) { /* * We expect the array to be a 1-D OID array; verify that. We don't * need to use deconstruct_array() since the array data is just going * to look like a C array of OID values. */ allParamCount = ARR_DIMS(DatumGetPointer(allParameterTypes))[0]; if (ARR_NDIM(DatumGetPointer(allParameterTypes)) != 1 || allParamCount <= 0 || ARR_ELEMTYPE(DatumGetPointer(allParameterTypes)) != OIDOID) elog(ERROR, "allParameterTypes is not a 1-D Oid array"); allParams = (Oid *) ARR_DATA_PTR(DatumGetPointer(allParameterTypes)); Assert(allParamCount >= parameterCount); /* we assume caller got the contents right */ } else { allParamCount = parameterCount; allParams = parameterTypes->values; } /* * Do not allow return type ANYARRAY or ANYELEMENT unless at least one * input argument is ANYARRAY or ANYELEMENT. Also, do not allow return * type INTERNAL unless at least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { switch (parameterTypes->values[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericInParam = true; break; case INTERNALOID: internalInParam = true; break; } } if (allParameterTypes != PointerGetDatum(NULL)) { for (i = 0; i < allParamCount; i++) { /* * We don't bother to distinguish input and output params here, so * if there is, say, just an input INTERNAL param then we will * still set internalOutParam. This is OK since we don't really * care. */ switch (allParams[i]) { case ANYARRAYOID: case ANYELEMENTOID: genericOutParam = true; break; case INTERNALOID: internalOutParam = true; break; } } } if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID || genericOutParam) && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("unsafe use of pseudo-type \"internal\""), errdetail("A function returning \"internal\" must have at least one \"internal\" argument."))); /* * don't allow functions of complex types that have the same name as * existing attributes of the type */ if (parameterCount == 1 && OidIsValid(parameterTypes->values[0]) && (relid = typeidTypeRelid(parameterTypes->values[0])) != InvalidOid && get_attnum(relid, procedureName) != InvalidAttrNumber) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_COLUMN), errmsg("\"%s\" is already an attribute of type %s", procedureName, format_type_be(parameterTypes->values[0])))); /* * All seems OK; prepare the data to be inserted into pg_proc. */ for (i = 0; i < Natts_pg_proc; ++i) { nulls[i] = ' '; values[i] = (Datum) 0; replaces[i] = 'r'; } namestrcpy(&procname, procedureName); values[Anum_pg_proc_proname - 1] = NameGetDatum(&procname); values[Anum_pg_proc_pronamespace - 1] = ObjectIdGetDatum(procNamespace); values[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(GetUserId()); values[Anum_pg_proc_prolang - 1] = ObjectIdGetDatum(languageObjectId); values[Anum_pg_proc_proisagg - 1] = BoolGetDatum(isAgg); values[Anum_pg_proc_prosecdef - 1] = BoolGetDatum(security_definer); values[Anum_pg_proc_proisstrict - 1] = BoolGetDatum(isStrict); values[Anum_pg_proc_proretset - 1] = BoolGetDatum(returnsSet); values[Anum_pg_proc_provolatile - 1] = CharGetDatum(volatility); values[Anum_pg_proc_pronargs - 1] = UInt16GetDatum(parameterCount); values[Anum_pg_proc_prorettype - 1] = ObjectIdGetDatum(returnType); values[Anum_pg_proc_proargtypes - 1] = PointerGetDatum(parameterTypes); if (allParameterTypes != PointerGetDatum(NULL)) values[Anum_pg_proc_proallargtypes - 1] = allParameterTypes; else nulls[Anum_pg_proc_proallargtypes - 1] = 'n'; if (parameterModes != PointerGetDatum(NULL)) values[Anum_pg_proc_proargmodes - 1] = parameterModes; else nulls[Anum_pg_proc_proargmodes - 1] = 'n'; if (parameterNames != PointerGetDatum(NULL)) values[Anum_pg_proc_proargnames - 1] = parameterNames; else nulls[Anum_pg_proc_proargnames - 1] = 'n'; values[Anum_pg_proc_prosrc - 1] = DirectFunctionCall1(textin, CStringGetDatum(prosrc)); values[Anum_pg_proc_probin - 1] = DirectFunctionCall1(textin, CStringGetDatum(probin)); /* start out with empty permissions */ nulls[Anum_pg_proc_proacl - 1] = 'n'; rel = heap_open(ProcedureRelationId, RowExclusiveLock); tupDesc = RelationGetDescr(rel); /* Check for pre-existing definition */ oldtup = SearchSysCache(PROCNAMEARGSNSP, PointerGetDatum(procedureName), PointerGetDatum(parameterTypes), ObjectIdGetDatum(procNamespace), 0); if (HeapTupleIsValid(oldtup)) { /* There is one; okay to replace it? */ Form_pg_proc oldproc = (Form_pg_proc) GETSTRUCT(oldtup); if (!replace) ereport(ERROR, (errcode(ERRCODE_DUPLICATE_FUNCTION), errmsg("function \"%s\" already exists with same argument types", procedureName))); if (!pg_proc_ownercheck(HeapTupleGetOid(oldtup), GetUserId())) aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, procedureName); /* * Not okay to change the return type of the existing proc, since * existing rules, views, etc may depend on the return type. */ if (returnType != oldproc->prorettype || returnsSet != oldproc->proretset) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errhint("Use DROP FUNCTION first."))); /* * If it returns RECORD, check for possible change of record type * implied by OUT parameters */ if (returnType == RECORDOID) { TupleDesc olddesc; TupleDesc newdesc; olddesc = build_function_result_tupdesc_t(oldtup); newdesc = build_function_result_tupdesc_d(allParameterTypes, parameterModes, parameterNames); if (olddesc == NULL && newdesc == NULL) /* ok, both are runtime-defined RECORDs */ ; else if (olddesc == NULL || newdesc == NULL || !equalTupleDescs(olddesc, newdesc)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot change return type of existing function"), errdetail("Row type defined by OUT parameters is different."), errhint("Use DROP FUNCTION first."))); } /* Can't change aggregate status, either */ if (oldproc->proisagg != isAgg) { if (oldproc->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is an aggregate", procedureName))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is not an aggregate", procedureName))); } /* do not change existing ownership or permissions, either */ replaces[Anum_pg_proc_proowner - 1] = ' '; replaces[Anum_pg_proc_proacl - 1] = ' '; /* Okay, do it... */ tup = heap_modifytuple(oldtup, tupDesc, values, nulls, replaces); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); is_update = true; } else { /* Creating a new procedure */ tup = heap_formtuple(tupDesc, values, nulls); simple_heap_insert(rel, tup); is_update = false; } /* Need to update indexes for either the insert or update case */ CatalogUpdateIndexes(rel, tup); retval = HeapTupleGetOid(tup); /* * Create dependencies for the new function. If we are updating an * existing function, first delete any existing pg_depend entries. */ if (is_update) { deleteDependencyRecordsFor(ProcedureRelationId, retval); deleteSharedDependencyRecordsFor(ProcedureRelationId, retval); } myself.classId = ProcedureRelationId; myself.objectId = retval; myself.objectSubId = 0; /* dependency on namespace */ referenced.classId = NamespaceRelationId; referenced.objectId = procNamespace; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on implementation language */ referenced.classId = LanguageRelationId; referenced.objectId = languageObjectId; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on return type */ referenced.classId = TypeRelationId; referenced.objectId = returnType; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); /* dependency on parameter types */ for (i = 0; i < allParamCount; i++) { referenced.classId = TypeRelationId; referenced.objectId = allParams[i]; referenced.objectSubId = 0; recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); } /* dependency on owner */ recordDependencyOnOwner(ProcedureRelationId, retval, GetUserId()); heap_freetuple(tup); heap_close(rel, RowExclusiveLock); /* Verify function body */ if (OidIsValid(languageValidator)) { /* Advance command counter so new tuple can be seen by validator */ CommandCounterIncrement(); OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval)); } return retval; }
void PLy_input_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc) { int i; PLyExecutionContext *exec_ctx = PLy_current_execution_context(); if (arg->is_rowtype == 0) elog(ERROR, "PLyTypeInfo struct is initialized for a Datum"); arg->is_rowtype = 1; if (arg->in.r.natts != desc->natts) { if (arg->in.r.atts) PLy_free(arg->in.r.atts); arg->in.r.natts = desc->natts; arg->in.r.atts = PLy_malloc0(desc->natts * sizeof(PLyDatumToOb)); } /* Can this be an unnamed tuple? If not, then an Assert would be enough */ if (desc->tdtypmod != -1) elog(ERROR, "received unnamed record type as input"); Assert(OidIsValid(desc->tdtypeid)); /* * RECORDOID means we got called to create input functions for a tuple * fetched by plpy.execute or for an anonymous record type */ if (desc->tdtypeid != RECORDOID) { HeapTuple relTup; /* Get the pg_class tuple corresponding to the type of the input */ arg->typ_relid = typeidTypeRelid(desc->tdtypeid); relTup = SearchSysCache1(RELOID, ObjectIdGetDatum(arg->typ_relid)); if (!HeapTupleIsValid(relTup)) elog(ERROR, "cache lookup failed for relation %u", arg->typ_relid); /* Remember XMIN and TID for later validation if cache is still OK */ arg->typrel_xmin = HeapTupleHeaderGetRawXmin(relTup->t_data); arg->typrel_tid = relTup->t_self; ReleaseSysCache(relTup); } for (i = 0; i < desc->natts; i++) { HeapTuple typeTup; if (desc->attrs[i]->attisdropped) continue; if (arg->in.r.atts[i].typoid == desc->attrs[i]->atttypid) continue; /* already set up this entry */ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->attrs[i]->atttypid)); if (!HeapTupleIsValid(typeTup)) elog(ERROR, "cache lookup failed for type %u", desc->attrs[i]->atttypid); PLy_input_datum_func2(&(arg->in.r.atts[i]), desc->attrs[i]->atttypid, typeTup, exec_ctx->curr_proc->langid, exec_ctx->curr_proc->trftypes); ReleaseSysCache(typeTup); } }