Re: ALTER FUNCTION / STRICT - Mailing list pgsql-patches

From Neil Conway
Subject Re: ALTER FUNCTION / STRICT
Date
Msg-id 42341A2A.2030706@samurai.com
Whole thread Raw
In response to Re: ALTER FUNCTION / STRICT  (Tom Lane <tgl@sss.pgh.pa.us>)
Responses Re: ALTER FUNCTION / STRICT
List pgsql-patches
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

pgsql-patches by date:

Previous
From: Neil Conway
Date:
Subject: Re: pgcrypto: openssl digest fix
Next
From: Marko Kreen
Date:
Subject: Re: pgcrypto: openssl digest fix