#include "nodes/pg_list.h"
Go to the source code of this file.
Functions | |
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) |
bool | function_parse_error_transpose (const char *prosrc) |
bool function_parse_error_transpose | ( | const char * | prosrc | ) |
Definition at line 942 of file pg_proc.c.
References ActivePortal, Assert, errposition(), geterrposition(), getinternalerrposition(), internalerrposition(), internalerrquery(), match_prosrc_to_query(), NULL, PORTAL_ACTIVE, PortalData::sourceText, and PortalData::status.
Referenced by plpgsql_compile_error_callback(), and sql_function_parse_error_callback().
{ int origerrposition; int newerrposition; const char *queryText; /* * Nothing to do unless we are dealing with a syntax error that has a * cursor position. * * Some PLs may prefer to report the error position as an internal error * to begin with, so check that too. */ origerrposition = geterrposition(); if (origerrposition <= 0) { origerrposition = getinternalerrposition(); if (origerrposition <= 0) return false; } /* We can get the original query text from the active portal (hack...) */ Assert(ActivePortal && ActivePortal->status == PORTAL_ACTIVE); queryText = ActivePortal->sourceText; /* Try to locate the prosrc in the original text */ newerrposition = match_prosrc_to_query(prosrc, queryText, origerrposition); if (newerrposition > 0) { /* Successful, so fix error position to reference original query */ errposition(newerrposition); /* Get rid of any report of the error as an "internal query" */ internalerrposition(0); internalerrquery(NULL); } else { /* * If unsuccessful, convert the position to an internal position * marker and give the function text as the internal query. */ errposition(0); internalerrposition(origerrposition); internalerrquery(prosrc); } return true; }
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 | |||
) |
Definition at line 68 of file pg_proc.c.
References ACL_KIND_PROC, ACL_OBJECT_FUNCTION, aclcheck_error(), ACLCHECK_NOT_OWNER, aclmembers(), Anum_pg_proc_proacl, Anum_pg_proc_proallargtypes, Anum_pg_proc_proargdefaults, Anum_pg_proc_proargmodes, Anum_pg_proc_proargnames, Anum_pg_proc_proargtypes, Anum_pg_proc_probin, Anum_pg_proc_proconfig, Anum_pg_proc_procost, Anum_pg_proc_proisagg, Anum_pg_proc_proisstrict, Anum_pg_proc_proiswindow, Anum_pg_proc_prolang, Anum_pg_proc_proleakproof, Anum_pg_proc_proname, Anum_pg_proc_pronamespace, Anum_pg_proc_pronargdefaults, Anum_pg_proc_pronargs, Anum_pg_proc_proowner, Anum_pg_proc_proretset, Anum_pg_proc_prorettype, Anum_pg_proc_prorows, Anum_pg_proc_prosecdef, Anum_pg_proc_prosrc, Anum_pg_proc_protransform, Anum_pg_proc_provariadic, Anum_pg_proc_provolatile, ANYARRAYOID, ANYELEMENTOID, ANYENUMOID, ANYNONARRAYOID, ANYOID, ANYRANGEOID, ARR_DATA_PTR, ARR_DIMS, ARR_ELEMTYPE, ARR_HASNULL, ARR_NDIM, Assert, AtEOXact_GUC(), BoolGetDatum, build_function_result_tupdesc_d(), build_function_result_tupdesc_t(), CatalogUpdateIndexes(), CharGetDatum, ObjectAddress::classId, CommandCounterIncrement(), CStringGetTextDatum, DatumGetPointer, deleteDependencyRecordsFor(), DEPENDENCY_NORMAL, oidvector::dim1, elog, equalTupleDescs(), ereport, errcode(), errdetail(), errhint(), errmsg(), errmsg_plural(), ERROR, exprType(), Float4GetDatum(), format_procedure(), format_type_be(), FUNC_MAX_ARGS, get_attnum(), get_element_type(), get_func_input_arg_names(), get_user_default_acl(), GETSTRUCT, GUC_ACTION_SAVE, heap_close, heap_form_tuple(), heap_freetuple(), heap_modify_tuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, i, INTERNALOID, InvalidAttrNumber, InvalidOid, InvokeObjectPostCreateHook, IsA, IsPolymorphicType, lfirst, list_head(), list_length(), lnext, NameGetDatum, namestrcpy(), NewGUCNestLevel(), NIL, nodeToString(), NULL, ObjectAddress::objectId, ObjectIdGetDatum, ObjectAddress::objectSubId, OidFunctionCall1, OidIsValid, OIDOID, pg_proc_ownercheck(), PGC_S_SESSION, PGC_SUSET, PGC_USERSET, PointerGetDatum, PointerIsValid, PROARGMODE_IN, PROARGMODE_INOUT, PROARGMODE_OUT, PROARGMODE_TABLE, PROARGMODE_VARIADIC, ProcedureRelationId, ProcessGUCArray(), PROCNAMEARGSNSP, recordDependencyOn(), recordDependencyOnCurrentExtension(), recordDependencyOnExpr(), recordDependencyOnOwner(), RECORDOID, RelationGetDescr, ReleaseSysCache(), RowExclusiveLock, SearchSysCache3, simple_heap_insert(), simple_heap_update(), stringToNode(), superuser(), SysCacheGetAttr(), HeapTupleData::t_self, TextDatumGetCString, typeidTypeRelid(), UInt16GetDatum, updateAclDependencies(), oidvector::values, and values.
Referenced by AggregateCreate(), CreateFunction(), CreateProceduralLanguage(), and makeRangeConstructors().
{ 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); } } /* Can't change aggregate or window-function status, either */ if (oldproc->proisagg != isAgg) { if (oldproc->proisagg) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is an aggregate function", procedureName))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is not an aggregate function", procedureName))); } if (oldproc->proiswindow != isWindowFunc) { if (oldproc->proiswindow) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is a window function", procedureName))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("function \"%s\" is not a window function", procedureName))); } /* * Do not change existing ownership or permissions, either. Note * dependency-update code below has to agree with this decision. */ replaces[Anum_pg_proc_proowner - 1] = false; replaces[Anum_pg_proc_proacl - 1] = false; /* Okay, do it... */ tup = heap_modify_tuple(oldtup, tupDesc, values, nulls, replaces); simple_heap_update(rel, &tup->t_self, tup); ReleaseSysCache(oldtup); is_update = true; } else { /* Creating a new procedure */ /* First, get default permissions and set up proacl */ proacl = get_user_default_acl(ACL_OBJECT_FUNCTION, proowner, procNamespace); if (proacl != NULL) values[Anum_pg_proc_proacl - 1] = PointerGetDatum(proacl); else nulls[Anum_pg_proc_proacl - 1] = true; tup = heap_form_tuple(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. * (However, since we are not changing ownership or permissions, the * shared dependencies do *not* need to change, and we leave them alone.) */ if (is_update) deleteDependencyRecordsFor(ProcedureRelationId, retval, true); 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 parameter default expressions */ if (parameterDefaults) recordDependencyOnExpr(&myself, (Node *) parameterDefaults, NIL, DEPENDENCY_NORMAL); /* dependency on owner */ if (!is_update) recordDependencyOnOwner(ProcedureRelationId, retval, proowner); /* dependency on any roles mentioned in ACL */ if (!is_update && proacl != NULL) { int nnewmembers; Oid *newmembers; nnewmembers = aclmembers(proacl, &newmembers); updateAclDependencies(ProcedureRelationId, retval, 0, proowner, 0, NULL, nnewmembers, newmembers); } /* dependency on extension */ recordDependencyOnCurrentExtension(&myself, is_update); heap_freetuple(tup); /* Post creation hook for new function */ InvokeObjectPostCreateHook(ProcedureRelationId, retval, 0); heap_close(rel, RowExclusiveLock); /* Verify function body */ if (OidIsValid(languageValidator)) { ArrayType *set_items; int save_nestlevel; /* Advance command counter so new tuple can be seen by validator */ CommandCounterIncrement(); /* Set per-function configuration parameters */ set_items = (ArrayType *) DatumGetPointer(proconfig); if (set_items) /* Need a new GUC nesting level */ { save_nestlevel = NewGUCNestLevel(); ProcessGUCArray(set_items, (superuser() ? PGC_SUSET : PGC_USERSET), PGC_S_SESSION, GUC_ACTION_SAVE); } else save_nestlevel = 0; /* keep compiler quiet */ OidFunctionCall1(languageValidator, ObjectIdGetDatum(retval)); if (set_items) AtEOXact_GUC(true, save_nestlevel); } return retval; }