X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbin%2Fpsql%2Fcommand.c;h=3608b725963e56fa9a228abd5500b9c0177e5b11;hb=e67efb01e886d69d40d1cd87fba4507e8bb1035e;hp=cea3942f013be4637cc5d8c946da4e15f6829233;hpb=2eda8dfb52ed9962920282d8384da8bb4c22514d;p=postgresql
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
index cea3942f01..3608b72596 100644
--- a/src/bin/psql/command.c
+++ b/src/bin/psql/command.c
@@ -1,9 +1,9 @@
/*
* psql - the PostgreSQL interactive terminal
*
- * Copyright (c) 2000-2009, PostgreSQL Global Development Group
+ * Copyright (c) 2000-2011, PostgreSQL Global Development Group
*
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.209 2009/10/07 22:14:24 alvherre Exp $
+ * src/bin/psql/command.c
*/
#include "postgres_fe.h"
#include "command.h"
@@ -57,11 +57,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,
- bool *edited);
+ int lineno, 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);
+static int strip_lineno_from_funcdesc(char *func);
static void minimal_error_message(PGresult *res);
static void printSSLInfo(void);
@@ -294,6 +295,28 @@ exec_command(const char *cmd,
free(opt);
}
+ /* \conninfo -- display information about the current connection */
+ else if (strcmp(cmd, "conninfo") == 0)
+ {
+ char *db = PQdb(pset.db);
+ char *host = PQhost(pset.db);
+
+ if (db == NULL)
+ printf(_("You are currently not connected to a database.\n"));
+ else
+ {
+ if (host == NULL)
+ host = DEFAULT_PGSOCKET_DIR;
+ /* If the host is an absolute path, the connection is via socket */
+ if (is_absolute_path(host))
+ printf(_("You are connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+ db, PQuser(pset.db), host, PQport(pset.db));
+ else
+ printf(_("You are connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+ db, PQuser(pset.db), host, PQport(pset.db));
+ }
+ }
+
/* \copy */
else if (pg_strcasecmp(cmd, "copy") == 0)
{
@@ -346,7 +369,7 @@ exec_command(const char *cmd,
success = describeTableDetails(pattern, show_verbose, show_system);
else
/* standard listing of interesting things */
- success = listTables("tvs", NULL, show_verbose, show_system);
+ success = listTables("tvsE", NULL, show_verbose, show_system);
break;
case 'a':
success = describeAggregates(pattern, show_verbose, show_system);
@@ -361,7 +384,7 @@ exec_command(const char *cmd,
success = listCasts(pattern);
break;
case 'd':
- if (strcmp(cmd, "ddp") == 0)
+ if (strncmp(cmd, "ddp", 3) == 0)
success = listDefaultACLs(pattern);
else
success = objectDescription(pattern, show_system);
@@ -393,12 +416,18 @@ exec_command(const char *cmd,
case 'l':
success = do_lo_list();
break;
+ case 'L':
+ success = listLanguages(pattern, show_verbose, show_system);
+ break;
case 'n':
- success = listSchemas(pattern, show_verbose);
+ success = listSchemas(pattern, show_verbose, show_system);
break;
case 'o':
success = describeOperators(pattern, show_system);
break;
+ case 'O':
+ success = listCollations(pattern, show_verbose, show_system);
+ break;
case 'p':
success = permissionsList(pattern);
break;
@@ -409,6 +438,7 @@ exec_command(const char *cmd,
case 'v':
case 'i':
case 's':
+ case 'E':
success = listTables(&cmd[1], pattern, show_verbose, show_system);
break;
case 'r':
@@ -418,7 +448,7 @@ exec_command(const char *cmd,
if (pattern)
pattern2 = psql_scan_slash_option(scan_state,
- OT_NORMAL, NULL, true);
+ OT_NORMAL, NULL, true);
success = listDbRoleSettings(pattern, pattern2);
}
else
@@ -460,11 +490,20 @@ exec_command(const char *cmd,
case 'w':
success = listForeignDataWrappers(pattern, show_verbose);
break;
+ case 't':
+ success = listForeignTables(pattern, show_verbose);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
break;
}
break;
+ case 'x': /* Extensions */
+ if (show_verbose)
+ success = listExtensionContents(pattern);
+ else
+ success = listExtensions(pattern);
+ break;
default:
status = PSQL_CMD_UNKNOWN;
}
@@ -475,8 +514,8 @@ exec_command(const char *cmd,
/*
- * \e or \edit -- edit the current query buffer (or a file and make it the
- * query buffer
+ * \e or \edit -- edit the current query buffer, or edit a file and make
+ * it the query buffer
*/
else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
{
@@ -488,17 +527,51 @@ exec_command(const char *cmd,
else
{
char *fname;
+ char *ln = NULL;
+ int lineno = -1;
fname = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
- expand_tilde(&fname);
if (fname)
- canonicalize_path(fname);
- if (do_edit(fname, query_buf, NULL))
- status = PSQL_CMD_NEWEDIT;
- else
- status = PSQL_CMD_ERROR;
- free(fname);
+ {
+ /* try to get separate lineno arg */
+ ln = psql_scan_slash_option(scan_state,
+ OT_NORMAL, NULL, true);
+ if (ln == NULL)
+ {
+ /* only one arg; maybe it is lineno not fname */
+ if (fname[0] &&
+ strspn(fname, "0123456789") == strlen(fname))
+ {
+ /* all digits, so assume it is lineno */
+ ln = fname;
+ fname = NULL;
+ }
+ }
+ }
+ if (ln)
+ {
+ lineno = atoi(ln);
+ if (lineno < 1)
+ {
+ psql_error("invalid line number: %s\n", ln);
+ status = PSQL_CMD_ERROR;
+ }
+ }
+ if (status != PSQL_CMD_ERROR)
+ {
+ expand_tilde(&fname);
+ if (fname)
+ canonicalize_path(fname);
+ if (do_edit(fname, query_buf, lineno, NULL))
+ status = PSQL_CMD_NEWEDIT;
+ else
+ status = PSQL_CMD_ERROR;
+ }
+ if (fname)
+ free(fname);
+ if (ln)
+ free(ln);
}
}
@@ -508,6 +581,8 @@ exec_command(const char *cmd,
*/
else if (strcmp(cmd, "ef") == 0)
{
+ int lineno = -1;
+
if (!query_buf)
{
psql_error("no query buffer\n");
@@ -520,7 +595,13 @@ exec_command(const char *cmd,
func = psql_scan_slash_option(scan_state,
OT_WHOLE_LINE, NULL, true);
- if (!func)
+ lineno = strip_lineno_from_funcdesc(func);
+ if (lineno == 0)
+ {
+ /* error already reported */
+ status = PSQL_CMD_ERROR;
+ }
+ else if (!func)
{
/* set up an empty command to fill in */
printfPQExpBuffer(query_buf,
@@ -541,6 +622,32 @@ exec_command(const char *cmd,
/* error already reported */
status = PSQL_CMD_ERROR;
}
+ else if (lineno > 0)
+ {
+ /*
+ * 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 ", and that there
+ * can be no such line before the real start of the function
+ * body. Increment lineno by the number of lines before that
+ * line, so that it becomes relative to the first line of the
+ * function definition.
+ */
+ const char *lines = query_buf->data;
+
+ while (*lines != '\0')
+ {
+ if (strncmp(lines, "AS ", 3) == 0)
+ break;
+ lineno++;
+ /* find start of next line */
+ lines = strchr(lines, '\n');
+ if (!lines)
+ break;
+ lines++;
+ }
+ }
+
if (func)
free(func);
}
@@ -549,7 +656,7 @@ exec_command(const char *cmd,
{
bool edited = false;
- if (!do_edit(0, query_buf, &edited))
+ if (!do_edit(NULL, query_buf, lineno, &edited))
status = PSQL_CMD_ERROR;
else if (!edited)
puts(_("No changes"));
@@ -651,6 +758,17 @@ exec_command(const char *cmd,
{
char *opt = psql_scan_slash_option(scan_state,
OT_WHOLE_LINE, NULL, false);
+ size_t len;
+
+ /* strip any trailing spaces and semicolons */
+ if (opt)
+ {
+ len = strlen(opt);
+ while (len > 0 &&
+ (isspace((unsigned char) opt[len - 1])
+ || opt[len - 1] == ';'))
+ opt[--len] = '\0';
+ }
helpSQL(opt, pset.popt.topt.pager);
free(opt);
@@ -666,8 +784,9 @@ exec_command(const char *cmd,
}
- /* \i is include file */
- else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
+ /* \i and \ir include files */
+ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0
+ || strcmp(cmd, "ir") == 0 || strcmp(cmd, "include_relative") == 0)
{
char *fname = psql_scan_slash_option(scan_state,
OT_NORMAL, NULL, true);
@@ -679,8 +798,12 @@ exec_command(const char *cmd,
}
else
{
+ bool include_relative;
+
+ include_relative = (strcmp(cmd, "ir") == 0
+ || strcmp(cmd, "include_relative") == 0);
expand_tilde(&fname);
- success = (process_file(fname, false) == EXIT_SUCCESS);
+ success = (process_file(fname, false, include_relative) == EXIT_SUCCESS);
free(fname);
}
}
@@ -981,6 +1104,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 ", 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 ", 3) == 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)
{
@@ -991,7 +1229,6 @@ exec_command(const char *cmd,
free(opt);
}
-
/* \T -- define html
attributes */
else if (strcmp(cmd, "T") == 0)
{
@@ -1213,7 +1450,7 @@ param_is_newly_set(const char *old_val, const char *new_val)
* Connects to a database with given parameters. If there exists an
* established connection, NULL values will be replaced with the ones
* in the current connection. Otherwise NULL will be passed for that
- * parameter to PQsetdbLogin(), so the libpq defaults will be used.
+ * parameter to PQconnectdbParams(), so the libpq defaults will be used.
*
* In interactive mode, if connection fails with the given parameters,
* the old connection will be kept.
@@ -1255,8 +1492,31 @@ do_connect(char *dbname, char *user, char *host, char *port)
while (true)
{
- n_conn = PQsetdbLogin(host, port, NULL, NULL,
- dbname, user, password);
+#define PARAMS_ARRAY_SIZE 8
+ const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
+ const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
+
+ keywords[0] = "host";
+ values[0] = host;
+ keywords[1] = "port";
+ values[1] = port;
+ keywords[2] = "user";
+ values[2] = user;
+ keywords[3] = "password";
+ values[3] = password;
+ keywords[4] = "dbname";
+ values[4] = dbname;
+ keywords[5] = "fallback_application_name";
+ values[5] = pset.progname;
+ keywords[6] = "client_encoding";
+ values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
+ keywords[7] = NULL;
+ values[7] = NULL;
+
+ n_conn = PQconnectdbParams(keywords, values, true);
+
+ free(keywords);
+ free(values);
/* We can immediately discard the password -- no longer needed */
if (password)
@@ -1310,23 +1570,29 @@ do_connect(char *dbname, char *user, char *host, char *port)
PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL);
pset.db = n_conn;
SyncVariables();
- connection_warnings(); /* Must be after SyncVariables */
+ connection_warnings(false); /* Must be after SyncVariables */
/* Tell the user about the new connection */
if (!pset.quiet)
{
- printf(_("You are now connected to database \"%s\""), PQdb(pset.db));
-
- if (param_is_newly_set(PQhost(o_conn), PQhost(pset.db)))
- printf(_(" on host \"%s\""), PQhost(pset.db));
-
- if (param_is_newly_set(PQport(o_conn), PQport(pset.db)))
- printf(_(" at port \"%s\""), PQport(pset.db));
-
- if (param_is_newly_set(PQuser(o_conn), PQuser(pset.db)))
- printf(_(" as user \"%s\""), PQuser(pset.db));
-
- printf(".\n");
+ if (param_is_newly_set(PQhost(o_conn), PQhost(pset.db)) ||
+ param_is_newly_set(PQport(o_conn), PQport(pset.db)))
+ {
+ char *host = PQhost(pset.db);
+
+ if (host == NULL)
+ host = DEFAULT_PGSOCKET_DIR;
+ /* If the host is an absolute path, the connection is via socket */
+ if (is_absolute_path(host))
+ printf(_("You are now connected to database \"%s\" as user \"%s\" via socket in \"%s\" at port \"%s\".\n"),
+ PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+ else
+ printf(_("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port \"%s\".\n"),
+ PQdb(pset.db), PQuser(pset.db), host, PQport(pset.db));
+ }
+ else
+ printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
+ PQdb(pset.db), PQuser(pset.db));
}
if (o_conn)
@@ -1336,7 +1602,7 @@ do_connect(char *dbname, char *user, char *host, char *port)
void
-connection_warnings(void)
+connection_warnings(bool in_startup)
{
if (!pset.quiet && !pset.notty)
{
@@ -1362,7 +1628,8 @@ connection_warnings(void)
printf(_("%s (%s, server %s)\n"),
pset.progname, PG_VERSION, server_version);
}
- else
+ /* For version match, only print psql banner on startup. */
+ else if (in_startup)
printf("%s (%s)\n", pset.progname, PG_VERSION);
if (pset.sversion / 100 != client_ver / 100)
@@ -1396,7 +1663,7 @@ printSSLInfo(void)
return; /* no SSL */
SSL_get_cipher_bits(ssl, &sslbits);
- printf(_("SSL connection (cipher: %s, bits: %i)\n"),
+ printf(_("SSL connection (cipher: %s, bits: %d)\n"),
SSL_get_cipher(ssl), sslbits);
#else
@@ -1482,11 +1749,11 @@ UnsyncVariables(void)
* If you do not specify a filename, the current query buffer will be copied
* into a temporary one.
*/
-
static bool
-editFile(const char *fname)
+editFile(const char *fname, int lineno)
{
const char *editorName;
+ const char *editor_lineno_arg = NULL;
char *sys;
int result;
@@ -1501,6 +1768,29 @@ editFile(const char *fname)
if (!editorName)
editorName = DEFAULT_EDITOR;
+ /* Get line number argument, if we need it. */
+ if (lineno > 0)
+ {
+ editor_lineno_arg = getenv("PSQL_EDITOR_LINENUMBER_ARG");
+#ifdef DEFAULT_EDITOR_LINENUMBER_ARG
+ if (!editor_lineno_arg)
+ editor_lineno_arg = DEFAULT_EDITOR_LINENUMBER_ARG;
+#endif
+ if (!editor_lineno_arg)
+ {
+ psql_error("environment variable PSQL_EDITOR_LINENUMBER_ARG must be set to specify a line number\n");
+ return false;
+ }
+ }
+
+ /* Allocate sufficient memory for command line. */
+ if (lineno > 0)
+ sys = pg_malloc(strlen(editorName)
+ + strlen(editor_lineno_arg) + 10 /* for integer */
+ + 1 + strlen(fname) + 10 + 1);
+ else
+ sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
+
/*
* On Unix the EDITOR value should *not* be quoted, since it might include
* switches, eg, EDITOR="pico -t"; it's up to the user to put quotes in it
@@ -1508,11 +1798,20 @@ editFile(const char *fname)
* severe brain damage in their command shell plus the fact that standard
* program paths include spaces.
*/
- sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
#ifndef WIN32
- sprintf(sys, "exec %s '%s'", editorName, fname);
+ if (lineno > 0)
+ sprintf(sys, "exec %s %s%d '%s'",
+ editorName, editor_lineno_arg, lineno, fname);
+ else
+ sprintf(sys, "exec %s '%s'",
+ editorName, fname);
#else
- sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE, editorName, fname);
+ if (lineno > 0)
+ sprintf(sys, SYSTEMQUOTE "\"%s\" %s%d \"%s\"" SYSTEMQUOTE,
+ editorName, editor_lineno_arg, lineno, fname);
+ else
+ sprintf(sys, SYSTEMQUOTE "\"%s\" \"%s\"" SYSTEMQUOTE,
+ editorName, fname);
#endif
result = system(sys);
if (result == -1)
@@ -1527,7 +1826,8 @@ editFile(const char *fname)
/* call this one */
static bool
-do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
+do_edit(const char *filename_arg, PQExpBuffer query_buf,
+ int lineno, bool *edited)
{
char fnametmp[MAXPGPATH];
FILE *stream = NULL;
@@ -1555,7 +1855,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
ret = GetTempPath(MAXPGPATH, tmpdir);
if (ret == 0 || ret > MAXPGPATH)
{
- psql_error("cannot locate temporary directory: %s",
+ psql_error("could not locate temporary directory: %s\n",
!ret ? strerror(errno) : "");
return false;
}
@@ -1619,7 +1919,7 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
/* call editor */
if (!error)
- error = !editFile(fname);
+ error = !editFile(fname, lineno);
if (!error && stat(fname, &after) != 0)
{
@@ -1677,22 +1977,49 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
* process_file
*
* Read commands from filename and then them to the main processing loop
- * Handler for \i, but can be used for other things as well. Returns
+ * Handler for \i and \ir, but can be used for other things as well. Returns
* MainLoop() error code.
+ *
+ * If use_relative_path is true and filename is not an absolute path, then open
+ * the file from where the currently processed file (if any) is located.
*/
int
-process_file(char *filename, bool single_txn)
+process_file(char *filename, bool single_txn, bool use_relative_path)
{
FILE *fd;
int result;
char *oldfilename;
+ char relpath[MAXPGPATH];
PGresult *res;
if (!filename)
return EXIT_FAILURE;
- canonicalize_path(filename);
- fd = fopen(filename, PG_BINARY_R);
+ if (strcmp(filename, "-") != 0)
+ {
+ canonicalize_path(filename);
+
+ /*
+ * If we were asked to resolve the pathname relative to the location
+ * of the currently executing script, and there is one, and this is
+ * a relative pathname, then prepend all but the last pathname
+ * component of the current script to this pathname.
+ */
+ if (use_relative_path && pset.inputfile && !is_absolute_path(filename)
+ && !has_drive_prefix(filename))
+ {
+ snprintf(relpath, MAXPGPATH, "%s", pset.inputfile);
+ get_parent_directory(relpath);
+ join_path_components(relpath, relpath, filename);
+ canonicalize_path(relpath);
+
+ filename = relpath;
+ }
+
+ fd = fopen(filename, PG_BINARY_R);
+ }
+ else
+ fd = stdin;
if (!fd)
{
@@ -1704,12 +2031,39 @@ process_file(char *filename, bool single_txn)
pset.inputfile = filename;
if (single_txn)
- res = PSQLexec("BEGIN", false);
+ {
+ if ((res = PSQLexec("BEGIN", false)) == NULL)
+ {
+ if (pset.on_error_stop)
+ {
+ result = EXIT_USER;
+ goto error;
+ }
+ }
+ else
+ PQclear(res);
+ }
+
result = MainLoop(fd);
+
if (single_txn)
- res = PSQLexec("COMMIT", false);
+ {
+ if ((res = PSQLexec("COMMIT", false)) == NULL)
+ {
+ if (pset.on_error_stop)
+ {
+ result = EXIT_USER;
+ goto error;
+ }
+ }
+ else
+ PQclear(res);
+ }
+
+error:
+ if (fd != stdin)
+ fclose(fd);
- fclose(fd);
pset.inputfile = oldfilename;
return result;
}
@@ -1788,6 +2142,28 @@ do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
printf(_("Output format is %s.\n"), _align2string(popt->topt.format));
}
+ /* set table line style */
+ else if (strcmp(param, "linestyle") == 0)
+ {
+ if (!value)
+ ;
+ else if (pg_strncasecmp("ascii", value, vallen) == 0)
+ popt->topt.line_style = &pg_asciiformat;
+ else if (pg_strncasecmp("old-ascii", value, vallen) == 0)
+ popt->topt.line_style = &pg_asciiformat_old;
+ else if (pg_strncasecmp("unicode", value, vallen) == 0)
+ popt->topt.line_style = &pg_utf8format;
+ else
+ {
+ psql_error("\\pset: allowed line styles are ascii, old-ascii, unicode\n");
+ return false;
+ }
+
+ if (!quiet)
+ printf(_("Line style is %s.\n"),
+ get_line_style(&popt->topt)->name);
+ }
+
/* set border style/width */
else if (strcmp(param, "border") == 0)
{
@@ -2102,6 +2478,69 @@ get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf)
return result;
}
+/*
+ * If the given argument of \ef ends with a line number, delete the line
+ * number from the argument string and return it as an integer. (We need
+ * this kluge because we're too lazy to parse \ef's function name argument
+ * carefully --- we just slop it up in OT_WHOLE_LINE mode.)
+ *
+ * Returns -1 if no line number is present, 0 on error, or a positive value
+ * on success.
+ */
+static int
+strip_lineno_from_funcdesc(char *func)
+{
+ char *c;
+ int lineno;
+
+ if (!func || func[0] == '\0')
+ return -1;
+
+ c = func + strlen(func) - 1;
+
+ /*
+ * This business of parsing backwards is dangerous as can be in a
+ * multibyte environment: there is no reason to believe that we are
+ * looking at the first byte of a character, nor are we necessarily
+ * working in a "safe" encoding. Fortunately the bitpatterns we are
+ * looking for are unlikely to occur as non-first bytes, but beware of
+ * trying to expand the set of cases that can be recognized. We must
+ * guard the macros by using isascii() first, too.
+ */
+
+ /* skip trailing whitespace */
+ while (c > func && isascii((unsigned char) *c) && isspace((unsigned char) *c))
+ c--;
+
+ /* must have a digit as last non-space char */
+ if (c == func || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c))
+ return -1;
+
+ /* find start of digit string */
+ while (c > func && isascii((unsigned char) *c) && isdigit((unsigned char) *c))
+ c--;
+
+ /* digits must be separated from func name by space or closing paren */
+ /* notice also that we are not allowing an empty func name ... */
+ if (c == func || !isascii((unsigned char) *c) ||
+ !(isspace((unsigned char) *c) || *c == ')'))
+ return -1;
+
+ /* parse digit string */
+ c++;
+ lineno = atoi(c);
+ if (lineno < 1)
+ {
+ psql_error("invalid line number: %s\n", c);
+ return 0;
+ }
+
+ /* strip digit string from func */
+ *c = '\0';
+
+ return lineno;
+}
+
/*
* Report just the primary error; this is to avoid cluttering the output
* with, for instance, a redisplay of the internally generated query