SQL procedures
authorPeter Eisentraut <peter_e@gmx.net>
Thu, 30 Nov 2017 13:46:13 +0000 (08:46 -0500)
committerPeter Eisentraut <peter_e@gmx.net>
Thu, 30 Nov 2017 16:03:20 +0000 (11:03 -0500)
This adds a new object type "procedure" that is similar to a function
but does not have a return type and is invoked by the new CALL statement
instead of SELECT or similar.  This implementation is aligned with the
SQL standard and compatible with or similar to other SQL implementations.

This commit adds new commands CALL, CREATE/ALTER/DROP PROCEDURE, as well
as ALTER/DROP ROUTINE that can refer to either a function or a
procedure (or an aggregate function, as an extension to SQL).  There is
also support for procedures in various utility commands such as COMMENT
and GRANT, as well as support in pg_dump and psql.  Support for defining
procedures is available in all the languages supplied by the core
distribution.

While this commit is mainly syntax sugar around existing functionality,
future features will rely on having procedures as a separate object
type.

Reviewed-by: Andrew Dunstan <andrew.dunstan@2ndquadrant.com>
92 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/ddl.sgml
doc/src/sgml/ecpg.sgml
doc/src/sgml/information_schema.sgml
doc/src/sgml/plperl.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/plpython.sgml
doc/src/sgml/pltcl.sgml
doc/src/sgml/ref/allfiles.sgml
doc/src/sgml/ref/alter_default_privileges.sgml
doc/src/sgml/ref/alter_extension.sgml
doc/src/sgml/ref/alter_function.sgml
doc/src/sgml/ref/alter_procedure.sgml [new file with mode: 0644]
doc/src/sgml/ref/alter_routine.sgml [new file with mode: 0644]
doc/src/sgml/ref/call.sgml [new file with mode: 0644]
doc/src/sgml/ref/comment.sgml
doc/src/sgml/ref/create_function.sgml
doc/src/sgml/ref/create_procedure.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_function.sgml
doc/src/sgml/ref/drop_procedure.sgml [new file with mode: 0644]
doc/src/sgml/ref/drop_routine.sgml [new file with mode: 0644]
doc/src/sgml/ref/grant.sgml
doc/src/sgml/ref/revoke.sgml
doc/src/sgml/ref/security_label.sgml
doc/src/sgml/reference.sgml
doc/src/sgml/xfunc.sgml
src/backend/catalog/aclchk.c
src/backend/catalog/information_schema.sql
src/backend/catalog/objectaddress.c
src/backend/catalog/pg_proc.c
src/backend/commands/aggregatecmds.c
src/backend/commands/alter.c
src/backend/commands/dropcmds.c
src/backend/commands/event_trigger.c
src/backend/commands/functioncmds.c
src/backend/commands/opclasscmds.c
src/backend/executor/functions.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/parse_agg.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_func.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/backend/utils/cache/lsyscache.c
src/bin/pg_dump/dumputils.c
src/bin/pg_dump/pg_backup_archiver.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/t/002_pg_dump.pl
src/bin/psql/describe.c
src/bin/psql/tab-complete.c
src/include/catalog/catversion.h
src/include/commands/defrem.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/kwlist.h
src/include/parser/parse_func.h
src/include/parser/parse_node.h
src/include/utils/lsyscache.h
src/interfaces/ecpg/preproc/ecpg.tokens
src/interfaces/ecpg/preproc/ecpg.trailer
src/interfaces/ecpg/preproc/ecpg_keywords.c
src/pl/plperl/GNUmakefile
src/pl/plperl/expected/plperl_call.out [new file with mode: 0644]
src/pl/plperl/plperl.c
src/pl/plperl/sql/plperl_call.sql [new file with mode: 0644]
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpython/Makefile
src/pl/plpython/expected/plpython_call.out [new file with mode: 0644]
src/pl/plpython/plpy_exec.c
src/pl/plpython/plpy_main.c
src/pl/plpython/plpy_procedure.c
src/pl/plpython/plpy_procedure.h
src/pl/plpython/sql/plpython_call.sql [new file with mode: 0644]
src/pl/tcl/Makefile
src/pl/tcl/expected/pltcl_call.out [new file with mode: 0644]
src/pl/tcl/pltcl.c
src/pl/tcl/sql/pltcl_call.sql [new file with mode: 0644]
src/test/regress/expected/create_procedure.out [new file with mode: 0644]
src/test/regress/expected/object_address.out
src/test/regress/expected/plpgsql.out
src/test/regress/expected/polymorphism.out
src/test/regress/expected/privileges.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/create_procedure.sql [new file with mode: 0644]
src/test/regress/sql/object_address.sql
src/test/regress/sql/plpgsql.sql
src/test/regress/sql/privileges.sql

index da881a773719bc77f5aa3d4cc508178e4b1850c9..3f02202cafb6adf18e7cb083f5c2052e38233cc4 100644 (file)
@@ -5241,7 +5241,7 @@ SCRAM-SHA-256$<replaceable>&lt;iteration count&gt;</replaceable>:<replaceable>&l
       <entry><structfield>prorettype</structfield></entry>
       <entry><type>oid</type></entry>
       <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
-      <entry>Data type of the return value</entry>
+      <entry>Data type of the return value, or null for a procedure</entry>
      </row>
 
      <row>
index e6f50ec819b2212e8cb01e8aad4a32c8fad54b66..9f583266de90d300944be3f1abfaecfe265d749a 100644 (file)
@@ -3947,7 +3947,7 @@ EXPLAIN SELECT count(*) FROM measurement WHERE logdate &gt;= DATE '2008-01-01';
 
    <listitem>
     <para>
-     Functions and operators
+     Functions, procedures, and operators
     </para>
    </listitem>
 
index d1872c1a5c68a832f7fc99e7d5d0d21f68f61635..5a8d1f1b95ba152790347b5e5faca9b9a161f42d 100644 (file)
@@ -4778,7 +4778,9 @@ EXEC SQL WHENEVER <replaceable>condition</replaceable> <replaceable>action</repl
       <term><literal>DO <replaceable>name</replaceable> (<replaceable>args</replaceable>)</literal></term>
       <listitem>
        <para>
-        Call the specified C functions with the specified arguments.
+        Call the specified C functions with the specified arguments.  (This
+        use is different from the meaning of <literal>CALL</literal>
+        and <literal>DO</literal> in the normal PostgreSQL grammar.)
        </para>
       </listitem>
      </varlistentry>
index 99b0ea8519ec831d500c42d883dce30d067dfc3a..0faa72f1d3f11b7d902b3bf3a2e34682014b5812 100644 (file)
@@ -3972,8 +3972,8 @@ ORDER BY c.ordinal_position;
   <title><literal>routines</literal></title>
 
   <para>
-   The view <literal>routines</literal> contains all functions in the
-   current database.  Only those functions are shown that the current
+   The view <literal>routines</literal> contains all functions and procedures in the
+   current database.  Only those functions and procedures are shown that the current
    user has access to (by way of being the owner or having some
    privilege).
   </para>
@@ -4037,8 +4037,8 @@ ORDER BY c.ordinal_position;
       <entry><literal>routine_type</literal></entry>
       <entry><type>character_data</type></entry>
       <entry>
-       Always <literal>FUNCTION</literal> (In the future there might
-       be other types of routines.)
+       <literal>FUNCTION</literal> for a
+       function, <literal>PROCEDURE</literal> for a procedure
       </entry>
      </row>
 
@@ -4087,7 +4087,7 @@ ORDER BY c.ordinal_position;
        the view <literal>element_types</literal>), else
        <literal>USER-DEFINED</literal> (in that case, the type is
        identified in <literal>type_udt_name</literal> and associated
-       columns).
+       columns).  Null for a procedure.
       </entry>
      </row>
 
@@ -4180,7 +4180,7 @@ ORDER BY c.ordinal_position;
       <entry><type>sql_identifier</type></entry>
       <entry>
        Name of the database that the return data type of the function
-       is defined in (always the current database)
+       is defined in (always the current database).  Null for a procedure.
       </entry>
      </row>
 
@@ -4189,7 +4189,7 @@ ORDER BY c.ordinal_position;
       <entry><type>sql_identifier</type></entry>
       <entry>
        Name of the schema that the return data type of the function is
-       defined in
+       defined in.  Null for a procedure.
       </entry>
      </row>
 
@@ -4197,7 +4197,7 @@ ORDER BY c.ordinal_position;
       <entry><literal>type_udt_name</literal></entry>
       <entry><type>sql_identifier</type></entry>
       <entry>
-       Name of the return data type of the function
+       Name of the return data type of the function.  Null for a procedure.
       </entry>
      </row>
 
@@ -4314,7 +4314,7 @@ ORDER BY c.ordinal_position;
       <entry>
        If the function automatically returns null if any of its
        arguments are null, then <literal>YES</literal>, else
-       <literal>NO</literal>.
+       <literal>NO</literal>.  Null for a procedure.
       </entry>
      </row>
 
index 33e39d85e400ebbfc5d99b1fac213833e206e817..100162dead56413e1817fea653d5ce2b222d0313 100644 (file)
@@ -67,6 +67,10 @@ $$ LANGUAGE plperl;
    as discussed below.
   </para>
 
+  <para>
+   In a PL/Perl procedure, any return value from the Perl code is ignored.
+  </para>
+
   <para>
    PL/Perl also supports anonymous code blocks called with the
    <xref linkend="sql-do"/> statement:
index 6d14b344487e6f6d29395788c2db0a271239d046..7d23ed437e2a97487ca242f524f9e72b4ba3e9c6 100644 (file)
 
     <para>
      Finally, a <application>PL/pgSQL</application> function can be declared to return
-     <type>void</type> if it has no useful return value.
+     <type>void</type> if it has no useful return value.  (Alternatively, it
+     could be written as a procedure in that case.)
     </para>
 
     <para>
@@ -1865,6 +1866,18 @@ SELECT * FROM get_available_flightid(CURRENT_DATE);
     </sect3>
    </sect2>
 
+   <sect2 id="plpgsql-statements-returning-procedure">
+    <title>Returning From a Procedure</title>
+
+    <para>
+     A procedure does not have a return value.  A procedure can therefore end
+     without a <command>RETURN</command> statement.  If
+     a <command>RETURN</command> statement is desired to exit the code early,
+     then <symbol>NULL</symbol> must be returned.  Returning any other value
+     will result in an error.
+    </para>
+   </sect2>
+
    <sect2 id="plpgsql-conditionals">
     <title>Conditionals</title>
 
@@ -5244,7 +5257,7 @@ show errors;
     <para>
      Here is how this function would end up in <productname>PostgreSQL</productname>:
 <programlisting>
-CREATE OR REPLACE FUNCTION cs_update_referrer_type_proc() RETURNS void AS $func$
+CREATE OR REPLACE PROCEDURE cs_update_referrer_type_proc() AS $func$
 DECLARE
     referrer_keys CURSOR IS
         SELECT * FROM cs_referrer_keys
index ec5f671632da008ce66562979161769cdc334fe5..0dbeee1fa2e08aed687d7404bee7778a35ebdefb 100644 (file)
@@ -207,7 +207,11 @@ $$ LANGUAGE plpythonu;
    <literal>yield</literal> (in case of a result-set statement).  If
    you do not provide a return value, Python returns the default
    <symbol>None</symbol>. <application>PL/Python</application> translates
-   Python's <symbol>None</symbol> into the SQL null value.
+   Python's <symbol>None</symbol> into the SQL null value.  In a procedure,
+   the result from the Python code must be <symbol>None</symbol> (typically
+   achieved by ending the procedure without a <literal>return</literal>
+   statement or by using a <literal>return</literal> statement without
+   argument); otherwise, an error will be raised.
   </para>
 
   <para>
index 0646a8ba0baee36a8be3b804a8f8757d04703ce7..8018783b0a074228fc7fbcb1ac6d884a99617e85 100644 (file)
@@ -97,7 +97,8 @@ $$ LANGUAGE pltcl;
      Tcl script as variables named <literal>1</literal>
      ... <literal><replaceable>n</replaceable></literal>.  The result is
      returned from the Tcl code in the usual way, with
-     a <literal>return</literal> statement.
+     a <literal>return</literal> statement.  In a procedure, the return value
+     from the Tcl code is ignored.
     </para>
 
     <para>
index 01acc2ef9dad1986af16c7b4e46afbeb22f4c6c8..22e6893211575e70799454a3b9802c751e622dde 100644 (file)
@@ -26,8 +26,10 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterOperatorClass SYSTEM "alter_opclass.sgml">
 <!ENTITY alterOperatorFamily SYSTEM "alter_opfamily.sgml">
 <!ENTITY alterPolicy        SYSTEM "alter_policy.sgml">
+<!ENTITY alterProcedure     SYSTEM "alter_procedure.sgml">
 <!ENTITY alterPublication   SYSTEM "alter_publication.sgml">
 <!ENTITY alterRole          SYSTEM "alter_role.sgml">
+<!ENTITY alterRoutine       SYSTEM "alter_routine.sgml">
 <!ENTITY alterRule          SYSTEM "alter_rule.sgml">
 <!ENTITY alterSchema        SYSTEM "alter_schema.sgml">
 <!ENTITY alterServer        SYSTEM "alter_server.sgml">
@@ -48,6 +50,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY alterView          SYSTEM "alter_view.sgml">
 <!ENTITY analyze            SYSTEM "analyze.sgml">
 <!ENTITY begin              SYSTEM "begin.sgml">
+<!ENTITY call               SYSTEM "call.sgml">
 <!ENTITY checkpoint         SYSTEM "checkpoint.sgml">
 <!ENTITY close              SYSTEM "close.sgml">
 <!ENTITY cluster            SYSTEM "cluster.sgml">
@@ -75,6 +78,7 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY createOperatorClass SYSTEM "create_opclass.sgml">
 <!ENTITY createOperatorFamily SYSTEM "create_opfamily.sgml">
 <!ENTITY createPolicy       SYSTEM "create_policy.sgml">
+<!ENTITY createProcedure    SYSTEM "create_procedure.sgml">
 <!ENTITY createPublication  SYSTEM "create_publication.sgml">
 <!ENTITY createRole         SYSTEM "create_role.sgml">
 <!ENTITY createRule         SYSTEM "create_rule.sgml">
@@ -122,8 +126,10 @@ Complete list of usable sgml source files in this directory.
 <!ENTITY dropOperatorFamily  SYSTEM "drop_opfamily.sgml">
 <!ENTITY dropOwned          SYSTEM "drop_owned.sgml">
 <!ENTITY dropPolicy         SYSTEM "drop_policy.sgml">
+<!ENTITY dropProcedure      SYSTEM "drop_procedure.sgml">
 <!ENTITY dropPublication    SYSTEM "drop_publication.sgml">
 <!ENTITY dropRole           SYSTEM "drop_role.sgml">
+<!ENTITY dropRoutine        SYSTEM "drop_routine.sgml">
 <!ENTITY dropRule           SYSTEM "drop_rule.sgml">
 <!ENTITY dropSchema         SYSTEM "drop_schema.sgml">
 <!ENTITY dropSequence       SYSTEM "drop_sequence.sgml">
index ab2c35b4dd010c6e85a679a6acb37cf107255d89..0c09f1db5cd11e061d956772a6e0891d13ee8d45 100644 (file)
@@ -39,7 +39,7 @@ GRANT { { USAGE | SELECT | UPDATE }
     TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
 
 GRANT { EXECUTE | ALL [ PRIVILEGES ] }
-    ON FUNCTIONS
+    ON { FUNCTIONS | ROUTINES }
     TO { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...] [ WITH GRANT OPTION ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
@@ -66,7 +66,7 @@ REVOKE [ GRANT OPTION FOR ]
 
 REVOKE [ GRANT OPTION FOR ]
     { EXECUTE | ALL [ PRIVILEGES ] }
-    ON FUNCTIONS
+    ON { FUNCTIONS | ROUTINES }
     FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
     [ CASCADE | RESTRICT ]
 
@@ -93,7 +93,13 @@ REVOKE [ GRANT OPTION FOR ]
    affect privileges assigned to already-existing objects.)  Currently,
    only the privileges for schemas, tables (including views and foreign
    tables), sequences, functions, and types (including domains) can be
-   altered.
+   altered.  For this command, functions include aggregates and procedures.
+   The words <literal>FUNCTIONS</literal> and <literal>ROUTINES</literal> are
+   equivalent in this command.  (<literal>ROUTINES</literal> is preferred
+   going forward as the standard term for functions and procedures taken
+   together.  In earlier PostgreSQL releases, only the
+   word <literal>FUNCTIONS</literal> was allowed.  It is not possible to set
+   default privileges for functions and procedures separately.)
   </para>
 
   <para>
index e54925507e11b2114b9eb07e40949974592d3f94..a2d405d6cdfb58d3b65e37ce72a13b23cc8a31fb 100644 (file)
@@ -45,6 +45,8 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
   OPERATOR CLASS <replaceable class="parameter">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
   OPERATOR FAMILY <replaceable class="parameter">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
   [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
+  PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
+  ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
   SCHEMA <replaceable class="parameter">object_name</replaceable> |
   SEQUENCE <replaceable class="parameter">object_name</replaceable> |
   SERVER <replaceable class="parameter">object_name</replaceable> |
@@ -170,12 +172,14 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
      <term><replaceable class="parameter">aggregate_name</replaceable></term>
      <term><replaceable class="parameter">function_name</replaceable></term>
      <term><replaceable class="parameter">operator_name</replaceable></term>
+     <term><replaceable class="parameter">procedure_name</replaceable></term>
+     <term><replaceable class="parameter">routine_name</replaceable></term>
      <listitem>
       <para>
        The name of an object to be added to or removed from the extension.
        Names of tables,
        aggregates, domains, foreign tables, functions, operators,
-       operator classes, operator families, sequences, text search objects,
+       operator classes, operator families, procedures, routines, sequences, text search objects,
        types, and views can be schema-qualified.
       </para>
      </listitem>
@@ -204,7 +208,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
 
      <listitem>
       <para>
-       The mode of a function or aggregate
+       The mode of a function, procedure, or aggregate
        argument: <literal>IN</literal>, <literal>OUT</literal>,
        <literal>INOUT</literal>, or <literal>VARIADIC</literal>.
        If omitted, the default is <literal>IN</literal>.
@@ -222,7 +226,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
 
      <listitem>
       <para>
-       The name of a function or aggregate argument.
+       The name of a function, procedure, or aggregate argument.
        Note that <command>ALTER EXTENSION</command> does not actually pay
        any attention to argument names, since only the argument data
        types are needed to determine the function's identity.
@@ -235,7 +239,7 @@ ALTER EXTENSION <replaceable class="parameter">name</replaceable> DROP <replacea
 
      <listitem>
       <para>
-       The data type of a function or aggregate argument.
+       The data type of a function, procedure, or aggregate argument.
       </para>
      </listitem>
     </varlistentry>
index 196d2dde0c0a9f0292ad74e31c3558221228dbd7..d8747e074821f9a1d91debd210368de102f7659a 100644 (file)
@@ -359,6 +359,8 @@ ALTER FUNCTION check_password(text) RESET search_path;
   <simplelist type="inline">
    <member><xref linkend="sql-createfunction"/></member>
    <member><xref linkend="sql-dropfunction"/></member>
+   <member><xref linkend="sql-alterprocedure"/></member>
+   <member><xref linkend="sql-alterroutine"/></member>
   </simplelist>
  </refsect1>
 </refentry>
diff --git a/doc/src/sgml/ref/alter_procedure.sgml b/doc/src/sgml/ref/alter_procedure.sgml
new file mode 100644 (file)
index 0000000..dae8007
--- /dev/null
@@ -0,0 +1,281 @@
+<!--
+doc/src/sgml/ref/alter_procedure.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-alterprocedure">
+ <indexterm zone="sql-alterprocedure">
+  <primary>ALTER PROCEDURE</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>ALTER PROCEDURE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER PROCEDURE</refname>
+  <refpurpose>change the definition of a procedure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    <replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
+ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    RENAME TO <replaceable>new_name</replaceable>
+ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    SET SCHEMA <replaceable>new_schema</replaceable>
+ALTER PROCEDURE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    DEPENDS ON EXTENSION <replaceable>extension_name</replaceable>
+
+<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
+
+    [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
+    SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
+    SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
+    RESET <replaceable class="parameter">configuration_parameter</replaceable>
+    RESET ALL
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER PROCEDURE</command> changes the definition of a
+   procedure.
+  </para>
+
+  <para>
+   You must own the procedure to use <command>ALTER PROCEDURE</command>.
+   To change a procedure's schema, you must also have <literal>CREATE</literal>
+   privilege on the new schema.
+   To alter the owner, you must also be a direct or indirect member of the new
+   owning role, and that role must have <literal>CREATE</literal> privilege on
+   the procedure's schema.  (These restrictions enforce that altering the owner
+   doesn't do anything you couldn't do by dropping and recreating the procedure.
+   However, a superuser can alter ownership of any procedure anyway.)
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of an existing procedure.  If no
+      argument list is specified, the name must be unique in its schema.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argmode</replaceable></term>
+
+    <listitem>
+     <para>
+      The mode of an argument: <literal>IN</literal>  or <literal>VARIADIC</literal>.
+      If omitted, the default is <literal>IN</literal>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argname</replaceable></term>
+
+    <listitem>
+     <para>
+      The name of an argument.
+      Note that <command>ALTER PROCEDURE</command> does not actually pay
+      any attention to argument names, since only the argument data
+      types are needed to determine the procedure's identity.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argtype</replaceable></term>
+
+    <listitem>
+     <para>
+      The data type(s) of the procedure's arguments (optionally
+      schema-qualified), if any.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">new_name</replaceable></term>
+    <listitem>
+     <para>
+      The new name of the procedure.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">new_owner</replaceable></term>
+    <listitem>
+     <para>
+      The new owner of the procedure.  Note that if the procedure is
+      marked <literal>SECURITY DEFINER</literal>, it will subsequently
+      execute as the new owner.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">new_schema</replaceable></term>
+    <listitem>
+     <para>
+      The new schema for the procedure.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">extension_name</replaceable></term>
+    <listitem>
+     <para>
+      The name of the extension that the procedure is to depend on.
+     </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 procedure is a security definer or not. The
+      key word <literal>EXTERNAL</literal> is ignored for SQL
+      conformance. See <xref linkend="sql-createprocedure"/> for more information about
+      this capability.
+     </para>
+    </listitem>
+   </varlistentry>
+
+     <varlistentry>
+      <term><replaceable>configuration_parameter</replaceable></term>
+      <term><replaceable>value</replaceable></term>
+      <listitem>
+       <para>
+        Add or change the assignment to be made to a configuration parameter
+        when the procedure is called.  If
+        <replaceable>value</replaceable> is <literal>DEFAULT</literal>
+        or, equivalently, <literal>RESET</literal> is used, the procedure-local
+        setting is removed, so that the procedure executes with the value
+        present in its environment.  Use <literal>RESET
+        ALL</literal> to clear all procedure-local settings.
+        <literal>SET FROM CURRENT</literal> saves the value of the parameter that
+        is current when <command>ALTER PROCEDURE</command> is executed as the value
+        to be applied when the procedure is entered.
+       </para>
+
+       <para>
+        See <xref linkend="sql-set"/> and
+        <xref linkend="runtime-config"/>
+        for more information about allowed parameter names and values.
+       </para>
+      </listitem>
+     </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+
+    <listitem>
+     <para>
+      Ignored for conformance with the SQL standard.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To rename the procedure <literal>insert_data</literal> with two arguments
+   of type <type>integer</type> to <literal>insert_record</literal>:
+<programlisting>
+ALTER PROCEDURE insert_data(integer, integer) RENAME TO insert_record;
+</programlisting>
+  </para>
+
+  <para>
+   To change the owner of the procedure <literal>insert_data</literal> with
+   two arguments of type <type>integer</type> to <literal>joe</literal>:
+<programlisting>
+ALTER PROCEDURE insert_data(integer, integer) OWNER TO joe;
+</programlisting>
+  </para>
+
+  <para>
+   To change the schema of the procedure <literal>insert_data</literal> with
+   two arguments of type <type>integer</type>
+   to <literal>accounting</literal>:
+<programlisting>
+ALTER PROCEDURE insert_data(integer, integer) SET SCHEMA accounting;
+</programlisting>
+  </para>
+
+  <para>
+   To mark the procedure <literal>insert_data(integer, integer)</literal> as
+   being dependent on the extension <literal>myext</literal>:
+<programlisting>
+ALTER PROCEDURE insert_data(integer, integer) DEPENDS ON EXTENSION myext;
+</programlisting>
+  </para>
+
+  <para>
+   To adjust the search path that is automatically set for a procedure:
+<programlisting>
+ALTER PROCEDURE check_password(text) SET search_path = admin, pg_temp;
+</programlisting>
+  </para>
+
+  <para>
+   To disable automatic setting of <varname>search_path</varname> for a procedure:
+<programlisting>
+ALTER PROCEDURE check_password(text) RESET search_path;
+</programlisting>
+   The procedure will now execute with whatever search path is used by its
+   caller.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   This statement is partially compatible with the <command>ALTER
+   PROCEDURE</command> statement in the SQL standard. The standard allows more
+   properties of a procedure to be modified, but does not provide the
+   ability to rename a procedure, make a procedure a security definer,
+   attach configuration parameter values to a procedure,
+   or change the owner, schema, or volatility of a procedure. The standard also
+   requires the <literal>RESTRICT</literal> key word, which is optional in
+   <productname>PostgreSQL</productname>.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createprocedure"/></member>
+   <member><xref linkend="sql-dropprocedure"/></member>
+   <member><xref linkend="sql-alterfunction"/></member>
+   <member><xref linkend="sql-alterroutine"/></member>
+  </simplelist>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/alter_routine.sgml b/doc/src/sgml/ref/alter_routine.sgml
new file mode 100644 (file)
index 0000000..d169969
--- /dev/null
@@ -0,0 +1,102 @@
+<!--
+doc/src/sgml/ref/alter_routine.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-alterroutine">
+ <indexterm zone="sql-alterroutine">
+  <primary>ALTER ROUTINE</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>ALTER ROUTINE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>ALTER ROUTINE</refname>
+  <refpurpose>change the definition of a routine</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+ALTER ROUTINE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    <replaceable class="parameter">action</replaceable> [ ... ] [ RESTRICT ]
+ALTER ROUTINE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    RENAME TO <replaceable>new_name</replaceable>
+ALTER ROUTINE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    OWNER TO { <replaceable>new_owner</replaceable> | CURRENT_USER | SESSION_USER }
+ALTER ROUTINE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    SET SCHEMA <replaceable>new_schema</replaceable>
+ALTER ROUTINE <replaceable>name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ]
+    DEPENDS ON EXTENSION <replaceable>extension_name</replaceable>
+
+<phrase>where <replaceable class="parameter">action</replaceable> is one of:</phrase>
+
+    IMMUTABLE | STABLE | VOLATILE | [ NOT ] LEAKPROOF
+    [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
+    PARALLEL { UNSAFE | RESTRICTED | SAFE }
+    COST <replaceable class="parameter">execution_cost</replaceable>
+    ROWS <replaceable class="parameter">result_rows</replaceable>
+    SET <replaceable class="parameter">configuration_parameter</replaceable> { TO | = } { <replaceable class="parameter">value</replaceable> | DEFAULT }
+    SET <replaceable class="parameter">configuration_parameter</replaceable> FROM CURRENT
+    RESET <replaceable class="parameter">configuration_parameter</replaceable>
+    RESET ALL
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>ALTER ROUTINE</command> changes the definition of a routine, which
+   can be an aggregate function, a normal function, or a procedure.  See
+   under <xref linkend="sql-alteraggregate"/>, <xref linkend="sql-alterfunction"/>,
+   and <xref linkend="sql-alterprocedure"/> for the description of the
+   parameters, more examples, and further details.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+
+  <para>
+   To rename the routine <literal>foo</literal> for type
+   <type>integer</type> to <literal>foobar</literal>:
+<programlisting>
+ALTER ROUTINE foo(integer) RENAME TO foobar;
+</programlisting>
+   This command will work independent of whether <literal>foo</literal> is an
+   aggregate, function, or procedure.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   This statement is partially compatible with the <command>ALTER
+   ROUTINE</command> statement in the SQL standard.  See
+   under <xref linkend="sql-alterfunction"/>
+   and <xref linkend="sql-alterprocedure"/> for more details.  Allowing
+   routine names to refer to aggregate functions is
+   a <productname>PostgreSQL</productname> extension.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alteraggregate"/></member>
+   <member><xref linkend="sql-alterfunction"/></member>
+   <member><xref linkend="sql-alterprocedure"/></member>
+   <member><xref linkend="sql-droproutine"/></member>
+  </simplelist>
+
+  <para>
+   Note that there is no <literal>CREATE ROUTINE</literal> command.
+  </para>
+ </refsect1>
+</refentry>
diff --git a/doc/src/sgml/ref/call.sgml b/doc/src/sgml/ref/call.sgml
new file mode 100644 (file)
index 0000000..2741d8d
--- /dev/null
@@ -0,0 +1,97 @@
+<!--
+doc/src/sgml/ref/call.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-call">
+ <indexterm zone="sql-call">
+  <primary>CALL</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CALL</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CALL</refname>
+  <refpurpose>invoke a procedure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CALL <replaceable class="parameter">name</replaceable> ( [ <replaceable class="parameter">argument</replaceable> ] [ , ...] )
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>CALL</command> executes a procedure.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+   <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of the procedure.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  <varlistentry>
+    <term><replaceable class="parameter">argument</replaceable></term>
+    <listitem>
+     <para>
+      An argument for the procedure call.
+      See <xref linkend="sql-syntax-calling-funcs"/> for the full details on
+      function and procedure call syntax, including use of named parameters.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1>
+  <title>Notes</title>
+
+  <para>
+   The user must have <literal>EXECUTE</literal> privilege on the procedure in
+   order to be allowed to invoke it.
+  </para>
+
+  <para>
+   To call a function (not a procedure), use <command>SELECT</command> instead.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Examples</title>
+<programlisting>
+CALL do_db_maintenance();
+</programlisting>
+ </refsect1>
+
+ <refsect1>
+  <title>Compatibility</title>
+
+  <para>
+   <command>CALL</command> conforms to the SQL standard.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createprocedure"/></member>
+  </simplelist>
+ </refsect1>
+</refentry>
index 7d66c1a34ca97cefd7a9880bc7d785047526a322..965c5a40ad72a54886043b5e398d67b890e53f94 100644 (file)
@@ -46,8 +46,10 @@ COMMENT ON
   OPERATOR FAMILY <replaceable class="parameter">object_name</replaceable> USING <replaceable class="parameter">index_method</replaceable> |
   POLICY <replaceable class="parameter">policy_name</replaceable> ON <replaceable class="parameter">table_name</replaceable> |
   [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
+  PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
   PUBLICATION <replaceable class="parameter">object_name</replaceable> |
   ROLE <replaceable class="parameter">object_name</replaceable> |
+  ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
   RULE <replaceable class="parameter">rule_name</replaceable> ON <replaceable class="parameter">table_name</replaceable> |
   SCHEMA <replaceable class="parameter">object_name</replaceable> |
   SEQUENCE <replaceable class="parameter">object_name</replaceable> |
@@ -121,13 +123,15 @@ COMMENT ON
     <term><replaceable class="parameter">function_name</replaceable></term>
     <term><replaceable class="parameter">operator_name</replaceable></term>
     <term><replaceable class="parameter">policy_name</replaceable></term>
+    <term><replaceable class="parameter">procedure_name</replaceable></term>
+    <term><replaceable class="parameter">routine_name</replaceable></term>
     <term><replaceable class="parameter">rule_name</replaceable></term>
     <term><replaceable class="parameter">trigger_name</replaceable></term>
     <listitem>
      <para>
       The name of the object to be commented.  Names of tables,
       aggregates, collations, conversions, domains, foreign tables, functions,
-      indexes, operators, operator classes, operator families, sequences,
+      indexes, operators, operator classes, operator families, procedures, routines, sequences,
       statistics, text search objects, types, and views can be
       schema-qualified. When commenting on a column,
       <replaceable class="parameter">relation_name</replaceable> must refer
@@ -170,7 +174,7 @@ COMMENT ON
     <term><replaceable class="parameter">argmode</replaceable></term>
     <listitem>
      <para>
-      The mode of a function or aggregate
+      The mode of a function, procedure, or aggregate
       argument: <literal>IN</literal>, <literal>OUT</literal>,
       <literal>INOUT</literal>, or <literal>VARIADIC</literal>.
       If omitted, the default is <literal>IN</literal>.
@@ -187,7 +191,7 @@ COMMENT ON
     <term><replaceable class="parameter">argname</replaceable></term>
     <listitem>
      <para>
-      The name of a function or aggregate argument.
+      The name of a function, procedure, or aggregate argument.
       Note that <command>COMMENT</command> does not actually pay
       any attention to argument names, since only the argument data
       types are needed to determine the function's identity.
@@ -199,7 +203,7 @@ COMMENT ON
     <term><replaceable class="parameter">argtype</replaceable></term>
     <listitem>
      <para>
-      The data type of a function or aggregate argument.
+      The data type of a function, procedure, or aggregate argument.
      </para>
     </listitem>
    </varlistentry>
@@ -325,6 +329,7 @@ COMMENT ON OPERATOR - (NONE, integer) IS 'Unary minus';
 COMMENT ON OPERATOR CLASS int4ops USING btree IS '4 byte integer operators for btrees';
 COMMENT ON OPERATOR FAMILY integer_ops USING btree IS 'all integer operators for btrees';
 COMMENT ON POLICY my_policy ON mytable IS 'Filter rows by users';
+COMMENT ON PROCEDURE my_proc (integer, integer) IS 'Runs a report';
 COMMENT ON ROLE my_role IS 'Administration group for finance tables';
 COMMENT ON RULE my_rule ON my_table IS 'Logs updates of employee records';
 COMMENT ON SCHEMA my_schema IS 'Departmental data';
index 75331165fef95699c3d0427b715efdda56845d26..fd229d1193733194c9c1191be7b3faad5dfb0e9c 100644 (file)
@@ -55,9 +55,9 @@ CREATE [ OR REPLACE ] FUNCTION
   <para>
    If a schema name is included, then the function is created in the
    specified schema.  Otherwise it is created in the current schema.
-   The name of the new function must not match any existing function
+   The name of the new function must not match any existing function or procedure
    with the same input argument types in the same schema.  However,
-   functions of different argument types can share a name (this is
+   functions and procedures of different argument types can share a name (this is
    called <firstterm>overloading</firstterm>).
   </para>
 
@@ -450,7 +450,7 @@ CREATE [ OR REPLACE ] FUNCTION
    </varlistentry>
 
     <varlistentry>
-     <term><replaceable class="parameter">execution_cost</replaceable></term>
+     <term><literal>COST</literal> <replaceable class="parameter">execution_cost</replaceable></term>
 
      <listitem>
       <para>
@@ -466,7 +466,7 @@ CREATE [ OR REPLACE ] FUNCTION
     </varlistentry>
 
     <varlistentry>
-     <term><replaceable class="parameter">result_rows</replaceable></term>
+     <term><literal>ROWS</literal> <replaceable class="parameter">result_rows</replaceable></term>
 
      <listitem>
       <para>
@@ -818,7 +818,7 @@ COMMIT;
   <title>Compatibility</title>
 
   <para>
-   A <command>CREATE FUNCTION</command> command is defined in SQL:1999 and later.
+   A <command>CREATE FUNCTION</command> command is defined in the SQL standard.
    The <productname>PostgreSQL</productname> version is similar but
    not fully compatible.  The attributes are not portable, neither are the
    different available languages.
diff --git a/doc/src/sgml/ref/create_procedure.sgml b/doc/src/sgml/ref/create_procedure.sgml
new file mode 100644 (file)
index 0000000..d712043
--- /dev/null
@@ -0,0 +1,341 @@
+<!--
+doc/src/sgml/ref/create_procedure.sgml
+-->
+
+<refentry id="sql-createprocedure">
+ <indexterm zone="sql-createprocedure">
+  <primary>CREATE PROCEDURE</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>CREATE PROCEDURE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>CREATE PROCEDURE</refname>
+  <refpurpose>define a new procedure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+CREATE [ OR REPLACE ] PROCEDURE
+    <replaceable class="parameter">name</replaceable> ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [ { DEFAULT | = } <replaceable class="parameter">default_expr</replaceable> ] [, ...] ] )
+  { LANGUAGE <replaceable class="parameter">lang_name</replaceable>
+    | TRANSFORM { FOR TYPE <replaceable class="parameter">type_name</replaceable> } [, ... ]
+    | [ EXTERNAL ] SECURITY INVOKER | [ EXTERNAL ] SECURITY DEFINER
+    | SET <replaceable class="parameter">configuration_parameter</replaceable> { TO <replaceable class="parameter">value</replaceable> | = <replaceable class="parameter">value</replaceable> | FROM CURRENT }
+    | AS '<replaceable class="parameter">definition</replaceable>'
+    | AS '<replaceable class="parameter">obj_file</replaceable>', '<replaceable class="parameter">link_symbol</replaceable>'
+  } ...
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1 id="sql-createprocedure-description">
+  <title>Description</title>
+
+  <para>
+   <command>CREATE PROCEDURE</command> defines a new procedure.
+   <command>CREATE OR REPLACE PROCEDURE</command> will either create a
+   new procedure, or replace an existing definition.
+   To be able to define a procedure, the user must have the
+   <literal>USAGE</literal> privilege on the language.
+  </para>
+
+  <para>
+   If a schema name is included, then the procedure is created in the
+   specified schema.  Otherwise it is created in the current schema.
+   The name of the new procedure must not match any existing procedure or function
+   with the same input argument types in the same schema.  However,
+   procedures and functions of different argument types can share a name (this is
+   called <firstterm>overloading</firstterm>).
+  </para>
+
+  <para>
+   To replace the current definition of an existing procedure, use
+   <command>CREATE OR REPLACE PROCEDURE</command>.  It is not possible
+   to change the name or argument types of a procedure this way (if you
+   tried, you would actually be creating a new, distinct procedure).
+  </para>
+
+  <para>
+   When <command>CREATE OR REPLACE PROCEDURE</command> is used to replace an
+   existing procedure, the ownership and permissions of the procedure
+   do not change.  All other procedure properties are assigned the
+   values specified or implied in the command.  You must own the procedure
+   to replace it (this includes being a member of the owning role).
+  </para>
+
+  <para>
+   The user that creates the procedure becomes the owner of the procedure.
+  </para>
+
+  <para>
+   To be able to create a procedure, you must have <literal>USAGE</literal>
+   privilege on the argument types.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+   <variablelist>
+    <varlistentry>
+     <term><replaceable class="parameter">name</replaceable></term>
+
+     <listitem>
+      <para>
+       The name (optionally schema-qualified) of the procedure to create.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">argmode</replaceable></term>
+
+     <listitem>
+      <para>
+       The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
+       If omitted, the default is <literal>IN</literal>.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">argname</replaceable></term>
+
+     <listitem>
+      <para>
+       The name of an argument.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">argtype</replaceable></term>
+
+     <listitem>
+      <para>
+       The data type(s) of the procedure's arguments (optionally
+       schema-qualified), if any. The argument types can be base, composite,
+       or domain types, or can reference the type of a table column.
+      </para>
+      <para>
+       Depending on the implementation language it might also be allowed
+       to specify <quote>pseudo-types</quote> such as <type>cstring</type>.
+       Pseudo-types indicate that the actual argument type is either
+       incompletely specified, or outside the set of ordinary SQL data types.
+      </para>
+      <para>
+       The type of a column is referenced by writing
+       <literal><replaceable
+       class="parameter">table_name</replaceable>.<replaceable
+       class="parameter">column_name</replaceable>%TYPE</literal>.
+       Using this feature can sometimes help make a procedure independent of
+       changes to the definition of a table.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">default_expr</replaceable></term>
+
+     <listitem>
+      <para>
+       An expression to be used as default value if the parameter is
+       not specified.  The expression has to be coercible to the
+       argument type of the parameter.
+       All input parameters following a
+       parameter with a default value must have default values as well.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">lang_name</replaceable></term>
+
+     <listitem>
+      <para>
+       The name of the language that the procedure is implemented in.
+       It can be <literal>sql</literal>, <literal>c</literal>,
+       <literal>internal</literal>, or the name of a user-defined
+       procedural language, e.g. <literal>plpgsql</literal>.  Enclosing the
+       name in single quotes is deprecated and requires matching case.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal>TRANSFORM { FOR TYPE <replaceable class="parameter">type_name</replaceable> } [, ... ] }</literal></term>
+
+     <listitem>
+      <para>
+       Lists which transforms a call to the procedure should apply.  Transforms
+       convert between SQL types and language-specific data types;
+       see <xref linkend="sql-createtransform"/>.  Procedural language
+       implementations usually have hardcoded knowledge of the built-in types,
+       so those don't need to be listed here.  If a procedural language
+       implementation does not know how to handle a type and no transform is
+       supplied, it will fall back to a default behavior for converting data
+       types, but this depends on the implementation.
+      </para>
+     </listitem>
+    </varlistentry>
+
+   <varlistentry>
+    <term><literal><optional>EXTERNAL</optional> SECURITY INVOKER</literal></term>
+    <term><literal><optional>EXTERNAL</optional> SECURITY DEFINER</literal></term>
+
+    <listitem>
+     <para><literal>SECURITY INVOKER</literal> indicates that the procedure
+      is to be executed with the privileges of the user that calls it.
+      That is the default.  <literal>SECURITY DEFINER</literal>
+      specifies that the procedure is to be executed with the
+      privileges of the user that owns it.
+     </para>
+
+     <para>
+      The key word <literal>EXTERNAL</literal> is allowed for SQL
+      conformance, but it is optional since, unlike in SQL, this feature
+      applies to all procedures not only external ones.
+     </para>
+    </listitem>
+   </varlistentry>
+
+    <varlistentry>
+     <term><replaceable>configuration_parameter</replaceable></term>
+     <term><replaceable>value</replaceable></term>
+     <listitem>
+      <para>
+       The <literal>SET</literal> clause causes the specified configuration
+       parameter to be set to the specified value when the procedure is
+       entered, and then restored to its prior value when the procedure exits.
+       <literal>SET FROM CURRENT</literal> saves the value of the parameter that
+       is current when <command>CREATE PROCEDURE</command> is executed as the value
+       to be applied when the procedure is entered.
+      </para>
+
+      <para>
+       If a <literal>SET</literal> clause is attached to a procedure, then
+       the effects of a <command>SET LOCAL</command> command executed inside the
+       procedure for the same variable are restricted to the procedure: the
+       configuration parameter's prior value is still restored at procedure exit.
+       However, an ordinary
+       <command>SET</command> command (without <literal>LOCAL</literal>) overrides the
+       <literal>SET</literal> clause, much as it would do for a previous <command>SET
+       LOCAL</command> command: the effects of such a command will persist after
+       procedure exit, unless the current transaction is rolled back.
+      </para>
+
+      <para>
+       See <xref linkend="sql-set"/> and
+       <xref linkend="runtime-config"/>
+       for more information about allowed parameter names and values.
+      </para>
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><replaceable class="parameter">definition</replaceable></term>
+
+     <listitem>
+      <para>
+       A string constant defining the procedure; the meaning depends on the
+       language.  It can be an internal procedure name, the path to an
+       object file, an SQL command, or text in a procedural language.
+      </para>
+
+      <para>
+       It is often helpful to use dollar quoting (see <xref
+       linkend="sql-syntax-dollar-quoting"/>) to write the procedure definition
+       string, rather than the normal single quote syntax.  Without dollar
+       quoting, any single quotes or backslashes in the procedure definition must
+       be escaped by doubling them.
+      </para>
+
+     </listitem>
+    </varlistentry>
+
+    <varlistentry>
+     <term><literal><replaceable class="parameter">obj_file</replaceable>, <replaceable class="parameter">link_symbol</replaceable></literal></term>
+
+     <listitem>
+      <para>
+       This form of the <literal>AS</literal> clause is used for
+       dynamically loadable C language procedures when the procedure name
+       in the C language source code is not the same as the name of
+       the SQL procedure. The string <replaceable
+       class="parameter">obj_file</replaceable> is the name of the shared
+       library file containing the compiled C procedure, and is interpreted
+       as for the <xref linkend="sql-load"/> command.  The string
+       <replaceable class="parameter">link_symbol</replaceable> is the
+       procedure's link symbol, that is, the name of the procedure in the C
+       language source code.  If the link symbol is omitted, it is assumed
+       to be the same as the name of the SQL procedure being defined.
+      </para>
+
+      <para>
+       When repeated <command>CREATE PROCEDURE</command> calls refer to
+       the same object file, the file is only loaded once per session.
+       To unload and
+       reload the file (perhaps during development), start a new session.
+      </para>
+
+     </listitem>
+    </varlistentry>
+   </variablelist>
+ </refsect1>
+
+ <refsect1 id="sql-createprocedure-notes">
+  <title>Notes</title>
+
+  <para>
+   See <xref linkend="sql-createfunction"/> for more details on function
+   creation that also apply to procedures.
+  </para>
+
+  <para>
+   Use <xref linkend="sql-call"/> to execute a procedure.
+  </para>
+ </refsect1>
+
+ <refsect1 id="sql-createprocedure-examples">
+  <title>Examples</title>
+
+<programlisting>
+CREATE PROCEDURE insert_data(a integer, b integer)
+LANGUAGE SQL
+AS $$
+INSERT INTO tbl VALUES (a);
+INSERT INTO tbl VALUES (b);
+$$;
+
+CALL insert_data(1, 2);
+</programlisting>
+ </refsect1>
+
+ <refsect1 id="sql-createprocedure-compat">
+  <title>Compatibility</title>
+
+  <para>
+   A <command>CREATE PROCEDURE</command> command is defined in the SQL
+   standard.  The <productname>PostgreSQL</productname> version is similar but
+   not fully compatible.  For details see
+   also <xref linkend="sql-createfunction"/>.
+  </para>
+ </refsect1>
+
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-alterprocedure"/></member>
+   <member><xref linkend="sql-dropprocedure"/></member>
+   <member><xref linkend="sql-call"/></member>
+   <member><xref linkend="sql-createfunction"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
index eda1a59c84653b78d99e6078dbb0228e16e4ca7d..127fdfe419770ed016dc8d28e2d1f1a3805f7c97 100644 (file)
@@ -185,6 +185,8 @@ DROP FUNCTION update_employee_salaries();
   <simplelist type="inline">
    <member><xref linkend="sql-createfunction"/></member>
    <member><xref linkend="sql-alterfunction"/></member>
+   <member><xref linkend="sql-dropprocedure"/></member>
+   <member><xref linkend="sql-droproutine"/></member>
   </simplelist>
  </refsect1>
 
diff --git a/doc/src/sgml/ref/drop_procedure.sgml b/doc/src/sgml/ref/drop_procedure.sgml
new file mode 100644 (file)
index 0000000..fef61b6
--- /dev/null
@@ -0,0 +1,162 @@
+<!--
+doc/src/sgml/ref/drop_procedure.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-dropprocedure">
+ <indexterm zone="sql-dropprocedure">
+  <primary>DROP PROCEDURE</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP PROCEDURE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP PROCEDURE</refname>
+  <refpurpose>remove a procedure</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP PROCEDURE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] [, ...]
+    [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP PROCEDURE</command> removes the definition of an existing
+   procedure. To execute this command the user must be the
+   owner of the procedure. The argument types to the
+   procedure must be specified, since several different procedures
+   can exist with the same name and different argument lists.
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>Parameters</title>
+
+  <variablelist>
+    <varlistentry>
+    <term><literal>IF EXISTS</literal></term>
+    <listitem>
+     <para>
+      Do not throw an error if the procedure does not exist. A notice is issued
+      in this case.
+     </para>
+    </listitem>
+   </varlistentry>
+
+  <varlistentry>
+    <term><replaceable class="parameter">name</replaceable></term>
+    <listitem>
+     <para>
+      The name (optionally schema-qualified) of an existing procedure.  If no
+      argument list is specified, the name must be unique in its schema.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argmode</replaceable></term>
+
+    <listitem>
+     <para>
+      The mode of an argument: <literal>IN</literal> or <literal>VARIADIC</literal>.
+      If omitted, the default is <literal>IN</literal>.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argname</replaceable></term>
+
+    <listitem>
+     <para>
+      The name of an argument.
+      Note that <command>DROP PROCEDURE</command> does not actually pay
+      any attention to argument names, since only the argument data
+      types are needed to determine the procedure's identity.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">argtype</replaceable></term>
+
+    <listitem>
+     <para>
+      The data type(s) of the procedure's arguments (optionally
+      schema-qualified), if any.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>CASCADE</literal></term>
+    <listitem>
+     <para>
+      Automatically drop objects that depend on the procedure,
+      and in turn all objects that depend on those objects
+      (see <xref linkend="ddl-depend"/>).
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><literal>RESTRICT</literal></term>
+    <listitem>
+     <para>
+      Refuse to drop the procedure if any objects depend on it.  This
+      is the default.
+     </para>
+    </listitem>
+   </varlistentry>
+  </variablelist>
+ </refsect1>
+
+ <refsect1 id="sql-dropprocedure-examples">
+  <title>Examples</title>
+
+<programlisting>
+DROP PROCEDURE do_db_maintenance();
+</programlisting>
+ </refsect1>
+
+ <refsect1 id="sql-dropprocedure-compatibility">
+  <title>Compatibility</title>
+
+  <para>
+   This command conforms to the SQL standard, with
+   these <productname>PostgreSQL</productname> extensions:
+   <itemizedlist>
+    <listitem>
+     <para>The standard only allows one procedure to be dropped per command.</para>
+    </listitem>
+    <listitem>
+     <para>The <literal>IF EXISTS</literal> option</para>
+    </listitem>
+    <listitem>
+     <para>The ability to specify argument modes and names</para>
+    </listitem>
+   </itemizedlist>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-createprocedure"/></member>
+   <member><xref linkend="sql-alterprocedure"/></member>
+   <member><xref linkend="sql-dropfunction"/></member>
+   <member><xref linkend="sql-droproutine"/></member>
+  </simplelist>
+ </refsect1>
+
+</refentry>
diff --git a/doc/src/sgml/ref/drop_routine.sgml b/doc/src/sgml/ref/drop_routine.sgml
new file mode 100644 (file)
index 0000000..5cd1a0f
--- /dev/null
@@ -0,0 +1,94 @@
+<!--
+doc/src/sgml/ref/drop_routine.sgml
+PostgreSQL documentation
+-->
+
+<refentry id="sql-droproutine">
+ <indexterm zone="sql-droproutine">
+  <primary>DROP ROUTINE</primary>
+ </indexterm>
+
+ <refmeta>
+  <refentrytitle>DROP ROUTINE</refentrytitle>
+  <manvolnum>7</manvolnum>
+  <refmiscinfo>SQL - Language Statements</refmiscinfo>
+ </refmeta>
+
+ <refnamediv>
+  <refname>DROP ROUTINE</refname>
+  <refpurpose>remove a routine</refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+<synopsis>
+DROP ROUTINE [ IF EXISTS ] <replaceable class="parameter">name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] [, ...]
+    [ CASCADE | RESTRICT ]
+</synopsis>
+ </refsynopsisdiv>
+
+ <refsect1>
+  <title>Description</title>
+
+  <para>
+   <command>DROP ROUTINE</command> removes the definition of an existing
+   routine, which can be an aggregate function, a normal function, or a
+   procedure.  See
+   under <xref linkend="sql-dropaggregate"/>, <xref linkend="sql-dropfunction"/>,
+   and <xref linkend="sql-dropprocedure"/> for the description of the
+   parameters, more examples, and further details.
+  </para>
+ </refsect1>
+
+ <refsect1 id="sql-droproutine-examples">
+  <title>Examples</title>
+
+  <para>
+   To drop the routine <literal>foo</literal> for type
+   <type>integer</type>:
+<programlisting>
+DROP ROUTINE foo(integer);
+</programlisting>
+   This command will work independent of whether <literal>foo</literal> is an
+   aggregate, function, or procedure.
+  </para>
+ </refsect1>
+
+ <refsect1 id="sql-droproutine-compatibility">
+  <title>Compatibility</title>
+
+  <para>
+   This command conforms to the SQL standard, with
+   these <productname>PostgreSQL</productname> extensions:
+   <itemizedlist>
+    <listitem>
+     <para>The standard only allows one routine to be dropped per command.</para>
+    </listitem>
+    <listitem>
+     <para>The <literal>IF EXISTS</literal> option</para>
+    </listitem>
+    <listitem>
+     <para>The ability to specify argument modes and names</para>
+    </listitem>
+    <listitem>
+     <para>Aggregate functions are an extension.</para>
+    </listitem>
+   </itemizedlist>
+  </para>
+ </refsect1>
+
+ <refsect1>
+  <title>See Also</title>
+
+  <simplelist type="inline">
+   <member><xref linkend="sql-dropaggregate"/></member>
+   <member><xref linkend="sql-dropfunction"/></member>
+   <member><xref linkend="sql-dropprocedure"/></member>
+   <member><xref linkend="sql-alterroutine"/></member>
+  </simplelist>
+
+  <para>
+   Note that there is no <literal>CREATE ROUTINE</literal> command.
+  </para>
+ </refsect1>
+
+</refentry>
index a5e895d09d7d800a23676f05ed667398a1744cfc..ff64c7a3bae65c3de3437ef2e8bef4af19bb8709 100644 (file)
@@ -55,8 +55,8 @@ GRANT { USAGE | ALL [ PRIVILEGES ] }
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
 
 GRANT { EXECUTE | ALL [ PRIVILEGES ] }
-    ON { FUNCTION <replaceable>function_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
-         | ALL FUNCTIONS IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
+    ON { { FUNCTION | PROCEDURE | ROUTINE } <replaceable>routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
+         | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA <replaceable class="parameter">schema_name</replaceable> [, ...] }
     TO <replaceable class="parameter">role_specification</replaceable> [, ...] [ WITH GRANT OPTION ]
 
 GRANT { USAGE | ALL [ PRIVILEGES ] }
@@ -96,7 +96,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
   <para>
    The <command>GRANT</command> command has two basic variants: one
    that grants privileges on a database object (table, column, view, foreign
-   table, sequence, database, foreign-data wrapper, foreign server, function,
+   table, sequence, database, foreign-data wrapper, foreign server, function, procedure,
    procedural language, schema, or tablespace), and one that grants
    membership in a role.  These variants are similar in many ways, but
    they are different enough to be described separately.
@@ -115,8 +115,11 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
   <para>
    There is also an option to grant privileges on all objects of the same
    type within one or more schemas.  This functionality is currently supported
-   only for tables, sequences, and functions (but note that <literal>ALL
-   TABLES</literal> is considered to include views and foreign tables).
+   only for tables, sequences, functions, and procedures.  <literal>ALL
+   TABLES</literal> also affects views and foreign tables, just like the
+   specific-object <command>GRANT</command> command.  <literal>ALL
+   FUNCTIONS</literal> also affects aggregate functions, but not procedures,
+   again just like the specific-object <command>GRANT</command> command.
   </para>
 
   <para>
@@ -169,7 +172,7 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
    granted to <literal>PUBLIC</literal> are as follows:
    <literal>CONNECT</literal> and <literal>TEMPORARY</literal> (create
    temporary tables) privileges for databases;
-   <literal>EXECUTE</literal> privilege for functions; and
+   <literal>EXECUTE</literal> privilege for functions and procedures; and
    <literal>USAGE</literal> privilege for languages and data types
    (including domains).
    The object owner can, of course, <command>REVOKE</command>
@@ -329,10 +332,12 @@ GRANT <replaceable class="parameter">role_name</replaceable> [, ...] TO <replace
      <term><literal>EXECUTE</literal></term>
      <listitem>
       <para>
-       Allows the use of the specified function and the use of any
-       operators that are implemented on top of the function.  This is
-       the only type of privilege that is applicable to functions.
-       (This syntax works for aggregate functions, as well.)
+       Allows the use of the specified function or procedure and the use of
+       any operators that are implemented on top of the function.  This is the
+       only type of privilege that is applicable to functions and procedures.
+       The <literal>FUNCTION</literal> syntax also works for aggregate
+       functions.  Alternatively, use <literal>ROUTINE</literal> to refer to a function,
+       aggregate function, or procedure regardless of what it is.
       </para>
      </listitem>
     </varlistentry>
index 4d133a782b6dd369b797eb336d82bb129ac4fd2e..7018202f144b5e38566e2f737aa20f691f6ba3cc 100644 (file)
@@ -70,8 +70,8 @@ REVOKE [ GRANT OPTION FOR ]
 
 REVOKE [ GRANT OPTION FOR ]
     { EXECUTE | ALL [ PRIVILEGES ] }
-    ON { FUNCTION <replaceable>function_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
-         | ALL FUNCTIONS IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
+    ON { { FUNCTION | PROCEDURE | ROUTINE } <replaceable>function_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">arg_name</replaceable> ] <replaceable class="parameter">arg_type</replaceable> [, ...] ] ) ] [, ...]
+         | ALL { FUNCTIONS | PROCEDURES | ROUTINES } IN SCHEMA <replaceable>schema_name</replaceable> [, ...] }
     FROM { [ GROUP ] <replaceable class="parameter">role_name</replaceable> | PUBLIC } [, ...]
     [ CASCADE | RESTRICT ]
 
index d52113e0359b7bf206e6290d0894926107142480..e9cfdec9f944642ae56ffe12b4deb4ba962cf253 100644 (file)
@@ -34,8 +34,10 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
   LARGE OBJECT <replaceable class="parameter">large_object_oid</replaceable> |
   MATERIALIZED VIEW <replaceable class="parameter">object_name</replaceable> |
   [ PROCEDURAL ] LANGUAGE <replaceable class="parameter">object_name</replaceable> |
+  PROCEDURE <replaceable class="parameter">procedure_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
   PUBLICATION <replaceable class="parameter">object_name</replaceable> |
   ROLE <replaceable class="parameter">object_name</replaceable> |
+  ROUTINE <replaceable class="parameter">routine_name</replaceable> [ ( [ [ <replaceable class="parameter">argmode</replaceable> ] [ <replaceable class="parameter">argname</replaceable> ] <replaceable class="parameter">argtype</replaceable> [, ...] ] ) ] |
   SCHEMA <replaceable class="parameter">object_name</replaceable> |
   SEQUENCE <replaceable class="parameter">object_name</replaceable> |
   SUBSCRIPTION <replaceable class="parameter">object_name</replaceable> |
@@ -93,10 +95,12 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
     <term><replaceable class="parameter">table_name.column_name</replaceable></term>
     <term><replaceable class="parameter">aggregate_name</replaceable></term>
     <term><replaceable class="parameter">function_name</replaceable></term>
+    <term><replaceable class="parameter">procedure_name</replaceable></term>
+    <term><replaceable class="parameter">routine_name</replaceable></term>
     <listitem>
      <para>
       The name of the object to be labeled.  Names of tables,
-      aggregates, domains, foreign tables, functions, sequences, types, and
+      aggregates, domains, foreign tables, functions, procedures, routines, sequences, types, and
       views can be schema-qualified.
      </para>
     </listitem>
@@ -119,7 +123,7 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
 
     <listitem>
      <para>
-      The mode of a function or aggregate
+      The mode of a function, procedure, or aggregate
       argument: <literal>IN</literal>, <literal>OUT</literal>,
       <literal>INOUT</literal>, or <literal>VARIADIC</literal>.
       If omitted, the default is <literal>IN</literal>.
@@ -137,7 +141,7 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
 
     <listitem>
      <para>
-      The name of a function or aggregate argument.
+      The name of a function, procedure, or aggregate argument.
       Note that <command>SECURITY LABEL</command> does not actually
       pay any attention to argument names, since only the argument data
       types are needed to determine the function's identity.
@@ -150,7 +154,7 @@ SECURITY LABEL [ FOR <replaceable class="parameter">provider</replaceable> ] ON
 
     <listitem>
      <para>
-      The data type of a function or aggregate argument.
+      The data type of a function, procedure, or aggregate argument.
      </para>
     </listitem>
    </varlistentry>
index d20eaa87e76c0f65546df90f8f6da1873f832d9f..d27fb414f7c55a369db99b43ce995a8717c853ea 100644 (file)
    &alterOperatorClass;
    &alterOperatorFamily;
    &alterPolicy;
+   &alterProcedure;
    &alterPublication;
    &alterRole;
+   &alterRoutine;
    &alterRule;
    &alterSchema;
    &alterSequence;
@@ -76,6 +78,7 @@
    &alterView;
    &analyze;
    &begin;
+   &call;
    &checkpoint;
    &close;
    &cluster;
    &createOperatorClass;
    &createOperatorFamily;
    &createPolicy;
+   &createProcedure;
    &createPublication;
    &createRole;
    &createRule;
    &dropOperatorFamily;
    &dropOwned;
    &dropPolicy;
+   &dropProcedure;
    &dropPublication;
    &dropRole;
+   &dropRoutine;
    &dropRule;
    &dropSchema;
    &dropSequence;
index 508ee7a96c0047a2bbfa9dfbbc92da22bfd4e133..bbc3766cc21ef1c63cb422552ff00ec171564bc0 100644 (file)
   </para>
   </sect1>
 
+  <sect1 id="xproc">
+   <title>User-defined Procedures</title>
+
+  <indexterm zone="xproc">
+   <primary>procedure</primary>
+   <secondary>user-defined</secondary>
+  </indexterm>
+
+   <para>
+    A procedure is a database object similar to a function.  The difference is
+    that a procedure does not return a value, so there is no return type
+    declaration.  While a function is called as part of a query or DML
+    command, a procedure is called explicitly using
+    the <xref linkend="sql-call"/> statement.
+   </para>
+
+   <para>
+    The explanations on how to define user-defined functions in the rest of
+    this chapter apply to procedures as well, except that
+    the <xref linkend="sql-createprocedure"/> command is used instead, there is
+    no return type, and some other features such as strictness don't apply.
+   </para>
+
+   <para>
+    Collectively, functions and procedures are also known
+    as <firstterm>routines</firstterm><indexterm><primary>routine</primary></indexterm>.
+    There are commands such as <xref linkend="sql-alterroutine"/>
+    and <xref linkend="sql-droproutine"/> that can operate on functions and
+    procedures without having to know which kind it is.  Note, however, that
+    there is no <literal>CREATE ROUTINE</literal> command.
+   </para>
+  </sect1>
+
   <sect1 id="xfunc-sql">
    <title>Query Language (<acronym>SQL</acronym>) Functions</title>
 
index ccde66a7dd71a2282b4e7c37688356a4ca7f87d2..e481cf3d118f78328d5a08814130639eaa82f567 100644 (file)
@@ -482,6 +482,14 @@ ExecuteGrantStmt(GrantStmt *stmt)
                        all_privileges = ACL_ALL_RIGHTS_NAMESPACE;
                        errormsg = gettext_noop("invalid privilege type %s for schema");
                        break;
+               case ACL_OBJECT_PROCEDURE:
+                       all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+                       errormsg = gettext_noop("invalid privilege type %s for procedure");
+                       break;
+               case ACL_OBJECT_ROUTINE:
+                       all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+                       errormsg = gettext_noop("invalid privilege type %s for routine");
+                       break;
                case ACL_OBJECT_TABLESPACE:
                        all_privileges = ACL_ALL_RIGHTS_TABLESPACE;
                        errormsg = gettext_noop("invalid privilege type %s for tablespace");
@@ -584,6 +592,8 @@ ExecGrantStmt_oids(InternalGrant *istmt)
                        ExecGrant_ForeignServer(istmt);
                        break;
                case ACL_OBJECT_FUNCTION:
+               case ACL_OBJECT_PROCEDURE:
+               case ACL_OBJECT_ROUTINE:
                        ExecGrant_Function(istmt);
                        break;
                case ACL_OBJECT_LANGUAGE:
@@ -671,7 +681,7 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
                                ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
                                Oid                     funcid;
 
-                               funcid = LookupFuncWithArgs(func, false);
+                               funcid = LookupFuncWithArgs(OBJECT_FUNCTION, func, false);
                                objects = lappend_oid(objects, funcid);
                        }
                        break;
@@ -709,6 +719,26 @@ objectNamesToOids(GrantObjectType objtype, List *objnames)
                                objects = lappend_oid(objects, oid);
                        }
                        break;
+               case ACL_OBJECT_PROCEDURE:
+                       foreach(cell, objnames)
+                       {
+                               ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
+                               Oid                     procid;
+
+                               procid = LookupFuncWithArgs(OBJECT_PROCEDURE, func, false);
+                               objects = lappend_oid(objects, procid);
+                       }
+                       break;
+               case ACL_OBJECT_ROUTINE:
+                       foreach(cell, objnames)
+                       {
+                               ObjectWithArgs *func = (ObjectWithArgs *) lfirst(cell);
+                               Oid                     routid;
+
+                               routid = LookupFuncWithArgs(OBJECT_ROUTINE, func, false);
+                               objects = lappend_oid(objects, routid);
+                       }
+                       break;
                case ACL_OBJECT_TABLESPACE:
                        foreach(cell, objnames)
                        {
@@ -785,19 +815,39 @@ objectsInSchemaToOids(GrantObjectType objtype, List *nspnames)
                                objects = list_concat(objects, objs);
                                break;
                        case ACL_OBJECT_FUNCTION:
+                       case ACL_OBJECT_PROCEDURE:
+                       case ACL_OBJECT_ROUTINE:
                                {
-                                       ScanKeyData key[1];
+                                       ScanKeyData key[2];
+                                       int                     keycount;
                                        Relation        rel;
                                        HeapScanDesc scan;
                                        HeapTuple       tuple;
 
-                                       ScanKeyInit(&key[0],
+                                       keycount = 0;
+                                       ScanKeyInit(&key[keycount++],
                                                                Anum_pg_proc_pronamespace,
                                                                BTEqualStrategyNumber, F_OIDEQ,
                                                                ObjectIdGetDatum(namespaceId));
 
+                                       /*
+                                        * When looking for functions, check for return type <>0.
+                                        * When looking for procedures, check for return type ==0.
+                                        * When looking for routines, don't check the return type.
+                                        */
+                                       if (objtype == ACL_OBJECT_FUNCTION)
+                                               ScanKeyInit(&key[keycount++],
+                                                                       Anum_pg_proc_prorettype,
+                                                                       BTEqualStrategyNumber, F_OIDNE,
+                                                                       InvalidOid);
+                                       else if (objtype == ACL_OBJECT_PROCEDURE)
+                                               ScanKeyInit(&key[keycount++],
+                                                                       Anum_pg_proc_prorettype,
+                                                                       BTEqualStrategyNumber, F_OIDEQ,
+                                                                       InvalidOid);
+
                                        rel = heap_open(ProcedureRelationId, AccessShareLock);
-                                       scan = heap_beginscan_catalog(rel, 1, key);
+                                       scan = heap_beginscan_catalog(rel, keycount, key);
 
                                        while ((tuple = heap_getnext(scan, ForwardScanDirection)) != NULL)
                                        {
@@ -955,6 +1005,14 @@ ExecAlterDefaultPrivilegesStmt(ParseState *pstate, AlterDefaultPrivilegesStmt *s
                        all_privileges = ACL_ALL_RIGHTS_FUNCTION;
                        errormsg = gettext_noop("invalid privilege type %s for function");
                        break;
+               case ACL_OBJECT_PROCEDURE:
+                       all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+                       errormsg = gettext_noop("invalid privilege type %s for procedure");
+                       break;
+               case ACL_OBJECT_ROUTINE:
+                       all_privileges = ACL_ALL_RIGHTS_FUNCTION;
+                       errormsg = gettext_noop("invalid privilege type %s for routine");
+                       break;
                case ACL_OBJECT_TYPE:
                        all_privileges = ACL_ALL_RIGHTS_TYPE;
                        errormsg = gettext_noop("invalid privilege type %s for type");
@@ -1423,7 +1481,7 @@ RemoveRoleFromObjectACL(Oid roleid, Oid classid, Oid objid)
                                istmt.objtype = ACL_OBJECT_TYPE;
                                break;
                        case ProcedureRelationId:
-                               istmt.objtype = ACL_OBJECT_FUNCTION;
+                               istmt.objtype = ACL_OBJECT_ROUTINE;
                                break;
                        case LanguageRelationId:
                                istmt.objtype = ACL_OBJECT_LANGUAGE;
index 236f6be37e846f5394527ffc70f9cbabe574c2c5..360725d59a3971b9c5876a0f8897fc23b81f057a 100644 (file)
@@ -1413,7 +1413,8 @@ CREATE VIEW routines AS
            CAST(current_database() AS sql_identifier) AS routine_catalog,
            CAST(n.nspname AS sql_identifier) AS routine_schema,
            CAST(p.proname AS sql_identifier) AS routine_name,
-           CAST('FUNCTION' AS character_data) AS routine_type,
+           CAST(CASE WHEN p.prorettype <> 0 THEN 'FUNCTION' ELSE 'PROCEDURE' END
+             AS character_data) AS routine_type,
            CAST(null AS sql_identifier) AS module_catalog,
            CAST(null AS sql_identifier) AS module_schema,
            CAST(null AS sql_identifier) AS module_name,
@@ -1422,7 +1423,8 @@ CREATE VIEW routines AS
            CAST(null AS sql_identifier) AS udt_name,
 
            CAST(
-             CASE WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ARRAY'
+             CASE WHEN p.prorettype = 0 THEN NULL
+                  WHEN t.typelem <> 0 AND t.typlen = -1 THEN 'ARRAY'
                   WHEN nt.nspname = 'pg_catalog' THEN format_type(t.oid, null)
                   ELSE 'USER-DEFINED' END AS character_data)
              AS data_type,
@@ -1440,7 +1442,7 @@ CREATE VIEW routines AS
            CAST(null AS cardinal_number) AS datetime_precision,
            CAST(null AS character_data) AS interval_type,
            CAST(null AS cardinal_number) AS interval_precision,
-           CAST(current_database() AS sql_identifier) AS type_udt_catalog,
+           CAST(CASE WHEN p.prorettype <> 0 THEN current_database() END AS sql_identifier) AS type_udt_catalog,
            CAST(nt.nspname AS sql_identifier) AS type_udt_schema,
            CAST(t.typname AS sql_identifier) AS type_udt_name,
            CAST(null AS sql_identifier) AS scope_catalog,
@@ -1462,7 +1464,8 @@ CREATE VIEW routines AS
            CAST('GENERAL' AS character_data) AS parameter_style,
            CAST(CASE WHEN p.provolatile = 'i' THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_deterministic,
            CAST('MODIFIES' AS character_data) AS sql_data_access,
-           CAST(CASE WHEN p.proisstrict THEN 'YES' ELSE 'NO' END AS yes_or_no) AS is_null_call,
+           CAST(CASE WHEN p.prorettype <> 0 THEN
+             CASE WHEN p.proisstrict THEN 'YES' ELSE 'NO' END END AS yes_or_no) AS is_null_call,
            CAST(null AS character_data) AS sql_path,
            CAST('YES' AS yes_or_no) AS schema_level_routine,
            CAST(0 AS cardinal_number) AS max_dynamic_result_sets,
@@ -1503,13 +1506,15 @@ CREATE VIEW routines AS
            CAST(null AS cardinal_number) AS result_cast_maximum_cardinality,
            CAST(null AS sql_identifier) AS result_cast_dtd_identifier
 
-    FROM pg_namespace n, pg_proc p, pg_language l,
-         pg_type t, pg_namespace nt
+    FROM (pg_namespace n
+          JOIN pg_proc p ON n.oid = p.pronamespace
+          JOIN pg_language l ON p.prolang = l.oid)
+         LEFT JOIN
+         (pg_type t JOIN pg_namespace nt ON t.typnamespace = nt.oid)
+         ON p.prorettype = t.oid
 
-    WHERE n.oid = p.pronamespace AND p.prolang = l.oid
-          AND p.prorettype = t.oid AND t.typnamespace = nt.oid
-          AND (pg_has_role(p.proowner, 'USAGE')
-               OR has_function_privilege(p.oid, 'EXECUTE'));
+    WHERE (pg_has_role(p.proowner, 'USAGE')
+           OR has_function_privilege(p.oid, 'EXECUTE'));
 
 GRANT SELECT ON routines TO PUBLIC;
 
index 8d55c76fc4afe4373c4b39ace537895501243137..9553675975d233dae5a813c20f9eb9b3062a420e 100644 (file)
@@ -566,6 +566,9 @@ static const struct object_type_map
        {
                "function", OBJECT_FUNCTION
        },
+       {
+               "procedure", OBJECT_PROCEDURE
+       },
        /* OCLASS_TYPE */
        {
                "type", OBJECT_TYPE
@@ -884,13 +887,11 @@ get_object_address(ObjectType objtype, Node *object,
                                address = get_object_address_type(objtype, castNode(TypeName, object), missing_ok);
                                break;
                        case OBJECT_AGGREGATE:
-                               address.classId = ProcedureRelationId;
-                               address.objectId = LookupAggWithArgs(castNode(ObjectWithArgs, object), missing_ok);
-                               address.objectSubId = 0;
-                               break;
                        case OBJECT_FUNCTION:
+                       case OBJECT_PROCEDURE:
+                       case OBJECT_ROUTINE:
                                address.classId = ProcedureRelationId;
-                               address.objectId = LookupFuncWithArgs(castNode(ObjectWithArgs, object), missing_ok);
+                               address.objectId = LookupFuncWithArgs(objtype, castNode(ObjectWithArgs, object), missing_ok);
                                address.objectSubId = 0;
                                break;
                        case OBJECT_OPERATOR:
@@ -2025,6 +2026,8 @@ pg_get_object_address(PG_FUNCTION_ARGS)
         */
        if (type == OBJECT_AGGREGATE ||
                type == OBJECT_FUNCTION ||
+               type == OBJECT_PROCEDURE ||
+               type == OBJECT_ROUTINE ||
                type == OBJECT_OPERATOR ||
                type == OBJECT_CAST ||
                type == OBJECT_AMOP ||
@@ -2168,6 +2171,8 @@ pg_get_object_address(PG_FUNCTION_ARGS)
                        objnode = (Node *) list_make2(name, args);
                        break;
                case OBJECT_FUNCTION:
+               case OBJECT_PROCEDURE:
+               case OBJECT_ROUTINE:
                case OBJECT_AGGREGATE:
                case OBJECT_OPERATOR:
                        {
@@ -2253,6 +2258,8 @@ check_object_ownership(Oid roleid, ObjectType objtype, ObjectAddress address,
                        break;
                case OBJECT_AGGREGATE:
                case OBJECT_FUNCTION:
+               case OBJECT_PROCEDURE:
+               case OBJECT_ROUTINE:
                        if (!pg_proc_ownercheck(address.objectId, roleid))
                                aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
                                                           NameListToString((castNode(ObjectWithArgs, object))->objname));
@@ -4026,6 +4033,8 @@ getProcedureTypeDescription(StringInfo buffer, Oid procid)
 
        if (procForm->proisagg)
                appendStringInfoString(buffer, "aggregate");
+       else if (procForm->prorettype == InvalidOid)
+               appendStringInfoString(buffer, "procedure");
        else
                appendStringInfoString(buffer, "function");
 
index 47916cfb544f552eff4a55970aae09f12f9cd78e..7d05e4bdb22cbad3dfc52c530840a35fc1189ff7 100644 (file)
@@ -857,7 +857,8 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
 
        /* Disallow pseudotype result */
        /* except for RECORD, VOID, or polymorphic */
-       if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
+       if (proc->prorettype &&
+               get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
                proc->prorettype != RECORDOID &&
                proc->prorettype != VOIDOID &&
                !IsPolymorphicType(proc->prorettype))
index adc9877e79ef83c5adfb4718f9af8fc716dc38d1..2e2ee883e260c8a37dd4e5a42afcf771b18227a5 100644 (file)
@@ -307,7 +307,7 @@ DefineAggregate(ParseState *pstate, List *name, List *args, bool oldstyle, List
                interpret_function_parameter_list(pstate,
                                                                                  args,
                                                                                  InvalidOid,
-                                                                                 true, /* is an aggregate */
+                                                                                 OBJECT_AGGREGATE,
                                                                                  &parameterTypes,
                                                                                  &allParameterTypes,
                                                                                  &parameterModes,
index 4f8147907c4e54a23d3f708b1186d5f1d30d5d9a..21e3f1efe1060e8a4aad334faef3b482a09a7f59 100644 (file)
@@ -378,6 +378,8 @@ ExecRenameStmt(RenameStmt *stmt)
                case OBJECT_OPCLASS:
                case OBJECT_OPFAMILY:
                case OBJECT_LANGUAGE:
+               case OBJECT_PROCEDURE:
+               case OBJECT_ROUTINE:
                case OBJECT_STATISTIC_EXT:
                case OBJECT_TSCONFIGURATION:
                case OBJECT_TSDICTIONARY:
@@ -495,6 +497,8 @@ ExecAlterObjectSchemaStmt(AlterObjectSchemaStmt *stmt,
                case OBJECT_OPERATOR:
                case OBJECT_OPCLASS:
                case OBJECT_OPFAMILY:
+               case OBJECT_PROCEDURE:
+               case OBJECT_ROUTINE:
                case OBJECT_STATISTIC_EXT:
                case OBJECT_TSCONFIGURATION:
                case OBJECT_TSDICTIONARY:
@@ -842,6 +846,8 @@ ExecAlterOwnerStmt(AlterOwnerStmt *stmt)
                case OBJECT_OPERATOR:
                case OBJECT_OPCLASS:
                case OBJECT_OPFAMILY:
+               case OBJECT_PROCEDURE:
+               case OBJECT_ROUTINE:
                case OBJECT_STATISTIC_EXT:
                case OBJECT_TABLESPACE:
                case OBJECT_TSDICTIONARY:
index 2b30677d6f917bf5db9b3b2d0a6dc054ae327e9c..7e6baa1928d008208d415ea36fca438bcae6441f 100644 (file)
@@ -26,6 +26,7 @@
 #include "nodes/makefuncs.h"
 #include "parser/parse_type.h"
 #include "utils/builtins.h"
+#include "utils/lsyscache.h"
 #include "utils/syscache.h"
 
 
@@ -91,21 +92,12 @@ RemoveObjects(DropStmt *stmt)
                 */
                if (stmt->removeType == OBJECT_FUNCTION)
                {
-                       Oid                     funcOid = address.objectId;
-                       HeapTuple       tup;
-
-                       tup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcOid));
-                       if (!HeapTupleIsValid(tup)) /* should not happen */
-                               elog(ERROR, "cache lookup failed for function %u", funcOid);
-
-                       if (((Form_pg_proc) GETSTRUCT(tup))->proisagg)
+                       if (get_func_isagg(address.objectId))
                                ereport(ERROR,
                                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                                 errmsg("\"%s\" is an aggregate function",
                                                                NameListToString(castNode(ObjectWithArgs, object)->objname)),
                                                 errhint("Use DROP AGGREGATE to drop aggregate functions.")));
-
-                       ReleaseSysCache(tup);
                }
 
                /* Check permissions. */
@@ -338,6 +330,32 @@ does_not_exist_skipping(ObjectType objtype, Node *object)
                                }
                                break;
                        }
+               case OBJECT_PROCEDURE:
+                       {
+                               ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
+
+                               if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
+                                       !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
+                               {
+                                       msg = gettext_noop("procedure %s(%s) does not exist, skipping");
+                                       name = NameListToString(owa->objname);
+                                       args = TypeNameListToString(owa->objargs);
+                               }
+                               break;
+                       }
+               case OBJECT_ROUTINE:
+                       {
+                               ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
+
+                               if (!schema_does_not_exist_skipping(owa->objname, &msg, &name) &&
+                                       !type_in_list_does_not_exist_skipping(owa->objargs, &msg, &name))
+                               {
+                                       msg = gettext_noop("routine %s(%s) does not exist, skipping");
+                                       name = NameListToString(owa->objname);
+                                       args = TypeNameListToString(owa->objargs);
+                               }
+                               break;
+                       }
                case OBJECT_AGGREGATE:
                        {
                                ObjectWithArgs *owa = castNode(ObjectWithArgs, object);
index fa7d0d015aa8dda1e2b861758b788462217ca5d8..a602c20b4161b694d0570e4c2dff2be6d729637f 100644 (file)
@@ -106,8 +106,10 @@ static event_trigger_support_data event_trigger_support[] = {
        {"OPERATOR CLASS", true},
        {"OPERATOR FAMILY", true},
        {"POLICY", true},
+       {"PROCEDURE", true},
        {"PUBLICATION", true},
        {"ROLE", false},
+       {"ROUTINE", true},
        {"RULE", true},
        {"SCHEMA", true},
        {"SEQUENCE", true},
@@ -1103,8 +1105,10 @@ EventTriggerSupportsObjectType(ObjectType obtype)
                case OBJECT_OPERATOR:
                case OBJECT_OPFAMILY:
                case OBJECT_POLICY:
+               case OBJECT_PROCEDURE:
                case OBJECT_PUBLICATION:
                case OBJECT_PUBLICATION_REL:
+               case OBJECT_ROUTINE:
                case OBJECT_RULE:
                case OBJECT_SCHEMA:
                case OBJECT_SEQUENCE:
@@ -1215,6 +1219,8 @@ EventTriggerSupportsGrantObjectType(GrantObjectType objtype)
                case ACL_OBJECT_LANGUAGE:
                case ACL_OBJECT_LARGEOBJECT:
                case ACL_OBJECT_NAMESPACE:
+               case ACL_OBJECT_PROCEDURE:
+               case ACL_OBJECT_ROUTINE:
                case ACL_OBJECT_TYPE:
                        return true;
 
@@ -2243,6 +2249,10 @@ stringify_grantobjtype(GrantObjectType objtype)
                        return "LARGE OBJECT";
                case ACL_OBJECT_NAMESPACE:
                        return "SCHEMA";
+               case ACL_OBJECT_PROCEDURE:
+                       return "PROCEDURE";
+               case ACL_OBJECT_ROUTINE:
+                       return "ROUTINE";
                case ACL_OBJECT_TABLESPACE:
                        return "TABLESPACE";
                case ACL_OBJECT_TYPE:
@@ -2285,6 +2295,10 @@ stringify_adefprivs_objtype(GrantObjectType objtype)
                        return "LARGE OBJECTS";
                case ACL_OBJECT_NAMESPACE:
                        return "SCHEMAS";
+               case ACL_OBJECT_PROCEDURE:
+                       return "PROCEDURES";
+               case ACL_OBJECT_ROUTINE:
+                       return "ROUTINES";
                case ACL_OBJECT_TABLESPACE:
                        return "TABLESPACES";
                case ACL_OBJECT_TYPE:
index 7de844b2cadff52a6d295eb66c30c0278f58d8e1..2a9c90133d182be3bf3b7e3aa25984fd1db4b579 100644 (file)
@@ -51,6 +51,8 @@
 #include "commands/alter.h"
 #include "commands/defrem.h"
 #include "commands/proclang.h"
+#include "executor/execdesc.h"
+#include "executor/executor.h"
 #include "miscadmin.h"
 #include "optimizer/var.h"
 #include "parser/parse_coerce.h"
@@ -179,7 +181,7 @@ void
 interpret_function_parameter_list(ParseState *pstate,
                                                                  List *parameters,
                                                                  Oid languageOid,
-                                                                 bool is_aggregate,
+                                                                 ObjectType objtype,
                                                                  oidvector **parameterTypes,
                                                                  ArrayType **allParameterTypes,
                                                                  ArrayType **parameterModes,
@@ -233,7 +235,7 @@ interpret_function_parameter_list(ParseState *pstate,
                                                         errmsg("SQL function cannot accept shell type %s",
                                                                        TypeNameToString(t))));
                                /* We don't allow creating aggregates on shell types either */
-                               else if (is_aggregate)
+                               else if (objtype == OBJECT_AGGREGATE)
                                        ereport(ERROR,
                                                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                                                         errmsg("aggregate cannot accept shell type %s",
@@ -262,16 +264,28 @@ interpret_function_parameter_list(ParseState *pstate,
 
                if (t->setof)
                {
-                       if (is_aggregate)
+                       if (objtype == OBJECT_AGGREGATE)
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                                                 errmsg("aggregates cannot accept set arguments")));
+                       else if (objtype == OBJECT_PROCEDURE)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("procedures cannot accept set arguments")));
                        else
                                ereport(ERROR,
                                                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                                                 errmsg("functions cannot accept set arguments")));
                }
 
+               if (objtype == OBJECT_PROCEDURE)
+               {
+                       if (fp->mode == FUNC_PARAM_OUT || fp->mode == FUNC_PARAM_INOUT)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                                (errmsg("procedures cannot have OUT parameters"))));
+               }
+
                /* handle input parameters */
                if (fp->mode != FUNC_PARAM_OUT && fp->mode != FUNC_PARAM_TABLE)
                {
@@ -451,6 +465,7 @@ interpret_function_parameter_list(ParseState *pstate,
  */
 static bool
 compute_common_attribute(ParseState *pstate,
+                                                bool is_procedure,
                                                 DefElem *defel,
                                                 DefElem **volatility_item,
                                                 DefElem **strict_item,
@@ -463,6 +478,8 @@ compute_common_attribute(ParseState *pstate,
 {
        if (strcmp(defel->defname, "volatility") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*volatility_item)
                        goto duplicate_error;
 
@@ -470,6 +487,8 @@ compute_common_attribute(ParseState *pstate,
        }
        else if (strcmp(defel->defname, "strict") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*strict_item)
                        goto duplicate_error;
 
@@ -484,6 +503,8 @@ compute_common_attribute(ParseState *pstate,
        }
        else if (strcmp(defel->defname, "leakproof") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*leakproof_item)
                        goto duplicate_error;
 
@@ -495,6 +516,8 @@ compute_common_attribute(ParseState *pstate,
        }
        else if (strcmp(defel->defname, "cost") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*cost_item)
                        goto duplicate_error;
 
@@ -502,6 +525,8 @@ compute_common_attribute(ParseState *pstate,
        }
        else if (strcmp(defel->defname, "rows") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*rows_item)
                        goto duplicate_error;
 
@@ -509,6 +534,8 @@ compute_common_attribute(ParseState *pstate,
        }
        else if (strcmp(defel->defname, "parallel") == 0)
        {
+               if (is_procedure)
+                       goto procedure_error;
                if (*parallel_item)
                        goto duplicate_error;
 
@@ -526,6 +553,13 @@ duplicate_error:
                         errmsg("conflicting or redundant options"),
                         parser_errposition(pstate, defel->location)));
        return false;                           /* keep compiler quiet */
+
+procedure_error:
+       ereport(ERROR,
+                       (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                        errmsg("invalid attribute in procedure definition"),
+                        parser_errposition(pstate, defel->location)));
+       return false;
 }
 
 static char
@@ -603,6 +637,7 @@ update_proconfig_value(ArrayType *a, List *set_items)
  */
 static void
 compute_attributes_sql_style(ParseState *pstate,
+                                                        bool is_procedure,
                                                         List *options,
                                                         List **as,
                                                         char **language,
@@ -669,9 +704,15 @@ compute_attributes_sql_style(ParseState *pstate,
                                                (errcode(ERRCODE_SYNTAX_ERROR),
                                                 errmsg("conflicting or redundant options"),
                                                 parser_errposition(pstate, defel->location)));
+                       if (is_procedure)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("invalid attribute in procedure definition"),
+                                                parser_errposition(pstate, defel->location)));
                        windowfunc_item = defel;
                }
                else if (compute_common_attribute(pstate,
+                                                                                 is_procedure,
                                                                                  defel,
                                                                                  &volatility_item,
                                                                                  &strict_item,
@@ -762,7 +803,7 @@ compute_attributes_sql_style(ParseState *pstate,
  *------------
  */
 static void
-compute_attributes_with_style(ParseState *pstate, List *parameters, bool *isStrict_p, char *volatility_p)
+compute_attributes_with_style(ParseState *pstate, bool is_procedure, List *parameters, bool *isStrict_p, char *volatility_p)
 {
        ListCell   *pl;
 
@@ -771,10 +812,22 @@ compute_attributes_with_style(ParseState *pstate, List *parameters, bool *isStri
                DefElem    *param = (DefElem *) lfirst(pl);
 
                if (pg_strcasecmp(param->defname, "isstrict") == 0)
+               {
+                       if (is_procedure)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("invalid attribute in procedure definition"),
+                                                parser_errposition(pstate, param->location)));
                        *isStrict_p = defGetBoolean(param);
+               }
                else if (pg_strcasecmp(param->defname, "iscachable") == 0)
                {
                        /* obsolete spelling of isImmutable */
+                       if (is_procedure)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
+                                                errmsg("invalid attribute in procedure definition"),
+                                                parser_errposition(pstate, param->location)));
                        if (defGetBoolean(param))
                                *volatility_p = PROVOLATILE_IMMUTABLE;
                }
@@ -916,6 +969,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
 
        /* override attributes from explicit list */
        compute_attributes_sql_style(pstate,
+                                                                stmt->is_procedure,
                                                                 stmt->options,
                                                                 &as_clause, &language, &transformDefElem,
                                                                 &isWindowFunc, &volatility,
@@ -990,7 +1044,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
        interpret_function_parameter_list(pstate,
                                                                          stmt->parameters,
                                                                          languageOid,
-                                                                         false,        /* not an aggregate */
+                                                                         stmt->is_procedure ? OBJECT_PROCEDURE : OBJECT_FUNCTION,
                                                                          &parameterTypes,
                                                                          &allParameterTypes,
                                                                          &parameterModes,
@@ -999,7 +1053,14 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
                                                                          &variadicArgType,
                                                                          &requiredResultType);
 
-       if (stmt->returnType)
+       if (stmt->is_procedure)
+       {
+               Assert(!stmt->returnType);
+
+               prorettype = InvalidOid;
+               returnsSet = false;
+       }
+       else if (stmt->returnType)
        {
                /* explicit RETURNS clause */
                compute_return_type(stmt->returnType, languageOid,
@@ -1045,7 +1106,7 @@ CreateFunction(ParseState *pstate, CreateFunctionStmt *stmt)
                trftypes = NULL;
        }
 
-       compute_attributes_with_style(pstate, stmt->withClause, &isStrict, &volatility);
+       compute_attributes_with_style(pstate, stmt->is_procedure, stmt->withClause, &isStrict, &volatility);
 
        interpret_AS_clause(languageOid, language, funcname, as_clause,
                                                &prosrc_str, &probin_str);
@@ -1168,6 +1229,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
        HeapTuple       tup;
        Oid                     funcOid;
        Form_pg_proc procForm;
+       bool            is_procedure;
        Relation        rel;
        ListCell   *l;
        DefElem    *volatility_item = NULL;
@@ -1182,7 +1244,7 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
 
        rel = heap_open(ProcedureRelationId, RowExclusiveLock);
 
-       funcOid = LookupFuncWithArgs(stmt->func, false);
+       funcOid = LookupFuncWithArgs(stmt->objtype, stmt->func, false);
 
        tup = SearchSysCacheCopy1(PROCOID, ObjectIdGetDatum(funcOid));
        if (!HeapTupleIsValid(tup)) /* should not happen */
@@ -1201,12 +1263,15 @@ AlterFunction(ParseState *pstate, AlterFunctionStmt *stmt)
                                 errmsg("\"%s\" is an aggregate function",
                                                NameListToString(stmt->func->objname))));
 
+       is_procedure = (procForm->prorettype == InvalidOid);
+
        /* Examine requested actions. */
        foreach(l, stmt->actions)
        {
                DefElem    *defel = (DefElem *) lfirst(l);
 
                if (compute_common_attribute(pstate,
+                                                                        is_procedure,
                                                                         defel,
                                                                         &volatility_item,
                                                                         &strict_item,
@@ -1472,7 +1537,7 @@ CreateCast(CreateCastStmt *stmt)
        {
                Form_pg_proc procstruct;
 
-               funcid = LookupFuncWithArgs(stmt->func, false);
+               funcid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->func, false);
 
                tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
                if (!HeapTupleIsValid(tuple))
@@ -1853,7 +1918,7 @@ CreateTransform(CreateTransformStmt *stmt)
         */
        if (stmt->fromsql)
        {
-               fromsqlfuncid = LookupFuncWithArgs(stmt->fromsql, false);
+               fromsqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->fromsql, false);
 
                if (!pg_proc_ownercheck(fromsqlfuncid, GetUserId()))
                        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->fromsql->objname));
@@ -1879,7 +1944,7 @@ CreateTransform(CreateTransformStmt *stmt)
 
        if (stmt->tosql)
        {
-               tosqlfuncid = LookupFuncWithArgs(stmt->tosql, false);
+               tosqlfuncid = LookupFuncWithArgs(OBJECT_FUNCTION, stmt->tosql, false);
 
                if (!pg_proc_ownercheck(tosqlfuncid, GetUserId()))
                        aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC, NameListToString(stmt->tosql->objname));
@@ -2168,3 +2233,80 @@ ExecuteDoStmt(DoStmt *stmt)
        /* execute the inline handler */
        OidFunctionCall1(laninline, PointerGetDatum(codeblock));
 }
+
+/*
+ * Execute CALL statement
+ */
+void
+ExecuteCallStmt(ParseState *pstate, CallStmt *stmt)
+{
+       List       *targs;
+       ListCell   *lc;
+       Node       *node;
+       FuncExpr   *fexpr;
+       int                     nargs;
+       int                     i;
+       AclResult   aclresult;
+       FmgrInfo        flinfo;
+       FunctionCallInfoData fcinfo;
+
+       targs = NIL;
+       foreach(lc, stmt->funccall->args)
+       {
+               targs = lappend(targs, transformExpr(pstate,
+                                                                                        (Node *) lfirst(lc),
+                                                                                        EXPR_KIND_CALL));
+       }
+
+       node = ParseFuncOrColumn(pstate,
+                                                        stmt->funccall->funcname,
+                                                        targs,
+                                                        pstate->p_last_srf,
+                                                        stmt->funccall,
+                                                        true,
+                                                        stmt->funccall->location);
+
+       fexpr = castNode(FuncExpr, node);
+
+       aclresult = pg_proc_aclcheck(fexpr->funcid, GetUserId(), ACL_EXECUTE);
+       if (aclresult != ACLCHECK_OK)
+               aclcheck_error(aclresult, ACL_KIND_PROC, get_func_name(fexpr->funcid));
+       InvokeFunctionExecuteHook(fexpr->funcid);
+
+       nargs = list_length(fexpr->args);
+
+       /* safety check; see ExecInitFunc() */
+       if (nargs > FUNC_MAX_ARGS)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg_plural("cannot pass more than %d argument to a procedure",
+                                                          "cannot pass more than %d arguments to a procedure",
+                                                          FUNC_MAX_ARGS,
+                                                          FUNC_MAX_ARGS)));
+
+       fmgr_info(fexpr->funcid, &flinfo);
+       InitFunctionCallInfoData(fcinfo, &flinfo, nargs, fexpr->inputcollid, NULL, NULL);
+
+       i = 0;
+       foreach (lc, fexpr->args)
+       {
+               EState     *estate;
+               ExprState  *exprstate;
+               ExprContext *econtext;
+               Datum           val;
+               bool            isnull;
+
+               estate = CreateExecutorState();
+               exprstate = ExecPrepareExpr(lfirst(lc), estate);
+               econtext = CreateStandaloneExprContext();
+               val = ExecEvalExprSwitchContext(exprstate, econtext, &isnull);
+               FreeExecutorState(estate);
+
+               fcinfo.arg[i] = val;
+               fcinfo.argnull[i] = isnull;
+
+               i++;
+       }
+
+       FunctionCallInvoke(&fcinfo);
+}
index 1641e68abec05097d71f860776532ba5745d42fc..35c7c67bf5137337615e342117cda3bbaeadcf27 100644 (file)
@@ -520,7 +520,7 @@ DefineOpClass(CreateOpClassStmt *stmt)
                                                         errmsg("invalid procedure number %d,"
                                                                        " must be between 1 and %d",
                                                                        item->number, maxProcNumber)));
-                               funcOid = LookupFuncWithArgs(item->name, false);
+                               funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
 #ifdef NOT_USED
                                /* XXX this is unnecessary given the superuser check above */
                                /* Caller must own function */
@@ -894,7 +894,7 @@ AlterOpFamilyAdd(AlterOpFamilyStmt *stmt, Oid amoid, Oid opfamilyoid,
                                                         errmsg("invalid procedure number %d,"
                                                                        " must be between 1 and %d",
                                                                        item->number, maxProcNumber)));
-                               funcOid = LookupFuncWithArgs(item->name, false);
+                               funcOid = LookupFuncWithArgs(OBJECT_FUNCTION, item->name, false);
 #ifdef NOT_USED
                                /* XXX this is unnecessary given the superuser check above */
                                /* Caller must own function */
index 98eb777421bbaaf238ef1ee4ecc6581edfd84d34..3caa343723515e98ced8ef19d204583754de3746 100644 (file)
@@ -390,6 +390,7 @@ sql_fn_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
                                                                  list_make1(param),
                                                                  pstate->p_last_srf,
                                                                  NULL,
+                                                                 false,
                                                                  cref->location);
        }
 
@@ -658,7 +659,8 @@ init_sql_fcache(FmgrInfo *finfo, Oid collation, bool lazyEvalOK)
        fcache->rettype = rettype;
 
        /* Fetch the typlen and byval info for the result type */
-       get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);
+       if (rettype)
+               get_typlenbyval(rettype, &fcache->typlen, &fcache->typbyval);
 
        /* Remember whether we're returning setof something */
        fcache->returnsSet = procedureStruct->proretset;
@@ -1321,8 +1323,8 @@ fmgr_sql(PG_FUNCTION_ARGS)
                }
                else
                {
-                       /* Should only get here for VOID functions */
-                       Assert(fcache->rettype == VOIDOID);
+                       /* Should only get here for procedures and VOID functions */
+                       Assert(fcache->rettype == InvalidOid || fcache->rettype == VOIDOID);
                        fcinfo->isnull = true;
                        result = (Datum) 0;
                }
@@ -1546,7 +1548,10 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
        if (modifyTargetList)
                *modifyTargetList = false;      /* initialize for no change */
        if (junkFilter)
-               *junkFilter = NULL;             /* initialize in case of VOID result */
+               *junkFilter = NULL;             /* initialize in case of procedure/VOID result */
+
+       if (!rettype)
+               return false;
 
        /*
         * Find the last canSetTag query in the list.  This isn't necessarily the
@@ -1591,7 +1596,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
        else
        {
                /* Empty function body, or last statement is a utility command */
-               if (rettype != VOIDOID)
+               if (rettype && rettype != VOIDOID)
                        ereport(ERROR,
                                        (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                                         errmsg("return type mismatch in function declared to return %s",
index d9ff8a7e510a2f0027f19e8d4d1ba6c57408aa53..aff9a62106d64bd52b68e98487fd968d40aa3016 100644 (file)
@@ -3210,6 +3210,16 @@ _copyClosePortalStmt(const ClosePortalStmt *from)
        return newnode;
 }
 
+static CallStmt *
+_copyCallStmt(const CallStmt *from)
+{
+       CallStmt *newnode = makeNode(CallStmt);
+
+       COPY_NODE_FIELD(funccall);
+
+       return newnode;
+}
+
 static ClusterStmt *
 _copyClusterStmt(const ClusterStmt *from)
 {
@@ -3411,6 +3421,7 @@ _copyCreateFunctionStmt(const CreateFunctionStmt *from)
        COPY_NODE_FIELD(funcname);
        COPY_NODE_FIELD(parameters);
        COPY_NODE_FIELD(returnType);
+       COPY_SCALAR_FIELD(is_procedure);
        COPY_NODE_FIELD(options);
        COPY_NODE_FIELD(withClause);
 
@@ -3435,6 +3446,7 @@ _copyAlterFunctionStmt(const AlterFunctionStmt *from)
 {
        AlterFunctionStmt *newnode = makeNode(AlterFunctionStmt);
 
+       COPY_SCALAR_FIELD(objtype);
        COPY_NODE_FIELD(func);
        COPY_NODE_FIELD(actions);
 
@@ -5104,6 +5116,9 @@ copyObjectImpl(const void *from)
                case T_ClosePortalStmt:
                        retval = _copyClosePortalStmt(from);
                        break;
+               case T_CallStmt:
+                       retval = _copyCallStmt(from);
+                       break;
                case T_ClusterStmt:
                        retval = _copyClusterStmt(from);
                        break;
index 2866fd7b4afd2bc73c2d42b69c4f948b7344b41f..2e869a9d5d8473c03befb3723d1471e4c8fb78e4 100644 (file)
@@ -1201,6 +1201,14 @@ _equalClosePortalStmt(const ClosePortalStmt *a, const ClosePortalStmt *b)
        return true;
 }
 
+static bool
+_equalCallStmt(const CallStmt *a, const CallStmt *b)
+{
+       COMPARE_NODE_FIELD(funccall);
+
+       return true;
+}
+
 static bool
 _equalClusterStmt(const ClusterStmt *a, const ClusterStmt *b)
 {
@@ -1364,6 +1372,7 @@ _equalCreateFunctionStmt(const CreateFunctionStmt *a, const CreateFunctionStmt *
        COMPARE_NODE_FIELD(funcname);
        COMPARE_NODE_FIELD(parameters);
        COMPARE_NODE_FIELD(returnType);
+       COMPARE_SCALAR_FIELD(is_procedure);
        COMPARE_NODE_FIELD(options);
        COMPARE_NODE_FIELD(withClause);
 
@@ -1384,6 +1393,7 @@ _equalFunctionParameter(const FunctionParameter *a, const FunctionParameter *b)
 static bool
 _equalAlterFunctionStmt(const AlterFunctionStmt *a, const AlterFunctionStmt *b)
 {
+       COMPARE_SCALAR_FIELD(objtype);
        COMPARE_NODE_FIELD(func);
        COMPARE_NODE_FIELD(actions);
 
@@ -3246,6 +3256,9 @@ equal(const void *a, const void *b)
                case T_ClosePortalStmt:
                        retval = _equalClosePortalStmt(a, b);
                        break;
+               case T_CallStmt:
+                       retval = _equalCallStmt(a, b);
+                       break;
                case T_ClusterStmt:
                        retval = _equalClusterStmt(a, b);
                        break;
index e5e2956564637e8a66b99931a8bcc67db4c7228d..6a2d5ad760f47b041ba9259ded08a2e37f00995d 100644 (file)
@@ -4401,6 +4401,7 @@ inline_function(Oid funcid, Oid result_type, Oid result_collid,
        if (funcform->prolang != SQLlanguageId ||
                funcform->prosecdef ||
                funcform->proretset ||
+               funcform->prorettype == InvalidOid ||
                funcform->prorettype == RECORDOID ||
                !heap_attisnull(func_tuple, Anum_pg_proc_proconfig) ||
                funcform->pronargs != list_length(args))
index c301ca465d471e7173dcbc97b4592c89fb6fed6c..ebfc94f89693efa6d0b8eade705648f9ee91c231 100644 (file)
@@ -253,7 +253,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
                AlterCompositeTypeStmt AlterUserMappingStmt
                AlterRoleStmt AlterRoleSetStmt AlterPolicyStmt
                AlterDefaultPrivilegesStmt DefACLAction
-               AnalyzeStmt ClosePortalStmt ClusterStmt CommentStmt
+               AnalyzeStmt CallStmt ClosePortalStmt ClusterStmt CommentStmt
                ConstraintsSetStmt CopyStmt CreateAsStmt CreateCastStmt
                CreateDomainStmt CreateExtensionStmt CreateGroupStmt CreateOpClassStmt
                CreateOpFamilyStmt AlterOpFamilyStmt CreatePLangStmt
@@ -611,7 +611,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
        BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT
        BOOLEAN_P BOTH BY
 
-       CACHE CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
+       CACHE CALL CALLED CASCADE CASCADED CASE CAST CATALOG_P CHAIN CHAR_P
        CHARACTER CHARACTERISTICS CHECK CHECKPOINT CLASS CLOSE
        CLUSTER COALESCE COLLATE COLLATION COLUMN COLUMNS COMMENT COMMENTS COMMIT
        COMMITTED CONCURRENTLY CONFIGURATION CONFLICT CONNECTION CONSTRAINT
@@ -660,14 +660,14 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
 
        PARALLEL PARSER PARTIAL PARTITION PASSING PASSWORD PLACING PLANS POLICY
        POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
-       PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROGRAM PUBLICATION
+       PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
 
        QUOTE
 
        RANGE READ REAL REASSIGN RECHECK RECURSIVE REF REFERENCES REFERENCING
        REFRESH REINDEX RELATIVE_P RELEASE RENAME REPEATABLE REPLACE REPLICA
        RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP
-       ROW ROWS RULE
+       ROUTINE ROUTINES ROW ROWS RULE
 
        SAVEPOINT SCHEMA SCHEMAS SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES
        SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW
@@ -845,6 +845,7 @@ stmt :
                        | AlterTSDictionaryStmt
                        | AlterUserMappingStmt
                        | AnalyzeStmt
+                       | CallStmt
                        | CheckPointStmt
                        | ClosePortalStmt
                        | ClusterStmt
@@ -940,6 +941,20 @@ stmt :
                                { $$ = NULL; }
                ;
 
+/*****************************************************************************
+ *
+ * CALL statement
+ *
+ *****************************************************************************/
+
+CallStmt:      CALL func_application
+                               {
+                                       CallStmt *n = makeNode(CallStmt);
+                                       n->funccall = castNode(FuncCall, $2);
+                                       $$ = (Node *)n;
+                               }
+               ;
+
 /*****************************************************************************
  *
  * Create a new Postgres DBMS role
@@ -4554,6 +4569,24 @@ AlterExtensionContentsStmt:
                                        n->object = (Node *) lcons(makeString($9), $7);
                                        $$ = (Node *)n;
                                }
+                       | ALTER EXTENSION name add_drop PROCEDURE function_with_argtypes
+                               {
+                                       AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
+                                       n->extname = $3;
+                                       n->action = $4;
+                                       n->objtype = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $6;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER EXTENSION name add_drop ROUTINE function_with_argtypes
+                               {
+                                       AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
+                                       n->extname = $3;
+                                       n->action = $4;
+                                       n->objtype = OBJECT_ROUTINE;
+                                       n->object = (Node *) $6;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER EXTENSION name add_drop SCHEMA name
                                {
                                        AlterExtensionContentsStmt *n = makeNode(AlterExtensionContentsStmt);
@@ -6436,6 +6469,22 @@ CommentStmt:
                                        n->comment = $8;
                                        $$ = (Node *) n;
                                }
+                       | COMMENT ON PROCEDURE function_with_argtypes IS comment_text
+                               {
+                                       CommentStmt *n = makeNode(CommentStmt);
+                                       n->objtype = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $4;
+                                       n->comment = $6;
+                                       $$ = (Node *) n;
+                               }
+                       | COMMENT ON ROUTINE function_with_argtypes IS comment_text
+                               {
+                                       CommentStmt *n = makeNode(CommentStmt);
+                                       n->objtype = OBJECT_ROUTINE;
+                                       n->object = (Node *) $4;
+                                       n->comment = $6;
+                                       $$ = (Node *) n;
+                               }
                        | COMMENT ON RULE name ON any_name IS comment_text
                                {
                                        CommentStmt *n = makeNode(CommentStmt);
@@ -6614,6 +6663,26 @@ SecLabelStmt:
                                        n->label = $9;
                                        $$ = (Node *) n;
                                }
+                       | SECURITY LABEL opt_provider ON PROCEDURE function_with_argtypes
+                         IS security_label
+                               {
+                                       SecLabelStmt *n = makeNode(SecLabelStmt);
+                                       n->provider = $3;
+                                       n->objtype = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $6;
+                                       n->label = $8;
+                                       $$ = (Node *) n;
+                               }
+                       | SECURITY LABEL opt_provider ON ROUTINE function_with_argtypes
+                         IS security_label
+                               {
+                                       SecLabelStmt *n = makeNode(SecLabelStmt);
+                                       n->provider = $3;
+                                       n->objtype = OBJECT_ROUTINE;
+                                       n->object = (Node *) $6;
+                                       n->label = $8;
+                                       $$ = (Node *) n;
+                               }
                ;
 
 opt_provider:  FOR NonReservedWord_or_Sconst   { $$ = $2; }
@@ -6977,6 +7046,22 @@ privilege_target:
                                        n->objs = $2;
                                        $$ = n;
                                }
+                       | PROCEDURE function_with_argtypes_list
+                               {
+                                       PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+                                       n->targtype = ACL_TARGET_OBJECT;
+                                       n->objtype = ACL_OBJECT_PROCEDURE;
+                                       n->objs = $2;
+                                       $$ = n;
+                               }
+                       | ROUTINE function_with_argtypes_list
+                               {
+                                       PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+                                       n->targtype = ACL_TARGET_OBJECT;
+                                       n->objtype = ACL_OBJECT_ROUTINE;
+                                       n->objs = $2;
+                                       $$ = n;
+                               }
                        | DATABASE name_list
                                {
                                        PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
@@ -7057,6 +7142,22 @@ privilege_target:
                                        n->objs = $5;
                                        $$ = n;
                                }
+                       | ALL PROCEDURES IN_P SCHEMA name_list
+                               {
+                                       PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+                                       n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+                                       n->objtype = ACL_OBJECT_PROCEDURE;
+                                       n->objs = $5;
+                                       $$ = n;
+                               }
+                       | ALL ROUTINES IN_P SCHEMA name_list
+                               {
+                                       PrivTarget *n = (PrivTarget *) palloc(sizeof(PrivTarget));
+                                       n->targtype = ACL_TARGET_ALL_IN_SCHEMA;
+                                       n->objtype = ACL_OBJECT_ROUTINE;
+                                       n->objs = $5;
+                                       $$ = n;
+                               }
                ;
 
 
@@ -7213,6 +7314,7 @@ DefACLAction:
 defacl_privilege_target:
                        TABLES                  { $$ = ACL_OBJECT_RELATION; }
                        | FUNCTIONS             { $$ = ACL_OBJECT_FUNCTION; }
+                       | ROUTINES              { $$ = ACL_OBJECT_FUNCTION; }
                        | SEQUENCES             { $$ = ACL_OBJECT_SEQUENCE; }
                        | TYPES_P               { $$ = ACL_OBJECT_TYPE; }
                        | SCHEMAS               { $$ = ACL_OBJECT_NAMESPACE; }
@@ -7413,6 +7515,18 @@ CreateFunctionStmt:
                                        n->withClause = $7;
                                        $$ = (Node *)n;
                                }
+                       | CREATE opt_or_replace PROCEDURE func_name func_args_with_defaults
+                         createfunc_opt_list
+                               {
+                                       CreateFunctionStmt *n = makeNode(CreateFunctionStmt);
+                                       n->replace = $2;
+                                       n->funcname = $4;
+                                       n->parameters = $5;
+                                       n->returnType = NULL;
+                                       n->is_procedure = true;
+                                       n->options = $6;
+                                       $$ = (Node *)n;
+                               }
                ;
 
 opt_or_replace:
@@ -7830,7 +7944,7 @@ table_func_column_list:
                ;
 
 /*****************************************************************************
- * ALTER FUNCTION
+ * ALTER FUNCTION / ALTER PROCEDURE / ALTER ROUTINE
  *
  * RENAME and OWNER subcommands are already provided by the generic
  * ALTER infrastructure, here we just specify alterations that can
@@ -7841,6 +7955,23 @@ AlterFunctionStmt:
                        ALTER FUNCTION function_with_argtypes alterfunc_opt_list opt_restrict
                                {
                                        AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+                                       n->objtype = OBJECT_FUNCTION;
+                                       n->func = $3;
+                                       n->actions = $4;
+                                       $$ = (Node *) n;
+                               }
+                       | ALTER PROCEDURE function_with_argtypes alterfunc_opt_list opt_restrict
+                               {
+                                       AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+                                       n->objtype = OBJECT_PROCEDURE;
+                                       n->func = $3;
+                                       n->actions = $4;
+                                       $$ = (Node *) n;
+                               }
+                       | ALTER ROUTINE function_with_argtypes alterfunc_opt_list opt_restrict
+                               {
+                                       AlterFunctionStmt *n = makeNode(AlterFunctionStmt);
+                                       n->objtype = OBJECT_ROUTINE;
                                        n->func = $3;
                                        n->actions = $4;
                                        $$ = (Node *) n;
@@ -7865,6 +7996,8 @@ opt_restrict:
  *             QUERY:
  *
  *             DROP FUNCTION funcname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
+ *             DROP PROCEDURE procname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
+ *             DROP ROUTINE routname (arg1, arg2, ...) [ RESTRICT | CASCADE ]
  *             DROP AGGREGATE aggname (arg1, ...) [ RESTRICT | CASCADE ]
  *             DROP OPERATOR opname (leftoperand_typ, rightoperand_typ) [ RESTRICT | CASCADE ]
  *
@@ -7891,6 +8024,46 @@ RemoveFuncStmt:
                                        n->concurrent = false;
                                        $$ = (Node *)n;
                                }
+                       | DROP PROCEDURE function_with_argtypes_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_PROCEDURE;
+                                       n->objects = $3;
+                                       n->behavior = $4;
+                                       n->missing_ok = false;
+                                       n->concurrent = false;
+                                       $$ = (Node *)n;
+                               }
+                       | DROP PROCEDURE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_PROCEDURE;
+                                       n->objects = $5;
+                                       n->behavior = $6;
+                                       n->missing_ok = true;
+                                       n->concurrent = false;
+                                       $$ = (Node *)n;
+                               }
+                       | DROP ROUTINE function_with_argtypes_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_ROUTINE;
+                                       n->objects = $3;
+                                       n->behavior = $4;
+                                       n->missing_ok = false;
+                                       n->concurrent = false;
+                                       $$ = (Node *)n;
+                               }
+                       | DROP ROUTINE IF_P EXISTS function_with_argtypes_list opt_drop_behavior
+                               {
+                                       DropStmt *n = makeNode(DropStmt);
+                                       n->removeType = OBJECT_ROUTINE;
+                                       n->objects = $5;
+                                       n->behavior = $6;
+                                       n->missing_ok = true;
+                                       n->concurrent = false;
+                                       $$ = (Node *)n;
+                               }
                ;
 
 RemoveAggrStmt:
@@ -8348,6 +8521,15 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
                                        n->missing_ok = true;
                                        $$ = (Node *)n;
                                }
+                       | ALTER PROCEDURE function_with_argtypes RENAME TO name
+                               {
+                                       RenameStmt *n = makeNode(RenameStmt);
+                                       n->renameType = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $3;
+                                       n->newname = $6;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER PUBLICATION name RENAME TO name
                                {
                                        RenameStmt *n = makeNode(RenameStmt);
@@ -8357,6 +8539,15 @@ RenameStmt: ALTER AGGREGATE aggregate_with_argtypes RENAME TO name
                                        n->missing_ok = false;
                                        $$ = (Node *)n;
                                }
+                       | ALTER ROUTINE function_with_argtypes RENAME TO name
+                               {
+                                       RenameStmt *n = makeNode(RenameStmt);
+                                       n->renameType = OBJECT_ROUTINE;
+                                       n->object = (Node *) $3;
+                                       n->newname = $6;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER SCHEMA name RENAME TO name
                                {
                                        RenameStmt *n = makeNode(RenameStmt);
@@ -8736,6 +8927,22 @@ AlterObjectDependsStmt:
                                        n->extname = makeString($7);
                                        $$ = (Node *)n;
                                }
+                       | ALTER PROCEDURE function_with_argtypes DEPENDS ON EXTENSION name
+                               {
+                                       AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+                                       n->objectType = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $3;
+                                       n->extname = makeString($7);
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER ROUTINE function_with_argtypes DEPENDS ON EXTENSION name
+                               {
+                                       AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
+                                       n->objectType = OBJECT_ROUTINE;
+                                       n->object = (Node *) $3;
+                                       n->extname = makeString($7);
+                                       $$ = (Node *)n;
+                               }
                        | ALTER TRIGGER name ON qualified_name DEPENDS ON EXTENSION name
                                {
                                        AlterObjectDependsStmt *n = makeNode(AlterObjectDependsStmt);
@@ -8851,6 +9058,24 @@ AlterObjectSchemaStmt:
                                        n->missing_ok = false;
                                        $$ = (Node *)n;
                                }
+                       | ALTER PROCEDURE function_with_argtypes SET SCHEMA name
+                               {
+                                       AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+                                       n->objectType = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $3;
+                                       n->newschema = $6;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER ROUTINE function_with_argtypes SET SCHEMA name
+                               {
+                                       AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
+                                       n->objectType = OBJECT_ROUTINE;
+                                       n->object = (Node *) $3;
+                                       n->newschema = $6;
+                                       n->missing_ok = false;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER TABLE relation_expr SET SCHEMA name
                                {
                                        AlterObjectSchemaStmt *n = makeNode(AlterObjectSchemaStmt);
@@ -9126,6 +9351,22 @@ AlterOwnerStmt: ALTER AGGREGATE aggregate_with_argtypes OWNER TO RoleSpec
                                        n->newowner = $9;
                                        $$ = (Node *)n;
                                }
+                       | ALTER PROCEDURE function_with_argtypes OWNER TO RoleSpec
+                               {
+                                       AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+                                       n->objectType = OBJECT_PROCEDURE;
+                                       n->object = (Node *) $3;
+                                       n->newowner = $6;
+                                       $$ = (Node *)n;
+                               }
+                       | ALTER ROUTINE function_with_argtypes OWNER TO RoleSpec
+                               {
+                                       AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
+                                       n->objectType = OBJECT_ROUTINE;
+                                       n->object = (Node *) $3;
+                                       n->newowner = $6;
+                                       $$ = (Node *)n;
+                               }
                        | ALTER SCHEMA name OWNER TO RoleSpec
                                {
                                        AlterOwnerStmt *n = makeNode(AlterOwnerStmt);
@@ -14689,6 +14930,7 @@ unreserved_keyword:
                        | BEGIN_P
                        | BY
                        | CACHE
+                       | CALL
                        | CALLED
                        | CASCADE
                        | CASCADED
@@ -14848,6 +15090,7 @@ unreserved_keyword:
                        | PRIVILEGES
                        | PROCEDURAL
                        | PROCEDURE
+                       | PROCEDURES
                        | PROGRAM
                        | PUBLICATION
                        | QUOTE
@@ -14874,6 +15117,8 @@ unreserved_keyword:
                        | ROLE
                        | ROLLBACK
                        | ROLLUP
+                       | ROUTINE
+                       | ROUTINES
                        | ROWS
                        | RULE
                        | SAVEPOINT
index 64111f315e33a56bac17eb66549c7e3ef2076548..4c4f4cdc3d7ac595005ea8d2cb0fac636a2f7c99 100644 (file)
@@ -508,6 +508,14 @@ check_agglevels_and_constraints(ParseState *pstate, Node *expr)
 
                        break;
 
+               case EXPR_KIND_CALL:
+                       if (isAgg)
+                               err = _("aggregate functions are not allowed in CALL arguments");
+                       else
+                               err = _("grouping operations are not allowed in CALL arguments");
+
+                       break;
+
                        /*
                         * There is intentionally no default: case here, so that the
                         * compiler will warn if we add a new ParseExprKind without
@@ -883,6 +891,9 @@ transformWindowFuncCall(ParseState *pstate, WindowFunc *wfunc,
                case EXPR_KIND_PARTITION_EXPRESSION:
                        err = _("window functions are not allowed in partition key expression");
                        break;
+               case EXPR_KIND_CALL:
+                       err = _("window functions are not allowed in CALL arguments");
+                       break;
 
                        /*
                         * There is intentionally no default: case here, so that the
index 86d1da067755987327894f674fce2c66487f459e..29f9da796fc5651545208ec7375ba3bd4e48f97b 100644 (file)
@@ -480,6 +480,7 @@ transformIndirection(ParseState *pstate, A_Indirection *ind)
                                                                                  list_make1(result),
                                                                                  last_srf,
                                                                                  NULL,
+                                                                                 false,
                                                                                  location);
                        if (newresult == NULL)
                                unknown_attribute(pstate, result, strVal(n), location);
@@ -629,6 +630,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                                                                         list_make1(node),
                                                                                         pstate->p_last_srf,
                                                                                         NULL,
+                                                                                        false,
                                                                                         cref->location);
                                }
                                break;
@@ -676,6 +678,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                                                                         list_make1(node),
                                                                                         pstate->p_last_srf,
                                                                                         NULL,
+                                                                                        false,
                                                                                         cref->location);
                                }
                                break;
@@ -736,6 +739,7 @@ transformColumnRef(ParseState *pstate, ColumnRef *cref)
                                                                                         list_make1(node),
                                                                                         pstate->p_last_srf,
                                                                                         NULL,
+                                                                                        false,
                                                                                         cref->location);
                                }
                                break;
@@ -1477,6 +1481,7 @@ transformFuncCall(ParseState *pstate, FuncCall *fn)
                                                         targs,
                                                         last_srf,
                                                         fn,
+                                                        false,
                                                         fn->location);
 }
 
@@ -1812,6 +1817,7 @@ transformSubLink(ParseState *pstate, SubLink *sublink)
                case EXPR_KIND_RETURNING:
                case EXPR_KIND_VALUES:
                case EXPR_KIND_VALUES_SINGLE:
+               case EXPR_KIND_CALL:
                        /* okay */
                        break;
                case EXPR_KIND_CHECK_CONSTRAINT:
@@ -3462,6 +3468,8 @@ ParseExprKindName(ParseExprKind exprKind)
                        return "WHEN";
                case EXPR_KIND_PARTITION_EXPRESSION:
                        return "PARTITION BY";
+               case EXPR_KIND_CALL:
+                       return "CALL";
 
                        /*
                         * There is intentionally no default: case here, so that the
index a11843332b0847823c71eb6bf4bbf1dad0c7d8a7..2f20516e766515fbd122634df460c73bc421a0fa 100644 (file)
@@ -71,7 +71,7 @@ static Node *ParseComplexProjection(ParseState *pstate, const char *funcname,
  */
 Node *
 ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
-                                 Node *last_srf, FuncCall *fn, int location)
+                                 Node *last_srf, FuncCall *fn, bool proc_call, int location)
 {
        bool            is_column = (fn == NULL);
        List       *agg_order = (fn ? fn->agg_order : NIL);
@@ -263,7 +263,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                                                   actual_arg_types[0], rettype, -1,
                                                   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL, location);
        }
-       else if (fdresult == FUNCDETAIL_NORMAL)
+       else if (fdresult == FUNCDETAIL_NORMAL || fdresult == FUNCDETAIL_PROCEDURE)
        {
                /*
                 * Normal function found; was there anything indicating it must be an
@@ -306,6 +306,26 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
                                         errmsg("OVER specified, but %s is not a window function nor an aggregate function",
                                                        NameListToString(funcname)),
                                         parser_errposition(pstate, location)));
+
+               if (fdresult == FUNCDETAIL_NORMAL && proc_call)
+           &nbs