2000-01-19 00:30:24 +01:00
|
|
|
/*
|
|
|
|
* psql - the PostgreSQL interactive terminal
|
|
|
|
*
|
2000-01-29 17:58:54 +01:00
|
|
|
* Copyright 2000 by PostgreSQL Global Development Group
|
2000-01-19 00:30:24 +01:00
|
|
|
*
|
2002-03-05 01:01:03 +01:00
|
|
|
* $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.67 2002/03/05 00:01:00 momjian Exp $
|
2000-01-19 00:30:24 +01:00
|
|
|
*/
|
2001-02-10 03:31:31 +01:00
|
|
|
#include "postgres_fe.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "command.h"
|
|
|
|
|
2000-01-19 00:30:24 +01:00
|
|
|
#include <errno.h>
|
2000-02-08 00:10:11 +01:00
|
|
|
#include <assert.h>
|
1999-11-04 22:56:02 +01:00
|
|
|
#include <ctype.h>
|
2001-05-07 21:31:33 +02:00
|
|
|
#ifdef HAVE_PWD_H
|
|
|
|
#include <pwd.h>
|
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifndef WIN32
|
1999-11-05 00:14:30 +01:00
|
|
|
#include <sys/types.h> /* for umask() */
|
2000-11-25 07:21:54 +01:00
|
|
|
#include <sys/stat.h> /* for stat() */
|
|
|
|
#include <fcntl.h> /* open() flags */
|
1999-11-05 00:14:30 +01:00
|
|
|
#include <unistd.h> /* for geteuid(), getpid(), stat() */
|
2000-02-08 00:10:11 +01:00
|
|
|
#else
|
|
|
|
#include <win32.h>
|
Here is a patch to make the current snapshot compile on Win32 (native, libpq
and psql) again. Changes are:
1) psql requires the includes of "io.h" and "fcntl.h" in command.c in order
to make a call to open() work (io.h for _open(), fcntl.h for the O_xxx)
2) PG_VERSION is no longer defined in version.h[.in], but in configure.in.
Since we don't do configure on native win32, we need to put it in
config.h.win32 :-(
3) Added define of SYSCONFDIR to config.h.win32 - libpq won't compile
without it. This functionality is *NOT* tested - it's just defined as "" for
now. May work, may not.
4) DEF_PGPORT renamed to DEF_PGPORT_STR
I have done the "basic tests" on it - it connects to a database, and I can
run queries. Haven't tested any of the fancier functions (yet).
However, I stepped on a much bigger problem when fixing psql to work. It no
longer works when linked against the .DLL version of libpq (which the
Makefile does for it). I have left it linked against this version anyway,
pending the comments I get on this mail :-)
The problem is that there are strings being allocated from libpq.dll using
PQExpBuffers (for example, initPQExpBuffer() on line 92 of input.c). These
are being allocated using the malloc function used by libpq.dll. This
function *may* be different from the malloc function used by psql.exe - only
the resulting pointer must be valid. And with the default linking methods,
it *WILL* be different. Later, psql.exe tries to free() this string, at
which point it crashes because the free() function can't find the allocated
block (it's on the allocated blocks list used by the runtime lib of
libpq.dll).
Shouldn't the right thing to do be to have psql call termPQExpBuffer() on
the data instead? As it is now, gets_fromFile() will just return the pointer
received from the PQExpBuffer.data (this may well be present at several
places - this is the one I was bitten by so far). Isn't that kind of
"accessing the internals of the PQExpBuffer structure" wrong? Instead,
perhaps it shuold make a copy of the string, adn then termPQExpBuffer() it?
In that case, the string will have been allocated from within the same
library as the free() is called.
I can get it to work just fine by doing this - changing from (around line
100 of input.c):
and the same a bit further down in the same function.
But, as I said above, this may be at more places in the code? Perhaps
someone more familiar to it could comment on that?
What do you think shuld be done about this? Personally, I go by the "If you
allocate a piece of memory using an interface, use the same interface to
free it", but the question is how to make it work :-)
Also, AFAIK this only affects psql.exe, so the changes made to the libpq
this patch are required no matter how the other issue is handled.
Regards,
Magnus
2001-01-24 04:42:38 +01:00
|
|
|
#include <io.h>
|
2001-01-27 22:49:59 +01:00
|
|
|
#include <fcntl.h>
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
2000-02-16 14:15:26 +01:00
|
|
|
#include "libpq-fe.h"
|
|
|
|
#include "pqexpbuffer.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "common.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "copy.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "describe.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "help.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "input.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "large_obj.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "mainloop.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
#include "print.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "settings.h"
|
2000-01-14 23:18:03 +01:00
|
|
|
#include "variables.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-19 06:01:16 +01:00
|
|
|
#ifdef MULTIBYTE
|
|
|
|
#include "mb/pg_wchar.h"
|
2000-02-20 15:28:28 +01:00
|
|
|
#else
|
|
|
|
/* Grand unified hard-coded badness */
|
|
|
|
#define pg_encoding_to_char(x) "SQL_ASCII"
|
2000-02-19 06:01:16 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
/* functions for use in this file */
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
static backslashResult exec_command(const char *cmd,
|
|
|
|
const char *options_string,
|
2000-04-12 19:17:23 +02:00
|
|
|
const char **continue_parse,
|
2000-01-19 00:30:24 +01:00
|
|
|
PQExpBuffer query_buf);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2002-02-25 22:37:42 +01:00
|
|
|
/* different ways for scan_option to handle parameter words */
|
2000-04-12 19:17:23 +02:00
|
|
|
enum option_type
|
|
|
|
{
|
2002-02-25 22:37:42 +01:00
|
|
|
OT_NORMAL, /* normal case */
|
|
|
|
OT_SQLID, /* treat as SQL identifier */
|
|
|
|
OT_SQLIDHACK, /* SQL identifier, but don't downcase */
|
|
|
|
OT_FILEPIPE /* it's a file or pipe */
|
2000-04-12 19:17:23 +02:00
|
|
|
};
|
2002-02-25 22:37:42 +01:00
|
|
|
|
|
|
|
static char *scan_option(char **string, enum option_type type,
|
|
|
|
char *quote, bool semicolon);
|
2000-04-12 19:17:23 +02:00
|
|
|
static char *unescape(const unsigned char *source, size_t len);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
|
|
|
|
static bool do_connect(const char *new_dbname, const char *new_user);
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
static bool do_shell(const char *command);
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*----------
|
|
|
|
* HandleSlashCmds:
|
|
|
|
*
|
|
|
|
* Handles all the different commands that start with '\',
|
|
|
|
* ordinarily called by MainLoop().
|
|
|
|
*
|
1999-11-13 03:04:54 +01:00
|
|
|
* 'line' is the current input line, which should not start with a '\'
|
|
|
|
* but with the actual command name
|
1999-11-04 22:56:02 +01:00
|
|
|
* (that is taken care of by MainLoop)
|
|
|
|
*
|
|
|
|
* 'query_buf' contains the query-so-far, which may be modified by
|
|
|
|
* execution of the backslash command (for example, \r clears it)
|
2000-02-08 00:10:11 +01:00
|
|
|
* query_buf can be NULL if there is no query so far.
|
1999-11-04 22:56:02 +01:00
|
|
|
*
|
|
|
|
* Returns a status code indicating what action is desired, see command.h.
|
|
|
|
*----------
|
|
|
|
*/
|
|
|
|
|
|
|
|
backslashResult
|
2000-01-14 23:18:03 +01:00
|
|
|
HandleSlashCmds(const char *line,
|
1999-11-05 00:14:30 +01:00
|
|
|
PQExpBuffer query_buf,
|
2000-01-19 00:30:24 +01:00
|
|
|
const char **end_of_cmd)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
backslashResult status = CMD_SKIP_LINE;
|
|
|
|
char *my_line;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *options_string = NULL;
|
1999-11-05 00:14:30 +01:00
|
|
|
size_t blank_loc;
|
|
|
|
const char *continue_parse = NULL; /* tell the mainloop where the
|
|
|
|
* backslash command ended */
|
|
|
|
|
2000-01-14 23:18:03 +01:00
|
|
|
#ifdef USE_ASSERT_CHECKING
|
2000-04-12 19:17:23 +02:00
|
|
|
assert(line);
|
2000-01-14 23:18:03 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
my_line = xstrdup(line);
|
|
|
|
|
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* Find the first whitespace. line[blank_loc] will now be the
|
|
|
|
* whitespace character or the \0 at the end
|
|
|
|
*
|
|
|
|
* Also look for a backslash, so stuff like \p\g works.
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
2000-04-15 01:43:44 +02:00
|
|
|
blank_loc = strcspn(my_line, " \t\n\r\\");
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (my_line[blank_loc] == '\\')
|
|
|
|
{
|
|
|
|
continue_parse = &my_line[blank_loc];
|
1999-11-13 03:04:54 +01:00
|
|
|
my_line[blank_loc] = '\0';
|
2000-07-17 20:24:33 +02:00
|
|
|
/* If it's a double backslash, we skip it. */
|
|
|
|
if (my_line[blank_loc + 1] == '\\')
|
|
|
|
continue_parse += 2;
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
/* do we have an option string? */
|
1999-11-13 03:04:54 +01:00
|
|
|
else if (my_line[blank_loc] != '\0')
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
options_string = &my_line[blank_loc + 1];
|
1999-11-05 00:14:30 +01:00
|
|
|
my_line[blank_loc] = '\0';
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
status = exec_command(my_line, options_string, &continue_parse, query_buf);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (status == CMD_UNKNOWN)
|
|
|
|
{
|
|
|
|
/*
|
2001-10-25 07:50:21 +02:00
|
|
|
* If the command was not recognized, try to parse it as a
|
|
|
|
* one-letter command with immediately following argument (a
|
|
|
|
* still-supported, but no longer encouraged, syntax).
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
|
|
|
char new_cmd[2];
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
new_cmd[0] = my_line[0];
|
1999-11-05 00:14:30 +01:00
|
|
|
new_cmd[1] = '\0';
|
|
|
|
|
2001-05-12 19:37:15 +02:00
|
|
|
/* use line for options, because my_line was clobbered above */
|
2001-04-18 22:53:08 +02:00
|
|
|
status = exec_command(new_cmd, line + 1, &continue_parse, query_buf);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2001-10-25 07:50:21 +02:00
|
|
|
/*
|
|
|
|
* continue_parse must be relative to my_line for calculation
|
|
|
|
* below
|
|
|
|
*/
|
2001-05-06 23:15:51 +02:00
|
|
|
continue_parse += my_line - line;
|
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
#if 0 /* turned out to be too annoying */
|
2000-12-03 21:45:40 +01:00
|
|
|
if (status != CMD_UNKNOWN && isalpha((unsigned char) new_cmd[0]))
|
2001-06-09 01:53:48 +02:00
|
|
|
psql_error("Warning: This syntax is deprecated.\n");
|
2000-04-16 17:46:40 +02:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (status == CMD_UNKNOWN)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
if (pset.cur_cmd_interactive)
|
2001-06-02 20:25:18 +02:00
|
|
|
fprintf(stderr, gettext("Invalid command \\%s. Try \\? for help.\n"), my_line);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
psql_error("invalid command \\%s\n", my_line);
|
1999-11-05 00:14:30 +01:00
|
|
|
status = CMD_ERROR;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (continue_parse && *continue_parse && *(continue_parse + 1) == '\\')
|
1999-11-05 00:14:30 +01:00
|
|
|
continue_parse += 2;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (end_of_cmd)
|
|
|
|
{
|
|
|
|
if (continue_parse)
|
|
|
|
*end_of_cmd = line + (continue_parse - my_line);
|
|
|
|
else
|
|
|
|
*end_of_cmd = line + strlen(line);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
free(my_line);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
return status;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static backslashResult
|
1999-11-05 00:14:30 +01:00
|
|
|
exec_command(const char *cmd,
|
|
|
|
const char *options_string,
|
2000-04-12 19:17:23 +02:00
|
|
|
const char **continue_parse,
|
2000-01-19 00:30:24 +01:00
|
|
|
PQExpBuffer query_buf)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
bool success = true; /* indicate here if the command ran ok or
|
|
|
|
* failed */
|
2000-01-14 23:18:03 +01:00
|
|
|
bool quiet = QUIET();
|
1999-11-05 00:14:30 +01:00
|
|
|
backslashResult status = CMD_SKIP_LINE;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *string,
|
2000-04-16 17:46:40 +02:00
|
|
|
*string_cpy,
|
2001-03-22 05:01:46 +01:00
|
|
|
*val;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The 'string' variable will be overwritten to point to the next
|
|
|
|
* token, hence we need an extra pointer so we can free this at the
|
|
|
|
* end.
|
|
|
|
*/
|
|
|
|
if (options_string)
|
|
|
|
string = string_cpy = xstrdup(options_string);
|
|
|
|
else
|
|
|
|
string = string_cpy = NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* \a -- toggle field alignment This makes little sense but we keep it
|
|
|
|
* around.
|
|
|
|
*/
|
1999-11-05 00:14:30 +01:00
|
|
|
if (strcmp(cmd, "a") == 0)
|
|
|
|
{
|
2000-01-14 23:18:03 +01:00
|
|
|
if (pset.popt.topt.format != PRINT_ALIGNED)
|
|
|
|
success = do_pset("format", "aligned", &pset.popt, quiet);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_pset("format", "unaligned", &pset.popt, quiet);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
/* \C -- override table title (formerly change HTML caption) */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "C") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *opt = scan_option(&string, OT_NORMAL, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_pset("title", opt, &pset.popt, quiet);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
/*----------
|
1999-11-05 00:14:30 +01:00
|
|
|
* \c or \connect -- connect to new database or as different user
|
|
|
|
*
|
2000-02-08 00:10:11 +01:00
|
|
|
* \c foo bar connect to db "foo" as user "bar"
|
2000-04-12 19:17:23 +02:00
|
|
|
* \c foo [-] connect to db "foo" as current user
|
|
|
|
* \c - bar connect to current db as user "bar"
|
|
|
|
* \c connect to default db as default user
|
|
|
|
*----------
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
|
|
|
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
char *opt1,
|
|
|
|
*opt2;
|
|
|
|
char opt1q,
|
|
|
|
opt2q;
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2002-02-25 22:37:42 +01:00
|
|
|
/*
|
|
|
|
* Ideally we should treat the arguments as SQL identifiers. But for
|
|
|
|
* backwards compatibility with 7.2 and older pg_dump files, we have
|
|
|
|
* to take unquoted arguments verbatim (don't downcase them).
|
|
|
|
* For now, double-quoted arguments may be stripped of double quotes
|
|
|
|
* (as if SQL identifiers). By 7.4 or so, pg_dump files can be
|
|
|
|
* expected to double-quote all mixed-case \connect arguments,
|
|
|
|
* and then we can get rid of OT_SQLIDHACK.
|
|
|
|
*/
|
|
|
|
opt1 = scan_option(&string, OT_SQLIDHACK, &opt1q, true);
|
|
|
|
opt2 = scan_option(&string, OT_SQLIDHACK, &opt2q, true);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
|
|
|
if (opt2)
|
1999-11-05 00:14:30 +01:00
|
|
|
/* gave username */
|
2000-04-12 19:17:23 +02:00
|
|
|
success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1,
|
|
|
|
!opt2q && (strcmp(opt2, "-") == 0 || strcmp(opt2, "") == 0) ? "" : opt2);
|
2000-02-08 00:10:11 +01:00
|
|
|
else if (opt1)
|
2000-04-12 19:17:23 +02:00
|
|
|
/* gave database name */
|
|
|
|
success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 || strcmp(opt1, "") == 0) ? "" : opt1, "");
|
|
|
|
else
|
|
|
|
/* connect to default db as default user */
|
|
|
|
success = do_connect(NULL, NULL);
|
|
|
|
|
|
|
|
free(opt1);
|
|
|
|
free(opt2);
|
2000-02-08 00:10:11 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2001-05-07 21:31:33 +02:00
|
|
|
/* \cd */
|
|
|
|
else if (strcmp(cmd, "cd") == 0)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
char *opt = scan_option(&string, OT_NORMAL, NULL, true);
|
|
|
|
char *dir;
|
2001-05-07 21:31:33 +02:00
|
|
|
|
|
|
|
if (opt)
|
|
|
|
dir = opt;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
#ifndef WIN32
|
|
|
|
struct passwd *pw;
|
|
|
|
|
|
|
|
pw = getpwuid(geteuid());
|
|
|
|
if (!pw)
|
|
|
|
{
|
|
|
|
psql_error("could not get home directory: %s\n", strerror(errno));
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
dir = pw->pw_dir;
|
2001-10-25 07:50:21 +02:00
|
|
|
#else /* WIN32 */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* On Windows, 'cd' without arguments prints the current
|
|
|
|
* directory, so if someone wants to code this here instead...
|
|
|
|
*/
|
2001-05-07 21:31:33 +02:00
|
|
|
dir = "/";
|
2001-11-05 18:46:40 +01:00
|
|
|
#endif /* WIN32 */
|
2001-05-07 21:31:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (chdir(dir) == -1)
|
|
|
|
{
|
|
|
|
psql_error("\\%s: could not change directory to '%s': %s\n",
|
|
|
|
cmd, dir, strerror(errno));
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (opt)
|
|
|
|
free(opt);
|
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \copy */
|
2000-01-14 23:18:03 +01:00
|
|
|
else if (strcasecmp(cmd, "copy") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-01-19 00:30:24 +01:00
|
|
|
success = do_copy(options_string);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (options_string)
|
|
|
|
string += strlen(string);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \copyright */
|
|
|
|
else if (strcmp(cmd, "copyright") == 0)
|
|
|
|
print_copyright();
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \d* commands */
|
|
|
|
else if (cmd[0] == 'd')
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
char *name;
|
|
|
|
bool show_verbose;
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
name = scan_option(&string, OT_SQLID, NULL, true);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
show_verbose = strchr(cmd, '+') ? true : false;
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
switch (cmd[1])
|
|
|
|
{
|
|
|
|
case '\0':
|
2000-04-12 19:17:23 +02:00
|
|
|
case '+':
|
2000-02-08 00:10:11 +01:00
|
|
|
if (name)
|
|
|
|
success = describeTableDetails(name, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2000-04-12 19:17:23 +02:00
|
|
|
/* standard listing of interesting things */
|
2000-01-14 23:18:03 +01:00
|
|
|
success = listTables("tvs", NULL, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'a':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = describeAggregates(name);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'd':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = objectDescription(name);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'f':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = describeFunctions(name, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'l':
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_lo_list();
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'o':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = describeOperators(name);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'p':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = permissionsList(name);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'T':
|
2000-02-08 00:10:11 +01:00
|
|
|
success = describeTypes(name, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
case 'v':
|
|
|
|
case 'i':
|
|
|
|
case 's':
|
|
|
|
case 'S':
|
2001-03-22 05:01:46 +01:00
|
|
|
success = listTables(&cmd[1], name, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2001-05-09 19:29:10 +02:00
|
|
|
case 'u':
|
|
|
|
success = describeUsers(name);
|
2001-10-25 07:50:21 +02:00
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
default:
|
|
|
|
status = CMD_UNKNOWN;
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
free(name);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/*
|
|
|
|
* \e or \edit -- edit the current query buffer (or a file and make it
|
|
|
|
* the query buffer
|
|
|
|
*/
|
|
|
|
else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
char *fname;
|
|
|
|
|
|
|
|
if (!query_buf)
|
|
|
|
{
|
|
|
|
psql_error("no query buffer\n");
|
|
|
|
status = CMD_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
fname = scan_option(&string, OT_NORMAL, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
|
|
|
|
free(fname);
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/* \echo and \qecho */
|
2000-04-12 19:17:23 +02:00
|
|
|
else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
char *value;
|
|
|
|
char quoted;
|
|
|
|
bool no_newline = false;
|
|
|
|
bool first = true;
|
|
|
|
FILE *fout;
|
|
|
|
|
|
|
|
if (strcmp(cmd, "qecho") == 0)
|
|
|
|
fout = pset.queryFout;
|
|
|
|
else
|
|
|
|
fout = stdout;
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
while ((value = scan_option(&string, OT_NORMAL, "ed, false)))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
if (!quoted && strcmp(value, "-n") == 0)
|
|
|
|
no_newline = true;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
|
|
|
fputc(' ', fout);
|
|
|
|
fputs(value, fout);
|
|
|
|
}
|
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
if (!no_newline)
|
|
|
|
fputs("\n", fout);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-21 03:05:12 +01:00
|
|
|
/* \encoding -- set/show client side encoding */
|
2000-02-20 15:28:28 +01:00
|
|
|
else if (strcmp(cmd, "encoding") == 0)
|
2000-02-19 06:01:16 +01:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *encoding = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-02-20 15:28:28 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!encoding)
|
2000-02-21 03:05:12 +01:00
|
|
|
/* show encoding */
|
|
|
|
puts(pg_encoding_to_char(pset.encoding));
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2000-02-21 03:05:12 +01:00
|
|
|
{
|
2000-02-20 15:28:28 +01:00
|
|
|
#ifdef MULTIBYTE
|
2000-02-21 03:05:12 +01:00
|
|
|
/* set encoding */
|
|
|
|
if (PQsetClientEncoding(pset.db, encoding) == -1)
|
|
|
|
psql_error("%s: invalid encoding name\n", encoding);
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* save encoding info into psql internal data */
|
|
|
|
pset.encoding = PQclientEncoding(pset.db);
|
|
|
|
SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding));
|
|
|
|
}
|
2000-02-20 15:28:28 +01:00
|
|
|
#else
|
2001-06-09 01:53:48 +02:00
|
|
|
psql_error("\\%s: multibyte support is not enabled\n", cmd);
|
2000-02-19 06:01:16 +01:00
|
|
|
#endif
|
2000-04-12 19:17:23 +02:00
|
|
|
free(encoding);
|
|
|
|
}
|
2000-02-20 15:28:28 +01:00
|
|
|
}
|
|
|
|
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
/* \f -- change field separator */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "f") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *fname = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_pset("fieldsep", fname, &pset.popt, quiet);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \g means send query */
|
|
|
|
else if (strcmp(cmd, "g") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *fname = scan_option(&string, OT_FILEPIPE, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!fname)
|
2000-01-14 23:18:03 +01:00
|
|
|
pset.gfname = NULL;
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2000-02-08 00:10:11 +01:00
|
|
|
pset.gfname = xstrdup(fname);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
1999-11-05 00:14:30 +01:00
|
|
|
status = CMD_SEND;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* help */
|
|
|
|
else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-04-15 01:43:44 +02:00
|
|
|
helpSQL(options_string ? &options_string[strspn(options_string, " \t\n\r")] : NULL);
|
2000-04-12 19:17:23 +02:00
|
|
|
/* set pointer to end of line */
|
|
|
|
if (string)
|
|
|
|
string += strlen(string);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* HTML mode */
|
|
|
|
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-01-14 23:18:03 +01:00
|
|
|
if (pset.popt.topt.format != PRINT_HTML)
|
|
|
|
success = do_pset("format", "html", &pset.popt, quiet);
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
else
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_pset("format", "aligned", &pset.popt, quiet);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* \i is include file */
|
|
|
|
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *fname = scan_option(&string, OT_NORMAL, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!fname)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-03-01 22:10:05 +01:00
|
|
|
success = (process_file(fname) == EXIT_SUCCESS);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \l is list databases */
|
|
|
|
else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
success = listAllDbs(false);
|
2000-01-12 20:36:36 +01:00
|
|
|
else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
success = listAllDbs(true);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* large object things
|
|
|
|
*/
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strncmp(cmd, "lo_", 3) == 0)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
char *opt1,
|
|
|
|
*opt2;
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
opt1 = scan_option(&string, OT_NORMAL, NULL, true);
|
|
|
|
opt2 = scan_option(&string, OT_NORMAL, NULL, true);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (strcmp(cmd + 3, "export") == 0)
|
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!opt2)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_lo_export(opt1, opt2);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (strcmp(cmd + 3, "import") == 0)
|
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!opt1)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
2001-03-22 05:01:46 +01:00
|
|
|
success = do_lo_import(opt1, opt2);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else if (strcmp(cmd + 3, "list") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_lo_list();
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
else if (strcmp(cmd + 3, "unlink") == 0)
|
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!opt1)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_lo_unlink(opt1);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
else
|
|
|
|
status = CMD_UNKNOWN;
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt1);
|
|
|
|
free(opt2);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \o -- set query output */
|
|
|
|
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *fname = scan_option(&string, OT_FILEPIPE, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = setQFout(fname);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* \p prints the current query buffer */
|
|
|
|
else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0)
|
|
|
|
{
|
|
|
|
if (query_buf && query_buf->len > 0)
|
|
|
|
puts(query_buf->data);
|
2000-01-12 20:36:36 +01:00
|
|
|
else if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Query buffer is empty."));
|
1999-12-10 01:26:35 +01:00
|
|
|
fflush(stdout);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \pset -- set printing parameters */
|
|
|
|
else if (strcmp(cmd, "pset") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
|
|
|
|
char *opt1 = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (!opt0)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_pset(opt0, opt1, &pset.popt, quiet);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt0);
|
|
|
|
free(opt1);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* \q or \quit */
|
|
|
|
else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0)
|
|
|
|
status = CMD_TERMINATE;
|
|
|
|
|
|
|
|
/* reset(clear) the buffer */
|
|
|
|
else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
|
|
|
|
{
|
|
|
|
resetPQExpBuffer(query_buf);
|
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Query buffer reset (cleared)."));
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \s save history in a file or show it on the screen */
|
|
|
|
else if (strcmp(cmd, "s") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *fname = scan_option(&string, OT_NORMAL, NULL, true);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = saveHistory(fname ? fname : "/dev/tty");
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (success && !quiet && fname)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Wrote history to %s.\n"), fname);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/* \set -- generalized set variable/option command */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "set") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
|
|
|
if (!opt0)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
|
|
|
/* list all variables */
|
|
|
|
|
|
|
|
/*
|
2000-04-12 19:17:23 +02:00
|
|
|
* XXX This is in utter violation of the GetVariable
|
|
|
|
* abstraction, but I have not bothered to do it better.
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
|
|
|
struct _variable *ptr;
|
|
|
|
|
2000-01-14 23:18:03 +01:00
|
|
|
for (ptr = pset.vars; ptr->next; ptr = ptr->next)
|
1999-11-05 00:14:30 +01:00
|
|
|
fprintf(stdout, "%s = '%s'\n", ptr->next->name, ptr->next->value);
|
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Set variable to the concatenation of the arguments.
|
|
|
|
*/
|
|
|
|
char *newval = NULL;
|
|
|
|
char *opt;
|
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
opt = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
newval = xstrdup(opt ? opt : "");
|
|
|
|
free(opt);
|
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
while ((opt = scan_option(&string, OT_NORMAL, NULL, false)))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
|
|
|
|
if (!newval)
|
|
|
|
{
|
|
|
|
psql_error("out of memory\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
strcat(newval, opt);
|
|
|
|
free(opt);
|
|
|
|
}
|
2000-02-08 00:10:11 +01:00
|
|
|
|
|
|
|
if (!SetVariable(pset.vars, opt0, newval))
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\%s: error\n", cmd);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
free(newval);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt0);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* \t -- turn off headers and row count */
|
|
|
|
else if (strcmp(cmd, "t") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_pset("tuples_only", NULL, &pset.popt, quiet);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* \T -- define html <table ...> attributes */
|
|
|
|
else if (strcmp(cmd, "T") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *value = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_pset("tableattr", value, &pset.popt, quiet);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(value);
|
|
|
|
}
|
|
|
|
|
2002-03-05 01:01:03 +01:00
|
|
|
/* \timing -- toggle timing of queries */
|
|
|
|
else if (strcmp(cmd, "timing") == 0)
|
|
|
|
{
|
|
|
|
pset.timing = !pset.timing;
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (pset.timing)
|
|
|
|
{
|
|
|
|
puts(gettext(("Timing is on.")));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
puts(gettext(("Timing is off.")));
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* \unset */
|
|
|
|
else if (strcmp(cmd, "unset") == 0)
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *opt = scan_option(&string, OT_NORMAL, NULL, false);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
if (!opt)
|
|
|
|
{
|
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
|
|
|
success = false;
|
|
|
|
}
|
2002-01-18 17:14:54 +01:00
|
|
|
else if (!SetVariable(pset.vars, opt, NULL))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
psql_error("\\%s: error\n", cmd);
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
free(opt);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* \w -- write query buffer to file */
|
|
|
|
else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0)
|
|
|
|
{
|
|
|
|
FILE *fd = NULL;
|
2000-02-08 00:10:11 +01:00
|
|
|
bool is_pipe = false;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *fname = NULL;
|
|
|
|
|
|
|
|
if (!query_buf)
|
|
|
|
{
|
|
|
|
psql_error("no query buffer\n");
|
|
|
|
status = CMD_ERROR;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
fname = scan_option(&string, OT_FILEPIPE, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
if (!fname)
|
|
|
|
{
|
|
|
|
psql_error("\\%s: missing required argument\n", cmd);
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (fname[0] == '|')
|
|
|
|
{
|
|
|
|
is_pipe = true;
|
|
|
|
fd = popen(&fname[1], "w");
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fd = fopen(fname, "w");
|
|
|
|
|
|
|
|
if (!fd)
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (fd)
|
|
|
|
{
|
|
|
|
int result;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (query_buf && query_buf->len > 0)
|
|
|
|
fprintf(fd, "%s\n", query_buf->data);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
if (is_pipe)
|
1999-11-05 00:14:30 +01:00
|
|
|
result = pclose(fd);
|
|
|
|
else
|
|
|
|
result = fclose(fd);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (result == EOF)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
}
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \x -- toggle expanded table representation */
|
|
|
|
else if (strcmp(cmd, "x") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_pset("expanded", NULL, &pset.popt, quiet);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/* \z -- list table rights (grant/revoke) */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "z") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2001-10-05 21:01:13 +02:00
|
|
|
char *opt = scan_option(&string, OT_SQLID, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
success = permissionsList(opt);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* \! -- shell escape */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "!") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
success = do_shell(options_string);
|
2000-04-12 19:17:23 +02:00
|
|
|
/* wind pointer to end of line */
|
|
|
|
if (string)
|
|
|
|
string += strlen(string);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* \? -- slash command help */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "?") == 0)
|
2000-01-14 23:18:03 +01:00
|
|
|
slashUsage();
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-03-01 22:10:05 +01:00
|
|
|
#if 0
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
1999-11-05 00:14:30 +01:00
|
|
|
* These commands don't do anything. I just use them to test the
|
|
|
|
* parser.
|
|
|
|
*/
|
|
|
|
else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0)
|
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
int i = 0;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *value;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
fprintf(stderr, "+ optstr = |%s|\n", options_string);
|
2001-10-05 21:01:13 +02:00
|
|
|
while ((value = scan_option(&string, OT_NORMAL, NULL, true)))
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
|
2000-04-12 19:17:23 +02:00
|
|
|
free(value);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
|
|
|
status = CMD_UNKNOWN;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!success)
|
|
|
|
status = CMD_ERROR;
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* eat the rest of the options string */
|
2001-10-05 21:01:13 +02:00
|
|
|
while ((val = scan_option(&string, OT_NORMAL, NULL, false)))
|
2000-05-05 11:38:40 +02:00
|
|
|
{
|
|
|
|
if (status != CMD_UNKNOWN)
|
|
|
|
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
|
|
|
|
}
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (options_string && continue_parse)
|
|
|
|
*continue_parse = options_string + (string - string_cpy);
|
|
|
|
free(string_cpy);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
return status;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/*
|
|
|
|
* scan_option()
|
|
|
|
*/
|
|
|
|
static char *
|
2001-10-05 21:01:13 +02:00
|
|
|
scan_option(char **string, enum option_type type, char *quote, bool semicolon)
|
2000-02-08 00:10:11 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
unsigned int pos = 0;
|
|
|
|
char *options_string;
|
|
|
|
char *return_val;
|
|
|
|
|
|
|
|
if (quote)
|
|
|
|
*quote = 0;
|
|
|
|
|
|
|
|
if (!string || !(*string))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
options_string = *string;
|
|
|
|
/* skip leading whitespace */
|
2000-04-15 01:43:44 +02:00
|
|
|
pos += strspn(options_string + pos, " \t\n\r");
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
switch (options_string[pos])
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Double quoted string
|
|
|
|
*/
|
|
|
|
case '"':
|
|
|
|
{
|
|
|
|
unsigned int jj;
|
|
|
|
unsigned short int bslash_count = 0;
|
|
|
|
|
|
|
|
/* scan for end of quote */
|
|
|
|
for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
|
|
|
|
{
|
|
|
|
if (options_string[jj] == '"' && bslash_count % 2 == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (options_string[jj] == '\\')
|
|
|
|
bslash_count++;
|
|
|
|
else
|
|
|
|
bslash_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options_string[jj] == 0)
|
|
|
|
{
|
2001-09-10 16:51:33 +02:00
|
|
|
psql_error("parse error at the end of line\n");
|
2000-04-12 19:17:23 +02:00
|
|
|
*string = &options_string[jj];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return_val = malloc(jj - pos + 2);
|
|
|
|
if (!return_val)
|
|
|
|
{
|
|
|
|
psql_error("out of memory\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If this is expected to be an SQL identifier like option
|
|
|
|
* then we strip out the double quotes
|
|
|
|
*/
|
2001-10-05 21:01:13 +02:00
|
|
|
|
2002-02-25 22:37:42 +01:00
|
|
|
if (type == OT_SQLID || type == OT_SQLIDHACK)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
unsigned int k,
|
|
|
|
cc;
|
|
|
|
|
|
|
|
bslash_count = 0;
|
|
|
|
cc = 0;
|
|
|
|
for (k = pos + 1; options_string[k]; k += PQmblen(&options_string[k], pset.encoding))
|
|
|
|
{
|
|
|
|
if (options_string[k] == '"' && bslash_count % 2 == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (options_string[jj] == '\\')
|
|
|
|
bslash_count++;
|
|
|
|
else
|
|
|
|
bslash_count = 0;
|
|
|
|
|
|
|
|
return_val[cc++] = options_string[k];
|
|
|
|
}
|
|
|
|
return_val[cc] = '\0';
|
|
|
|
}
|
2000-04-15 01:43:44 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
strncpy(return_val, &options_string[pos], jj - pos + 1);
|
|
|
|
return_val[jj - pos + 1] = '\0';
|
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
*string = options_string + jj + 1;
|
|
|
|
if (quote)
|
|
|
|
*quote = '"';
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* A single quote has a psql internal meaning, such as for
|
|
|
|
* delimiting file names, and it also allows for such escape
|
|
|
|
* sequences as \t.
|
|
|
|
*/
|
|
|
|
case '\'':
|
|
|
|
{
|
|
|
|
unsigned int jj;
|
|
|
|
unsigned short int bslash_count = 0;
|
|
|
|
|
|
|
|
for (jj = pos + 1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding))
|
|
|
|
{
|
|
|
|
if (options_string[jj] == '\'' && bslash_count % 2 == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (options_string[jj] == '\\')
|
|
|
|
bslash_count++;
|
|
|
|
else
|
|
|
|
bslash_count = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options_string[jj] == 0)
|
|
|
|
{
|
2001-09-10 16:51:33 +02:00
|
|
|
psql_error("parse error at the end of line\n");
|
2000-04-12 19:17:23 +02:00
|
|
|
*string = &options_string[jj];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return_val = unescape(&options_string[pos + 1], jj - pos - 1);
|
|
|
|
*string = &options_string[jj + 1];
|
|
|
|
if (quote)
|
|
|
|
*quote = '\'';
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Backticks are for command substitution, like in shells
|
|
|
|
*/
|
|
|
|
case '`':
|
|
|
|
{
|
|
|
|
bool error = false;
|
|
|
|
FILE *fd = NULL;
|
|
|
|
char *file;
|
|
|
|
PQExpBufferData output;
|
|
|
|
char buf[512];
|
|
|
|
size_t result,
|
|
|
|
len;
|
|
|
|
|
|
|
|
len = strcspn(options_string + pos + 1, "`");
|
|
|
|
if (options_string[pos + 1 + len] == 0)
|
|
|
|
{
|
2001-09-10 16:51:33 +02:00
|
|
|
psql_error("parse error at the end of line\n");
|
2000-04-12 19:17:23 +02:00
|
|
|
*string = &options_string[pos + 1 + len];
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
options_string[pos + 1 + len] = '\0';
|
|
|
|
file = options_string + pos + 1;
|
|
|
|
|
|
|
|
fd = popen(file, "r");
|
|
|
|
if (!fd)
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", file, strerror(errno));
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
initPQExpBuffer(&output);
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
result = fread(buf, 1, 512, fd);
|
|
|
|
if (ferror(fd))
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", file, strerror(errno));
|
|
|
|
error = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
appendBinaryPQExpBuffer(&output, buf, result);
|
|
|
|
} while (!feof(fd));
|
|
|
|
appendPQExpBufferChar(&output, '\0');
|
|
|
|
|
|
|
|
if (pclose(fd) == -1)
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", file, strerror(errno));
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error)
|
|
|
|
{
|
|
|
|
if (output.data[strlen(output.data) - 1] == '\n')
|
|
|
|
output.data[strlen(output.data) - 1] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!error)
|
|
|
|
return_val = output.data;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return_val = xstrdup("");
|
|
|
|
termPQExpBuffer(&output);
|
|
|
|
}
|
|
|
|
options_string[pos + 1 + len] = '`';
|
|
|
|
*string = options_string + pos + len + 2;
|
|
|
|
if (quote)
|
|
|
|
*quote = '`';
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* end of line
|
|
|
|
*/
|
|
|
|
case 0:
|
|
|
|
*string = &options_string[pos];
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Variable substitution
|
|
|
|
*/
|
|
|
|
case ':':
|
|
|
|
{
|
|
|
|
size_t token_end;
|
|
|
|
const char *value;
|
|
|
|
char save_char;
|
|
|
|
|
2000-04-15 01:43:44 +02:00
|
|
|
token_end = strcspn(&options_string[pos + 1], " \t\n\r");
|
2000-04-12 19:17:23 +02:00
|
|
|
save_char = options_string[pos + token_end + 1];
|
|
|
|
options_string[pos + token_end + 1] = '\0';
|
|
|
|
value = GetVariable(pset.vars, options_string + pos + 1);
|
|
|
|
if (!value)
|
|
|
|
value = "";
|
|
|
|
return_val = xstrdup(value);
|
|
|
|
options_string[pos + token_end + 1] = save_char;
|
|
|
|
*string = &options_string[pos + token_end + 1];
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Next command
|
|
|
|
*/
|
|
|
|
case '\\':
|
|
|
|
*string = options_string + pos;
|
|
|
|
return NULL;
|
|
|
|
break;
|
|
|
|
|
2000-04-15 01:43:44 +02:00
|
|
|
/*
|
2001-03-22 05:01:46 +01:00
|
|
|
* | could be the beginning of a pipe if so, take rest of line
|
|
|
|
* as command
|
2000-04-15 01:43:44 +02:00
|
|
|
*/
|
|
|
|
case '|':
|
|
|
|
if (type == OT_FILEPIPE)
|
|
|
|
{
|
|
|
|
*string += strlen(options_string + pos);
|
|
|
|
return xstrdup(options_string + pos);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
/* fallthrough for other option types */
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* A normal word
|
|
|
|
*/
|
|
|
|
default:
|
|
|
|
{
|
|
|
|
size_t token_end;
|
|
|
|
char *cp;
|
|
|
|
|
2000-04-15 01:43:44 +02:00
|
|
|
token_end = strcspn(&options_string[pos], " \t\n\r");
|
2000-04-12 19:17:23 +02:00
|
|
|
return_val = malloc(token_end + 1);
|
|
|
|
if (!return_val)
|
|
|
|
{
|
|
|
|
psql_error("out of memory\n");
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
|
|
|
strncpy(return_val, &options_string[pos], token_end);
|
|
|
|
return_val[token_end] = 0;
|
|
|
|
|
2001-10-05 21:01:13 +02:00
|
|
|
/* Strip any trailing semi-colons for some types */
|
2001-10-25 07:50:21 +02:00
|
|
|
if (semicolon)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = strlen(return_val) - 1; i && return_val[i] == ';'; i--);
|
|
|
|
if (i < strlen(return_val) - 1)
|
|
|
|
return_val[i + 1] = '\0';
|
2001-10-05 21:01:13 +02:00
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (type == OT_SQLID)
|
|
|
|
for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))
|
2000-12-03 21:45:40 +01:00
|
|
|
if (isupper((unsigned char) *cp))
|
|
|
|
*cp = tolower((unsigned char) *cp);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
*string = &options_string[pos + token_end];
|
|
|
|
return return_val;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2000-02-08 00:10:11 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/*
|
|
|
|
* unescape
|
|
|
|
*
|
|
|
|
* Replaces \n, \t, and the like.
|
|
|
|
*
|
|
|
|
* The return value is malloc()'ed.
|
|
|
|
*/
|
|
|
|
static char *
|
2000-02-08 00:10:11 +01:00
|
|
|
unescape(const unsigned char *source, size_t len)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
const unsigned char *p;
|
1999-11-05 00:14:30 +01:00
|
|
|
bool esc = false; /* Last character we saw was the escape
|
|
|
|
* character */
|
|
|
|
char *destination,
|
|
|
|
*tmp;
|
|
|
|
size_t length;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
#ifdef USE_ASSERT_CHECKING
|
1999-11-05 00:14:30 +01:00
|
|
|
assert(source);
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
length = Min(len, strlen(source)) + 1;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
tmp = destination = malloc(length);
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!tmp)
|
|
|
|
{
|
2000-01-19 00:30:24 +01:00
|
|
|
psql_error("out of memory\n");
|
1999-11-05 00:14:30 +01:00
|
|
|
exit(EXIT_FAILURE);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
for (p = source; p - source < len && *p; p += PQmblen(p, pset.encoding))
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
if (esc)
|
|
|
|
{
|
|
|
|
char c;
|
|
|
|
|
|
|
|
switch (*p)
|
|
|
|
{
|
|
|
|
case 'n':
|
|
|
|
c = '\n';
|
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
c = '\t';
|
|
|
|
break;
|
2000-02-08 00:10:11 +01:00
|
|
|
case 'b':
|
|
|
|
c = '\b';
|
|
|
|
break;
|
|
|
|
case 'r':
|
|
|
|
c = '\r';
|
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
case 'f':
|
|
|
|
c = '\f';
|
|
|
|
break;
|
|
|
|
case '0':
|
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
{
|
|
|
|
long int l;
|
|
|
|
char *end;
|
|
|
|
|
|
|
|
l = strtol(p, &end, 0);
|
|
|
|
c = l;
|
|
|
|
p = end - 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
c = *p;
|
|
|
|
}
|
|
|
|
*tmp++ = c;
|
|
|
|
esc = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (*p == '\\')
|
|
|
|
esc = true;
|
|
|
|
|
|
|
|
else
|
|
|
|
{
|
2001-03-22 05:01:46 +01:00
|
|
|
int i;
|
|
|
|
const unsigned char *mp = p;
|
2001-02-17 11:03:33 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
for (i = 0; i < PQmblen(p, pset.encoding); i++)
|
|
|
|
*tmp++ = *mp++;
|
1999-11-05 00:14:30 +01:00
|
|
|
esc = false;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
*tmp = '\0';
|
|
|
|
return destination;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* do_connect
|
|
|
|
* -- handler for \connect
|
|
|
|
*
|
|
|
|
* Connects to a database (new_dbname) as a certain user (new_user).
|
|
|
|
* The new user can be NULL. A db name of "-" is the same as the old one.
|
2000-01-14 23:18:03 +01:00
|
|
|
* (That is, the one currently in pset. But pset.db can also be NULL. A NULL
|
1999-11-04 22:56:02 +01:00
|
|
|
* dbname is handled by libpq.)
|
2001-04-18 22:53:08 +02:00
|
|
|
* Returns true if all ok, false if the new connection couldn't be established.
|
|
|
|
* The old connection will be kept if the session is interactive.
|
1999-11-04 22:56:02 +01:00
|
|
|
*/
|
* Includes tab completion. It's not magic, but it's very cool. At any
rate
it's better than what used to be there.
* Does proper SQL "host variable" substitution as pointed out by Andreas
Zeugwetter (thanks): select * from :foo; Also some changes in how ':'
and ';' are treated (escape with \ to send to backend). This does
_not_
affect the '::' cast operator, but perhaps others that contain : or ;
(but there are none right now).
* To show description with a <something> listing, append '?' to command
name, e.g., \df?. This seemed to be the convenient and logical
solution.
Or append a '+' to see more useless information, e.g., \df+.
* Fixed fflush()'ing bug pointed out by Jan during the regression test
discussion.
* Added LastOid variable. This ought to take care of TODO item "Add a
function to return the last inserted oid, for use in psql scripts"
(under CLIENTS)
E.g.,
insert into foo values(...);
insert into bar values(..., :LastOid);
\echo $LastOid
* \d command shows constraints, rules, and triggers defined on the table
(in addition to indices)
* Various fixes, optimizations, corrections
* Documentation update as well
Note: This now requires snprintf(), which, if necessary, is taken from
src/backend/port. This is certainly a little weird, but it should
suffice
until a source tree cleanup is done.
Enjoy.
--
Peter Eisentraut Sernanders väg 10:115
1999-11-26 05:24:17 +01:00
|
|
|
static bool
|
2000-01-14 23:18:03 +01:00
|
|
|
do_connect(const char *new_dbname, const char *new_user)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2000-01-14 23:18:03 +01:00
|
|
|
PGconn *oldconn = pset.db;
|
1999-11-05 00:14:30 +01:00
|
|
|
const char *dbparam = NULL;
|
|
|
|
const char *userparam = NULL;
|
In the spirit of TODO item
* Add use of 'const' for varibles in source tree
(which is misspelled, btw.)
I went through the front-end libpq code and did so. This affects in
particular the various accessor functions (such as PQdb() and
PQgetvalue()) as well as, by necessity, the internal helpers they use.
I have been really thorough in that regard, perhaps some people will find
it annoying that things like
char * foo = PQgetvalue(res, 0, 0)
will generate a warning. On the other hand it _should_ generate one. This
is no real compatibility break, although a few clients will have to be
fixed to suppress warnings. (Which again would be in the spirit of the
above TODO.)
In addition I replaced some int's by size_t's and removed some warnings
(and generated some new ones -- grmpf!). Also I rewrote PQoidStatus (so it
actually honors the const!) and supplied a new function PQoidValue that
returns a proper Oid type. This is only front-end stuff, none of the
communicaton stuff was touched.
The psql patch also adds some new consts to honor the new libpq situation,
as well as fixes a fatal condition that resulted when using the -V
(--version) option and there is no database listening.
So, to summarize, the psql you should definitely put in (with or without
the libpq). If you think I went too far with the const-mania in libpq, let
me know and I'll make adjustments. If you approve it, I will also update
the docs.
-Peter
--
Peter Eisentraut Sernanders vaeg 10:115
1999-11-11 01:10:14 +01:00
|
|
|
const char *pwparam = NULL;
|
1999-11-05 00:14:30 +01:00
|
|
|
char *prompted_password = NULL;
|
|
|
|
bool need_pass;
|
|
|
|
bool success = false;
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* Delete variables (in case we fail before setting them anew) */
|
|
|
|
SetVariable(pset.vars, "DBNAME", NULL);
|
|
|
|
SetVariable(pset.vars, "USER", NULL);
|
|
|
|
SetVariable(pset.vars, "HOST", NULL);
|
|
|
|
SetVariable(pset.vars, "PORT", NULL);
|
|
|
|
SetVariable(pset.vars, "ENCODING", NULL);
|
2000-01-14 23:18:03 +01:00
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
/* If dbname is "" then use old name, else new one (even if NULL) */
|
|
|
|
if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "") == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
dbparam = PQdb(oldconn);
|
|
|
|
else
|
|
|
|
dbparam = new_dbname;
|
|
|
|
|
2000-01-14 23:18:03 +01:00
|
|
|
/* If user is "" then use the old one */
|
2000-04-12 19:17:23 +02:00
|
|
|
if (new_user && PQuser(oldconn) && strcmp(new_user, "") == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
userparam = PQuser(oldconn);
|
|
|
|
else
|
|
|
|
userparam = new_user;
|
|
|
|
|
|
|
|
/* need to prompt for password? */
|
2000-01-14 23:18:03 +01:00
|
|
|
if (pset.getPassword)
|
2002-02-20 23:47:12 +01:00
|
|
|
pwparam = prompted_password = simple_prompt("Password: ", 100, false);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/*
|
2002-02-20 23:47:12 +01:00
|
|
|
* Use old password (if any) if no new one given and we are
|
|
|
|
* reconnecting as same user
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
2001-10-11 18:54:18 +02:00
|
|
|
if (!pwparam && oldconn && PQuser(oldconn) && userparam &&
|
|
|
|
strcmp(PQuser(oldconn), userparam) == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
pwparam = PQpass(oldconn);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
do
|
|
|
|
{
|
|
|
|
need_pass = false;
|
2000-01-14 23:18:03 +01:00
|
|
|
pset.db = PQsetdbLogin(PQhost(oldconn), PQport(oldconn),
|
2000-04-12 19:17:23 +02:00
|
|
|
NULL, NULL, dbparam, userparam, pwparam);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2000-01-14 23:18:03 +01:00
|
|
|
if (PQstatus(pset.db) == CONNECTION_BAD &&
|
2000-11-27 03:20:36 +01:00
|
|
|
strcmp(PQerrorMessage(pset.db), "fe_sendauth: no password supplied\n") == 0 &&
|
|
|
|
!feof(stdin))
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-09-17 22:33:45 +02:00
|
|
|
PQfinish(pset.db);
|
1999-11-05 00:14:30 +01:00
|
|
|
need_pass = true;
|
|
|
|
free(prompted_password);
|
|
|
|
prompted_password = NULL;
|
|
|
|
pwparam = prompted_password = simple_prompt("Password: ", 100, false);
|
|
|
|
}
|
|
|
|
} while (need_pass);
|
|
|
|
|
|
|
|
free(prompted_password);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If connection failed, try at least keep the old one. That's
|
|
|
|
* probably more convenient than just kicking you out of the program.
|
|
|
|
*/
|
2000-01-14 23:18:03 +01:00
|
|
|
if (!pset.db || PQstatus(pset.db) == CONNECTION_BAD)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
if (pset.cur_cmd_interactive)
|
|
|
|
{
|
|
|
|
psql_error("%s", PQerrorMessage(pset.db));
|
|
|
|
PQfinish(pset.db);
|
|
|
|
if (oldconn)
|
|
|
|
{
|
2001-06-02 20:25:18 +02:00
|
|
|
fputs(gettext("Previous connection kept\n"), stderr);
|
2000-04-12 19:17:23 +02:00
|
|
|
pset.db = oldconn;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pset.db = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* we don't want unpredictable things to happen in scripting
|
|
|
|
* mode
|
|
|
|
*/
|
|
|
|
psql_error("\\connect: %s", PQerrorMessage(pset.db));
|
|
|
|
PQfinish(pset.db);
|
1999-11-05 00:14:30 +01:00
|
|
|
if (oldconn)
|
|
|
|
PQfinish(oldconn);
|
2000-04-12 19:17:23 +02:00
|
|
|
pset.db = NULL;
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
|
|
|
{
|
2000-01-14 23:18:03 +01:00
|
|
|
if (!QUIET())
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
|
|
|
if (userparam != new_user) /* no new user */
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("You are now connected to database %s.\n"), dbparam);
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (dbparam != new_dbname) /* no new db */
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("You are now connected as new user %s.\n"), new_user);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
|
|
|
/* both new */
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("You are now connected to database %s as user %s.\n"),
|
2000-01-14 23:18:03 +01:00
|
|
|
PQdb(pset.db), PQuser(pset.db));
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (oldconn)
|
|
|
|
PQfinish(oldconn);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
success = true;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
|
|
|
|
pset.encoding = PQclientEncoding(pset.db);
|
2000-01-19 00:30:24 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* Update variables */
|
|
|
|
SetVariable(pset.vars, "DBNAME", PQdb(pset.db));
|
|
|
|
SetVariable(pset.vars, "USER", PQuser(pset.db));
|
|
|
|
SetVariable(pset.vars, "HOST", PQhost(pset.db));
|
|
|
|
SetVariable(pset.vars, "PORT", PQport(pset.db));
|
|
|
|
SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding));
|
2000-01-14 23:18:03 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
pset.issuper = test_superuser(PQuser(pset.db));
|
2000-01-29 17:58:54 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
return success;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-01-29 17:58:54 +01:00
|
|
|
/*
|
|
|
|
* Test if the given user is a database superuser.
|
2000-02-08 00:10:11 +01:00
|
|
|
* (Is used to set up the prompt right.)
|
2000-01-29 17:58:54 +01:00
|
|
|
*/
|
|
|
|
bool
|
2000-04-12 19:17:23 +02:00
|
|
|
test_superuser(const char *username)
|
2000-01-29 17:58:54 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
PGresult *res;
|
|
|
|
char buf[64 + NAMEDATALEN];
|
|
|
|
bool answer;
|
|
|
|
|
|
|
|
if (!username)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
sprintf(buf, "SELECT usesuper FROM pg_user WHERE usename = '%.*s'", NAMEDATALEN, username);
|
|
|
|
res = PSQLexec(buf);
|
|
|
|
|
|
|
|
answer =
|
|
|
|
(PQntuples(res) > 0 && PQnfields(res) > 0
|
|
|
|
&& !PQgetisnull(res, 0, 0)
|
|
|
|
&& PQgetvalue(res, 0, 0)
|
|
|
|
&& strcmp(PQgetvalue(res, 0, 0), "t") == 0);
|
|
|
|
PQclear(res);
|
|
|
|
return answer;
|
2000-01-29 17:58:54 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/*
|
|
|
|
* do_edit -- handler for \e
|
|
|
|
*
|
|
|
|
* If you do not specify a filename, the current query buffer will be copied
|
|
|
|
* into a temporary one.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static bool
|
|
|
|
editFile(const char *fname)
|
|
|
|
{
|
2000-02-08 00:10:11 +01:00
|
|
|
const char *editorName;
|
1999-11-05 00:14:30 +01:00
|
|
|
char *sys;
|
|
|
|
int result;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
#ifdef USE_ASSERT_CHECKING
|
1999-11-05 00:14:30 +01:00
|
|
|
assert(fname);
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!fname)
|
|
|
|
return false;
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* Find an editor to use */
|
|
|
|
editorName = getenv("PSQL_EDITOR");
|
|
|
|
if (!editorName)
|
|
|
|
editorName = getenv("EDITOR");
|
|
|
|
if (!editorName)
|
|
|
|
editorName = getenv("VISUAL");
|
|
|
|
if (!editorName)
|
|
|
|
editorName = DEFAULT_EDITOR;
|
|
|
|
|
|
|
|
sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
|
|
|
|
if (!sys)
|
|
|
|
return false;
|
|
|
|
sprintf(sys, "exec %s %s", editorName, fname);
|
|
|
|
result = system(sys);
|
2000-01-19 00:30:24 +01:00
|
|
|
if (result == -1)
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("could not start editor %s\n", editorName);
|
|
|
|
else if (result == 127)
|
2000-01-19 00:30:24 +01:00
|
|
|
psql_error("could not start /bin/sh\n");
|
1999-11-05 00:14:30 +01:00
|
|
|
free(sys);
|
|
|
|
|
|
|
|
return result == 0;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* call this one */
|
|
|
|
static bool
|
|
|
|
do_edit(const char *filename_arg, PQExpBuffer query_buf)
|
|
|
|
{
|
2000-01-12 20:36:36 +01:00
|
|
|
char fnametmp[MAXPGPATH];
|
2000-11-26 12:09:32 +01:00
|
|
|
FILE *stream = NULL;
|
1999-11-05 00:14:30 +01:00
|
|
|
const char *fname;
|
|
|
|
bool error = false;
|
2000-11-25 07:21:54 +01:00
|
|
|
int fd;
|
2001-03-22 05:01:46 +01:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifndef WIN32
|
1999-11-05 00:14:30 +01:00
|
|
|
struct stat before,
|
|
|
|
after;
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (filename_arg)
|
|
|
|
fname = filename_arg;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a temp file to edit */
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifndef WIN32
|
2000-04-12 19:17:23 +02:00
|
|
|
const char *tmpdirenv = getenv("TMPDIR");
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-01-12 20:36:36 +01:00
|
|
|
sprintf(fnametmp, "%s/psql.edit.%ld.%ld",
|
2000-04-12 19:17:23 +02:00
|
|
|
tmpdirenv ? tmpdirenv : "/tmp",
|
|
|
|
(long) geteuid(), (long) getpid());
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
1999-11-05 00:14:30 +01:00
|
|
|
GetTempFileName(".", "psql", 0, fnametmp);
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
fname = (const char *) fnametmp;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2001-03-22 05:01:46 +01:00
|
|
|
fd = open(fname, O_WRONLY | O_CREAT | O_EXCL, 0600);
|
2000-11-25 07:21:54 +01:00
|
|
|
if (fd != -1)
|
|
|
|
stream = fdopen(fd, "w");
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-11-25 07:21:54 +01:00
|
|
|
if (fd == -1 || !stream)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2001-06-09 01:53:48 +02:00
|
|
|
psql_error("could not open temporary file %s: %s\n", fname, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
unsigned int ql = query_buf->len;
|
|
|
|
|
|
|
|
if (ql == 0 || query_buf->data[ql - 1] != '\n')
|
|
|
|
{
|
|
|
|
appendPQExpBufferChar(query_buf, '\n');
|
|
|
|
ql++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fwrite(query_buf->data, 1, ql, stream) != ql)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
fclose(stream);
|
|
|
|
remove(fname);
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fclose(stream);
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
if (!error && stat(fname, &before) != 0)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
1999-11-04 22:56:02 +01:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* call editor */
|
|
|
|
if (!error)
|
|
|
|
error = !editFile(fname);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
#ifndef WIN32
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!error && stat(fname, &after) != 0)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
error = true;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!error && before.st_mtime != after.st_mtime)
|
|
|
|
{
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!error)
|
|
|
|
{
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
stream = fopen(fname, "r");
|
|
|
|
if (!stream)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* read file back in */
|
|
|
|
char line[1024];
|
|
|
|
|
|
|
|
resetPQExpBuffer(query_buf);
|
2000-11-27 03:20:36 +01:00
|
|
|
while (fgets(line, sizeof(line), stream) != NULL)
|
2000-03-27 23:11:37 +02:00
|
|
|
appendPQExpBufferStr(query_buf, line);
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (ferror(stream))
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
|
|
|
error = true;
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
fclose(stream);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
2000-04-11 19:35:50 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* remove temp file */
|
2000-04-11 19:35:50 +02:00
|
|
|
if (!filename_arg)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
if (remove(fname) == -1)
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
|
|
|
error = true;
|
|
|
|
}
|
|
|
|
}
|
2000-03-27 23:11:37 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
return !error;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2000-02-20 15:28:28 +01:00
|
|
|
int
|
2000-01-19 00:30:24 +01:00
|
|
|
process_file(char *filename)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
FILE *fd;
|
|
|
|
int result;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *oldfilename;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!filename)
|
|
|
|
return false;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
fd = fopen(filename, "r");
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!fd)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("%s: %s\n", filename, strerror(errno));
|
1999-11-05 00:14:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
oldfilename = pset.inputfile;
|
|
|
|
pset.inputfile = filename;
|
2000-01-19 00:30:24 +01:00
|
|
|
result = MainLoop(fd);
|
1999-11-05 00:14:30 +01:00
|
|
|
fclose(fd);
|
2000-01-23 02:27:39 +01:00
|
|
|
pset.inputfile = oldfilename;
|
2000-02-20 15:28:28 +01:00
|
|
|
return result;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do_pset
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
static const char *
|
|
|
|
_align2string(enum printFormat in)
|
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
switch (in)
|
|
|
|
{
|
2001-10-25 07:50:21 +02:00
|
|
|
case PRINT_NOTHING:
|
1999-11-05 00:14:30 +01:00
|
|
|
return "nothing";
|
|
|
|
break;
|
|
|
|
case PRINT_UNALIGNED:
|
|
|
|
return "unaligned";
|
|
|
|
break;
|
|
|
|
case PRINT_ALIGNED:
|
|
|
|
return "aligned";
|
|
|
|
break;
|
|
|
|
case PRINT_HTML:
|
|
|
|
return "html";
|
|
|
|
break;
|
|
|
|
case PRINT_LATEX:
|
|
|
|
return "latex";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return "unknown";
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
2000-04-12 19:17:23 +02:00
|
|
|
do_pset(const char *param, const char *value, printQueryOpt *popt, bool quiet)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
size_t vallen = 0;
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifdef USE_ASSERT_CHECKING
|
1999-11-05 00:14:30 +01:00
|
|
|
assert(param);
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!param)
|
|
|
|
return false;
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (value)
|
|
|
|
vallen = strlen(value);
|
|
|
|
|
|
|
|
/* set format */
|
|
|
|
if (strcmp(param, "format") == 0)
|
|
|
|
{
|
|
|
|
if (!value)
|
|
|
|
;
|
|
|
|
else if (strncasecmp("unaligned", value, vallen) == 0)
|
|
|
|
popt->topt.format = PRINT_UNALIGNED;
|
|
|
|
else if (strncasecmp("aligned", value, vallen) == 0)
|
|
|
|
popt->topt.format = PRINT_ALIGNED;
|
|
|
|
else if (strncasecmp("html", value, vallen) == 0)
|
|
|
|
popt->topt.format = PRINT_HTML;
|
|
|
|
else if (strncasecmp("latex", value, vallen) == 0)
|
|
|
|
popt->topt.format = PRINT_LATEX;
|
|
|
|
else
|
|
|
|
{
|
2000-01-19 00:30:24 +01:00
|
|
|
psql_error("\\pset: allowed formats are unaligned, aligned, html, latex\n");
|
1999-11-05 00:14:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Output format is %s.\n"), _align2string(popt->topt.format));
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* set border style/width */
|
|
|
|
else if (strcmp(param, "border") == 0)
|
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
popt->topt.border = atoi(value);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Border style is %d.\n"), popt->topt.border);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* set expanded/vertical mode */
|
|
|
|
else if (strcmp(param, "x") == 0 || strcmp(param, "expanded") == 0 || strcmp(param, "vertical") == 0)
|
|
|
|
{
|
|
|
|
popt->topt.expanded = !popt->topt.expanded;
|
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(popt->topt.expanded
|
|
|
|
? gettext("Expanded display is on.\n")
|
|
|
|
: gettext("Expanded display is off.\n"));
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* null display */
|
|
|
|
else if (strcmp(param, "null") == 0)
|
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
free(popt->nullPrint);
|
|
|
|
popt->nullPrint = xstrdup(value);
|
|
|
|
}
|
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Null display is '%s'.\n"), popt->nullPrint ? popt->nullPrint : "");
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* field separator for unaligned text */
|
|
|
|
else if (strcmp(param, "fieldsep") == 0)
|
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
free(popt->topt.fieldSep);
|
|
|
|
popt->topt.fieldSep = xstrdup(value);
|
|
|
|
}
|
|
|
|
if (!quiet)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Field separator is '%s'.\n"), popt->topt.fieldSep);
|
2000-01-19 00:30:24 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/* record separator for unaligned text */
|
|
|
|
else if (strcmp(param, "recordsep") == 0)
|
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
free(popt->topt.recordSep);
|
|
|
|
popt->topt.recordSep = xstrdup(value);
|
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (strcmp(popt->topt.recordSep, "\n") == 0)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Record separator is <newline>."));
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Record separator is '%s'.\n"), popt->topt.recordSep);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* toggle between full and barebones format */
|
|
|
|
else if (strcmp(param, "t") == 0 || strcmp(param, "tuples_only") == 0)
|
|
|
|
{
|
|
|
|
popt->topt.tuples_only = !popt->topt.tuples_only;
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->topt.tuples_only)
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Showing only tuples."));
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Tuples only is off."));
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* set title override */
|
|
|
|
else if (strcmp(param, "title") == 0)
|
|
|
|
{
|
|
|
|
free(popt->title);
|
|
|
|
if (!value)
|
|
|
|
popt->title = NULL;
|
|
|
|
else
|
|
|
|
popt->title = xstrdup(value);
|
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->title)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Title is \"%s\".\n"), popt->title);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Title is unset.\n"));
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* set HTML table tag options */
|
|
|
|
else if (strcmp(param, "T") == 0 || strcmp(param, "tableattr") == 0)
|
|
|
|
{
|
|
|
|
free(popt->topt.tableAttr);
|
|
|
|
if (!value)
|
|
|
|
popt->topt.tableAttr = NULL;
|
|
|
|
else
|
|
|
|
popt->topt.tableAttr = xstrdup(value);
|
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->topt.tableAttr)
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Table attribute is \"%s\".\n"), popt->topt.tableAttr);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
printf(gettext("Table attributes unset.\n"));
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* toggle use of pager */
|
|
|
|
else if (strcmp(param, "pager") == 0)
|
|
|
|
{
|
|
|
|
popt->topt.pager = !popt->topt.pager;
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->topt.pager)
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Using pager is on."));
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Using pager is off."));
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2001-05-12 21:44:46 +02:00
|
|
|
/* disable "(x rows)" footer */
|
|
|
|
else if (strcmp(param, "footer") == 0)
|
|
|
|
{
|
|
|
|
popt->default_footer = !popt->default_footer;
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->default_footer)
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Default footer is on."));
|
2001-05-12 21:44:46 +02:00
|
|
|
else
|
2001-06-02 20:25:18 +02:00
|
|
|
puts(gettext("Default footer is off."));
|
2001-05-12 21:44:46 +02:00
|
|
|
}
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
else
|
|
|
|
{
|
2000-01-19 00:30:24 +01:00
|
|
|
psql_error("\\pset: unknown option: %s\n", param);
|
1999-11-05 00:14:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2000-02-08 00:10:11 +01:00
|
|
|
#define DEFAULT_SHELL "/bin/sh"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
static bool
|
|
|
|
do_shell(const char *command)
|
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
int result;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!command)
|
|
|
|
{
|
|
|
|
char *sys;
|
2000-02-08 00:10:11 +01:00
|
|
|
const char *shellName;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
shellName = getenv("SHELL");
|
|
|
|
if (shellName == NULL)
|
|
|
|
shellName = DEFAULT_SHELL;
|
|
|
|
|
|
|
|
sys = malloc(strlen(shellName) + 16);
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!sys)
|
|
|
|
{
|
|
|
|
psql_error("out of memory\n");
|
|
|
|
if (pset.cur_cmd_interactive)
|
|
|
|
return false;
|
|
|
|
else
|
|
|
|
exit(EXIT_FAILURE);
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
sprintf(sys, "exec %s", shellName);
|
|
|
|
result = system(sys);
|
|
|
|
free(sys);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
result = system(command);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (result == 127 || result == -1)
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
psql_error("\\!: failed\n");
|
1999-11-05 00:14:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|