2000-01-19 00:30:24 +01:00
|
|
|
/*
|
|
|
|
* psql - the PostgreSQL interactive terminal
|
|
|
|
*
|
2005-01-01 06:43:09 +01:00
|
|
|
* Copyright (c) 2000-2005, PostgreSQL Global Development Group
|
2000-01-19 00:30:24 +01:00
|
|
|
*
|
2005-12-23 02:16:38 +01:00
|
|
|
* $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.157 2005/12/23 01:16:38 tgl 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"
|
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
#ifdef WIN32_CLIENT_ONLY /* needed for BCC */
|
2005-04-29 15:42:21 +02:00
|
|
|
#undef mkdir
|
|
|
|
#endif
|
|
|
|
|
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>
|
2002-10-03 19:09:42 +02:00
|
|
|
#include <direct.h>
|
2004-11-04 23:25:14 +01:00
|
|
|
#ifndef WIN32_CLIENT_ONLY
|
|
|
|
#include <sys/types.h> /* for umask() */
|
|
|
|
#include <sys/stat.h> /* for stat() */
|
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
2000-02-16 14:15:26 +01:00
|
|
|
#include "libpq-fe.h"
|
|
|
|
#include "pqexpbuffer.h"
|
2005-12-18 03:17:16 +01:00
|
|
|
#include "dumputils.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"
|
2004-02-19 20:40:09 +01:00
|
|
|
#include "psqlscan.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "settings.h"
|
2000-01-14 23:18:03 +01:00
|
|
|
#include "variables.h"
|
2000-02-19 06:01:16 +01:00
|
|
|
#include "mb/pg_wchar.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/* functions for use in this file */
|
1999-11-05 00:14:30 +01:00
|
|
|
static backslashResult exec_command(const char *cmd,
|
2004-08-29 07:07:03 +02:00
|
|
|
PsqlScanState scan_state,
|
|
|
|
PQExpBuffer query_buf);
|
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);
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/*----------
|
|
|
|
* HandleSlashCmds:
|
|
|
|
*
|
|
|
|
* Handles all the different commands that start with '\',
|
|
|
|
* ordinarily called by MainLoop().
|
|
|
|
*
|
2004-02-19 20:40:09 +01:00
|
|
|
* scan_state is a lexer working state that is set to continue scanning
|
|
|
|
* just after the '\'. The lexer is advanced past the command and all
|
|
|
|
* arguments on return.
|
1999-11-04 22:56:02 +01:00
|
|
|
*
|
|
|
|
* 'query_buf' contains the query-so-far, which may be modified by
|
2004-02-19 20:40:09 +01:00
|
|
|
* 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
|
2004-02-19 20:40:09 +01:00
|
|
|
HandleSlashCmds(PsqlScanState scan_state,
|
|
|
|
PQExpBuffer query_buf)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2005-12-18 03:17:16 +01:00
|
|
|
backslashResult status = PSQL_CMD_SKIP_LINE;
|
2004-02-19 20:40:09 +01:00
|
|
|
char *cmd;
|
|
|
|
char *arg;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
psql_assert(scan_state);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/* Parse off the command name */
|
|
|
|
cmd = psql_scan_slash_command(scan_state);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/* And try to execute it */
|
|
|
|
status = exec_command(cmd, scan_state, query_buf);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2005-12-18 03:17:16 +01:00
|
|
|
if (status == PSQL_CMD_UNKNOWN && strlen(cmd) > 1)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +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
|
|
|
*/
|
2004-08-29 07:07:03 +02:00
|
|
|
char new_cmd[2];
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/* don't change cmd until we know it's okay */
|
|
|
|
new_cmd[0] = cmd[0];
|
1999-11-05 00:14:30 +01:00
|
|
|
new_cmd[1] = '\0';
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
psql_scan_slash_pushback(scan_state, cmd + 1);
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
status = exec_command(new_cmd, scan_state, query_buf);
|
|
|
|
|
2005-12-18 03:17:16 +01:00
|
|
|
if (status != PSQL_CMD_UNKNOWN)
|
2004-02-19 20:40:09 +01:00
|
|
|
{
|
|
|
|
/* adjust cmd for possible messages below */
|
|
|
|
cmd[1] = '\0';
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2005-12-18 03:17:16 +01:00
|
|
|
if (status == PSQL_CMD_UNKNOWN)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
if (pset.cur_cmd_interactive)
|
2005-02-22 05:43:23 +01:00
|
|
|
fprintf(stderr, _("Invalid command \\%s. Try \\? for help.\n"), cmd);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2004-02-19 20:40:09 +01:00
|
|
|
psql_error("invalid command \\%s\n", cmd);
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_ERROR;
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2005-12-18 03:17:16 +01:00
|
|
|
if (status != PSQL_CMD_ERROR)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2004-12-19 20:39:47 +01:00
|
|
|
/* eat any remaining arguments after a valid command */
|
|
|
|
/* note we suppress evaluation of backticks here */
|
|
|
|
while ((arg = psql_scan_slash_option(scan_state,
|
|
|
|
OT_VERBATIM, NULL, false)))
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
psql_error("\\%s: extra argument \"%s\" ignored\n", cmd, arg);
|
2004-12-19 20:39:47 +01:00
|
|
|
free(arg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* silently throw away rest of line after an erroneous command */
|
|
|
|
while ((arg = psql_scan_slash_option(scan_state,
|
|
|
|
OT_WHOLE_LINE, NULL, false)))
|
|
|
|
free(arg);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/* if there is a trailing \\, swallow it */
|
|
|
|
psql_scan_slash_command_end(scan_state);
|
|
|
|
|
|
|
|
free(cmd);
|
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
|
|
|
}
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
/*
|
|
|
|
* Subroutine to actually try to execute a backslash command.
|
|
|
|
*/
|
1999-11-04 22:56:02 +01:00
|
|
|
static backslashResult
|
1999-11-05 00:14:30 +01:00
|
|
|
exec_command(const char *cmd,
|
2004-02-19 20:40:09 +01:00
|
|
|
PsqlScanState scan_state,
|
|
|
|
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();
|
2005-12-18 03:17:16 +01:00
|
|
|
backslashResult status = PSQL_CMD_SKIP_LINE;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* \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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
|
|
|
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
|
|
|
/*
|
2005-10-15 04:49:52 +02: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.
|
2002-02-25 22:37:42 +01:00
|
|
|
*/
|
2004-02-19 20:40:09 +01:00
|
|
|
opt1 = psql_scan_slash_option(scan_state,
|
|
|
|
OT_SQLIDHACK, &opt1q, true);
|
|
|
|
opt2 = psql_scan_slash_option(scan_state,
|
|
|
|
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)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, true);
|
2001-10-25 07:50:21 +02:00
|
|
|
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)
|
|
|
|
{
|
2003-07-23 10:47:41 +02:00
|
|
|
psql_error("\\%s: could not change directory to \"%s\": %s\n",
|
2001-05-07 21:31:33 +02:00
|
|
|
cmd, dir, strerror(errno));
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
|
2005-06-10 01:28:10 +02:00
|
|
|
if (pset.dirname)
|
|
|
|
pfree(pset.dirname);
|
|
|
|
pset.dirname = pg_strdup(dir);
|
|
|
|
canonicalize_path(pset.dirname);
|
|
|
|
|
2001-05-07 21:31:33 +02:00
|
|
|
if (opt)
|
|
|
|
free(opt);
|
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \copy */
|
2004-05-07 02:24:59 +02:00
|
|
|
else if (pg_strcasecmp(cmd, "copy") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_WHOLE_LINE, NULL, false);
|
2004-02-19 20:40:09 +01:00
|
|
|
|
|
|
|
success = do_copy(opt);
|
|
|
|
free(opt);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
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')
|
|
|
|
{
|
2002-08-10 05:56:24 +02:00
|
|
|
char *pattern;
|
2000-04-12 19:17:23 +02:00
|
|
|
bool show_verbose;
|
2001-10-25 07:50:21 +02:00
|
|
|
|
2002-08-10 05:56:24 +02:00
|
|
|
/* We don't do SQLID reduction on the pattern yet */
|
2004-02-19 20:40:09 +01:00
|
|
|
pattern = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, 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 '+':
|
2002-08-10 05:56:24 +02:00
|
|
|
if (pattern)
|
|
|
|
success = describeTableDetails(pattern, 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':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = describeAggregates(pattern, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2004-06-18 08:14:31 +02:00
|
|
|
case 'b':
|
2004-07-15 05:56:06 +02:00
|
|
|
success = describeTablespaces(pattern, show_verbose);
|
2004-06-18 08:14:31 +02:00
|
|
|
break;
|
2003-01-07 21:56:07 +01:00
|
|
|
case 'c':
|
|
|
|
success = listConversions(pattern);
|
|
|
|
break;
|
|
|
|
case 'C':
|
|
|
|
success = listCasts(pattern);
|
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
case 'd':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = objectDescription(pattern);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2003-01-07 21:56:07 +01:00
|
|
|
case 'D':
|
|
|
|
success = listDomains(pattern);
|
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
case 'f':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = describeFunctions(pattern, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2003-12-01 23:21:54 +01:00
|
|
|
case 'g':
|
2005-08-14 20:49:30 +02:00
|
|
|
/* no longer distinct from \du */
|
|
|
|
success = describeRoles(pattern);
|
2003-12-01 23:21:54 +01:00
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
case 'l':
|
2000-01-14 23:18:03 +01:00
|
|
|
success = do_lo_list();
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2003-01-07 21:56:07 +01:00
|
|
|
case 'n':
|
2004-07-13 18:48:16 +02:00
|
|
|
success = listSchemas(pattern, show_verbose);
|
2003-01-07 21:56:07 +01:00
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
case 'o':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = describeOperators(pattern);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'p':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = permissionsList(pattern);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 'T':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = describeTypes(pattern, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
|
|
|
case 't':
|
|
|
|
case 'v':
|
|
|
|
case 'i':
|
|
|
|
case 's':
|
|
|
|
case 'S':
|
2002-08-10 05:56:24 +02:00
|
|
|
success = listTables(&cmd[1], pattern, show_verbose);
|
1999-11-05 00:14:30 +01:00
|
|
|
break;
|
2001-05-09 19:29:10 +02:00
|
|
|
case 'u':
|
2005-08-14 20:49:30 +02:00
|
|
|
success = describeRoles(pattern);
|
2001-10-25 07:50:21 +02:00
|
|
|
break;
|
2003-01-07 21:56:07 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
default:
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_UNKNOWN;
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
2002-08-10 05:56:24 +02:00
|
|
|
|
|
|
|
if (pattern)
|
|
|
|
free(pattern);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* \e or \edit -- edit the current query buffer (or a file and make it the
|
|
|
|
* query buffer
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
|
|
|
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");
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_ERROR;
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
fname = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, true);
|
2004-01-09 22:12:20 +01:00
|
|
|
expand_tilde(&fname);
|
2004-08-13 16:47:23 +02:00
|
|
|
if (fname)
|
|
|
|
canonicalize_path(fname);
|
2005-12-18 03:17:16 +01:00
|
|
|
status = do_edit(fname, query_buf) ? PSQL_CMD_NEWEDIT : PSQL_CMD_ERROR;
|
2000-04-12 19:17:23 +02:00
|
|
|
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
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
while ((value = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *encoding = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_NORMAL, NULL, false);
|
2000-02-20 15:28:28 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!encoding)
|
2003-06-28 02:12:40 +02:00
|
|
|
{
|
2003-09-16 19:59:02 +02:00
|
|
|
/* show encoding */
|
2000-02-21 03:05:12 +01:00
|
|
|
puts(pg_encoding_to_char(pset.encoding));
|
2003-06-28 02:12:40 +02:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2000-02-21 03:05:12 +01:00
|
|
|
{
|
|
|
|
/* set encoding */
|
|
|
|
if (PQsetClientEncoding(pset.db, encoding) == -1)
|
2002-09-22 22:57:21 +02:00
|
|
|
psql_error("%s: invalid encoding name or conversion procedure not found\n", encoding);
|
2000-02-21 03:05:12 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* save encoding info into psql internal data */
|
|
|
|
pset.encoding = PQclientEncoding(pset.db);
|
2003-06-28 02:12:40 +02:00
|
|
|
pset.popt.topt.encoding = pset.encoding;
|
|
|
|
SetVariable(pset.vars, "ENCODING",
|
|
|
|
pg_encoding_to_char(pset.encoding));
|
2000-02-21 03:05:12 +01:00
|
|
|
}
|
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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *fname = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
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)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *fname = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
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
|
2004-01-09 22:12:20 +01:00
|
|
|
{
|
|
|
|
expand_tilde(&fname);
|
2004-01-25 04:07:22 +01:00
|
|
|
pset.gfname = pg_strdup(fname);
|
2004-01-09 22:12:20 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
free(fname);
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_WHOLE_LINE, NULL, false);
|
2004-02-19 20:40:09 +01:00
|
|
|
|
|
|
|
helpSQL(opt, pset.popt.topt.pager);
|
|
|
|
free(opt);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
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)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *fname = psql_scan_slash_option(scan_state,
|
|
|
|
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
|
|
|
{
|
2004-01-09 22:12:20 +01:00
|
|
|
expand_tilde(&fname);
|
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
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
opt1 = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, true);
|
|
|
|
opt2 = psql_scan_slash_option(scan_state,
|
|
|
|
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
|
2004-01-09 22:12:20 +01:00
|
|
|
{
|
|
|
|
expand_tilde(&opt2);
|
2000-02-08 00:10:11 +01:00
|
|
|
success = do_lo_export(opt1, opt2);
|
2004-01-09 22:12:20 +01:00
|
|
|
}
|
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
|
2004-01-09 22:12:20 +01:00
|
|
|
{
|
|
|
|
expand_tilde(&opt1);
|
2001-03-22 05:01:46 +01:00
|
|
|
success = do_lo_import(opt1, opt2);
|
2004-01-09 22:12:20 +01:00
|
|
|
}
|
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
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *fname = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_FILEPIPE, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2004-01-09 22:12:20 +01:00
|
|
|
expand_tilde(&fname);
|
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)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Query buffer is empty."));
|
1999-12-10 01:26:35 +01:00
|
|
|
fflush(stdout);
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2005-12-18 03:17:16 +01:00
|
|
|
/* \password -- set user password */
|
|
|
|
else if (strcmp(cmd, "password") == 0)
|
|
|
|
{
|
|
|
|
char *pw1;
|
|
|
|
char *pw2;
|
|
|
|
|
|
|
|
pw1 = simple_prompt("Enter new password: ", 100, false);
|
|
|
|
pw2 = simple_prompt("Enter it again: ", 100, false);
|
|
|
|
|
|
|
|
if (strcmp(pw1, pw2) != 0)
|
|
|
|
{
|
|
|
|
fprintf(stderr, _("Passwords didn't match.\n"));
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char *opt0 = psql_scan_slash_option(scan_state, OT_SQLID, NULL, true);
|
|
|
|
char *user;
|
2005-12-23 02:16:38 +01:00
|
|
|
char *encrypted_password;
|
2005-12-18 03:17:16 +01:00
|
|
|
|
|
|
|
if (opt0)
|
|
|
|
user = opt0;
|
|
|
|
else
|
|
|
|
user = PQuser(pset.db);
|
|
|
|
|
2005-12-23 02:16:38 +01:00
|
|
|
encrypted_password = pg_make_encrypted_password(pw1, user);
|
|
|
|
|
|
|
|
if (!encrypted_password)
|
2005-12-18 03:17:16 +01:00
|
|
|
{
|
|
|
|
fprintf(stderr, _("Password encryption failed.\n"));
|
|
|
|
success = false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
PQExpBufferData buf;
|
|
|
|
PGresult *res;
|
|
|
|
|
|
|
|
initPQExpBuffer(&buf);
|
2005-12-23 02:16:38 +01:00
|
|
|
printfPQExpBuffer(&buf, "ALTER USER %s PASSWORD '%s';",
|
2005-12-18 03:17:16 +01:00
|
|
|
fmtId(user), encrypted_password);
|
|
|
|
res = PSQLexec(buf.data, false);
|
|
|
|
termPQExpBuffer(&buf);
|
|
|
|
if (!res)
|
|
|
|
success = false;
|
|
|
|
else
|
|
|
|
PQclear(res);
|
2005-12-23 02:16:38 +01:00
|
|
|
PQfreemem(encrypted_password);
|
2005-12-18 03:17:16 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
free(pw1);
|
|
|
|
free(pw2);
|
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* \pset -- set printing parameters */
|
|
|
|
else if (strcmp(cmd, "pset") == 0)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt0 = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, false);
|
|
|
|
char *opt1 = psql_scan_slash_option(scan_state,
|
|
|
|
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)
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_TERMINATE;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/* reset(clear) the buffer */
|
|
|
|
else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0)
|
|
|
|
{
|
|
|
|
resetPQExpBuffer(query_buf);
|
2004-02-19 20:40:09 +01:00
|
|
|
psql_scan_reset(scan_state);
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!quiet)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("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)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *fname = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, true);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-01-09 22:12:20 +01:00
|
|
|
expand_tilde(&fname);
|
2004-03-27 19:01:40 +01:00
|
|
|
/* This scrolls off the screen when using /dev/tty */
|
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)
|
2005-06-10 01:28:10 +02:00
|
|
|
printf(gettext("Wrote history to file \"%s/%s\".\n"),
|
|
|
|
pset.dirname ? pset.dirname : ".", fname);
|
2004-11-30 21:00:34 +01:00
|
|
|
if (!fname)
|
|
|
|
putchar('\n');
|
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)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt0 = psql_scan_slash_option(scan_state,
|
|
|
|
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 */
|
2003-03-20 07:43:35 +01:00
|
|
|
PrintVariables(pset.vars);
|
1999-11-05 00:14:30 +01:00
|
|
|
success = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2000-04-12 19:17:23 +02:00
|
|
|
/*
|
|
|
|
* Set variable to the concatenation of the arguments.
|
|
|
|
*/
|
2004-02-19 20:40:09 +01:00
|
|
|
char *newval;
|
2000-04-12 19:17:23 +02:00
|
|
|
char *opt;
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
opt = psql_scan_slash_option(scan_state,
|
|
|
|
OT_NORMAL, NULL, false);
|
2004-01-25 04:07:22 +01:00
|
|
|
newval = pg_strdup(opt ? opt : "");
|
2000-04-12 19:17:23 +02:00
|
|
|
free(opt);
|
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
while ((opt = psql_scan_slash_option(scan_state,
|
|
|
|
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
|
|
|
|
2003-06-28 02:12:40 +02:00
|
|
|
if (SetVariable(pset.vars, opt0, newval))
|
|
|
|
{
|
|
|
|
/* Check for special variables */
|
2003-07-28 02:14:43 +02:00
|
|
|
if (strcmp(opt0, "VERBOSITY") == 0)
|
|
|
|
SyncVerbosityVariable();
|
2003-06-28 02:12:40 +02:00
|
|
|
}
|
|
|
|
else
|
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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *value = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
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)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Timing is on."));
|
2002-03-05 01:01:03 +01:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Timing is off."));
|
2002-03-05 01:01:03 +01:00
|
|
|
}
|
|
|
|
}
|
2002-09-04 22:31:48 +02:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* \unset */
|
|
|
|
else if (strcmp(cmd, "unset") == 0)
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
|
|
|
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");
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_ERROR;
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
fname = psql_scan_slash_option(scan_state,
|
|
|
|
OT_FILEPIPE, NULL, true);
|
2004-01-09 22:12:20 +01:00
|
|
|
expand_tilde(&fname);
|
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
|
2004-07-11 23:34:04 +02:00
|
|
|
{
|
|
|
|
canonicalize_path(fname);
|
2000-04-12 19:17:23 +02:00
|
|
|
fd = fopen(fname, "w");
|
2004-07-11 23:34:04 +02:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
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
|
|
|
|
2002-08-10 05:56:24 +02:00
|
|
|
/* \z -- list table rights (equivalent to \dp) */
|
1999-11-05 00:14:30 +01:00
|
|
|
else if (strcmp(cmd, "z") == 0)
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *pattern = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_NORMAL, NULL, true);
|
2000-04-12 19:17:23 +02:00
|
|
|
|
2002-08-10 05:56:24 +02:00
|
|
|
success = permissionsList(pattern);
|
|
|
|
if (pattern)
|
|
|
|
free(pattern);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
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
|
|
|
{
|
2004-02-19 20:40:09 +01:00
|
|
|
char *opt = psql_scan_slash_option(scan_state,
|
2005-10-15 04:49:52 +02:00
|
|
|
OT_WHOLE_LINE, NULL, false);
|
2004-02-19 20:40:09 +01:00
|
|
|
|
|
|
|
success = do_shell(opt);
|
|
|
|
free(opt);
|
2000-04-12 19:17:23 +02:00
|
|
|
}
|
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)
|
2002-07-15 03:56:25 +02:00
|
|
|
slashUsage(pset.popt.topt.pager);
|
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
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* These commands don't do anything. I just use them to test the parser.
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
|
|
|
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
|
|
|
|
2004-02-19 20:40:09 +01:00
|
|
|
while ((value = psql_scan_slash_option(scan_state,
|
|
|
|
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
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_UNKNOWN;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!success)
|
2005-12-18 03:17:16 +01:00
|
|
|
status = PSQL_CMD_ERROR;
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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;
|
2005-10-15 04:49:52 +02:00
|
|
|
char *password_prompt = 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) */
|
2003-06-28 02:12:40 +02:00
|
|
|
UnsyncVariables();
|
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;
|
|
|
|
|
2005-10-15 04:49:52 +02:00
|
|
|
if (userparam == NULL)
|
2005-07-25 19:17:41 +02:00
|
|
|
password_prompt = strdup("Password: ");
|
|
|
|
else
|
|
|
|
{
|
2005-12-08 22:18:22 +01:00
|
|
|
password_prompt = malloc(strlen(_("Password for user %s: ")) - 2 +
|
2005-07-25 19:17:41 +02:00
|
|
|
strlen(userparam) + 1);
|
2005-12-08 22:18:22 +01:00
|
|
|
sprintf(password_prompt, _("Password for user %s: "), userparam);
|
2005-07-25 19:17:41 +02:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* need to prompt for password? */
|
2000-01-14 23:18:03 +01:00
|
|
|
if (pset.getPassword)
|
2005-07-25 19:17:41 +02:00
|
|
|
pwparam = prompted_password = simple_prompt(password_prompt, 100, false);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02: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 &&
|
2004-10-16 05:10:17 +02:00
|
|
|
strcmp(PQerrorMessage(pset.db), PQnoPasswordSupplied) == 0 &&
|
2000-11-27 03:20:36 +01:00
|
|
|
!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;
|
2005-07-25 19:17:41 +02:00
|
|
|
pwparam = prompted_password = simple_prompt(password_prompt, 100, false);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
} while (need_pass);
|
|
|
|
|
|
|
|
free(prompted_password);
|
2005-07-25 19:17:41 +02:00
|
|
|
free(password_prompt);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* If connection failed, try at least keep the old one. That's probably
|
|
|
|
* more convenient than just kicking you out of the program.
|
1999-11-05 00:14:30 +01:00
|
|
|
*/
|
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)
|
|
|
|
{
|
2005-02-22 05:43:23 +01:00
|
|
|
fputs(_("Previous connection kept\n"), stderr);
|
2000-04-12 19:17:23 +02:00
|
|
|
pset.db = oldconn;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
pset.db = NULL;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* we don't want unpredictable things to happen in scripting mode
|
2000-04-12 19:17:23 +02:00
|
|
|
*/
|
|
|
|
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 */
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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 */
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("You are now connected as new user \"%s\".\n"), new_user);
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2004-10-07 17:21:58 +02:00
|
|
|
/* both new */
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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);
|
2000-01-19 00:30:24 +01:00
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
/* Update variables */
|
2003-06-28 02:12:40 +02:00
|
|
|
SyncVariables();
|
|
|
|
|
|
|
|
return success;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* SyncVariables
|
|
|
|
*
|
|
|
|
* Make psql's internal variables agree with connection state upon
|
|
|
|
* establishing a new connection.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
SyncVariables(void)
|
|
|
|
{
|
|
|
|
/* get stuff from connection */
|
|
|
|
pset.encoding = PQclientEncoding(pset.db);
|
|
|
|
pset.popt.topt.encoding = pset.encoding;
|
|
|
|
|
2000-04-12 19:17:23 +02:00
|
|
|
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
|
|
|
|
2003-06-28 02:12:40 +02:00
|
|
|
/* send stuff to it, too */
|
2003-07-28 02:14:43 +02:00
|
|
|
SyncVerbosityVariable();
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2000-01-29 17:58:54 +01:00
|
|
|
/*
|
2003-06-28 02:12:40 +02:00
|
|
|
* UnsyncVariables
|
|
|
|
*
|
|
|
|
* Clear variables that should be not be set when there is no connection.
|
2000-01-29 17:58:54 +01:00
|
|
|
*/
|
2003-06-28 02:12:40 +02:00
|
|
|
void
|
|
|
|
UnsyncVariables(void)
|
2000-01-29 17:58:54 +01:00
|
|
|
{
|
2003-06-28 02:12:40 +02:00
|
|
|
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-29 17:58:54 +01:00
|
|
|
}
|
|
|
|
|
2003-06-28 02:12:40 +02:00
|
|
|
/*
|
2003-07-28 02:14:43 +02:00
|
|
|
* Update connection state from VERBOSITY variable
|
2003-06-28 02:12:40 +02:00
|
|
|
*/
|
|
|
|
void
|
2003-07-28 02:14:43 +02:00
|
|
|
SyncVerbosityVariable(void)
|
2003-06-28 02:12:40 +02:00
|
|
|
{
|
2003-07-28 02:14:43 +02:00
|
|
|
switch (SwitchVariable(pset.vars, "VERBOSITY",
|
2003-06-28 02:12:40 +02:00
|
|
|
"default", "terse", "verbose", NULL))
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
case 1: /* default */
|
2004-03-21 23:29:11 +01:00
|
|
|
pset.verbosity = PQERRORS_DEFAULT;
|
2003-06-28 02:12:40 +02:00
|
|
|
break;
|
2003-08-04 02:43:34 +02:00
|
|
|
case 2: /* terse */
|
2004-03-21 23:29:11 +01:00
|
|
|
pset.verbosity = PQERRORS_TERSE;
|
2003-06-28 02:12:40 +02:00
|
|
|
break;
|
2003-08-04 02:43:34 +02:00
|
|
|
case 3: /* verbose */
|
2004-03-21 23:29:11 +01:00
|
|
|
pset.verbosity = PQERRORS_VERBOSE;
|
2003-06-28 02:12:40 +02:00
|
|
|
break;
|
|
|
|
default: /* not set or unrecognized value */
|
2004-03-21 23:29:11 +01:00
|
|
|
pset.verbosity = PQERRORS_DEFAULT;
|
2003-06-28 02:12:40 +02:00
|
|
|
break;
|
|
|
|
}
|
2004-03-21 23:29:11 +01:00
|
|
|
|
|
|
|
PQsetErrorVerbosity(pset.db, pset.verbosity);
|
2003-06-28 02:12:40 +02:00
|
|
|
}
|
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
|
|
|
|
2003-12-01 23:14:40 +01:00
|
|
|
psql_assert(fname);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
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;
|
|
|
|
|
2004-11-16 00:15:12 +01:00
|
|
|
/*
|
|
|
|
* 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
|
|
|
|
* if necessary. But this policy is not very workable on Windows, due to
|
|
|
|
* severe brain damage in their command shell plus the fact that standard
|
|
|
|
* program paths include spaces.
|
|
|
|
*/
|
2004-01-25 04:07:22 +01:00
|
|
|
sys = pg_malloc(strlen(editorName) + strlen(fname) + 10 + 1);
|
2003-04-04 22:40:45 +02:00
|
|
|
#ifndef WIN32
|
2004-11-16 00:15:12 +01:00
|
|
|
sprintf(sys, "exec %s '%s'", editorName, fname);
|
|
|
|
#else
|
|
|
|
sprintf(sys, "%s\"%s\" \"%s\"%s",
|
|
|
|
SYSTEMQUOTE, editorName, fname, SYSTEMQUOTE);
|
2003-04-04 22:40:45 +02:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
result = system(sys);
|
2000-01-19 00:30:24 +01:00
|
|
|
if (result == -1)
|
2003-07-23 10:47:41 +02:00
|
|
|
psql_error("could not start editor \"%s\"\n", editorName);
|
2000-04-12 19:17:23 +02:00
|
|
|
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
|
|
|
|
2004-11-04 23:25:14 +01:00
|
|
|
#ifndef WIN32_CLIENT_ONLY
|
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;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* make a temp file to edit */
|
1999-11-04 22:56:02 +01:00
|
|
|
#ifndef WIN32
|
2004-11-04 23:25:14 +01:00
|
|
|
const char *tmpdir = getenv("TMPDIR");
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2004-11-04 23:25:14 +01:00
|
|
|
if (!tmpdir)
|
|
|
|
tmpdir = "/tmp";
|
|
|
|
#else
|
2005-10-15 04:49:52 +02:00
|
|
|
char tmpdir[MAXPGPATH];
|
|
|
|
int ret;
|
2004-11-04 23:25:14 +01:00
|
|
|
|
|
|
|
ret = GetTempPath(MAXPGPATH, tmpdir);
|
|
|
|
if (ret == 0 || ret > MAXPGPATH)
|
|
|
|
{
|
2004-11-09 15:39:44 +01:00
|
|
|
psql_error("cannot locate temporary directory: %s",
|
2005-10-15 04:49:52 +02:00
|
|
|
!ret ? strerror(errno) : "");
|
2004-11-04 23:25:14 +01:00
|
|
|
return false;
|
|
|
|
}
|
2005-10-15 04:49:52 +02:00
|
|
|
|
2004-11-04 23:25:14 +01:00
|
|
|
/*
|
2005-10-15 04:49:52 +02:00
|
|
|
* No canonicalize_path() here. EDIT.EXE run from CMD.EXE prepends the
|
|
|
|
* current directory to the supplied path unless we use only
|
|
|
|
* backslashes, so we do that.
|
2004-11-04 23:25:14 +01:00
|
|
|
*/
|
|
|
|
#endif
|
|
|
|
#ifndef WIN32
|
2005-03-11 18:20:35 +01:00
|
|
|
snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d", tmpdir,
|
2005-10-15 04:49:52 +02:00
|
|
|
"/", (int) getpid());
|
1999-11-04 22:56:02 +01:00
|
|
|
#else
|
2005-03-11 18:20:35 +01:00
|
|
|
snprintf(fnametmp, sizeof(fnametmp), "%s%spsql.edit.%d", tmpdir,
|
2005-10-15 04:49:52 +02:00
|
|
|
"" /* trailing separator already present */ , (int) getpid());
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
2004-11-04 23:25:14 +01:00
|
|
|
|
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
|
|
|
{
|
2003-07-23 10:47:41 +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;
|
|
|
|
}
|
2004-01-26 23:35:32 +01:00
|
|
|
else if (fclose(stream) != 0)
|
|
|
|
{
|
|
|
|
psql_error("%s: %s\n", fname, strerror(errno));
|
|
|
|
remove(fname);
|
|
|
|
error = true;
|
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-11-04 23:25:14 +01:00
|
|
|
#ifndef WIN32_CLIENT_ONLY
|
1999-11-05 00:14:30 +01:00
|
|
|
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
|
|
|
|
2004-11-04 23:25:14 +01:00
|
|
|
#ifndef WIN32_CLIENT_ONLY
|
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
|
2004-07-11 15:29:16 +02:00
|
|
|
stream = fopen(fname, PG_BINARY_R);
|
1999-11-05 00:14:30 +01:00
|
|
|
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
|
|
|
|
2003-02-13 05:08:16 +01:00
|
|
|
#ifdef USE_READLINE
|
2003-02-19 05:04:04 +01:00
|
|
|
#ifdef HAVE_REPLACE_HISTORY_ENTRY
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
replace_history_entry(where_history(), query_buf->data, NULL);
|
2003-02-19 05:04:04 +01:00
|
|
|
#else
|
|
|
|
add_history(query_buf->data);
|
|
|
|
#endif
|
2003-02-13 05:08:16 +01:00
|
|
|
#endif
|
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
|
2005-09-20 20:59:02 +02:00
|
|
|
* Handler for \i, but can be used for other things as well. Returns
|
|
|
|
* MainLoop() error code.
|
1999-11-04 22:56:02 +01:00
|
|
|
*/
|
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)
|
2005-09-20 20:59:02 +02:00
|
|
|
return EXIT_FAILURE;
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2004-07-11 23:34:04 +02:00
|
|
|
canonicalize_path(filename);
|
2004-07-11 15:29:16 +02:00
|
|
|
fd = fopen(filename, PG_BINARY_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));
|
2005-09-20 20:59:02 +02:00
|
|
|
return EXIT_FAILURE;
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
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;
|
2005-06-09 17:27:27 +02:00
|
|
|
case PRINT_TROFF_MS:
|
|
|
|
return "troff-ms";
|
|
|
|
break;
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
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;
|
|
|
|
|
2003-12-01 23:14:40 +01:00
|
|
|
psql_assert(param);
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (value)
|
|
|
|
vallen = strlen(value);
|
|
|
|
|
|
|
|
/* set format */
|
|
|
|
if (strcmp(param, "format") == 0)
|
|
|
|
{
|
|
|
|
if (!value)
|
|
|
|
;
|
2004-05-07 02:24:59 +02:00
|
|
|
else if (pg_strncasecmp("unaligned", value, vallen) == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
popt->topt.format = PRINT_UNALIGNED;
|
2004-05-07 02:24:59 +02:00
|
|
|
else if (pg_strncasecmp("aligned", value, vallen) == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
popt->topt.format = PRINT_ALIGNED;
|
2004-05-07 02:24:59 +02:00
|
|
|
else if (pg_strncasecmp("html", value, vallen) == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
popt->topt.format = PRINT_HTML;
|
2004-05-07 02:24:59 +02:00
|
|
|
else if (pg_strncasecmp("latex", value, vallen) == 0)
|
1999-11-05 00:14:30 +01:00
|
|
|
popt->topt.format = PRINT_LATEX;
|
2005-06-09 17:27:27 +02:00
|
|
|
else if (pg_strncasecmp("troff-ms", value, vallen) == 0)
|
|
|
|
popt->topt.format = PRINT_TROFF_MS;
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
|
|
|
{
|
2005-06-09 17:27:27 +02:00
|
|
|
psql_error("\\pset: allowed formats are unaligned, aligned, html, latex, troff-ms\n");
|
1999-11-05 00:14:30 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!quiet)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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
|
2005-02-22 05:43:23 +01:00
|
|
|
? _("Expanded display is on.\n")
|
|
|
|
: _("Expanded display is off.\n"));
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2005-07-18 22:57:53 +02:00
|
|
|
/* locale-aware numeric output */
|
|
|
|
else if (strcmp(param, "numericlocale") == 0)
|
2005-07-10 05:46:13 +02:00
|
|
|
{
|
2005-07-18 22:57:53 +02:00
|
|
|
popt->topt.numericLocale = !popt->topt.numericLocale;
|
2005-07-14 10:42:37 +02:00
|
|
|
if (!quiet)
|
2005-07-10 05:46:13 +02:00
|
|
|
{
|
2005-07-18 22:57:53 +02:00
|
|
|
if (popt->topt.numericLocale)
|
|
|
|
puts(_("Showing locale-adjusted numeric output."));
|
2005-07-14 10:42:37 +02:00
|
|
|
else
|
2005-07-18 22:57:53 +02:00
|
|
|
puts(_("Locale-adjusted numeric output is off."));
|
2005-07-10 05:46:13 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* null display */
|
|
|
|
else if (strcmp(param, "null") == 0)
|
|
|
|
{
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
free(popt->nullPrint);
|
2004-01-25 04:07:22 +01:00
|
|
|
popt->nullPrint = pg_strdup(value);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
if (!quiet)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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);
|
2004-01-25 04:07:22 +01:00
|
|
|
popt->topt.fieldSep = pg_strdup(value);
|
1999-11-05 00:14:30 +01:00
|
|
|
}
|
|
|
|
if (!quiet)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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);
|
2004-01-25 04:07:22 +01:00
|
|
|
popt->topt.recordSep = pg_strdup(value);
|
2000-01-19 00:30:24 +01:00
|
|
|
}
|
2000-04-12 19:17:23 +02:00
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (strcmp(popt->topt.recordSep, "\n") == 0)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("Record separator is <newline>."));
|
2000-04-12 19:17:23 +02:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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
|
|
|
|
2005-07-14 08:49:58 +02:00
|
|
|
/* toggle between full and tuples-only format */
|
1999-11-05 00:14:30 +01:00
|
|
|
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)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Showing only tuples."));
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("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
|
2004-01-25 04:07:22 +01:00
|
|
|
popt->title = pg_strdup(value);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->title)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("Title is \"%s\".\n"), popt->title);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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
|
2004-01-25 04:07:22 +01:00
|
|
|
popt->topt.tableAttr = pg_strdup(value);
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
if (!quiet)
|
|
|
|
{
|
|
|
|
if (popt->topt.tableAttr)
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("Table attribute is \"%s\".\n"), popt->topt.tableAttr);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
printf(_("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)
|
|
|
|
{
|
2004-05-07 02:24:59 +02:00
|
|
|
if (value && pg_strcasecmp(value, "always") == 0)
|
2003-08-04 02:43:34 +02:00
|
|
|
popt->topt.pager = 2;
|
2002-11-08 20:12:21 +01:00
|
|
|
else if (popt->topt.pager == 1)
|
2003-08-04 02:43:34 +02:00
|
|
|
popt->topt.pager = 0;
|
2002-11-08 20:12:21 +01:00
|
|
|
else
|
2003-08-04 02:43:34 +02:00
|
|
|
popt->topt.pager = 1;
|
1999-11-05 00:14:30 +01:00
|
|
|
if (!quiet)
|
|
|
|
{
|
2002-11-08 20:12:21 +01:00
|
|
|
if (popt->topt.pager == 1)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Pager is used for long output."));
|
2002-11-08 20:12:21 +01:00
|
|
|
else if (popt->topt.pager == 2)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Pager is always used."));
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Pager usage 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)
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("Default footer is on."));
|
2001-05-12 21:44:46 +02:00
|
|
|
else
|
2005-02-22 05:43:23 +01:00
|
|
|
puts(_("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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2004-11-06 05:29:40 +01:00
|
|
|
#ifndef WIN32
|
2000-02-08 00:10:11 +01:00
|
|
|
#define DEFAULT_SHELL "/bin/sh"
|
2004-11-06 05:29:40 +01:00
|
|
|
#else
|
2004-11-06 18:56:40 +01:00
|
|
|
/*
|
|
|
|
* CMD.EXE is in different places in different Win32 releases so we
|
|
|
|
* have to rely on the path to find it.
|
|
|
|
*/
|
|
|
|
#define DEFAULT_SHELL "cmd.exe"
|
2004-11-06 05:29:40 +01:00
|
|
|
#endif
|
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;
|
2004-11-06 06:20:41 +01:00
|
|
|
const char *shellName;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2004-11-06 05:29:40 +01:00
|
|
|
shellName = getenv("SHELL");
|
2004-11-04 23:25:14 +01:00
|
|
|
#ifdef WIN32
|
|
|
|
if (shellName == NULL)
|
2004-11-06 05:29:40 +01:00
|
|
|
shellName = getenv("COMSPEC");
|
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
if (shellName == NULL)
|
|
|
|
shellName = DEFAULT_SHELL;
|
|
|
|
|
2004-01-25 04:07:22 +01:00
|
|
|
sys = pg_malloc(strlen(shellName) + 16);
|
2005-03-16 22:27:23 +01:00
|
|
|
#ifndef WIN32
|
2003-08-04 02:43:34 +02:00
|
|
|
sprintf(sys,
|
2004-11-30 20:01:28 +01:00
|
|
|
/* See EDITOR handling comment for an explaination */
|
|
|
|
"exec %s", shellName);
|
|
|
|
#else
|
2005-03-16 22:27:23 +01:00
|
|
|
sprintf(sys,
|
|
|
|
/* See EDITOR handling comment for an explaination */
|
2004-11-04 23:25:14 +01:00
|
|
|
"%s\"%s\"%s", SYSTEMQUOTE, shellName, SYSTEMQUOTE);
|
2004-11-30 20:01:28 +01:00
|
|
|
#endif
|
1999-11-05 00:14:30 +01:00
|
|
|
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
|
|
|
}
|