Adjust psql's new \ef command to present an empty CREATE FUNCTION template
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Sep 2008 20:18:08 +0000 (20:18 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Sep 2008 20:18:08 +0000 (20:18 +0000)
for editing if no function name is specified.  This seems a much cleaner way
to offer that functionality than the original patch had.  In passing,
de-clutter the error displays that are given for a bogus function-name
argument, and standardize on "$function$" as the default delimiter for the
function body.  (The original coding would use the shortest possible
dollar-quote delimiter, which seems to create unnecessarily high risk of
later conflicts with the user-modified function body.)

doc/src/sgml/ref/psql-ref.sgml
src/backend/utils/adt/ruleutils.c
src/bin/psql/command.c

index 2eedbb54b4698c5b56579afa2ce889b0eed2309c..464cf8ec7f26fde5edf35b38de91d231ccce2c2e 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.210 2008/09/06 00:01:21 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.211 2008/09/06 20:18:08 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -1161,7 +1161,7 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\edit</literal> (or <literal>\e</literal>) <literal>[ <replaceable class="parameter">filename</replaceable> ]</literal></term>
+        <term><literal>\edit</literal> (or <literal>\e</literal>) <literal><optional> <replaceable class="parameter">filename</replaceable> </optional></literal></term>
 
         <listitem>
         <para>
@@ -1196,7 +1196,7 @@ testdb=&gt;
 
 
       <varlistentry>
-        <term><literal>\ef <replaceable class="parameter">function_description</replaceable> </literal></term>
+        <term><literal>\ef <optional> <replaceable class="parameter">function_description</replaceable> </optional></literal></term>
 
         <listitem>
         <para>
@@ -1214,6 +1214,11 @@ testdb=&gt;
          The argument types must be given if there is more
          than one function of the same name.
         </para>
+
+        <para>
+         If no function is specified, a blank <command>CREATE FUNCTION</>
+         template is presented for editing.
+        </para>
         </listitem>
       </varlistentry>
 
index 07c39fefdad572e747b30d60b2beb6f7c9231168..71fea45ddd0c58ea138e6eed7ef3466626f9e3e9 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.283 2008/09/06 00:01:21 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.284 2008/09/06 20:18:08 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1543,9 +1543,15 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                elog(ERROR, "null prosrc");
        prosrc = TextDatumGetCString(tmp);
 
-       /* We always use dollar quoting.  Figure out a suitable delimiter. */
+       /*
+        * We always use dollar quoting.  Figure out a suitable delimiter.
+        *
+        * 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.
+        */
        initStringInfo(&dq);
-       appendStringInfoChar(&dq, '$');
+       appendStringInfoString(&dq, "$function");
        while (strstr(prosrc, dq.data) != NULL)
                appendStringInfoChar(&dq, 'x');
        appendStringInfoChar(&dq, '$');
index e1887a2472d402b2ab909569f9004ead447e471f..3089c69d10840343d61a7315890b55a5379b9b0e 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.194 2008/09/06 00:01:24 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.195 2008/09/06 20:18:08 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
@@ -62,6 +62,7 @@ static bool do_connect(char *dbname, char *user, char *host, char *port);
 static bool do_shell(const char *command);
 static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
 static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf);
+static void minimal_error_message(PGresult *res);
 
 #ifdef USE_SSL
 static void printSSLInfo(void);
