postgresql/src/backend/commands/functioncmds.c

1560 lines
42 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* functioncmds.c
*
* Routines for CREATE and DROP FUNCTION commands and CREATE and DROP
* CAST commands.
*
* Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.72 2006/03/05 15:58:24 momjian Exp $
*
* DESCRIPTION
* These routines take the parse tree and pick out the
* appropriate arguments/flags, and pass the results to the
* corresponding "FooDefine" routines (in src/catalog) that do
* the actual catalog-munging. These routines also verify permission
* of the user to execute the command.
*
* NOTES
* These things must be defined and committed in the following order:
* "create function":
* input/output, recv/send procedures
* "create type":
* type
* "create operator":
* operators
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/genam.h"
#include "access/heapam.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/namespace.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/proclang.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "parser/parse_func.h"
#include "parser/parse_type.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/lsyscache.h"
#include "utils/syscache.h"
static void AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId);
/*
* 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
2002-09-04 22:31:48 +02:00
* 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)
{
2002-09-04 22:31:48 +02:00
Oid rettype;
rettype = LookupTypeName(returnType);
if (OidIsValid(rettype))
{
if (!get_typisdefined(rettype))
{
if (languageOid == SQLlanguageId)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
2005-10-15 04:49:52 +02:00
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))));
}
}
else
{
2002-09-04 22:31:48 +02:00
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)));
/* 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);
Assert(OidIsValid(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;
2005-10-15 04:49:52 +02:00
*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(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),
2005-10-15 04:49:52 +02:00
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++;
}
/* Now construct the proper outputs as needed */
*parameterTypes = buildoidvector(inTypes, inCount);
if (outCount > 0)
{
*allParameterTypes = construct_array(allTypes, parameterCount, OIDOID,
sizeof(Oid), true, 'i');
*parameterModes = construct_array(paramModes, parameterCount, CHAROID,
1, true, 'c');
if (outCount > 1)
*requiredResultType = RECORDOID;
/* otherwise we set requiredResultType correctly above */
}
else
{
*allParameterTypes = NULL;
*parameterModes = NULL;
}
if (have_names)
{
for (i = 0; i < parameterCount; i++)
{
if (paramNames[i] == PointerGetDatum(NULL))
paramNames[i] = DirectFunctionCall1(textin,
CStringGetDatum(""));
}
*parameterNames = construct_array(paramNames, parameterCount, TEXTOID,
-1, false, 'i');
}
else
*parameterNames = NULL;
}
/*
* Recognize one of the options that can be passed to both CREATE
* FUNCTION and ALTER FUNCTION and return it via one of the out
* parameters. Returns true if the passed option was recognized. If
* the out parameter we were going to assign to points to non-NULL,
* raise a duplicate error.
*/
static bool
compute_common_attribute(DefElem *defel,
DefElem **volatility_item,
DefElem **strict_item,
DefElem **security_item)
{
if (strcmp(defel->defname, "volatility") == 0)
{
if (*volatility_item)
goto duplicate_error;
*volatility_item = defel;
}
else if (strcmp(defel->defname, "strict") == 0)
{
if (*strict_item)
goto duplicate_error;
*strict_item = defel;
}
else if (strcmp(defel->defname, "security") == 0)
{
if (*security_item)
goto duplicate_error;
*security_item = defel;
}
else
return false;
/* Recognized an option */
return true;
duplicate_error:
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
2005-10-15 04:49:52 +02:00
return false; /* keep compiler quiet */
}
static char
interpret_func_volatility(DefElem *defel)
{
2005-10-15 04:49:52 +02:00
char *str = strVal(defel->arg);
if (strcmp(str, "immutable") == 0)
return PROVOLATILE_IMMUTABLE;
else if (strcmp(str, "stable") == 0)
return PROVOLATILE_STABLE;
else if (strcmp(str, "volatile") == 0)
return PROVOLATILE_VOLATILE;
else
{
elog(ERROR, "invalid volatility \"%s\"", str);
2005-10-15 04:49:52 +02:00
return 0; /* keep compiler quiet */
}
}
/*
* Dissect the list of options assembled in gram.y into function
* attributes.
*/
static void
compute_attributes_sql_style(List *options,
List **as,
char **language,
char *volatility_p,
bool *strict_p,
bool *security_definer)
{
ListCell *option;
2002-09-04 22:31:48 +02:00
DefElem *as_item = NULL;
DefElem *language_item = NULL;
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_item = NULL;
foreach(option, options)
{
DefElem *defel = (DefElem *) lfirst(option);
2002-09-04 22:31:48 +02:00
if (strcmp(defel->defname, "as") == 0)
{
if (as_item)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
as_item = defel;
}
2002-09-04 22:31:48 +02:00
else if (strcmp(defel->defname, "language") == 0)
{
if (language_item)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("conflicting or redundant options")));
language_item = defel;
}
else if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
&security_item))
{
/* recognized common option */
continue;
}
else
elog(ERROR, "option \"%s\" not recognized",
defel->defname);
}
/* process required items */
if (as_item)
2002-09-04 22:31:48 +02:00
*as = (List *) as_item->arg;
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("no function body specified")));
*as = NIL; /* keep compiler quiet */
}
if (language_item)
*language = strVal(language_item->arg);
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("no language specified")));
*language = NULL; /* keep compiler quiet */
}
/* process optional items */
if (volatility_item)
*volatility_p = interpret_func_volatility(volatility_item);
if (strict_item)
*strict_p = intVal(strict_item->arg);
if (security_item)
*security_definer = intVal(security_item->arg);
}
/*-------------
2005-03-13 06:19:26 +01:00
* Interpret the parameters *parameters and return their contents via
* *isStrict_p and *volatility_p.
*
* These parameters supply optional information about a function.
2005-03-13 06:19:26 +01:00
* All have defaults if not specified. Parameters:
*
* * isStrict means the function should not be called when any NULL
* inputs are present; instead a NULL result value should be assumed.
*
* * volatility tells the optimizer whether the function's result can
* be assumed to be repeatable over multiple evaluations.
*------------
*/
static void
compute_attributes_with_style(List *parameters, bool *isStrict_p, char *volatility_p)
{
ListCell *pl;
foreach(pl, parameters)
{
DefElem *param = (DefElem *) lfirst(pl);
if (pg_strcasecmp(param->defname, "isstrict") == 0)
*isStrict_p = defGetBoolean(param);
else if (pg_strcasecmp(param->defname, "iscachable") == 0)
{
/* obsolete spelling of isImmutable */
if (defGetBoolean(param))
*volatility_p = PROVOLATILE_IMMUTABLE;
}
else
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
2005-10-15 04:49:52 +02:00
errmsg("unrecognized function attribute \"%s\" ignored",
param->defname)));
}
}
/*
* For a dynamically linked C language object, the form of the clause is
*
* AS <object file name> [, <link symbol name> ]
*
* In all other cases
*
* AS <object reference, or sql code>
*/
static void
interpret_AS_clause(Oid languageOid, const char *languageName, List *as,
char **prosrc_str_p, char **probin_str_p)
{
Assert(as != NIL);
if (languageOid == ClanguageId)
{
/*
2005-10-15 04:49:52 +02:00
* For "C" language, store the file name in probin and, when given,
* the link symbol name in prosrc.
*/
*probin_str_p = strVal(linitial(as));
if (list_length(as) == 1)
*prosrc_str_p = "-";
else
*prosrc_str_p = strVal(lsecond(as));
}
else
{
/* Everything else wants the given string in prosrc. */
*prosrc_str_p = strVal(linitial(as));
*probin_str_p = "-";
if (list_length(as) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("only one AS item needed for language \"%s\"",
languageName)));
}
}
/*
* CreateFunction
* Execute a CREATE FUNCTION utility statement.
*/
void
CreateFunction(CreateFunctionStmt *stmt)
{
char *probin_str;
char *prosrc_str;
Oid prorettype;
bool returnsSet;
char *language;
char *languageName;
Oid languageOid;
Oid languageValidator;
char *funcname;
Oid namespaceId;
AclResult aclresult;
oidvector *parameterTypes;
ArrayType *allParameterTypes;
ArrayType *parameterModes;
ArrayType *parameterNames;
Oid requiredResultType;
bool isStrict,
security;
char volatility;
HeapTuple languageTuple;
Form_pg_language languageStruct;
List *as_clause;
/* Convert list of names to a name and namespace */
namespaceId = QualifiedNameGetCreationNamespace(stmt->funcname,
&funcname);
/* Check we have creation rights in target namespace */
aclresult = pg_namespace_aclcheck(namespaceId, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceId));
/* default attributes */
isStrict = false;
security = false;
volatility = PROVOLATILE_VOLATILE;
/* override attributes from explicit list */
compute_attributes_sql_style(stmt->options,
2005-10-15 04:49:52 +02:00
&as_clause, &language, &volatility, &isStrict, &security);
/* Convert language name to canonical case */
languageName = case_translate_language_name(language);
/* Look up the language and validate permissions */
languageTuple = SearchSysCache(LANGNAME,
PointerGetDatum(languageName),
0, 0, 0);
if (!HeapTupleIsValid(languageTuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("language \"%s\" does not exist", languageName),
(PLTemplateExists(languageName) ?
errhint("Use CREATE LANGUAGE to load the language into the database.") : 0)));
2004-08-29 07:07:03 +02:00
languageOid = HeapTupleGetOid(languageTuple);
languageStruct = (Form_pg_language) GETSTRUCT(languageTuple);
if (languageStruct->lanpltrusted)
{
/* if trusted language, need USAGE privilege */
AclResult aclresult;
aclresult = pg_language_aclcheck(languageOid, GetUserId(), ACL_USAGE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_LANGUAGE,
NameStr(languageStruct->lanname));
}
else
{
/* if untrusted language, must be superuser */
if (!superuser())
aclcheck_error(ACLCHECK_NO_PRIV, ACL_KIND_LANGUAGE,
NameStr(languageStruct->lanname));
}
languageValidator = languageStruct->lanvalidator;
ReleaseSysCache(languageTuple);
/*
* Convert remaining parameters of CREATE to form wanted by
* ProcedureCreate.
*/
examine_parameter_list(stmt->parameters, languageOid,
&parameterTypes,
&allParameterTypes,
&parameterModes,
&parameterNames,
&requiredResultType);
if (stmt->returnType)
{
/* explicit RETURNS clause */
compute_return_type(stmt->returnType, languageOid,
&prorettype, &returnsSet);
if (OidIsValid(requiredResultType) && prorettype != requiredResultType)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("function result type must be %s because of OUT parameters",
format_type_be(requiredResultType))));
}
else if (OidIsValid(requiredResultType))
{
/* default RETURNS clause from OUT parameters */
prorettype = requiredResultType;
returnsSet = false;
}
else
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("function result type must be specified")));
/* Alternative possibility: default to RETURNS VOID */
prorettype = VOIDOID;
returnsSet = false;
}
compute_attributes_with_style(stmt->withClause, &isStrict, &volatility);
interpret_AS_clause(languageOid, languageName, as_clause,
&prosrc_str, &probin_str);
if (languageOid == INTERNALlanguageId)
{
/*
2002-09-04 22:31:48 +02:00
* In PostgreSQL versions before 6.5, the SQL name of the created
* function could not be different from the internal name, and
2005-10-15 04:49:52 +02:00
* "prosrc" wasn't used. So there is code out there that does CREATE
* FUNCTION xyz AS '' LANGUAGE internal. To preserve some modicum of
2005-10-15 04:49:52 +02:00
* backwards compatibility, accept an empty "prosrc" value as meaning
* the supplied SQL function name.
*/
if (strlen(prosrc_str) == 0)
prosrc_str = funcname;
}
if (languageOid == ClanguageId)
{
/* If link symbol is specified as "-", substitute procedure name */
if (strcmp(prosrc_str, "-") == 0)
prosrc_str = funcname;
}
/*
2005-10-15 04:49:52 +02:00
* And now that we have all the parameters, and know we're permitted to do
* so, go ahead and create the function.
*/
ProcedureCreate(funcname,
namespaceId,
stmt->replace,
returnsSet,
prorettype,
languageOid,
languageValidator,
prosrc_str, /* converted to text later */
probin_str, /* converted to text later */
false, /* not an aggregate */
security,
isStrict,
volatility,
parameterTypes,
PointerGetDatum(allParameterTypes),
PointerGetDatum(parameterModes),
PointerGetDatum(parameterNames));
}
/*
* RemoveFunction
* Deletes a function.
*/
void
RemoveFunction(RemoveFuncStmt *stmt)
{
List *functionName = stmt->funcname;
2002-09-04 22:31:48 +02:00
List *argTypes = stmt->args; /* list of TypeName nodes */
Oid funcOid;
HeapTuple tup;
ObjectAddress object;
/*
* Find the function, do permissions and validity checks
*/
funcOid = LookupFuncNameTypeNames(functionName, argTypes, false);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
2002-09-04 22:31:48 +02:00
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
/* Permission check: must own func or its namespace */
if (!pg_proc_ownercheck(funcOid, GetUserId()) &&
2005-10-15 04:49:52 +02:00
!pg_namespace_ownercheck(((Form_pg_proc) GETSTRUCT(tup))->pronamespace,
GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(functionName));
if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(functionName)),
2005-10-15 04:49:52 +02:00
errhint("Use DROP AGGREGATE to drop aggregate functions.")));
if (((Form_pg_proc) GETSTRUCT(tup))->prolang == INTERNALlanguageId)
{
/* "Helpful" NOTICE when removing a builtin function ... */
ereport(NOTICE,
(errcode(ERRCODE_WARNING),
errmsg("removing built-in function \"%s\"",
NameListToString(functionName))));
}
ReleaseSysCache(tup);
/*
* Do the deletion
*/
object.classId = ProcedureRelationId;
object.objectId = funcOid;
object.objectSubId = 0;
performDeletion(&object, stmt->behavior);
}
/*
* Guts of function deletion.
*
* Note: this is also used for aggregate deletion, since the OIDs of
* both functions and aggregates point to pg_proc.
*/
void
RemoveFunctionById(Oid funcOid)
{
Relation relation;
HeapTuple tup;
bool isagg;
/*
* Delete the pg_proc tuple.
*/
relation = heap_open(ProcedureRelationId, RowExclusiveLock);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
2002-09-04 22:31:48 +02:00
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
isagg = ((Form_pg_proc) GETSTRUCT(tup))->proisagg;
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
/*
* If there's a pg_aggregate tuple, delete that too.
*/
if (isagg)
{
relation = heap_open(AggregateRelationId, RowExclusiveLock);
tup = SearchSysCache(AGGFNOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
2002-09-04 22:31:48 +02:00
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for pg_aggregate tuple for function %u", funcOid);
simple_heap_delete(relation, &tup->t_self);
ReleaseSysCache(tup);
heap_close(relation, RowExclusiveLock);
}
}
2003-06-27 16:45:32 +02:00
/*
* Rename function
*/
void
RenameFunction(List *name, List *argtypes, const char *newname)
{
Oid procOid;
Oid namespaceOid;
HeapTuple tup;
Form_pg_proc procForm;
2003-06-27 16:45:32 +02:00
Relation rel;
AclResult aclresult;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
2003-06-27 16:45:32 +02:00
procOid = LookupFuncNameTypeNames(name, argtypes, false);
2003-06-27 16:45:32 +02:00
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
2003-06-27 16:45:32 +02:00
if (procForm->proisagg)
2003-06-27 16:45:32 +02:00
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(name)),
2005-10-15 04:49:52 +02:00
errhint("Use ALTER AGGREGATE to rename aggregate functions.")));
2003-06-27 16:45:32 +02:00
namespaceOid = procForm->pronamespace;
2003-06-27 16:45:32 +02:00
/* make sure the new name doesn't exist */
if (SearchSysCacheExists(PROCNAMEARGSNSP,
2003-06-27 16:45:32 +02:00
CStringGetDatum(newname),
PointerGetDatum(&procForm->proargtypes),
ObjectIdGetDatum(namespaceOid),
0))
2003-06-27 16:45:32 +02:00
{
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function %s already exists in schema \"%s\"",
funcname_signature_string(newname,
procForm->pronargs,
2005-10-15 04:49:52 +02:00
procForm->proargtypes.values),
get_namespace_name(namespaceOid))));
2003-06-27 16:45:32 +02:00
}
/* must be owner */
if (!pg_proc_ownercheck(procOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(name));
2003-06-27 16:45:32 +02:00
/* must have CREATE privilege on namespace */
aclresult = pg_namespace_aclcheck(namespaceOid, GetUserId(), ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(namespaceOid));
2003-06-27 16:45:32 +02:00
/* rename */
namestrcpy(&(procForm->proname), newname);
2003-06-27 16:45:32 +02:00
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
/*
* Change function owner by name and args
*/
void
AlterFunctionOwner(List *name, List *argtypes, Oid newOwnerId)
{
Relation rel;
Oid procOid;
HeapTuple tup;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
procOid = LookupFuncNameTypeNames(name, argtypes, false);
tup = SearchSysCache(PROCOID,
2004-08-29 07:07:03 +02:00
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(name)),
2004-08-29 07:07:03 +02:00
errhint("Use ALTER AGGREGATE to change owner of aggregate functions.")));
AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
/*
* Change function owner by Oid
*/
void
AlterFunctionOwner_oid(Oid procOid, Oid newOwnerId)
{
Relation rel;
HeapTuple tup;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tup = SearchSysCache(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", procOid);
AlterFunctionOwner_internal(rel, tup, newOwnerId);
heap_close(rel, NoLock);
}
static void
AlterFunctionOwner_internal(Relation rel, HeapTuple tup, Oid newOwnerId)
{
Form_pg_proc procForm;
AclResult aclresult;
Oid procOid;
Assert(RelationGetRelid(rel) == ProcedureRelationId);
Assert(tup->t_tableOid == ProcedureRelationId);
procForm = (Form_pg_proc) GETSTRUCT(tup);
procOid = HeapTupleGetOid(tup);
2004-08-29 07:07:03 +02:00
/*
* If the new owner is the same as the existing owner, consider the
* command to have succeeded. This is for dump restoration purposes.
*/
if (procForm->proowner != newOwnerId)
{
Datum repl_val[Natts_pg_proc];
char repl_null[Natts_pg_proc];
char repl_repl[Natts_pg_proc];
2004-08-29 07:07:03 +02:00
Acl *newAcl;
Datum aclDatum;
bool isNull;
HeapTuple newtuple;
/* Superusers can always do it */
if (!superuser())
{
/* Otherwise, must be owner of the existing object */
2005-10-15 04:49:52 +02:00
if (!pg_proc_ownercheck(procOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameStr(procForm->proname));
/* Must be able to become new owner */
check_is_member_of_role(GetUserId(), newOwnerId);
/* New owner must have CREATE privilege on namespace */
aclresult = pg_namespace_aclcheck(procForm->pronamespace,
newOwnerId,
ACL_CREATE);
if (aclresult != ACLCHECK_OK)
aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
get_namespace_name(procForm->pronamespace));
}
memset(repl_null, ' ', sizeof(repl_null));
memset(repl_repl, ' ', sizeof(repl_repl));
repl_repl[Anum_pg_proc_proowner - 1] = 'r';
repl_val[Anum_pg_proc_proowner - 1] = ObjectIdGetDatum(newOwnerId);
/*
* Determine the modified ACL for the new owner. This is only
* necessary when the ACL is non-null.
*/
aclDatum = SysCacheGetAttr(PROCOID, tup,
Anum_pg_proc_proacl,
&isNull);
if (!isNull)
{
newAcl = aclnewowner(DatumGetAclP(aclDatum),
procForm->proowner, newOwnerId);
repl_repl[Anum_pg_proc_proacl - 1] = 'r';
repl_val[Anum_pg_proc_proacl - 1] = PointerGetDatum(newAcl);
}
newtuple = heap_modifytuple(tup, RelationGetDescr(rel), repl_val,
repl_null, repl_repl);
simple_heap_update(rel, &newtuple->t_self, newtuple);
CatalogUpdateIndexes(rel, newtuple);
heap_freetuple(newtuple);
/* Update owner dependency reference */
changeDependencyOnOwner(ProcedureRelationId, procOid, newOwnerId);
}
ReleaseSysCache(tup);
}
/*
* Implements the ALTER FUNCTION utility command (except for the
* RENAME and OWNER clauses, which are handled as part of the generic
* ALTER framework).
*/
void
AlterFunction(AlterFunctionStmt *stmt)
{
2005-10-15 04:49:52 +02:00
HeapTuple tup;
Oid funcOid;
Form_pg_proc procForm;
2005-10-15 04:49:52 +02:00
Relation rel;
ListCell *l;
DefElem *volatility_item = NULL;
DefElem *strict_item = NULL;
DefElem *security_def_item = NULL;
rel = heap_open(ProcedureRelationId, RowExclusiveLock);
funcOid = LookupFuncNameTypeNames(stmt->func->funcname,
stmt->func->funcargs,
false);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
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 (!pg_proc_ownercheck(funcOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(stmt->func->funcname));
if (procForm->proisagg)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("\"%s\" is an aggregate function",
NameListToString(stmt->func->funcname))));
/* Examine requested actions. */
2005-10-15 04:49:52 +02:00
foreach(l, stmt->actions)
{
2005-10-15 04:49:52 +02:00
DefElem *defel = (DefElem *) lfirst(l);
if (compute_common_attribute(defel,
&volatility_item,
&strict_item,
&security_def_item) == false)
elog(ERROR, "option \"%s\" not recognized", defel->defname);
}
if (volatility_item)
procForm->provolatile = interpret_func_volatility(volatility_item);
if (strict_item)
procForm->proisstrict = intVal(strict_item->arg);
if (security_def_item)
procForm->prosecdef = intVal(security_def_item->arg);
/* Do the update */
simple_heap_update(rel, &tup->t_self, tup);
CatalogUpdateIndexes(rel, tup);
heap_close(rel, NoLock);
heap_freetuple(tup);
}
2003-06-27 16:45:32 +02:00
/*
* SetFunctionReturnType - change declared return type of a function
*
* This is presently only used for adjusting legacy functions that return
* OPAQUE to return whatever we find their correct definition should be.
* The caller should emit a suitable warning explaining what we did.
*/
void
SetFunctionReturnType(Oid funcOid, Oid newRetType)
{
Relation pg_proc_rel;
HeapTuple tup;
Form_pg_proc procForm;
pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
2003-08-04 02:43:34 +02:00
if (procForm->prorettype != OPAQUEOID) /* caller messed up */
elog(ERROR, "function %u doesn't return OPAQUE", funcOid);
/* okay to overwrite copied tuple */
procForm->prorettype = newRetType;
/* update the catalog and its indexes */
simple_heap_update(pg_proc_rel, &tup->t_self, tup);
CatalogUpdateIndexes(pg_proc_rel, tup);
heap_close(pg_proc_rel, RowExclusiveLock);
}
/*
* SetFunctionArgType - change declared argument type of a function
*
* As above, but change an argument's type.
*/
void
SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType)
{
Relation pg_proc_rel;
HeapTuple tup;
Form_pg_proc procForm;
pg_proc_rel = heap_open(ProcedureRelationId, RowExclusiveLock);
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(funcOid),
0, 0, 0);
if (!HeapTupleIsValid(tup)) /* should not happen */
elog(ERROR, "cache lookup failed for function %u", funcOid);
procForm = (Form_pg_proc) GETSTRUCT(tup);
if (argIndex < 0 || argIndex >= procForm->pronargs ||
procForm->proargtypes.values[argIndex] != OPAQUEOID)
elog(ERROR, "function %u doesn't take OPAQUE", funcOid);
/* okay to overwrite copied tuple */
procForm->proargtypes.values[argIndex] = newArgType;
/* update the catalog and its indexes */
simple_heap_update(pg_proc_rel, &tup->t_self, tup);
CatalogUpdateIndexes(pg_proc_rel, tup);
heap_close(pg_proc_rel, RowExclusiveLock);
}
/*
* CREATE CAST
*/
void
CreateCast(CreateCastStmt *stmt)
{
Oid sourcetypeid;
Oid targettypeid;
Oid funcid;
int nargs;
char castcontext;
Relation relation;
HeapTuple tuple;
Datum values[Natts_pg_cast];
char nulls[Natts_pg_cast];
ObjectAddress myself,
2002-09-04 22:31:48 +02:00
referenced;
sourcetypeid = LookupTypeName(stmt->sourcetype);
if (!OidIsValid(sourcetypeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("source data type %s does not exist",
TypeNameToString(stmt->sourcetype))));
targettypeid = LookupTypeName(stmt->targettype);
if (!OidIsValid(targettypeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("target data type %s does not exist",
TypeNameToString(stmt->targettype))));
/* No shells, no pseudo-types allowed */
if (!get_typisdefined(sourcetypeid))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("source data type %s is only a shell",
TypeNameToString(stmt->sourcetype))));
if (!get_typisdefined(targettypeid))
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("target data type %s is only a shell",
TypeNameToString(stmt->targettype))));
if (get_typtype(sourcetypeid) == 'p')
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("source data type %s is a pseudo-type",
TypeNameToString(stmt->sourcetype))));
if (get_typtype(targettypeid) == 'p')
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("target data type %s is a pseudo-type",
TypeNameToString(stmt->targettype))));
/* Permission check */
if (!pg_type_ownercheck(sourcetypeid, GetUserId())
&& !pg_type_ownercheck(targettypeid, GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of type %s or type %s",
TypeNameToString(stmt->sourcetype),
TypeNameToString(stmt->targettype))));
if (stmt->func != NULL)
{
Form_pg_proc procstruct;
funcid = LookupFuncNameTypeNames(stmt->func->funcname,
stmt->func->funcargs,
false);
tuple = SearchSysCache(PROCOID,
ObjectIdGetDatum(funcid),
0, 0, 0);
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),
2005-10-15 04:49:52 +02:00
errmsg("cast function must take one to three arguments")));
if (procstruct->proargtypes.values[0] != sourcetypeid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2005-10-15 04:49:52 +02:00
errmsg("argument of cast function must match source data type")));
if (nargs > 1 && procstruct->proargtypes.values[1] != INT4OID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2005-10-15 04:49:52 +02:00
errmsg("second argument of cast function must be type integer")));
if (nargs > 2 && procstruct->proargtypes.values[2] != BOOLOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2005-10-15 04:49:52 +02:00
errmsg("third argument of cast function must be type boolean")));
if (procstruct->prorettype != targettypeid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("return data type of cast function must match target data type")));
2003-08-04 02:43:34 +02:00
/*
2005-10-15 04:49:52 +02:00
* Restricting the volatility of a cast function may or may not be a
* good idea in the abstract, but it definitely breaks many old
2003-08-04 02:43:34 +02:00
* user-defined types. Disable this check --- tgl 2/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->proisagg)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2005-10-15 04:49:52 +02:00
errmsg("cast function must not be an aggregate function")));
if (procstruct->proretset)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
errmsg("cast function must not return a set")));
ReleaseSysCache(tuple);
}
else
{
2003-08-04 02:43:34 +02:00
int16 typ1len;
int16 typ2len;
bool typ1byval;
bool typ2byval;
char typ1align;
char typ2align;
/* indicates binary coercibility */
funcid = InvalidOid;
nargs = 0;
/*
* Must be superuser to create binary-compatible casts, since
* erroneous casts can easily crash the backend.
*/
if (!superuser())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
2005-10-15 04:49:52 +02:00
errmsg("must be superuser to create a cast WITHOUT FUNCTION")));
/*
* Also, insist that the types match as to size, alignment, and
2005-10-15 04:49:52 +02:00
* pass-by-value attributes; this provides at least a crude check that
* they have similar representations. A pair of types that fail this
* test should certainly not be equated.
*/
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")));
}
/*
* Allow source and target types to be same only for length coercion
* functions. We assume a multi-arg function does length coercion.
*/
if (sourcetypeid == targettypeid && nargs < 2)
ereport(ERROR,
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
2005-10-15 04:49:52 +02:00
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;
case COERCION_EXPLICIT:
castcontext = COERCION_CODE_EXPLICIT;
break;
default:
elog(ERROR, "unrecognized CoercionContext: %d", stmt->context);
castcontext = 0; /* keep compiler quiet */
break;
}
relation = heap_open(CastRelationId, RowExclusiveLock);
/*
2005-10-15 04:49:52 +02:00
* Check for duplicate. This is just to give a friendly error message,
* the unique index would catch it anyway (so no need to sweat about race
* conditions).
*/
tuple = SearchSysCache(CASTSOURCETARGET,
ObjectIdGetDatum(sourcetypeid),
ObjectIdGetDatum(targettypeid),
0, 0);
if (HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT),
errmsg("cast from type %s to type %s already exists",
TypeNameToString(stmt->sourcetype),
TypeNameToString(stmt->targettype))));
/* ready to go */
2002-09-04 22:31:48 +02:00
values[Anum_pg_cast_castsource - 1] = ObjectIdGetDatum(sourcetypeid);
values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
MemSet(nulls, ' ', Natts_pg_cast);
tuple = heap_formtuple(RelationGetDescr(relation), values, nulls);
simple_heap_insert(relation, tuple);
CatalogUpdateIndexes(relation, tuple);
/* make dependency entries */
myself.classId = CastRelationId;
myself.objectId = HeapTupleGetOid(tuple);
myself.objectSubId = 0;
/* dependency on source type */
referenced.classId = TypeRelationId;
referenced.objectId = sourcetypeid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on target type */
referenced.classId = TypeRelationId;
referenced.objectId = targettypeid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
/* dependency on function */
if (OidIsValid(funcid))
{
referenced.classId = ProcedureRelationId;
referenced.objectId = funcid;
referenced.objectSubId = 0;
recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
}
heap_freetuple(tuple);
heap_close(relation, RowExclusiveLock);
}
/*
* DROP CAST
*/
void
DropCast(DropCastStmt *stmt)
{
Oid sourcetypeid;
Oid targettypeid;
HeapTuple tuple;
ObjectAddress object;
sourcetypeid = LookupTypeName(stmt->sourcetype);
if (!OidIsValid(sourcetypeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("source data type %s does not exist",
TypeNameToString(stmt->sourcetype))));
targettypeid = LookupTypeName(stmt->targettype);
if (!OidIsValid(targettypeid))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("target data type %s does not exist",
TypeNameToString(stmt->targettype))));
tuple = SearchSysCache(CASTSOURCETARGET,
2002-09-04 22:31:48 +02:00
ObjectIdGetDatum(sourcetypeid),
ObjectIdGetDatum(targettypeid),
0, 0);
if (!HeapTupleIsValid(tuple))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("cast from type %s to type %s does not exist",
TypeNameToString(stmt->sourcetype),
TypeNameToString(stmt->targettype))));
/* Permission check */
if (!pg_type_ownercheck(sourcetypeid, GetUserId())
&& !pg_type_ownercheck(targettypeid, GetUserId()))
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be owner of type %s or type %s",
TypeNameToString(stmt->sourcetype),
TypeNameToString(stmt->targettype))));
/*
* Do the deletion
*/
object.classId = CastRelationId;
object.objectId = HeapTupleGetOid(tuple);
object.objectSubId = 0;
ReleaseSysCache(tuple);
performDeletion(&object, stmt->behavior);
}
void
DropCastById(Oid castOid)
{
Relation relation;
ScanKeyData scankey;
SysScanDesc scan;
HeapTuple tuple;
relation = heap_open(CastRelationId, RowExclusiveLock);
ScanKeyInit(&scankey,
ObjectIdAttributeNumber,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(castOid));
scan = systable_beginscan(relation, CastOidIndexId, true,
SnapshotNow, 1, &scankey);
tuple = systable_getnext(scan);
if (!HeapTupleIsValid(tuple))
elog(ERROR, "could not find tuple for cast %u", castOid);
simple_heap_delete(relation, &tuple->t_self);
systable_endscan(scan);
heap_close(relation, RowExclusiveLock);
}
/*
* Execute ALTER FUNCTION SET SCHEMA
*/
void
AlterFunctionNamespace(List *name, List *argtypes, const char *newschema)
{
2005-10-15 04:49:52 +02:00
Oid procOid;
Oid oldNspOid;
Oid nspOid;
HeapTuple tup;
Relation procRel;
Form_pg_proc proc;
procRel = heap_open(ProcedureRelationId, RowExclusiveLock);
/* get function OID */
procOid = LookupFuncNameTypeNames(name, argtypes, false);
/* check permissions on function */
if (!pg_proc_ownercheck(procOid, GetUserId()))
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
NameListToString(name));
tup = SearchSysCacheCopy(PROCOID,
ObjectIdGetDatum(procOid),
0, 0, 0);
if (!HeapTupleIsValid(tup))
elog(ERROR, "cache lookup failed for function %u", procOid);
proc = (Form_pg_proc) GETSTRUCT(tup);
oldNspOid = proc->pronamespace;
/* get schema OID and check its permissions */
nspOid = LookupCreationNamespace(newschema);
if (oldNspOid == nspOid)
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" is already in schema \"%s\"",
NameListToString(name),
newschema)));
/* disallow renaming into or out of temp schemas */
if (isAnyTempNamespace(nspOid) || isAnyTempNamespace(oldNspOid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2005-10-15 04:49:52 +02:00
errmsg("cannot move objects into or out of temporary schemas")));
/* same for TOAST schema */
if (nspOid == PG_TOAST_NAMESPACE || oldNspOid == PG_TOAST_NAMESPACE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("cannot move objects into or out of TOAST schema")));
/* check for duplicate name (more friendly than unique-index failure) */
if (SearchSysCacheExists(PROCNAMEARGSNSP,
CStringGetDatum(NameStr(proc->proname)),
PointerGetDatum(&proc->proargtypes),
ObjectIdGetDatum(nspOid),
0))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_FUNCTION),
errmsg("function \"%s\" already exists in schema \"%s\"",
NameStr(proc->proname),
newschema)));
/* OK, modify the pg_proc row */
/* tup is a copy, so we can scribble directly on it */
proc->pronamespace = nspOid;
simple_heap_update(procRel, &tup->t_self, tup);
CatalogUpdateIndexes(procRel, tup);
/* Update dependency on schema */
if (changeDependencyFor(ProcedureRelationId, procOid,
NamespaceRelationId, oldNspOid, nspOid) != 1)
elog(ERROR, "failed to change schema dependency for function \"%s\"",
NameListToString(name));
heap_freetuple(tup);
heap_close(procRel, RowExclusiveLock);
}