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);
+}