@@ -433,8 +434,6 @@ exec_command(const char *cmd,
         */
        else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
        {
-               char       *fname;
-
                if (!query_buf)
                {
                        psql_error("no query buffer\n");
@@ -442,6 +441,8 @@ exec_command(const char *cmd,
                }
                else
                {
+                       char       *fname;
+
                        fname = psql_scan_slash_option(scan_state,
                                                                                   OT_NORMAL, NULL, true);
                        expand_tilde(&fname);
@@ -456,53 +457,59 @@ exec_command(const char *cmd,
        }
 
        /*
-        * \ef -- edit the named function in $EDITOR.
+        * \ef -- edit the named function, or present a blank CREATE FUNCTION
+        * template if no argument is given
         */
        else if (strcmp(cmd, "ef") == 0)
        {
-               char   *func;
-               Oid             foid;
-
-               func = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, true);
-               if (!func)
-               {
-                       psql_error("no function name specified\n");
-                       status = PSQL_CMD_ERROR;
-               }
-               else if (!lookup_function_oid(pset.db, func, &foid))
-               {
-                       psql_error(PQerrorMessage(pset.db));
-                       status = PSQL_CMD_ERROR;
-               }
-               else if (!query_buf)
+               if (!query_buf)
                {
                        psql_error("no query buffer\n");
                        status = PSQL_CMD_ERROR;
                }
-               else if (!get_create_function_cmd(pset.db, foid, query_buf))
-               {
-                       psql_error(PQerrorMessage(pset.db));
-                       status = PSQL_CMD_ERROR;
-               }
                else
                {
-                       bool edited = false;
+                       char       *func;
+                       Oid                     foid;
 
-                       if (!do_edit(0, query_buf, &edited))
+                       func = psql_scan_slash_option(scan_state,
+                                                                                 OT_WHOLE_LINE, NULL, true);
+                       if (!func)
                        {
+                               /* set up an empty command to fill in */
+                               printfPQExpBuffer(query_buf,
+                                                                 "CREATE FUNCTION ( )\n"
+                                                                 " RETURNS \n"
+                                                                 " LANGUAGE \n"
+                                                                 " -- common options:  IMMUTABLE  STABLE  STRICT  SECURITY DEFINER\n"
+                                                                 "AS $function$\n"
+                                                                 "\n$function$\n");
+                       }
+                       else if (!lookup_function_oid(pset.db, func, &foid))
+                       {
+                               /* error already reported */
                                status = PSQL_CMD_ERROR;
                        }
-                       else if (!edited)
+                       else if (!get_create_function_cmd(pset.db, foid, query_buf))
                        {
-                               printf("No changes\n");
+                               /* error already reported */
+                               status = PSQL_CMD_ERROR;
                        }
+                       if (func)
+                               free(func);
+               }
+
+               if (status != PSQL_CMD_ERROR)
+               {
+                       bool edited = false;
+
+                       if (!do_edit(0, query_buf, &edited))
+                               status = PSQL_CMD_ERROR;
+                       else if (!edited)
+                               puts(_("No changes."));
                        else
-                       {
                                status = PSQL_CMD_NEWEDIT;
-                       }
                }
-               if (func)
-                       free(func);
        }
 
        /* \echo and \qecho */
@@ -1998,7 +2005,10 @@ lookup_function_oid(PGconn *conn, const char *desc, Oid *foid)
        if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
                *foid = atooid(PQgetvalue(res, 0, 0));
        else
+       {
+               minimal_error_message(res);
                result = false;
+       }
 
        PQclear(res);
        destroyPQExpBuffer(query);
@@ -2027,10 +2037,42 @@ get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf)
                appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0));
        }
        else
+       {
+               minimal_error_message(res);
                result = false;
+       }
 
        PQclear(res);
        destroyPQExpBuffer(query);
 
        return result;
 }
+
+/*
+ * Report just the primary error; this is to avoid cluttering the output
+ * with, for instance, a redisplay of the internally generated query
+ */
+static void
+minimal_error_message(PGresult *res)
+{
+       PQExpBuffer msg;
+       const char *fld;
+
+       msg = createPQExpBuffer();
+
+       fld = PQresultErrorField(res, PG_DIAG_SEVERITY);
+       if (fld)
+               printfPQExpBuffer(msg, "%s:  ", fld);
+       else
+               printfPQExpBuffer(msg, "ERROR:  ");
+       fld = PQresultErrorField(res, PG_DIAG_MESSAGE_PRIMARY);
+       if (fld)
+               appendPQExpBufferStr(msg, fld);
+       else
+               appendPQExpBufferStr(msg, "(not available)");
+       appendPQExpBufferStr(msg, "\n");
+
+       psql_error(msg->data);
+
+       destroyPQExpBuffer(msg);
+}