Commit 2c863ca8 authored by Tom Lane's avatar Tom Lane

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
parent 2cdcf459
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.443 2008/07/21 04:47:00 tgl Exp $ -->
<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.444 2008/09/06 00:01:21 tgl Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
......@@ -11562,6 +11562,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<primary>pg_get_ruledef</primary>
</indexterm>
<indexterm>
<primary>pg_get_functiondef</primary>
</indexterm>
<indexterm>
<primary>pg_get_function_arguments</primary>
</indexterm>
......@@ -11643,6 +11647,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
<entry>decompile internal form of an expression, assuming that any Vars
in it refer to the relation indicated by the second parameter</entry>
</row>
<row>
<entry><literal><function>pg_get_functiondef</function>(<parameter>func_oid</parameter>)</literal></entry>
<entry><type>text</type></entry>
<entry>get definition of a function</entry>
</row>
<row>
<entry><literal><function>pg_get_function_arguments</function>(<parameter>func_oid</parameter>)</literal></entry>
<entry><type>text</type></entry>
......@@ -11756,6 +11765,8 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
</para>
<para>
<function>pg_get_functiondef</> returns a complete
<command>CREATE OR REPLACE FUNCTION</> statement for a function.
<function>pg_get_function_arguments</function> returns the argument list
of a function, in the form it would need to appear in within
<command>CREATE FUNCTION</>.
......
<!--
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.209 2008/07/03 03:37:16 tgl Exp $
$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.210 2008/09/06 00:01:21 tgl Exp $
PostgreSQL documentation
-->
......@@ -1195,6 +1195,29 @@ testdb=&gt;
</varlistentry>
<varlistentry>
<term><literal>\ef <replaceable class="parameter">function_description</replaceable> </literal></term>
<listitem>
<para>
This command fetches and edits the definition of the named function,
in the form of a <command>CREATE OR REPLACE FUNCTION</> command.
Editing is done in the same way as for <literal>\e</>.
After the editor exits, the updated command waits in the query buffer;
type semicolon or <literal>\g</> to send it, or <literal>\r</>
to cancel.
</para>
<para>
The target function can be specified by name alone, or by name
and arguments, for example <literal>foo(integer, text)</>.
The argument types must be given if there is more
than one function of the same name.
</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>\echo <replaceable class="parameter">text</replaceable> [ ... ]</literal></term>
<listitem>
......
This diff is collapsed.
......@@ -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;
}
......@@ -37,7 +37,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.482 2008/09/01 20:42:45 tgl Exp $
* $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.483 2008/09/06 00:01:24 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -53,6 +53,6 @@
*/
/* yyyymmddN */
#define CATALOG_VERSION_NO 200808311
#define CATALOG_VERSION_NO 200809051
#endif
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.512 2008/08/25 11:18:43 mha Exp $
* $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.513 2008/09/06 00:01:24 tgl Exp $
*
* NOTES
* The script catalog/genbki.sh reads this file and generates .bki
......@@ -2292,6 +2292,8 @@ DATA(insert OID = 1716 ( pg_get_expr PGNSP PGUID 12 1 0 0 f f t f s 2 25 "2
DESCR("deparse an encoded expression");
DATA(insert OID = 1665 ( pg_get_serial_sequence PGNSP PGUID 12 1 0 0 f f t f s 2 25 "25 25" _null_ _null_ _null_ pg_get_serial_sequence _null_ _null_ _null_ ));
DESCR("name of sequence for a serial column");
DATA(insert OID = 2098 ( pg_get_functiondef PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_functiondef _null_ _null_ _null_ ));
DESCR("definition of a function");
DATA(insert OID = 2162 ( pg_get_function_arguments PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_arguments _null_ _null_ _null_ ));
DESCR("argument list of a function");
DATA(insert OID = 2165 ( pg_get_function_result PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_ pg_get_function_result _null_ _null_ _null_ ));
......
......@@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.319 2008/07/18 03:32:53 tgl Exp $
* $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.320 2008/09/06 00:01:25 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -551,6 +551,7 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS);
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS);
extern Datum pg_get_functiondef(PG_FUNCTION_ARGS);
extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS);
extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
extern char *deparse_expression(Node *expr, List *dpcontext,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment