Add procedure support to pg_get_functiondef
authorPeter Eisentraut <[email protected]>
Tue, 13 Feb 2018 15:34:04 +0000 (10:34 -0500)
committerPeter Eisentraut <[email protected]>
Tue, 13 Feb 2018 20:13:44 +0000 (15:13 -0500)
This also makes procedures work in psql's \ef and \sf commands.

Reported-by: Pavel Stehule <[email protected]>
doc/src/sgml/func.sgml
doc/src/sgml/ref/psql-ref.sgml
src/backend/utils/adt/ruleutils.c
src/test/regress/expected/create_procedure.out
src/test/regress/sql/create_procedure.sql

index 640ff09a7b913b6de93136d198460764a5bac622..4be31b082a8a559b8eaf35bcbc4ab8192ba61ba4 100644 (file)
@@ -17008,22 +17008,22 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
       <row>
        <entry><literal><function>pg_get_functiondef(<parameter>func_oid</parameter>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get definition of a function</entry>
+       <entry>get definition of a function or procedure</entry>
       </row>
       <row>
        <entry><literal><function>pg_get_function_arguments(<parameter>func_oid</parameter>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get argument list of function's definition (with default values)</entry>
+       <entry>get argument list of function's or procedure's definition (with default values)</entry>
       </row>
       <row>
        <entry><literal><function>pg_get_function_identity_arguments(<parameter>func_oid</parameter>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get argument list to identify a function (without default values)</entry>
+       <entry>get argument list to identify a function or procedure (without default values)</entry>
       </row>
       <row>
        <entry><literal><function>pg_get_function_result(<parameter>func_oid</parameter>)</function></literal></entry>
        <entry><type>text</type></entry>
-       <entry>get <literal>RETURNS</literal> clause for function</entry>
+       <entry>get <literal>RETURNS</literal> clause for function (returns null for a procedure)</entry>
       </row>
       <row>
        <entry><literal><function>pg_get_indexdef(<parameter>index_oid</parameter>)</function></literal></entry>
index 6f9b30b673cc9effb3a568325ee1f543cd1a8295..8bd9b9387ec4fcf0c7654741b2e6c1e779f1076a 100644 (file)
@@ -1815,8 +1815,9 @@ Tue Oct 26 21:40:57 CEST 1999
 
         <listitem>
         <para>
-         This command fetches and edits the definition of the named function,
-         in the form of a <command>CREATE OR REPLACE FUNCTION</command> command.
+         This command fetches and edits the definition of the named function or procedure,
+         in the form of a <command>CREATE OR REPLACE FUNCTION</command> or
+         <command>CREATE OR REPLACE PROCEDURE</command> command.
          Editing is done in the same way as for <literal>\edit</literal>.
          After the editor exits, the updated command waits in the query buffer;
          type semicolon or <literal>\g</literal> to send it, or <literal>\r</literal>
@@ -2970,8 +2971,9 @@ testdb=&gt; <userinput>\setenv LESS -imx4F</userinput>
 
         <listitem>
         <para>
-         This command fetches and shows the definition of the named function,
-         in the form of a <command>CREATE OR REPLACE FUNCTION</command> command.
+         This command fetches and shows the definition of the named function or procedure,
+         in the form of a <command>CREATE OR REPLACE FUNCTION</command> or
+         <command>CREATE OR REPLACE PROCEDURE</command> command.
          The definition is printed to the current query output channel,
          as set by <command>\o</command>.
         </para>
index 3bb468bdada610847992d9209abdb2e198754b2e..ba9fab4582fdd0601a982762c2e9e8718ea6e573 100644 (file)
@@ -2449,6 +2449,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        StringInfoData dq;
        HeapTuple       proctup;
        Form_pg_proc proc;
+       bool            isfunction;
        Datum           tmp;
        bool            isnull;
        const char *prosrc;
@@ -2472,20 +2473,28 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("\"%s\" is an aggregate function", name)));
 
+       isfunction = (proc->prorettype != InvalidOid);
+
        /*
         * We always qualify the function name, to ensure the right function gets
         * replaced.
         */
        nsp = get_namespace_name(proc->pronamespace);
-       appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(",
+       appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
+                                        isfunction ? "FUNCTION" : "PROCEDURE",
                                         quote_qualified_identifier(nsp, name));
        (void) print_function_arguments(&buf, proctup, false, true);
-       appendStringInfoString(&buf, ")\n RETURNS ");
-       print_function_rettype(&buf, proctup);
+       appendStringInfoString(&buf, ")\n");
+       if (isfunction)
+       {
+               appendStringInfoString(&buf, " RETURNS ");
+               print_function_rettype(&buf, proctup);
+               appendStringInfoChar(&buf, '\n');
+       }
 
        print_function_trftypes(&buf, proctup);
 
-       appendStringInfo(&buf, "\n LANGUAGE %s\n",
+       appendStringInfo(&buf, " LANGUAGE %s\n",
                                         quote_identifier(get_language_name(proc->prolang, false)));
 
        /* Emit some miscellaneous options on one line */
@@ -2607,10 +2616,11 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
         *
         * Since the user is likely to be editing the function body string, we
         * shouldn't use a short delimiter that he might easily create a conflict
-        * with.  Hence prefer "$function$", but extend if needed.
+        * with.  Hence prefer "$function$"/"$procedure$", but extend if needed.
         */
        initStringInfo(&dq);
-       appendStringInfoString(&dq, "$function");
+       appendStringInfoChar(&dq, '$');
+       appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
        while (strstr(prosrc, dq.data) != NULL)
                appendStringInfoChar(&dq, 'x');
        appendStringInfoChar(&dq, '$');
index 873907dc4348351bea4a3517f1a41c253985ef80..e7bede24faafa389c5a780ed415f22ac69de9cd7 100644 (file)
@@ -30,6 +30,17 @@ CALL ptest1(substring(random()::text, 1, 1));  -- ok, volatile arg
  public | ptest1 |                  | x text              | proc
 (1 row)
 
+SELECT pg_get_functiondef('ptest1'::regproc);
+                pg_get_functiondef                 
+---------------------------------------------------
+ CREATE OR REPLACE PROCEDURE public.ptest1(x text)+
+  LANGUAGE sql                                    +
+ AS $procedure$                                   +
+ INSERT INTO cp_test VALUES (1, x);               +
+ $procedure$                                      +
+(1 row)
+
 SELECT * FROM cp_test ORDER BY b COLLATE "C";
  a |   b   
 ---+-------
index d65e568a64ed87939a011bd5648dc39467fc9370..774c12ee34b1af0d1b14f78829d3562fa99590ba 100644 (file)
@@ -17,6 +17,7 @@ CALL ptest1('xy' || 'zzy');  -- ok, constant-folded arg
 CALL ptest1(substring(random()::text, 1, 1));  -- ok, volatile arg
 
 \df ptest1
+SELECT pg_get_functiondef('ptest1'::regproc);
 
 SELECT * FROM cp_test ORDER BY b COLLATE "C";