Tom Lane wrote:
> 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> [, ...] ] )
<replaceableclass="PARAMETER">action</replaceable> [, ... ] [ RESTRICT ]
ALTER FUNCTION <replaceable>name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] )
RENAMETO <replaceable>newname</replaceable>
ALTER FUNCTION <replaceable>name</replaceable> ( [ <replaceable class="parameter">type</replaceable> [, ...] ] )
OWNERTO <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