diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2008-09-06 00:01:25 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2008-09-06 00:01:25 +0000 |
commit | 2c863ca818ba0a9704dbfe24eb578870b54bfee8 (patch) | |
tree | bab9a88358ecf46545ca7fe6d71b18c22cf9488f /src/bin/psql/command.c | |
parent | 2cdcf459ba6cbaa241e0699728f4454bed72fedf (diff) |
Implement a psql command "\ef" to edit the definition of a function.
In support of that, create a backend function pg_get_functiondef().
The psql command is functional but maybe a bit rough around the edges...
Abhijit Menon-Sen
Diffstat (limited to 'src/bin/psql/command.c')
-rw-r--r-- | src/bin/psql/command.c | 132 |
1 files changed, 127 insertions, 5 deletions
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 958e134a309..e1887a2472d 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.193 2008/08/16 00:16:56 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.194 2008/09/06 00:01:24 tgl Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -56,9 +56,12 @@ static backslashResult exec_command(const char *cmd, PsqlScanState scan_state, PQExpBuffer query_buf); -static bool do_edit(const char *filename_arg, PQExpBuffer query_buf); +static bool do_edit(const char *filename_arg, PQExpBuffer query_buf, + bool *edited); 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); #ifdef USE_SSL static void printSSLInfo(void); @@ -444,11 +447,64 @@ exec_command(const char *cmd, expand_tilde(&fname); if (fname) canonicalize_path(fname); - status = do_edit(fname, query_buf) ? PSQL_CMD_NEWEDIT : PSQL_CMD_ERROR; + if (do_edit(fname, query_buf, NULL)) + status = PSQL_CMD_NEWEDIT; + else + status = PSQL_CMD_ERROR; free(fname); } } + /* + * \ef -- edit the named function in $EDITOR. + */ + 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) + { + 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; + + if (!do_edit(0, query_buf, &edited)) + { + status = PSQL_CMD_ERROR; + } + else if (!edited) + { + printf("No changes\n"); + } + else + { + status = PSQL_CMD_NEWEDIT; + } + } + if (func) + free(func); + } + /* \echo and \qecho */ else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0) { @@ -1410,7 +1466,7 @@ editFile(const char *fname) /* call this one */ static bool -do_edit(const char *filename_arg, PQExpBuffer query_buf) +do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited) { char fnametmp[MAXPGPATH]; FILE *stream = NULL; @@ -1532,10 +1588,13 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) psql_error("%s: %s\n", fname, strerror(errno)); error = true; } + else if (edited) + { + *edited = true; + } fclose(stream); } - } /* remove temp file */ @@ -1912,3 +1971,66 @@ do_shell(const char *command) } return true; } + +/* + * This function takes a function description, e.g. "x" or "x(int)", and + * issues a query on the given connection to retrieve the function's OID + * using a cast to regproc or regprocedure (as appropriate). The result, + * if there is one, is returned at *foid. Note that we'll fail if the + * function doesn't exist OR if there are multiple matching candidates + * OR if there's something syntactically wrong with the function description; + * unfortunately it can be hard to tell the difference. + */ +static bool +lookup_function_oid(PGconn *conn, const char *desc, Oid *foid) +{ + bool result = true; + PQExpBuffer query; + PGresult *res; + + query = createPQExpBuffer(); + printfPQExpBuffer(query, "SELECT "); + appendStringLiteralConn(query, desc, conn); + appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid", + strchr(desc, '(') ? "regprocedure" : "regproc"); + + res = PQexec(conn, query->data); + if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) + *foid = atooid(PQgetvalue(res, 0, 0)); + else + result = false; + + PQclear(res); + destroyPQExpBuffer(query); + + return result; +} + +/* + * Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the + * function with the given OID. If successful, the result is stored in buf. + */ +static bool +get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf) +{ + bool result = true; + PQExpBuffer query; + PGresult *res; + + query = createPQExpBuffer(); + printfPQExpBuffer(query, "SELECT pg_catalog.pg_get_functiondef(%u)", oid); + + res = PQexec(conn, query->data); + if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1) + { + resetPQExpBuffer(buf); + appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0)); + } + else + result = false; + + PQclear(res); + destroyPQExpBuffer(query); + + return result; +} |