diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 47b1e99e05..a93d3d594c 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ @@ -2136,6 +2136,33 @@ lo_import 152801 + + \sf[+] function_description + + + + This command fetches and shows the definition of the named function, + in the form of a CREATE OR REPLACE FUNCTION command. + The definition is printed to the current query output channel, + as set by \o. + + + + The target function can be specified by name alone, or by name + and arguments, for example foo(integer, text). + The argument types must be given if there is more + than one function of the same name. + + + + If + is appended to the command name, then the + output lines are numbered, with the first line of the function body + being line 1. + + + + + \t diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 687cbca2ca..e902f5e95c 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 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 822cb59717..b02f9db9b5 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 5368d4ac45..0c2d5bc780 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 ||