Commit 78bc83fe authored by Bruce Momjian's avatar Bruce Momjian

* 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
parent c83b4d1c
This diff is collapsed.
......@@ -7,7 +7,7 @@
#
#
# IDENTIFICATION
# $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.17 1999/11/08 15:59:59 momjian Exp $
# $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.18 1999/11/26 04:24:16 momjian Exp $
#
#-------------------------------------------------------------------------
......@@ -30,10 +30,18 @@ endif
OBJS=command.o common.o help.o input.o stringutils.o mainloop.o \
copy.o startup.o prompt.o variables.o large_obj.o print.o describe.o \
@STRDUP@ @STRERROR2@
tab-complete.o @STRDUP@ @STRERROR2@ @SNPRINTF@
all: submake psql
# Move this to the utils directory
ifneq (@SNPRINTF@,)
OBJS+=../../backend/port/snprintf.o
../../backend/port/snprintf.o:
$(MAKE) -C ../../backend/port snprintf.o
endif
psql: $(OBJS) $(LIBPQDIR)/libpq.a
$(CC) -o psql -L$(LIBPQDIR) $(OBJS) -lpq $(LDFLAGS)
......@@ -69,7 +77,7 @@ clean:
# sql_help.h is gone, for it needs the docs in the right place to be
# regenerated. -- (pe)
distclean: clean
maintainer-clean: clean
rm -f sql_help.h
ifeq (depend,$(wildcard depend))
......
......@@ -33,7 +33,7 @@
#endif
/* functions for use in this file only */
/* functions for use in this file */
static backslashResult exec_command(const char *cmd,
char *const * options,
......@@ -41,16 +41,24 @@ static backslashResult exec_command(const char *cmd,
PQExpBuffer query_buf,
PsqlSettings *pset);
static bool
do_edit(const char *filename_arg, PQExpBuffer query_buf);
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
static char *
unescape(const char *source, PsqlSettings *pset);
static char * unescape(const char *source, PsqlSettings *pset);
static bool
do_shell(const char *command);
static bool do_connect(const char *new_dbname,
const char *new_user,
PsqlSettings *pset);
static bool do_shell(const char *command);
/*
* Perhaps this should be changed to "infinity",
* but there is no convincing reason to bother
* at this point.
*/
#define NR_OPTIONS 16
/*----------
* HandleSlashCmds:
......@@ -78,7 +86,7 @@ HandleSlashCmds(PsqlSettings *pset,
{
backslashResult status = CMD_SKIP_LINE;
char *my_line;
char *options[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
char *options[NR_OPTIONS+1];
char *token;
const char *options_string = NULL;
const char *cmd;
......@@ -120,7 +128,7 @@ HandleSlashCmds(PsqlSettings *pset,
i = 0;
token = strtokx(options_string, " \t", "\"'`", '\\', &quote, &pos);
for (i = 0; token && i < 16; i++)
for (i = 0; token && i < NR_OPTIONS; i++)
{
switch (quote)
{
......@@ -194,14 +202,15 @@ HandleSlashCmds(PsqlSettings *pset,
options[i] = xstrdup(interpolate_var(token + 1, pset));
else
options[i] = xstrdup(token);
break;
}
if (continue_parse)
break;
token = strtokx(NULL, " \t", "\"'`", '\\', &quote, &pos);
}
} /* for */
options[i] = NULL;
}
cmd = my_line;
......@@ -216,11 +225,11 @@ HandleSlashCmds(PsqlSettings *pset,
* arguments to start immediately after the command, but that is
* no longer encouraged.
*/
const char *new_options[17];
const char *new_options[NR_OPTIONS+1];
char new_cmd[2];
int i;
for (i = 1; i < 17; i++)
for (i = 1; i < NR_OPTIONS+1; i++)
new_options[i] = options[i - 1];
new_options[0] = cmd + 1;
......@@ -249,7 +258,7 @@ HandleSlashCmds(PsqlSettings *pset,
}
/* clean up */
for (i = 0; i < 16 && options[i]; i++)
for (i = 0; i < NR_OPTIONS && options[i]; i++)
free(options[i]);
free(my_line);
......@@ -274,10 +283,7 @@ exec_command(const char *cmd,
backslashResult status = CMD_SKIP_LINE;
/*
* \a -- toggle field alignment This is deprecated and makes no sense,
* but we keep it around.
*/
/* \a -- toggle field alignment This makes little sense but we keep it around. */
if (strcmp(cmd, "a") == 0)
{
if (pset->popt.topt.format != PRINT_ALIGNED)
......@@ -287,22 +293,19 @@ exec_command(const char *cmd,
}
/*
* \C -- override table title (formerly change HTML caption) This is
* deprecated.
*/
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
success = do_pset("title", options[0], &pset->popt, quiet);
/*
/*----------
* \c or \connect -- connect to new database or as different user
*
* \c foo bar : connect to db "foo" as user "bar" \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
* \c foo bar: connect to db "foo" as user "bar"
* \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
*----------
*/
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
{
......@@ -335,35 +338,39 @@ exec_command(const char *cmd,
/* \d* commands */
else if (cmd[0] == 'd')
{
bool show_verbose = strchr(cmd, '+') ? true : false;
bool show_desc = strchr(cmd, '?') ? true : false;
switch (cmd[1])
{
case '\0':
case '?':
if (options[0])
success = describeTableDetails(options[0], pset);
success = describeTableDetails(options[0], pset, show_desc);
else
success = listTables("tvs", NULL, pset); /* standard listing of
* interesting things */
/* standard listing of interesting things */
success = listTables("tvs", NULL, pset, show_desc);
break;
case 'a':
success = describeAggregates(options[0], pset);
success = describeAggregates(options[0], pset, show_verbose, show_desc);
break;
case 'd':
success = objectDescription(options[0], pset);
break;
case 'f':
success = describeFunctions(options[0], pset);
success = describeFunctions(options[0], pset, show_verbose, show_desc);
break;
case 'l':
success = do_lo_list(pset);
success = do_lo_list(pset, show_desc);
break;
case 'o':
success = describeOperators(options[0], pset);
success = describeOperators(options[0], pset, show_verbose, show_desc);
break;
case 'p':
success = permissionsList(options[0], pset);
break;
case 'T':
success = describeTypes(options[0], pset);
success = describeTypes(options[0], pset, show_verbose, show_desc);
break;
case 't':
case 'v':
......@@ -371,9 +378,9 @@ exec_command(const char *cmd,
case 's':
case 'S':
if (cmd[1] == 'S' && cmd[2] == '\0')
success = listTables("Stvs", NULL, pset);
success = listTables("Stvs", NULL, pset, show_desc);
else
success = listTables(&cmd[1], options[0], pset);
success = listTables(&cmd[1], options[0], pset, show_desc);
break;
default:
status = CMD_UNKNOWN;
......@@ -399,14 +406,10 @@ exec_command(const char *cmd,
fputs("\n", stdout);
}
/*
* \f -- change field separator (This is deprecated in favour of
* \pset.)
*/
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
success = do_pset("fieldsep", options[0], &pset->popt, quiet);
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
......@@ -419,12 +422,27 @@ exec_command(const char *cmd,
/* help */
else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
helpSQL(options_string);
{
char buf[256] = "";
int i;
for (i=0; options && options[i] && strlen(buf)<255; i++)
{
strncat(buf, options[i], 255 - strlen(buf));
if (strlen(buf)<255 && options[i+1])
strcat(buf, " ");
}
buf[255] = '\0';
helpSQL(buf);
}
/* HTML mode */
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
{
if (pset->popt.topt.format != PRINT_HTML)
success = do_pset("format", "html", &pset->popt, quiet);
else
success = do_pset("format", "aligned", &pset->popt, quiet);
}
/* \i is include file */
......@@ -439,9 +457,12 @@ exec_command(const char *cmd,
success = process_file(options[0], pset);
}
/* \l is list databases */
else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
success = listAllDbs(pset);
success = listAllDbs(pset, false);
else if (strcmp(cmd, "l?") == 0 || strcmp(cmd, "list?") == 0)
success = listAllDbs(pset, true);
/* large object things */
......@@ -470,7 +491,9 @@ exec_command(const char *cmd,
}
else if (strcmp(cmd + 3, "list") == 0)
success = do_lo_list(pset);
success = do_lo_list(pset, false);
else if (strcmp(cmd + 3, "list?") == 0)
success = do_lo_list(pset, true);
else if (strcmp(cmd + 3, "unlink") == 0)
{
......@@ -828,7 +851,7 @@ unescape(const char *source, PsqlSettings *pset)
* Returns true if all ok, false if the new connection couldn't be established
* but the old one was set back. Otherwise it terminates the program.
*/
bool
static bool
do_connect(const char *new_dbname, const char *new_user, PsqlSettings *pset)
{
PGconn *oldconn = pset->db;
......
......@@ -29,10 +29,6 @@ backslashResult HandleSlashCmds(PsqlSettings *pset,
PQExpBuffer query_buf,
const char **end_of_cmd);
bool do_connect(const char *new_dbname,
const char *new_user,
PsqlSettings *pset);
bool process_file(const char *filename,
PsqlSettings *pset);
......
......@@ -3,11 +3,11 @@
#include "common.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#include <stdio.h>
#include <string.h>
#ifndef HAVE_STRDUP
#include <strdup.h>
#endif
......@@ -15,9 +15,12 @@
#include <assert.h>
#ifndef WIN32
#include <unistd.h> /* for write() */
#else
#include <io.h> /* for _write() */
#endif
#include <libpq-fe.h>
#include <postgres_ext.h>
#include <pqsignal.h>
#include <version.h>
......@@ -30,6 +33,7 @@
#ifdef WIN32
#define popen(x,y) _popen(x,y)
#define pclose(x) _pclose(x)
#define write(a,b,c) _write(a,b,c)
#endif
......@@ -210,11 +214,13 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
/*
* interpolate_var()
*
* If the variable is a regular psql variable, just return its value.
* If it's a magic variable, return that value.
* The idea here is that certain variables have a "magic" meaning, such as
* LastOid. However, you can assign to those variables, but that will shadow
* the magic meaning, until you unset it. If nothing matches, the value of
* the environment variable is used.
*
* This function only returns NULL if you feed in NULL. Otherwise it's ready for
* immediate consumption.
* This function only returns NULL if you feed in NULL's (don't do that).
* Otherwise, the return value is ready for immediate consumption.
*/
const char *
interpolate_var(const char *name, PsqlSettings *pset)
......@@ -229,14 +235,9 @@ interpolate_var(const char *name, PsqlSettings *pset)
return NULL;
#endif
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name))
{
var = GetVariable(pset->vars, name);
if (var)
return var;
else
return "";
}
/* otherwise return magic variable */
......@@ -279,11 +280,16 @@ interpolate_var(const char *name, PsqlSettings *pset)
return "";
}
/*
* env vars (if env vars are all caps there should be no prob,
* otherwise you're on your own
*/
if (strcmp(name, "LastOid") == 0)
{
static char buf[24];
if (pset->lastOid == InvalidOid)
return "";
sprintf(buf, "%u", pset->lastOid);
return buf;
}
/* env vars */
if ((var = getenv(name)))
return var;
......@@ -293,42 +299,31 @@ interpolate_var(const char *name, PsqlSettings *pset)
/*
* Code to support command cancellation.
*
* If interactive, we enable a SIGINT signal catcher before we start a
* query that sends a cancel request to the backend.
* Note that sending the cancel directly from the signal handler is safe
* only because PQrequestCancel is carefully written to make it so. We
* have to be very careful what else we do in the signal handler.
* Code to support query cancellation
*
* Writing on stderr is potentially dangerous, if the signal interrupted
* some stdio operation on stderr. On Unix we can avoid trouble by using
* write() instead; on Windows that's probably not workable, but we can
* at least avoid trusting printf by using the more primitive fputs().
* Before we start a query, we enable a SIGINT signal catcher that sends a
* cancel request to the backend. Note that sending the cancel directly from
* the signal handler is safe because PQrequestCancel() is written to make it
* so. We have to be very careful what else we do in the signal handler. This
* includes using write() for output.
*/
PGconn *cancelConn;
#ifdef WIN32
#define safe_write_stderr(String) fputs(s, stderr)
#else
#define safe_write_stderr(String) write(fileno(stderr), String, strlen(String))
#endif
static PGconn *cancelConn;
#define write_stderr(String) write(fileno(stderr), String, strlen(String))
static void
handle_sigint(SIGNAL_ARGS)
{
/* accept signal if no connection */
if (cancelConn == NULL)
exit(1);
return;
/* Try to send cancel request */
if (PQrequestCancel(cancelConn))
safe_write_stderr("\nCANCEL request sent\n");
write_stderr("\nCancel request sent\n");
else
{
safe_write_stderr("\nCould not send cancel request: ");
safe_write_stderr(PQerrorMessage(cancelConn));
write_stderr("\nCould not send cancel request: ");
write_stderr(PQerrorMessage(cancelConn));
}
}
......@@ -348,7 +343,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
if (!pset->db)
{
fputs("You are not currently connected to a database.\n", stderr);
fputs("You are currently not connected to a database.\n", stderr);
return NULL;
}
......@@ -367,7 +362,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
res = PQexec(pset->db, query);
pqsignal(SIGINT, SIG_DFL); /* no control-C is back to normal */
pqsignal(SIGINT, SIG_DFL); /* now control-C is back to normal */
if (PQstatus(pset->db) == CONNECTION_BAD)
{
......@@ -393,7 +388,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
return res;
else
{
fprintf(stderr, "%s", PQerrorMessage(pset->db));
fputs(PQerrorMessage(pset->db), pset->queryFout);
PQclear(res);
return NULL;
}
......@@ -422,7 +417,7 @@ SendQuery(PsqlSettings *pset, const char *query)
if (!pset->db)
{
fputs("You are not currently connected to a database.\n", stderr);
fputs("You are currently not connected to a database.\n", stderr);
return false;
}
......@@ -430,8 +425,8 @@ SendQuery(PsqlSettings *pset, const char *query)
{
char buf[3];
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
"QUERY: %s\n"
printf("***(Single step mode: Verify query)*********************************************\n"
"%s\n"
"***(press return to proceed or enter x and return to cancel)********************\n",
query);
fflush(stdout);
......@@ -492,11 +487,15 @@ SendQuery(PsqlSettings *pset, const char *query)
break;
case PGRES_COMMAND_OK:
success = true;
pset->lastOid = PQoidValue(results);
if (!GetVariableBool(pset->vars, "quiet")) {
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
fflush(pset->queryFout);
}
break;
case PGRES_COPY_OUT:
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
if (pset->cur_cmd_interactive && !GetVariableBool(pset->vars, "quiet"))
puts("Copy command returns:");
success = handleCopyOut(pset->db, pset->queryFout);
......@@ -516,6 +515,7 @@ SendQuery(PsqlSettings *pset, const char *query)
case PGRES_BAD_RESPONSE:
success = false;
fputs(PQerrorMessage(pset->db), pset->queryFout);
fflush(pset->queryFout);
break;
}
......
This diff is collapsed.
#ifndef DESCRIBE_H
#define DESCRIBE_H
#include <c.h>
#include "settings.h"
/* \da */
bool
describeAggregates(const char *name, PsqlSettings *pset);
bool describeAggregates(const char *name, PsqlSettings *pset, bool verbose, bool desc);
/* \df */
bool
describeFunctions(const char *name, PsqlSettings *pset);
bool describeFunctions(const char *name, PsqlSettings *pset, bool verbose, bool desc);
/* \dT */
bool
describeTypes(const char *name, PsqlSettings *pset);
bool describeTypes(const char *name, PsqlSettings *pset, bool verbose, bool desc);
/* \do */
bool
describeOperators(const char *name, PsqlSettings *pset);
bool describeOperators(const char *name, PsqlSettings *pset, bool verbose, bool desc);
/* \dp (formerly \z) */
bool
permissionsList(const char *name, PsqlSettings *pset);
/* \z (or \dp) */
bool permissionsList(const char *name, PsqlSettings *pset);
/* \dd */
bool
objectDescription(const char *object, PsqlSettings *pset);
bool objectDescription(const char *object, PsqlSettings *pset);
/* \d foo */
bool
describeTableDetails(const char *name, PsqlSettings *pset);
bool describeTableDetails(const char *name, PsqlSettings *pset, bool desc);
/* \l */
bool
listAllDbs(PsqlSettings *pset);
bool listAllDbs(PsqlSettings *pset, bool desc);
/* \dt, \di, \dS, etc. */
bool
listTables(const char *infotype, const char *name, PsqlSettings *pset);
/* \dt, \di, \ds, \dS, etc. */
bool listTables(const char *infotype, const char *name, PsqlSettings *pset, bool desc);
#endif /* DESCRIBE_H */
......@@ -180,7 +180,7 @@ slashUsage(PsqlSettings *pset)
fprintf(fout, " \\c[onnect] [dbname|- [user|?]] -- connect to new database (now '%s')\n", PQdb(pset->db));
fprintf(fout, " \\copy [binary] <table> [with oids] {from|to} <fname>[using delimiters '<char>']\n");
fprintf(fout, " \\copyright -- show PostgreSQL copyright\n");
fprintf(fout, " \\d -- list tables, views, and sequences\n");
fprintf(fout, " \\d <table> -- describe table (or view, index, sequence)\n");
fprintf(fout, " \\d{i|s|t|v|S}-- list only indices/sequences/tables/views/system tables\n");
fprintf(fout, " \\da -- list aggregates\n");
fprintf(fout, " \\dd [object] -- list comment for table, type, function, or operator\n");
......
......@@ -4,7 +4,8 @@
#include <pqexpbuffer.h>
/* Note that this file does not depend on any other files in psql. */
#include "settings.h"
#include "tab-complete.h"
/* Runtime options for turning off readline and history */
/* (of course there is no runtime command for doing that :) */
......@@ -96,13 +97,14 @@ gets_fromFile(FILE *source)
* The only "flag" right now is 1 for use readline & history.
*/
void
initializeInput(int flags)
initializeInput(int flags, PsqlSettings *pset)
{
#ifdef USE_READLINE
if (flags == 1)
{
useReadline = true;
rl_readline_name = "psql";
initialize_readline(&(pset->db));
}
#endif
......
......@@ -37,20 +37,15 @@
#endif
char *
gets_interactive(const char *prompt);
char * gets_interactive(const char *prompt);
char *
gets_fromFile(FILE *source);
char * gets_fromFile(FILE *source);
void
initializeInput(int flags);
void initializeInput(int flags, PsqlSettings *pset);
bool
saveHistory(const char *fname);
bool saveHistory(const char *fname);
void
finishInput(void);
void finishInput(void);
#endif
......@@ -220,6 +220,7 @@ do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_a
fprintf(pset->queryFout, "lo_import %d\n", loid);
pset->lastOid = loid;
return true;
}
......@@ -311,21 +312,20 @@ do_lo_unlink(PsqlSettings *pset, const char *loid_arg)
* Show all large objects in database, with comments if desired
*/
bool
do_lo_list(PsqlSettings *pset)
do_lo_list(PsqlSettings *pset, bool desc)
{
PGresult *res;
char descbuf[512];
char buf[512];
printQueryOpt myopt = pset->popt;
descbuf[0] = '\0';
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
if (GetVariableBool(pset->vars, "description"))
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
strcat(descbuf, "\nFROM pg_class, pg_user\n"
strcpy(buf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
if (desc)
strcat(buf, ",\n obj_description(pg_class.oid) as \"Description\"");
strcat(buf, "\nFROM pg_class, pg_user\n"
"WHERE usesysid = relowner AND relkind = 'l'\n"
"ORDER BY \"ID\"");
res = PSQLexec(pset, descbuf);
res = PSQLexec(pset, buf);
if (!res)
return false;
......
#ifndef LARGE_OBJ_H
#define LARGE_OBJ_H
#include <c.h>
#include "settings.h"
bool do_lo_export(PsqlSettings *pset, const char *loid_arg, const char *filename_arg);
bool do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_arg);
bool do_lo_unlink(PsqlSettings *pset, const char *loid_arg);
bool do_lo_list(PsqlSettings *pset);
bool do_lo_list(PsqlSettings *pset, bool desc);
#endif /* LARGE_OBJ_H */
......@@ -16,19 +16,20 @@
/* MainLoop()
/*
* Main processing loop for reading lines of input
* and sending them to the backend.
*
* This loop is re-entrant. May be called by \i command
* which reads input from a file.
*
* FIXME: rewrite this whole thing with flex
*/
int
MainLoop(PsqlSettings *pset, FILE *source)
{
PQExpBuffer query_buf; /* buffer for query being accumulated */
char *line; /* current line of input */
char *xcomment; /* start of extended comment */
int len; /* length of the line */
int successResult = EXIT_SUCCESS;
backslashResult slashCmdStatus;
......@@ -37,6 +38,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
bool success;
char in_quote; /* == 0 for no in_quote */
bool was_bslash; /* backslash */
bool xcomment; /* in extended comment */
int paren_level;
unsigned int query_start;
......@@ -49,7 +51,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
bool prev_cmd_interactive;
bool die_on_error;
const char *interpol_char;
/* Save old settings */
......@@ -68,7 +69,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
exit(EXIT_FAILURE);
}
xcomment = NULL;
xcomment = false;
in_quote = 0;
paren_level = 0;
slashCmdStatus = CMD_UNKNOWN; /* set default */
......@@ -87,7 +88,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
line = strdup(query_buf->data);
resetPQExpBuffer(query_buf);
/* reset parsing state since we are rescanning whole query */
xcomment = NULL;
xcomment = false;
in_quote = 0;
paren_level = 0;
}
......@@ -106,7 +107,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
prompt_status = PROMPT_SINGLEQUOTE;
else if (in_quote && in_quote == '"')
prompt_status = PROMPT_DOUBLEQUOTE;
else if (xcomment != NULL)
else if (xcomment)
prompt_status = PROMPT_COMMENT;
else if (query_buf->len > 0)
prompt_status = PROMPT_CONTINUE;
......@@ -120,10 +121,11 @@ MainLoop(PsqlSettings *pset, FILE *source)
}
/* Setting these will not have effect until next line */
/* Setting this will not have effect until next line. (Faster.
Also think about what happens if there is an error processing
_this_ command.)
*/
die_on_error = GetVariableBool(pset->vars, "die_on_error");
interpol_char = GetVariable(pset->vars, "sql_interpol");;
/*
* query_buf holds query already accumulated. line is the
......@@ -144,11 +146,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
continue;
}
/* not currently inside an extended comment? */
if (xcomment)
xcomment = line;
/* strip trailing backslashes, they don't have a clear meaning */
while (1)
{
......@@ -160,52 +157,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
break;
}
/* echo back if input is from file and flag is set */
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
fprintf(stderr, "%s\n", line);
/* interpolate variables into SQL */
len = strlen(line);
thislen = PQmblen(line);
for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])))
{
if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i])
{
size_t in_length,
out_length;
const char *value;
char *new;
bool closer; /* did we have a closing delimiter
* or just an end of line? */
in_length = strcspn(&line[i + thislen], interpol_char);
closer = line[i + thislen + in_length] == line[i];
line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value);
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
if (!new)
{
perror("malloc");
exit(EXIT_FAILURE);
}
new[0] = '\0';
strncat(new, line, i);
strcat(new, value);
if (closer)
strcat(new, line + i + 2 + in_length);
free(line);
line = new;
i += out_length;
}
}
/* nothing left on line? then ignore */
if (line[0] == '\0')
{
......@@ -213,6 +164,12 @@ MainLoop(PsqlSettings *pset, FILE *source)
continue;
}
/* echo back if input is from file and flag is set */
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
puts(line);
slashCmdStatus = CMD_UNKNOWN;
len = strlen(line);
......@@ -224,24 +181,15 @@ MainLoop(PsqlSettings *pset, FILE *source)
* The current character is at line[i], the prior character at line[i
* - prevlen], the next character at line[i + thislen].
*/
prevlen = 0;
thislen = (len > 0) ? PQmblen(line) : 0;
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
success = true;
for (i = 0; i < len; ADVANCE_1)
for (i = 0, prevlen = 0, thislen = (len > 0) ? PQmblen(line) : 0;
i < len;
ADVANCE_1)
{
if (!success && die_on_error)
break;
/* was the previous character a backslash? */
if (i > 0 && line[i - prevlen] == '\\')
was_bslash = true;
else
was_bslash = false;
was_bslash = (i > 0 && line[i - prevlen] == '\\');
/* in quote? */
if (in_quote)
......@@ -256,11 +204,11 @@ MainLoop(PsqlSettings *pset, FILE *source)
in_quote = line[i];
/* in extended comment? */
else if (xcomment != NULL)
else if (xcomment)
{
if (line[i] == '*' && line[i + thislen] == '/')
{
xcomment = NULL;
xcomment = false;
ADVANCE_1;
}
}
......@@ -268,7 +216,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
/* start of extended comment? */
else if (line[i] == '/' && line[i + thislen] == '*')
{
xcomment = &line[i];
xcomment = true;
ADVANCE_1;
}
......@@ -287,8 +235,45 @@ MainLoop(PsqlSettings *pset, FILE *source)
else if (line[i] == ')' && paren_level > 0)
paren_level--;
/* colon -> substitute variable */
/* we need to be on the watch for the '::' operator */
else if (line[i] == ':' && !was_bslash &&
strspn(line+i+thislen, VALID_VARIABLE_CHARS)>0 &&
(prevlen > 0 && line[i-prevlen]!=':')
)
{
size_t in_length,
out_length;
const char *value;
char *new;
char after; /* the character after the variable name
will be temporarily overwritten */
in_length = strspn(&line[i + thislen], VALID_VARIABLE_CHARS);
after = line[i + thislen + in_length];
line[i + thislen + in_length] = '\0';
value = interpolate_var(&line[i + thislen], pset);
out_length = strlen(value);
new = malloc(len + out_length - (1 + in_length) + 1);
if (!new)
{
perror("malloc");
exit(EXIT_FAILURE);
}
sprintf(new, "%.*s%s%c", i, line, value, after);
if (after)
strcat(new, line + i + 1 + in_length + 1);
free(line);
line = new;
continue; /* reparse the just substituted */
}
/* semicolon? then send query */
else if (line[i] == ';' && !was_bslash && paren_level == 0)
else if (line[i] == ';' && !was_bslash)
{
line[i] = '\0';
/* is there anything else on the line? */
......@@ -312,6 +297,15 @@ MainLoop(PsqlSettings *pset, FILE *source)
query_start = i + thislen;
}
/* if you have a burning need to send a semicolon or colon to
the backend ... */
else if (was_bslash && (line[i] == ';' || line[i] == ':'))
{
/* remove the backslash */
memmove(line + i - prevlen, line + i, len - i + 1);
len--;
}
/* backslash command */
else if (was_bslash)
{
......@@ -355,7 +349,13 @@ MainLoop(PsqlSettings *pset, FILE *source)
else
break;
}
}
/* stop the script after error */
if (!success && die_on_error)
break;
} /* for (line) */
if (!success && die_on_error && !pset->cur_cmd_interactive)
......
......@@ -371,11 +371,10 @@ print_aligned_vertical(const char *title, const char * const * headers,
{
if (!opt_barebones)
{
char *div_copy = strdup(divider);
char *record_str = malloc(32);
size_t record_str_len;
if (!div_copy || !record_str)
if (!record_str)
{
perror("malloc");
exit(EXIT_FAILURE);
......@@ -386,29 +385,32 @@ print_aligned_vertical(const char *title, const char * const * headers,
else
sprintf(record_str, "[ RECORD %d ]", record++);
record_str_len = strlen(record_str);
if (record_str_len + opt_border > strlen(div_copy))
{
void *new;
new = realloc(div_copy, record_str_len + opt_border);
if (!new)
if (record_str_len + opt_border > strlen(divider))
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
else
{
perror("realloc");
char *div_copy = strdup(divider);
if (!div_copy) {
perror("malloc");
exit(EXIT_FAILURE);
}
div_copy = new;
}
strncpy(div_copy + opt_border, record_str, record_str_len);
fprintf(fout, "%s\n", div_copy);
free(record_str);
free(div_copy);
}
else if (i != 0 && opt_border < 2)
free(record_str);
}
else if (i != 0 || opt_border == 2)
fprintf(fout, "%s\n", divider);
}
if (opt_border == 2)
fputs("| ", fout);
fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
if (opt_border > 0)
fputs(" | ", fout);
else
......
......@@ -85,7 +85,7 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
p++)
{
MemSet(buf, 0, MAX_PROMPT_SIZE + 1);
memset(buf, 0, MAX_PROMPT_SIZE + 1);
if (esc)
{
switch (*p)
......
#ifndef SETTINGS_H
#define SETTINGS_H
#include <config.h>
#include <c.h>
#include <stdio.h>
#include <stdlib.h>
#include <libpq-fe.h>
#include <c.h>
#include <postgres_ext.h>
#include "variables.h"
#include "print.h"
......@@ -41,6 +43,8 @@ typedef struct _psqlSettings
bool has_client_encoding; /* was PGCLIENTENCODING set on
* startup? */
Oid lastOid; /* saves oid from insert command
because people want it so badly */
} PsqlSettings;
......
......@@ -88,7 +88,7 @@ main(int argc, char **argv)
char *password = NULL;
bool need_pass;
MemSet(&settings, 0, sizeof settings);
memset(&settings, 0, sizeof settings);
settings.cur_cmd_source = stdin;
settings.cur_cmd_interactive = false;
......@@ -161,7 +161,7 @@ main(int argc, char **argv)
if (options.action == ACT_LIST_DB)
{
int success = listAllDbs(&settings);
int success = listAllDbs(&settings, false);
PQfinish(settings.db);
exit(!success);
......@@ -187,7 +187,7 @@ main(int argc, char **argv)
process_psqlrc(&settings);
initializeInput(options.no_readline ? 0 : 1);
initializeInput(options.no_readline ? 0 : 1, &settings);
/* Now find something to do */
......@@ -270,7 +270,7 @@ parse_options(int argc, char *argv[], PsqlSettings *pset, struct adhoc_opts * op
extern int optind;
int c;
MemSet(options, 0, sizeof *options);
memset(options, 0, sizeof *options);
#ifdef HAVE_GETOPT_LONG
while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1)
......
This diff is collapsed.
#ifndef TAB_COMPLETE_H
#define TAB_COMPLETE_H
#include <libpq-fe.h>
void initialize_readline(PGconn ** conn);
#endif
......@@ -11,7 +11,8 @@
#define VARIABLES_H
#include <c.h>
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"
struct _variable
{
......
# Makefile for Microsoft Visual C++ 5.0 (or compat)
!IF "$(OS)" == "Windows_NT"
NULL=
!ELSE
NULL=nul
!ENDIF
CPP=cl.exe
OUTDIR=.\Release
INTDIR=.\Release
# Begin Custom Macros
OutDir=.\Release
# End Custom Macros
ALL : "$(OUTDIR)\psql.exe"
CLEAN :
-@erase "$(INTDIR)\psql.obj"
-@erase "$(INTDIR)\stringutils.obj"
-@erase "$(INTDIR)\getopt.obj"
-@erase "$(INTDIR)\vc50.idb"
-@erase "$(OUTDIR)\psql.exe"
"$(OUTDIR)" :
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D\
"_MBCS" /Fp"$(INTDIR)\psql.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c \
/I ..\..\include /I ..\..\interfaces\libpq /D "HAVE_STRDUP" /D "BLCKSZ=8192"
!IFDEF MULTIBYTE
!IFNDEF MBFLAGS
MBFLAGS="-DMULTIBYTE=$(MULTIBYTE)"
!ENDIF
CPP_PROJ=$(MBFLAGS) $(CPP_PROJ)
!ENDIF
CPP_OBJS=.\Release/
CPP_SBRS=.
LINK32=link.exe
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:no\
/pdb:"$(OUTDIR)\psql.pdb" /machine:I386 /out:"$(OUTDIR)\psql.exe"
LINK32_OBJS= \
"$(INTDIR)\psql.obj" \
"$(INTDIR)\stringutils.obj" \
"$(INTDIR)\getopt.obj" \
"..\..\interfaces\libpq\Release\libpqdll.lib"
"$(OUTDIR)\psql.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
$(LINK32) @<<
$(LINK32_FLAGS) $(LINK32_OBJS)
<<
"$(OUTDIR)\getopt.obj" : "$(OUTDIR)" ..\..\utils\getopt.c
$(CPP) @<<
$(CPP_PROJ) ..\..\utils\getopt.c
<<
.c{$(CPP_OBJS)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
.cpp{$(CPP_OBJS)}.obj::
$(CPP) @<<
$(CPP_PROJ) $<
<<
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment