From 409c144d8343f7f2284ff90979edfec3c487e9ee Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sat, 6 Sep 2008 20:18:08 +0000 Subject: [PATCH] Adjust psql's new \ef command to present an empty CREATE FUNCTION template 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 | 11 ++- src/backend/utils/adt/ruleutils.c | 12 +++- src/bin/psql/command.c | 108 +++++++++++++++++++++--------- 3 files changed, 92 insertions(+), 39 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 2eedbb54b46..464cf8ec7f2 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -1161,7 +1161,7 @@ testdb=> - \edit (or \e) [ filename ] + \edit (or \e) filename @@ -1196,7 +1196,7 @@ testdb=> - \ef function_description + \ef function_description @@ -1214,6 +1214,11 @@ testdb=> The argument types must be given if there is more than one function of the same name. + + + If no function is specified, a blank CREATE FUNCTION + template is presented for editing. + diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c index 07c39fefdad..71fea45ddd0 100644 --- a/src/backend/utils/adt/ruleutils.c +++ b/src/backend/utils/adt/ruleutils.c @@ -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, '$'); diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index e1887a2472d..3089c69d108 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -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 + { + char *func; + Oid foid; + + 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 (!get_create_function_cmd(pset.db, foid, query_buf)) + { + /* 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) - { - printf("No changes\n"); - } + 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); +}