2000-01-19 00:30:24 +01:00
|
|
|
/*
|
|
|
|
* psql - the PostgreSQL interactive terminal
|
|
|
|
*
|
2006-03-05 16:59:11 +01:00
|
|
|
* Copyright (c) 2000-2006, PostgreSQL Global Development Group
|
2000-01-19 00:30:24 +01:00
|
|
|
*
|
2006-03-21 14:38:12 +01:00
|
|
|
* $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.53 2006/03/21 13:38:12 momjian Exp $
|
2000-01-19 00:30:24 +01:00
|
|
|
*/
|
2001-02-10 03:31:31 +01:00
|
|
|
#include "postgres_fe.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
|
2000-02-16 14:15:26 +01:00
|
|
|
#include "pqexpbuffer.h"
|
2006-02-11 22:55:35 +01:00
|
|
|
#include "input.h"
|
* 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
|
|
|
#include "settings.h"
|
|
|
|
#include "tab-complete.h"
|
2000-02-08 00:10:11 +01:00
|
|
|
#include "common.h"
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2005-01-06 19:29:11 +01:00
|
|
|
#ifndef WIN32
|
|
|
|
#define PSQLHISTORY ".psql_history"
|
|
|
|
#else
|
|
|
|
#define PSQLHISTORY "psql_history"
|
|
|
|
#endif
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/* Runtime options for turning off readline and history */
|
|
|
|
/* (of course there is no runtime command for doing that :) */
|
|
|
|
#ifdef USE_READLINE
|
|
|
|
static bool useReadline;
|
|
|
|
static bool useHistory;
|
2005-10-15 04:49:52 +02:00
|
|
|
char *psql_history;
|
2005-06-10 17:34:26 +02:00
|
|
|
|
2006-02-12 06:24:38 +01:00
|
|
|
/*
|
|
|
|
* Preserve newlines in saved queries by mapping '\n' to NL_IN_HISTORY
|
|
|
|
*
|
|
|
|
* It is assumed NL_IN_HISTORY will never be entered by the user
|
|
|
|
* nor appear inside a multi-byte string. 0x00 is not properly
|
|
|
|
* handled by the readline routines so it can not be used
|
|
|
|
* for this purpose.
|
|
|
|
*/
|
|
|
|
#define NL_IN_HISTORY 0x01
|
2003-03-20 07:00:12 +01:00
|
|
|
|
|
|
|
enum histcontrol
|
|
|
|
{
|
2003-08-04 02:43:34 +02:00
|
|
|
hctl_none = 0,
|
|
|
|
hctl_ignorespace = 1,
|
|
|
|
hctl_ignoredups = 2,
|
|
|
|
hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups
|
2003-03-20 07:00:12 +01:00
|
|
|
};
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
|
|
|
|
2001-09-12 01:08:07 +02:00
|
|
|
#ifdef HAVE_ATEXIT
|
2001-10-25 07:50:21 +02:00
|
|
|
static void finishInput(void);
|
2001-09-12 01:08:07 +02:00
|
|
|
#else
|
|
|
|
/* designed for use with on_exit() */
|
2001-10-25 07:50:21 +02:00
|
|
|
static void finishInput(int, void *);
|
2001-09-12 01:08:07 +02:00
|
|
|
#endif
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2003-03-20 07:00:12 +01:00
|
|
|
#ifdef USE_READLINE
|
|
|
|
static enum histcontrol
|
|
|
|
GetHistControlConfig(void)
|
|
|
|
{
|
|
|
|
enum histcontrol HC;
|
|
|
|
const char *var;
|
|
|
|
|
|
|
|
var = GetVariable(pset.vars, "HISTCONTROL");
|
|
|
|
|
2003-08-04 02:43:34 +02:00
|
|
|
if (!var)
|
|
|
|
HC = hctl_none;
|
|
|
|
else if (strcmp(var, "ignorespace") == 0)
|
|
|
|
HC = hctl_ignorespace;
|
|
|
|
else if (strcmp(var, "ignoredups") == 0)
|
|
|
|
HC = hctl_ignoredups;
|
|
|
|
else if (strcmp(var, "ignoreboth") == 0)
|
|
|
|
HC = hctl_ignoreboth;
|
|
|
|
else
|
|
|
|
HC = hctl_none;
|
2003-03-20 07:00:12 +01:00
|
|
|
|
|
|
|
return HC;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
static char *
|
|
|
|
gets_basic(const char prompt[])
|
|
|
|
{
|
|
|
|
fputs(prompt, stdout);
|
|
|
|
fflush(stdout);
|
|
|
|
return gets_fromFile(stdin);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
/*
|
|
|
|
* gets_interactive()
|
|
|
|
*
|
|
|
|
* Gets a line of interactive input, using readline of desired.
|
2004-01-24 20:38:49 +01:00
|
|
|
* The result is malloc'ed.
|
1999-11-04 22:56:02 +01:00
|
|
|
*/
|
|
|
|
char *
|
2003-03-20 07:00:12 +01:00
|
|
|
gets_interactive(const char *prompt)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2003-03-20 07:00:12 +01:00
|
|
|
#ifdef USE_READLINE
|
1999-11-05 00:14:30 +01:00
|
|
|
char *s;
|
2000-04-12 19:17:23 +02:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (useReadline)
|
2003-07-25 21:27:06 +02:00
|
|
|
/* On some platforms, readline is declared as readline(char *) */
|
|
|
|
s = readline((char *) prompt);
|
1999-11-05 00:14:30 +01:00
|
|
|
else
|
2003-03-20 07:00:12 +01:00
|
|
|
s = gets_basic(prompt);
|
|
|
|
|
2006-02-11 22:55:35 +01:00
|
|
|
return s;
|
|
|
|
#else
|
|
|
|
return gets_basic(prompt);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Put the line in the history buffer and also add the trailing \n */
|
2006-02-13 18:09:25 +01:00
|
|
|
void
|
2006-03-06 05:45:21 +01:00
|
|
|
pg_append_history(char *s, PQExpBuffer history_buf)
|
2006-02-11 22:55:35 +01:00
|
|
|
{
|
|
|
|
#ifdef USE_READLINE
|
|
|
|
|
|
|
|
int slen;
|
|
|
|
if (useReadline && useHistory && s && s[0])
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2006-02-11 22:55:35 +01:00
|
|
|
slen = strlen(s);
|
|
|
|
if (s[slen-1] == '\n')
|
|
|
|
appendPQExpBufferStr(history_buf, s);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
appendPQExpBufferStr(history_buf, s);
|
|
|
|
appendPQExpBufferChar(history_buf, '\n');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
2006-02-11 22:55:35 +01:00
|
|
|
|
2006-03-06 05:45:21 +01:00
|
|
|
/*
|
|
|
|
* Feed the string to readline
|
|
|
|
*/
|
2006-02-13 18:09:25 +01:00
|
|
|
void
|
2006-03-06 05:45:21 +01:00
|
|
|
pg_write_history(char *s)
|
2006-02-11 22:55:35 +01:00
|
|
|
{
|
2006-03-06 05:45:21 +01:00
|
|
|
#ifdef USE_READLINE
|
2006-02-11 22:55:35 +01:00
|
|
|
static char *prev_hist;
|
|
|
|
int slen, i;
|
|
|
|
|
|
|
|
if (useReadline && useHistory )
|
|
|
|
{
|
|
|
|
enum histcontrol HC;
|
|
|
|
|
2006-03-21 14:38:12 +01:00
|
|
|
/* Flushing of empty buffer should do nothing */
|
|
|
|
if (*s == 0)
|
|
|
|
return;
|
|
|
|
|
2006-02-11 22:55:35 +01:00
|
|
|
prev_hist = NULL;
|
|
|
|
|
2003-03-20 07:00:12 +01:00
|
|
|
HC = GetHistControlConfig();
|
|
|
|
|
|
|
|
if (((HC & hctl_ignorespace) && s[0] == ' ') ||
|
2005-10-15 04:49:52 +02:00
|
|
|
((HC & hctl_ignoredups) && prev_hist && strcmp(s, prev_hist) == 0))
|
2003-08-04 02:43:34 +02:00
|
|
|
{
|
|
|
|
/* Ignore this line as far as history is concerned */
|
2003-03-20 07:00:12 +01:00
|
|
|
}
|
|
|
|
else
|
2000-04-12 19:17:23 +02:00
|
|
|
{
|
|
|
|
free(prev_hist);
|
2006-02-11 22:55:35 +01:00
|
|
|
slen = strlen(s);
|
|
|
|
/* Trim the trailing \n's */
|
|
|
|
for (i = slen-1; i >= 0 && s[i] == '\n'; i--)
|
|
|
|
;
|
|
|
|
s[i + 1] = '\0';
|
2004-01-25 04:07:22 +01:00
|
|
|
prev_hist = pg_strdup(s);
|
2000-04-12 19:17:23 +02:00
|
|
|
add_history(s);
|
|
|
|
}
|
|
|
|
}
|
2003-03-20 07:00:12 +01:00
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
2006-02-13 18:09:25 +01:00
|
|
|
void
|
2006-03-06 05:45:21 +01:00
|
|
|
pg_clear_history(PQExpBuffer history_buf)
|
2006-02-11 22:55:35 +01:00
|
|
|
{
|
|
|
|
#ifdef USE_READLINE
|
|
|
|
if (useReadline && useHistory)
|
|
|
|
resetPQExpBuffer(history_buf);
|
|
|
|
#endif
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* gets_fromFile
|
|
|
|
*
|
|
|
|
* Gets a line of noninteractive input from a file (which could be stdin).
|
|
|
|
*/
|
|
|
|
char *
|
|
|
|
gets_fromFile(FILE *source)
|
|
|
|
{
|
1999-11-05 00:14:30 +01:00
|
|
|
PQExpBufferData buffer;
|
|
|
|
char line[1024];
|
|
|
|
|
|
|
|
initPQExpBuffer(&buffer);
|
|
|
|
|
2000-11-27 03:20:36 +01:00
|
|
|
while (fgets(line, sizeof(line), source) != NULL)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
|
|
|
appendPQExpBufferStr(&buffer, line);
|
|
|
|
if (buffer.data[buffer.len - 1] == '\n')
|
|
|
|
{
|
|
|
|
buffer.data[buffer.len - 1] = '\0';
|
|
|
|
return buffer.data;
|
|
|
|
}
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
if (buffer.len > 0)
|
|
|
|
return buffer.data; /* EOF after reading some bufferload(s) */
|
1999-11-04 22:56:02 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
/* EOF, so return null */
|
|
|
|
termPQExpBuffer(&buffer);
|
|
|
|
return NULL;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-02-13 15:57:15 +01:00
|
|
|
#ifdef USE_READLINE
|
2006-02-13 18:09:25 +01:00
|
|
|
static void
|
|
|
|
encode_history(void)
|
2006-02-11 22:55:35 +01:00
|
|
|
{
|
|
|
|
HIST_ENTRY *cur_hist;
|
|
|
|
char *cur_ptr;
|
|
|
|
|
|
|
|
for (history_set_pos(0), cur_hist = current_history();
|
|
|
|
cur_hist; cur_hist = next_history())
|
|
|
|
for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
|
|
|
|
if (*cur_ptr == '\n')
|
2006-02-12 06:24:38 +01:00
|
|
|
*cur_ptr = NL_IN_HISTORY;
|
2006-02-11 22:55:35 +01:00
|
|
|
}
|
|
|
|
|
2006-02-13 18:09:25 +01:00
|
|
|
static void
|
|
|
|
decode_history(void)
|
2006-02-11 22:55:35 +01:00
|
|
|
{
|
|
|
|
HIST_ENTRY *cur_hist;
|
|
|
|
char *cur_ptr;
|
|
|
|
|
|
|
|
for (history_set_pos(0), cur_hist = current_history();
|
|
|
|
cur_hist; cur_hist = next_history())
|
|
|
|
for (cur_ptr = cur_hist->line; *cur_ptr; cur_ptr++)
|
2006-02-12 06:24:38 +01:00
|
|
|
if (*cur_ptr == NL_IN_HISTORY)
|
2006-02-11 22:55:35 +01:00
|
|
|
*cur_ptr = '\n';
|
|
|
|
}
|
2006-02-13 15:57:15 +01:00
|
|
|
#endif /* USE_READLINE */
|
2006-02-11 22:55:35 +01:00
|
|
|
|
1999-11-04 22:56:02 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Put any startup stuff related to input in here. It's good to maintain
|
|
|
|
* abstraction this way.
|
|
|
|
*
|
|
|
|
* The only "flag" right now is 1 for use readline & history.
|
|
|
|
*/
|
|
|
|
void
|
2000-01-14 23:18:03 +01:00
|
|
|
initializeInput(int flags)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
|
|
|
#ifdef USE_READLINE
|
2003-03-20 07:00:12 +01:00
|
|
|
if (flags & 1)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2004-08-29 07:07:03 +02:00
|
|
|
char home[MAXPGPATH];
|
2003-03-20 07:00:12 +01:00
|
|
|
|
1999-11-05 00:14:30 +01:00
|
|
|
useReadline = true;
|
2000-04-12 19:17:23 +02:00
|
|
|
initialize_readline();
|
1999-11-05 00:14:30 +01:00
|
|
|
|
|
|
|
useHistory = true;
|
2003-08-26 20:35:31 +02:00
|
|
|
if (GetVariable(pset.vars, "HISTSIZE") == NULL)
|
|
|
|
SetVariable(pset.vars, "HISTSIZE", "500");
|
1999-11-05 00:14:30 +01:00
|
|
|
using_history();
|
2005-06-10 17:34:26 +02:00
|
|
|
|
|
|
|
if (GetVariable(pset.vars, "HISTFILE") == NULL)
|
|
|
|
{
|
|
|
|
if (get_home_path(home))
|
|
|
|
{
|
|
|
|
psql_history = pg_malloc(strlen(home) + 1 +
|
|
|
|
strlen(PSQLHISTORY) + 1);
|
|
|
|
snprintf(psql_history, MAXPGPATH, "%s/%s", home, PSQLHISTORY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2005-06-10 17:34:26 +02:00
|
|
|
psql_history = pg_strdup(GetVariable(pset.vars, "HISTFILE"));
|
|
|
|
expand_tilde(&psql_history);
|
|
|
|
}
|
2004-01-24 20:38:49 +01:00
|
|
|
|
2005-06-10 17:34:26 +02:00
|
|
|
if (psql_history)
|
2004-01-24 20:38:49 +01:00
|
|
|
read_history(psql_history);
|
2006-02-11 22:55:35 +01:00
|
|
|
|
|
|
|
decode_history();
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
#endif
|
2000-02-20 15:28:28 +01:00
|
|
|
|
2001-02-27 09:13:31 +01:00
|
|
|
#ifdef HAVE_ATEXIT
|
2000-04-12 19:17:23 +02:00
|
|
|
atexit(finishInput);
|
2001-02-27 09:13:31 +01:00
|
|
|
#else
|
2001-09-12 01:08:07 +02:00
|
|
|
on_exit(finishInput, NULL);
|
2001-02-27 09:13:31 +01:00
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2006-03-21 14:38:12 +01:00
|
|
|
/* This function is designed for saving the readline history when user
|
|
|
|
* run \s command or when psql finishes.
|
|
|
|
* We have an argument named encodeFlag to handle those cases differently
|
|
|
|
* In that case of call via \s we don't really need to encode \n as \x01,
|
|
|
|
* but when we save history for Readline we must do that conversion
|
|
|
|
*/
|
1999-11-04 22:56:02 +01:00
|
|
|
bool
|
2006-03-21 14:38:12 +01:00
|
|
|
saveHistory(char *fname, bool encodeFlag)
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2002-04-11 00:47:09 +02:00
|
|
|
#ifdef USE_READLINE
|
2000-02-08 00:10:11 +01:00
|
|
|
if (useHistory && fname)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2006-03-21 14:38:12 +01:00
|
|
|
if (encodeFlag)
|
|
|
|
encode_history();
|
2003-03-20 07:00:12 +01:00
|
|
|
if (write_history(fname) == 0)
|
2003-08-04 02:43:34 +02:00
|
|
|
return true;
|
2003-03-20 07:00:12 +01:00
|
|
|
|
2003-07-23 10:47:41 +02:00
|
|
|
psql_error("could not save history to file \"%s\": %s\n", fname, strerror(errno));
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
2004-10-06 20:39:16 +02:00
|
|
|
#else
|
2005-10-15 04:49:52 +02:00
|
|
|
psql_error("history is not supported by this installation\n");
|
1999-11-04 22:56:02 +01:00
|
|
|
#endif
|
2003-03-20 07:00:12 +01:00
|
|
|
|
|
|
|
return false;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-09-12 01:08:07 +02:00
|
|
|
static void
|
|
|
|
#ifdef HAVE_ATEXIT
|
1999-11-04 22:56:02 +01:00
|
|
|
finishInput(void)
|
2001-09-12 01:08:07 +02:00
|
|
|
#else
|
|
|
|
finishInput(int exitstatus, void *arg)
|
|
|
|
#endif
|
1999-11-04 22:56:02 +01:00
|
|
|
{
|
2002-04-11 00:47:09 +02:00
|
|
|
#ifdef USE_READLINE
|
2005-06-10 17:34:26 +02:00
|
|
|
if (useHistory && psql_history)
|
1999-11-05 00:14:30 +01:00
|
|
|
{
|
2005-06-10 17:34:26 +02:00
|
|
|
int hist_size;
|
1999-11-05 00:14:30 +01:00
|
|
|
|
2005-06-10 17:34:26 +02:00
|
|
|
hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true);
|
|
|
|
if (hist_size >= 0)
|
|
|
|
stifle_history(hist_size);
|
|
|
|
|
2006-03-21 14:38:12 +01:00
|
|
|
saveHistory(psql_history, true);
|
2005-06-10 17:34:26 +02:00
|
|
|
free(psql_history);
|
|
|
|
psql_history = NULL;
|
1999-11-04 22:56:02 +01:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|