Sunday, June 15, 2008

[HACKERS] psql: \edit-function

diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index f66fd7e..3724533 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -35,6 +35,7 @@
#include "libpq-fe.h"
#include "pqexpbuffer.h"
#include "dumputils.h"
+#include "dumpfunc.h"

#include "common.h"
#include "copy.h"
@@ -53,7 +54,7 @@
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 *, PQExpBuffer, bool *);
static bool do_connect(char *dbname, char *user, char *host, char *port);
static bool do_shell(const char *command);

@@ -432,11 +433,71 @@ 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, 0))
+ status = PSQL_CMD_NEWEDIT;
+ else
+ status = PSQL_CMD_ERROR;
free(fname);
}
}

+ /*
+ * \ef -- edit the named function in $EDITOR.
+ */
+
+ else if (strcmp(cmd, "ef") == 0)
+ {
+ Oid foid;
+ char *func;
+
+ 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;
+ }
+
+ if (!lookup_function_oid(pset.db, func, &foid))
+ {
+ psql_error(PQerrorMessage(pset.db));
+ status = PSQL_CMD_ERROR;
+ }
+ else
+ {
+ bool edited = false;
+
+ termPQExpBuffer(query_buf);
+ if (foid)
+ {
+ char *s = create_or_replace_function_text(foid);
+ appendPQExpBufferStr(query_buf, s);
+ free(s);
+ }
+ else
+ {
+ printfPQExpBuffer(query_buf,
+ "CREATE FUNCTION %s%s RETURNS ... AS $$\n"
+ "...\n"
+ "$$ LANGUAGE '...'\n",
+ func, strchr(func,'(') ? "" : "(...)" );
+ }
+
+ if (!do_edit(0, query_buf, &edited))
+ {
+ status = PSQL_CMD_ERROR;
+ }
+ else if (!edited)
+ {
+ printf("No changes\n");
+ }
+ else
+ {
+ status = PSQL_CMD_SEND;
+ }
+ free(func);
+ }
+ }
+
/* \echo and \qecho */
else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
{
@@ -1298,7 +1359,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;
@@ -1420,6 +1481,10 @@ 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);
}
Hi.

I'm working on a patch where if you say "\ef foo" in psql, it'll start
$EDITOR with a "CREATE OR REPLACE FUNCTION" statement to recreate the
function. So you edit and save and quit, and if you made any changes,
psql will execute the statement.

The psql(/command.c) parts of this are quite simple. I've attached a
patch to demonstrate the idea.

The problem is, of course, generating the "CREATE OR REPLACE" statement.
There is some code to do this in pg_dump.c:dumpFunc(), but it's strongly
tied to pg_dump (global variables, output to Archive *, dependencies on
other functions, etc.).

I could either try to duplicate this code (and there's a lot of it), or
rip dumpFunc() and its dependencies out of pg_dump into dumpfunc.c and
make it callable both by pg_dump and psql. I've done some work towards
the latter, so I know it's possible, but it's a lot of work, which I
don't want to do if it won't be accepted anyway.

I would appreciate some advice on how to proceed.

-- ams

No comments: