aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2010-08-14 13:59:49 +0000
committerTom Lane <tgl@sss.pgh.pa.us>2010-08-14 13:59:49 +0000
commitb6e06942c6b880b79097049757a8d140cd4316c0 (patch)
tree83a9a7989612cae943d87e171dca28b730950635
parent946b07804478d379d29b2b2038a5729b3cad2f55 (diff)
downloadpostgresql-b6e06942c6b880b79097049757a8d140cd4316c0.tar.gz
postgresql-b6e06942c6b880b79097049757a8d140cd4316c0.zip
Add a \sf (show function) command to psql, for those times when you need to
look at a function but don't wish to fire up an editor. Pavel Stehule, reviewed by Jan Urbanski
-rw-r--r--doc/src/sgml/ref/psql-ref.sgml29
-rw-r--r--src/bin/psql/command.c120
-rw-r--r--src/bin/psql/help.c5
-rw-r--r--src/bin/psql/tab-complete.c6
4 files changed, 152 insertions, 8 deletions
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
index 47b1e99e054..a93d3d594cf 100644
--- a/doc/src/sgml/ref/psql-ref.sgml
+++ b/doc/src/sgml/ref/psql-ref.sgml
@@ -1,5 +1,5 @@
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.248 2010/08/12 00:40:59 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.249 2010/08/14 13:59:49 tgl Exp $
PostgreSQL documentation
-->
@@ -2137,6 +2137,33 @@ lo_import 152801
<varlistentry>
+ <term><literal>\sf[+] <replaceable class="parameter">function_description</> </literal></term>
+
+ <listitem>
+ <para>
+ This command fetches and shows the definition of the named function,
+ in the form of a <command>CREATE OR REPLACE FUNCTION</> command.
+ The definition is printed to the current query output channel,
+ as set by <command>\o</command>.
+ </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>
+
+ <para>
+ If <literal>+</literal> is appended to the command name, then the
+ output lines are numbered, with the first line of the function body
+ being line 1.
+ </para>
+ </listitem>
+ </varlistentry>
+
+
+ <varlistentry>
<term><literal>\t</literal></term>
<listitem>
<para>
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index 687cbca2ca6..e902f5e95c0 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.226 2010/08/12 00:40:59 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.227 2010/08/14 13:59:49 tgl Exp $
*/
#include "postgres_fe.h"
#include "command.h"
@@ -1083,6 +1083,121 @@ exec_command(const char *cmd,
free(opt0);
}
+ /* \sf -- show a function's source code */
+ else if (strcmp(cmd, "sf") == 0 || strcmp(cmd, "sf+") == 0)
+ {
+ bool show_linenumbers = (strcmp(cmd, "sf+") == 0);
+ PQExpBuffer func_buf;
+ char *func;
+ Oid foid = InvalidOid;
+
+ func_buf = createPQExpBuffer();
+ func = psql_scan_slash_option(scan_state,
+ OT_WHOLE_LINE, NULL, true);
+ if (!func)
+ {
+ psql_error("function name is required\n");
+ status = PSQL_CMD_ERROR;
+ }
+ 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, func_buf))
+ {
+ /* error already reported */
+ status = PSQL_CMD_ERROR;
+ }
+ else
+ {
+ FILE *output;
+ bool is_pager;
+
+ /* Select output stream: stdout, pager, or file */
+ if (pset.queryFout == stdout)
+ {
+ /* count lines in function to see if pager is needed */
+ int lineno = 0;
+ const char *lines = func_buf->data;
+
+ while (*lines != '\0')
+ {
+ lineno++;
+ /* find start of next line */
+ lines = strchr(lines, '\n');
+ if (!lines)
+ break;
+ lines++;
+ }
+
+ output = PageOutput(lineno, pset.popt.topt.pager);
+ is_pager = true;
+ }
+ else
+ {
+ /* use previously set output file, without pager */
+ output = pset.queryFout;
+ is_pager = false;
+ }
+
+ if (show_linenumbers)
+ {
+ bool in_header = true;
+ int lineno = 0;
+ char *lines = func_buf->data;
+
+ /*
+ * lineno "1" should correspond to the first line of the
+ * function body. We expect that pg_get_functiondef() will
+ * emit that on a line beginning with "AS $function", and that
+ * there can be no such line before the real start of the
+ * function body.
+ *
+ * Note that this loop scribbles on func_buf.
+ */
+ while (*lines != '\0')
+ {
+ char *eol;
+
+ if (in_header && strncmp(lines, "AS $function", 12) == 0)
+ in_header = false;
+ /* increment lineno only for body's lines */
+ if (!in_header)
+ lineno++;
+
+ /* find and mark end of current line */
+ eol = strchr(lines, '\n');
+ if (eol != NULL)
+ *eol = '\0';
+
+ /* show current line as appropriate */
+ if (in_header)
+ fprintf(output, " %s\n", lines);
+ else
+ fprintf(output, "%-7d %s\n", lineno, lines);
+
+ /* advance to next line, if any */
+ if (eol == NULL)
+ break;
+ lines = ++eol;
+ }
+ }
+ else
+ {
+ /* just send the function definition to output */
+ fputs(func_buf->data, output);
+ }
+
+ if (is_pager)
+ ClosePager(output);
+ }
+
+ if (func)
+ free(func);
+ destroyPQExpBuffer(func_buf);
+ }
+
/* \t -- turn off headers and row count */
else if (strcmp(cmd, "t") == 0)
{
@@ -1093,7 +1208,6 @@ exec_command(const char *cmd,
free(opt);
}
-
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
@@ -1667,7 +1781,7 @@ editFile(const char *fname, int lineno)
editorName, fname);
#else
if (lineno > 0)
- sprintf(sys, SYSTEMQUOTE "\"%s\" %s%d \"%s\"" SYSTEMQUOTE,
+ sprintf(sys, SYSTEMQUOTE "\"%s\" %s%d \"%s\"" SYSTEMQUOTE,
editorName, editor_lineno_switch, lineno, fname);
else
sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE,
diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c
index 822cb597174..b02f9db9b54 100644
--- a/src/bin/psql/help.c
+++ b/src/bin/psql/help.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.162 2010/08/13 20:56:18 tgl Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/help.c,v 1.163 2010/08/14 13:59:49 tgl Exp $
*/
#include "postgres_fe.h"
@@ -158,7 +158,7 @@ slashUsage(unsigned short int pager)
{
FILE *output;
- output = PageOutput(89, pager);
+ output = PageOutput(90, pager);
/* if you add/remove a line here, change the row count above */
@@ -220,6 +220,7 @@ slashUsage(unsigned short int pager)
fprintf(output, _(" \\du[+] [PATTERN] list roles (users)\n"));
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
fprintf(output, _(" \\l[+] list all databases\n"));
+ fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
fprintf(output, "\n");
diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c
index 5368d4ac45c..0c2d5bc7809 100644
--- a/src/bin/psql/tab-complete.c
+++ b/src/bin/psql/tab-complete.c
@@ -3,7 +3,7 @@
*
* Copyright (c) 2000-2010, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.201 2010/07/20 03:54:19 rhaas Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/tab-complete.c,v 1.202 2010/08/14 13:59:49 tgl Exp $
*/
/*----------------------------------------------------------------------
@@ -644,7 +644,7 @@ psql_completion(char *text, int start, int end)
"\\f", "\\g", "\\h", "\\help", "\\H", "\\i", "\\l",
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
- "\\set", "\\t", "\\T",
+ "\\set", "\\sf", "\\t", "\\T",
"\\timing", "\\unset", "\\x", "\\w", "\\z", "\\!", NULL
};
@@ -2517,6 +2517,8 @@ psql_completion(char *text, int start, int end)
COMPLETE_WITH_LIST(my_list);
}
+ else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
+ COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
else if (strcmp(prev_wd, "\\cd") == 0 ||
strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
strcmp(prev_wd, "\\g") == 0 ||