You realize of course that that can already be done with CREATE OR REPLACE FUNCTION.
Good point; that makes me less wary of breaking dependencies on existing functions via ALTER, since in any case that can already be done.
Incidentally, is there a reason that DROP FUNCTION doesn't use the FuncWithArgs node?
Probably just historical, but why bother changing it?
It's just a small cleanup, but it seems inconsistent to me to have an abstraction "function name with args" that is only used in some of the situations where it's applicable. I know, I'm ornery about these things :)
Attached is a revised patch that also allows security definer and function volatility to be changed. Barring any objections, I'll apply this tomorrow (I'm going to take a closer look at the patch before applying it -- there might be a few details I want to polish up...)
-Neil
Index: doc/src/sgml/ref/alter_function.sgml =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/ref/alter_function.sgml,v retrieving revision 1.5 diff -c -r1.5 alter_function.sgml *** doc/src/sgml/ref/alter_function.sgml 25 Jun 2004 21:55:50 -0000 1.5 --- doc/src/sgml/ref/alter_function.sgml 13 Mar 2005 06:37:42 -0000 *************** *** 20,27 **** --- 20,34 ---- <refsynopsisdiv> <synopsis> + ALTER FUNCTION <replaceable>name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) <replaceable class="PARAMETER">action</replaceable> [, ... ] [ RESTRICT ] ALTER FUNCTION <replaceable>name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) RENAME TO <replaceable>newname</replaceable> ALTER FUNCTION <replaceable>name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] ) OWNER TO <replaceable>newowner</replaceable> + + where <replaceable class="PARAMETER">action</replaceable> is one of: + + CALLED ON NULL INPUT | RETURNS NULL ON NULL INPUT | STRICT + IMMUTABLE | STRICT | VOLATILE + [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER </synopsis> </refsynopsisdiv> *************** *** 69,79 **** <term><replaceable class="parameter">newowner</replaceable></term> <listitem> <para> ! The new owner of the function. ! To change the owner of a function, you must be a superuser. ! Note that if the function is marked ! <literal>SECURITY DEFINER</literal>, ! it will subsequently execute as the new owner. </para> </listitem> </varlistentry> --- 76,140 ---- <term><replaceable class="parameter">newowner</replaceable></term> <listitem> <para> ! The new owner of the function. To change the owner of a ! function, you must be a superuser. Note that if the function is ! marked <literal>SECURITY DEFINER</literal>, it will subsequently ! execute as the new owner. ! </para> ! </listitem> ! </varlistentry> ! ! <varlistentry> ! <term><literal>CALLED ON NULL INPUT</literal></term> ! <term><literal>RETURNS NULL ON NULL INPUT</literal></term> ! <term><literal>STRICT</literal></term> ! ! <listitem> ! <para> ! <literal>CALLED ON NULL INPUT</literal> changes the function so ! that it will be invoked when some or all of its arguments are ! null. <literal>RETURNS NULL ON NULL INPUT</literal> or ! <literal>STRICT</literal> changes the function so that it ! always returns null if any of its arguments are null. See <xref ! linkend="sql-createfunction"> for more information. ! </para> ! </listitem> ! </varlistentry> ! ! <varlistentry> ! <term><literal>IMMUTABLE</literal></term> ! <term><literal>STABLE</literal></term> ! <term><literal>VOLATILE</literal></term> ! ! <listitem> ! <para> ! Change the volatility of the function to the specified ! type. See <xref linkend="sql-createfunction"> for more ! information about function volatility. ! </para> ! </listitem> ! </varlistentry> ! ! <varlistentry> ! <term><literal><optional>EXTERNAL</optional> SECURITY INVOKER</literal></term> ! <term><literal><optional>EXTERNAL</optional> SECURITY DEFINER</literal></term> ! ! <listitem> ! <para> ! Change whether the function is a security definer or not. The ! key word <literal>EXTERNAL</literal> is ignored for SQL ! conformance. See <xref linkend="sql-createfunction"> for more ! information about this capability. ! </para> ! </listitem> ! </varlistentry> ! ! <varlistentry> ! <term><literal>RESTRICT</literal></term> ! ! <listitem> ! <para> ! Ignored for conformance with the SQL standard. </para> </listitem> </varlistentry> *************** *** 104,112 **** <title>Compatibility</title> <para> ! There is an <command>ALTER FUNCTION</command> statement in the SQL ! standard, but it does not provide the option to rename the ! function or change the owner. </para> </refsect1> --- 165,177 ---- <title>Compatibility</title> <para> ! This statement is partially compatible with the <command>ALTER ! FUNCTION</> statement in the SQL standard. The standard allows more ! properties of a function to be modified, but does not provide the ! ability to rename a function, make a function a security definer, ! or change the owner or volatility of a function. The standard also ! requires the <literal>RESTRICT</> key word; it is optional in ! <productname>PostgreSQL</>. </para> </refsect1> Index: doc/src/sgml/ref/alter_index.sgml =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/doc/src/sgml/ref/alter_index.sgml,v retrieving revision 1.4 diff -c -r1.4 alter_index.sgml *** doc/src/sgml/ref/alter_index.sgml 24 Aug 2004 00:06:51 -0000 1.4 --- doc/src/sgml/ref/alter_index.sgml 13 Mar 2005 05:22:49 -0000 *************** *** 20,29 **** <refsynopsisdiv> <synopsis> ! ALTER INDEX <replaceable class="PARAMETER">name</replaceable> ! <replaceable class="PARAMETER">action</replaceable> [, ... ] ! ALTER INDEX <replaceable class="PARAMETER">name</replaceable> ! RENAME TO <replaceable class="PARAMETER">new_name</replaceable> where <replaceable class="PARAMETER">action</replaceable> is one of: --- 20,27 ---- <refsynopsisdiv> <synopsis> ! ALTER INDEX <replaceable class="PARAMETER">name</replaceable> <replaceable class="PARAMETER">action</replaceable> [, ... ] ! ALTER INDEX <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable> where <replaceable class="PARAMETER">action</replaceable> is one of: Index: src/backend/commands/functioncmds.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/commands/functioncmds.c,v retrieving revision 1.55 diff -c -r1.55 functioncmds.c *** src/backend/commands/functioncmds.c 13 Mar 2005 05:19:26 -0000 1.55 --- src/backend/commands/functioncmds.c 13 Mar 2005 06:03:03 -0000 *************** *** 3,9 **** * functioncmds.c * * Routines for CREATE and DROP FUNCTION commands and CREATE and DROP ! * CAST commands. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California --- 3,9 ---- * functioncmds.c * * Routines for CREATE and DROP FUNCTION commands and CREATE and DROP ! * CAST commands. * * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California *************** *** 195,206 **** return parameterCount; } /* * Dissect the list of options assembled in gram.y into function * attributes. */ - static void compute_attributes_sql_style(List *options, List **as, --- 195,268 ---- return parameterCount; } + /* + * Recognize one of the options that can be passed to both CREATE + * FUNCTION and ALTER FUNCTION. Returns true if the specified option + * was recognized. If the out parameter we were going to assign to + * pointers 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"))); + return false; /* keep compiler quiet */ + } + + static char + interpret_func_volatility(DefElem *defel) + { + 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); + 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, *************** *** 236,264 **** errmsg("conflicting or redundant options"))); language_item = defel; } ! else if (strcmp(defel->defname, "volatility") == 0) ! { ! if (volatility_item) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("conflicting or redundant options"))); ! volatility_item = defel; ! } ! else if (strcmp(defel->defname, "strict") == 0) ! { ! if (strict_item) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("conflicting or redundant options"))); ! strict_item = defel; ! } ! else if (strcmp(defel->defname, "security") == 0) { ! if (security_item) ! ereport(ERROR, ! (errcode(ERRCODE_SYNTAX_ERROR), ! errmsg("conflicting or redundant options"))); ! security_item = defel; } else elog(ERROR, "option \"%s\" not recognized", --- 298,310 ---- 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", *************** *** 280,297 **** errmsg("no language specified"))); if (volatility_item) ! { ! if (strcmp(strVal(volatility_item->arg), "immutable") == 0) ! *volatility_p = PROVOLATILE_IMMUTABLE; ! else if (strcmp(strVal(volatility_item->arg), "stable") == 0) ! *volatility_p = PROVOLATILE_STABLE; ! else if (strcmp(strVal(volatility_item->arg), "volatile") == 0) ! *volatility_p = PROVOLATILE_VOLATILE; ! else ! elog(ERROR, "invalid volatility \"%s\"", ! strVal(volatility_item->arg)); ! } ! if (strict_item) *strict_p = intVal(strict_item->arg); if (security_item) --- 326,332 ---- errmsg("no language specified"))); if (volatility_item) ! *volatility_p = interpret_func_volatility(volatility_item); if (strict_item) *strict_p = intVal(strict_item->arg); if (security_item) *************** *** 301,307 **** /*------------- * Interpret the parameters *parameters and return their contents via ! * out parameters *isStrict_p and *volatility_p. * * These parameters supply optional information about a function. * All have defaults if not specified. Parameters: --- 336,342 ---- /*------------- * Interpret the parameters *parameters and return their contents via ! * *isStrict_p and *volatility_p. * * These parameters supply optional information about a function. * All have defaults if not specified. Parameters: *************** *** 347,355 **** * 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) --- 382,388 ---- *************** *** 799,805 **** --- 832,905 ---- heap_close(rel, NoLock); } + /* + * 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) + { + HeapTuple tup; + Oid funcOid; + Form_pg_proc procForm; + Relation rel; + ListCell *l; + DefElem *volatility_item = NULL; + DefElem *strict_item = NULL; + DefElem *security_def_item = NULL; + + rel = heap_openr(ProcedureRelationName, 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. */ + foreach (l, stmt->actions) + { + 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); + } /* * SetFunctionReturnType - change declared return type of a function Index: src/backend/nodes/copyfuncs.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/nodes/copyfuncs.c,v retrieving revision 1.297 diff -c -r1.297 copyfuncs.c *** src/backend/nodes/copyfuncs.c 10 Mar 2005 23:21:21 -0000 1.297 --- src/backend/nodes/copyfuncs.c 13 Mar 2005 05:22:49 -0000 *************** *** 1892,1897 **** --- 1892,1908 ---- return newnode; } + static AlterFunctionStmt * + _copyAlterFunctionStmt(AlterFunctionStmt *from) + { + AlterFunctionStmt *newnode = makeNode(AlterFunctionStmt); + + COPY_NODE_FIELD(func); + COPY_NODE_FIELD(actions); + + return newnode; + } + static RemoveAggrStmt * _copyRemoveAggrStmt(RemoveAggrStmt *from) { *************** *** 2882,2887 **** --- 2893,2901 ---- case T_FunctionParameter: retval = _copyFunctionParameter(from); break; + case T_AlterFunctionStmt: + retval = _copyAlterFunctionStmt(from); + break; case T_RemoveAggrStmt: retval = _copyRemoveAggrStmt(from); break; Index: src/backend/nodes/equalfuncs.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/nodes/equalfuncs.c,v retrieving revision 1.236 diff -c -r1.236 equalfuncs.c *** src/backend/nodes/equalfuncs.c 10 Mar 2005 23:21:21 -0000 1.236 --- src/backend/nodes/equalfuncs.c 13 Mar 2005 05:22:49 -0000 *************** *** 954,959 **** --- 954,968 ---- } static bool + _equalAlterFunctionStmt(AlterFunctionStmt *a, AlterFunctionStmt *b) + { + COMPARE_NODE_FIELD(func); + COMPARE_NODE_FIELD(actions); + + return true; + } + + static bool _equalRemoveAggrStmt(RemoveAggrStmt *a, RemoveAggrStmt *b) { COMPARE_NODE_FIELD(aggname); *************** *** 2014,2019 **** --- 2023,2031 ---- case T_FunctionParameter: retval = _equalFunctionParameter(a, b); break; + case T_AlterFunctionStmt: + retval = _equalAlterFunctionStmt(a, b); + break; case T_RemoveAggrStmt: retval = _equalRemoveAggrStmt(a, b); break; Index: src/backend/parser/gram.y =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/parser/gram.y,v retrieving revision 2.483 diff -c -r2.483 gram.y *** src/backend/parser/gram.y 2 Feb 2005 06:36:01 -0000 2.483 --- src/backend/parser/gram.y 13 Mar 2005 05:22:49 -0000 *************** *** 142,148 **** DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt GrantStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt ! CreateFunctionStmt ReindexStmt RemoveAggrStmt RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RuleActionStmt RuleActionStmtOrEmpty RuleStmt SelectStmt TransactionStmt TruncateStmt --- 142,148 ---- DropUserStmt DropdbStmt DropTableSpaceStmt ExplainStmt FetchStmt GrantStmt IndexStmt InsertStmt ListenStmt LoadStmt LockStmt NotifyStmt ExplainableStmt PreparableStmt ! CreateFunctionStmt AlterFunctionStmt ReindexStmt RemoveAggrStmt RemoveFuncStmt RemoveOperStmt RenameStmt RevokeStmt RuleActionStmt RuleActionStmtOrEmpty RuleStmt SelectStmt TransactionStmt TruncateStmt *************** *** 213,219 **** %type <list> stmtblock stmtmulti OptTableElementList TableElementList OptInherit definition opt_distinct opt_definition func_args ! func_args_list func_as createfunc_opt_list oper_argtypes RuleActionList RuleActionMulti opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params --- 213,219 ---- %type <list> stmtblock stmtmulti OptTableElementList TableElementList OptInherit definition opt_distinct opt_definition func_args ! func_args_list func_as createfunc_opt_list alterfunc_opt_list oper_argtypes RuleActionList RuleActionMulti opt_column_list columnList opt_name_list sort_clause opt_sort_clause sortby_list index_params *************** *** 231,237 **** %type <range> into_clause OptTempTableName ! %type <defelt> createfunc_opt_item %type <fun_param> func_arg %type <typnam> func_return func_type aggr_argtype --- 231,237 ---- %type <range> into_clause OptTempTableName ! %type <defelt> createfunc_opt_item common_func_opt_item %type <fun_param> func_arg %type <typnam> func_return func_type aggr_argtype *************** *** 486,491 **** --- 486,492 ---- stmt : AlterDatabaseSetStmt | AlterDomainStmt + | AlterFunctionStmt | AlterGroupStmt | AlterOwnerStmt | AlterSeqStmt *************** *** 3371,3384 **** | createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); } ; ! createfunc_opt_item: ! AS func_as { ! $$ = makeDefElem("as", (Node *)$2); } ! | LANGUAGE ColId_or_Sconst { ! $$ = makeDefElem("language", (Node *)makeString($2)); } | IMMUTABLE { --- 3372,3392 ---- | createfunc_opt_list createfunc_opt_item { $$ = lappend($1, $2); } ; ! /* ! * Options common to both CREATE FUNCTION and ALTER FUNCTION ! */ ! common_func_opt_item: ! CALLED ON NULL_P INPUT_P { ! $$ = makeDefElem("strict", (Node *)makeInteger(FALSE)); } ! | RETURNS NULL_P ON NULL_P INPUT_P { ! $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); ! } ! | STRICT_P ! { ! $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); } | IMMUTABLE { *************** *** 3392,3409 **** { $$ = makeDefElem("volatility", (Node *)makeString("volatile")); } ! | CALLED ON NULL_P INPUT_P ! { ! $$ = makeDefElem("strict", (Node *)makeInteger(FALSE)); ! } ! | RETURNS NULL_P ON NULL_P INPUT_P ! { ! $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); ! } ! | STRICT_P ! { ! $$ = makeDefElem("strict", (Node *)makeInteger(TRUE)); ! } | EXTERNAL SECURITY DEFINER { $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); --- 3400,3406 ---- { $$ = makeDefElem("volatility", (Node *)makeString("volatile")); } ! | EXTERNAL SECURITY DEFINER { $$ = makeDefElem("security", (Node *)makeInteger(TRUE)); *************** *** 3422,3427 **** --- 3419,3439 ---- } ; + createfunc_opt_item: + AS func_as + { + $$ = makeDefElem("as", (Node *)$2); + } + | LANGUAGE ColId_or_Sconst + { + $$ = makeDefElem("language", (Node *)makeString($2)); + } + | common_func_opt_item + { + $$ = $1; + } + ; + func_as: Sconst { $$ = list_make1(makeString($1)); } | Sconst ',' Sconst { *************** *** 3434,3439 **** --- 3446,3481 ---- | /*EMPTY*/ { $$ = NIL; } ; + /***************************************************************************** + * ALTER FUNCTION + * + * RENAME and OWNER subcommands are already provided by the generic + * ALTER infrastructure, here we just specify alterations that can + * only be applied to functions. + * + *****************************************************************************/ + AlterFunctionStmt: + ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict + { + AlterFunctionStmt *n = makeNode(AlterFunctionStmt); + n->func = (FuncWithArgs *) $3; + n->actions = $4; + $$ = (Node *) n; + } + ; + + alterfunc_opt_list: + /* At least one option must be specified */ + common_func_opt_item { $$ = list_make1($1); } + | alterfunc_opt_list common_func_opt_item { $$ = lappend($1, $2); } + ; + + /* Ignored, merely for SQL compliance */ + opt_restrict: + RESTRICT + | /* EMPTY */ + ; + /***************************************************************************** * Index: src/backend/tcop/utility.c =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/backend/tcop/utility.c,v retrieving revision 1.233 diff -c -r1.233 utility.c *** src/backend/tcop/utility.c 27 Jan 2005 03:18:10 -0000 1.233 --- src/backend/tcop/utility.c 13 Mar 2005 05:22:49 -0000 *************** *** 277,282 **** --- 277,283 ---- { case T_AlterDatabaseSetStmt: case T_AlterDomainStmt: + case T_AlterFunctionStmt: case T_AlterGroupStmt: case T_AlterOwnerStmt: case T_AlterSeqStmt: *************** *** 711,716 **** --- 712,721 ---- CreateFunction((CreateFunctionStmt *) parsetree); break; + case T_AlterFunctionStmt: /* ALTER FUNCTION */ + AlterFunction((AlterFunctionStmt *) parsetree); + break; + case T_IndexStmt: /* CREATE INDEX */ { IndexStmt *stmt = (IndexStmt *) parsetree; *************** *** 1394,1403 **** --- 1399,1413 ---- tag = "ALTER TABLE"; } break; + case T_AlterDomainStmt: tag = "ALTER DOMAIN"; break; + case T_AlterFunctionStmt: + tag = "ALTER FUNCTION"; + break; + case T_GrantStmt: { GrantStmt *stmt = (GrantStmt *) parsetree; Index: src/include/commands/defrem.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/commands/defrem.h,v retrieving revision 1.62 diff -c -r1.62 defrem.h *** src/include/commands/defrem.h 31 Dec 2004 22:03:28 -0000 1.62 --- src/include/commands/defrem.h 13 Mar 2005 05:22:49 -0000 *************** *** 49,54 **** --- 49,55 ---- extern void SetFunctionArgType(Oid funcOid, int argIndex, Oid newArgType); extern void RenameFunction(List *name, List *argtypes, const char *newname); extern void AlterFunctionOwner(List *name, List *argtypes, AclId newOwnerSysId); + extern void AlterFunction(AlterFunctionStmt *stmt); extern void CreateCast(CreateCastStmt *stmt); extern void DropCast(DropCastStmt *stmt); extern void DropCastById(Oid castOid); Index: src/include/nodes/nodes.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/nodes/nodes.h,v retrieving revision 1.163 diff -c -r1.163 nodes.h *** src/include/nodes/nodes.h 31 Dec 2004 22:03:34 -0000 1.163 --- src/include/nodes/nodes.h 13 Mar 2005 05:22:49 -0000 *************** *** 223,228 **** --- 223,229 ---- T_FetchStmt, T_IndexStmt, T_CreateFunctionStmt, + T_AlterFunctionStmt, T_RemoveAggrStmt, T_RemoveFuncStmt, T_RemoveOperStmt, Index: src/include/nodes/parsenodes.h =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/include/nodes/parsenodes.h,v retrieving revision 1.273 diff -c -r1.273 parsenodes.h *** src/include/nodes/parsenodes.h 10 Mar 2005 23:21:24 -0000 1.273 --- src/include/nodes/parsenodes.h 13 Mar 2005 05:22:49 -0000 *************** *** 1397,1402 **** --- 1397,1409 ---- /* someday add IN/OUT/INOUT indicator here */ } FunctionParameter; + typedef struct AlterFunctionStmt + { + NodeTag type; + FuncWithArgs *func; /* name and args of function */ + List *actions; /* list of DefElem */ + } AlterFunctionStmt; + /* ---------------------- * Drop Aggregate Statement * ---------------------- Index: src/test/regress/expected/alter_table.out =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/expected/alter_table.out,v retrieving revision 1.86 diff -c -r1.86 alter_table.out *** src/test/regress/expected/alter_table.out 22 Jan 2005 05:12:23 -0000 1.86 --- src/test/regress/expected/alter_table.out 13 Mar 2005 05:22:49 -0000 *************** *** 1235,1237 **** --- 1235,1272 ---- (3 rows) drop table another; + -- + -- alter function + -- + create function test_strict(text) returns text as + 'select coalesce($1, ''got passed a null'');' + language sql returns null on null input; + select test_strict(NULL); + test_strict + ------------- + + (1 row) + + alter function test_strict(text) called on null input; + select test_strict(NULL); + test_strict + ------------------- + got passed a null + (1 row) + + create function non_strict(text) returns text as + 'select coalesce($1, ''got passed a null'');' + language sql called on null input; + select non_strict(NULL); + non_strict + ------------------- + got passed a null + (1 row) + + alter function non_strict(text) returns null on null input; + select non_strict(NULL); + non_strict + ------------ + + (1 row) + Index: src/test/regress/sql/alter_table.sql =================================================================== RCS file: /Users/neilc/local/cvs/pgsql/src/test/regress/sql/alter_table.sql,v retrieving revision 1.48 diff -c -r1.48 alter_table.sql *** src/test/regress/sql/alter_table.sql 22 Jan 2005 05:12:33 -0000 1.48 --- src/test/regress/sql/alter_table.sql 13 Mar 2005 05:22:49 -0000 *************** *** 975,977 **** --- 975,994 ---- select * from another; drop table another; + + -- + -- alter function + -- + create function test_strict(text) returns text as + 'select coalesce($1, ''got passed a null'');' + language sql returns null on null input; + select test_strict(NULL); + alter function test_strict(text) called on null input; + select test_strict(NULL); + + create function non_strict(text) returns text as + 'select coalesce($1, ''got passed a null'');' + language sql called on null input; + select non_strict(NULL); + alter function non_strict(text) returns null on null input; + select non_strict(NULL); \ No newline at end of file
---------------------------(end of broadcast)--------------------------- TIP 2: you can get off all lists at once with the unregister command (send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])