mirror of https://github.com/citusdata/citus.git
Remove csql, \stage is no longer needed
parent
640bb8863b
commit
4ecd6b58fb
14
Makefile
14
Makefile
|
@ -10,7 +10,7 @@ endif
|
|||
|
||||
include Makefile.global
|
||||
|
||||
all: extension csql
|
||||
all: extension
|
||||
|
||||
# build extension
|
||||
extension:
|
||||
|
@ -30,18 +30,6 @@ clean-extension:
|
|||
install: install-extension install-headers
|
||||
clean: clean-extension
|
||||
|
||||
# build csql binary
|
||||
csql:
|
||||
$(MAKE) -C src/bin/csql/ all
|
||||
install-csql: csql
|
||||
$(MAKE) -C src/bin/csql/ install
|
||||
clean-csql:
|
||||
$(MAKE) -C src/bin/csql/ clean
|
||||
.PHONY: csql install-csql clean-csql
|
||||
# Add to generic targets
|
||||
install: install-csql
|
||||
clean: clean-csql
|
||||
|
||||
# apply or check style
|
||||
reindent:
|
||||
cd ${citus_abs_top_srcdir} && citus_indent --quiet
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
/psqlscan.c
|
||||
/csql
|
|
@ -1,45 +0,0 @@
|
|||
#-------------------------------------------------------------------------
|
||||
#
|
||||
# Makefile for src/bin/csql
|
||||
#
|
||||
# Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
|
||||
# Portions Copyright (c) 1994, Regents of the University of California
|
||||
#
|
||||
# src/bin/csql/Makefile
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
citus_subdir = src/bin/csql
|
||||
citus_top_builddir = ../../..
|
||||
|
||||
PROGRAM = csql
|
||||
|
||||
PGFILEDESC = "csql - the Citus interactive terminal"
|
||||
PGAPPICON=win32
|
||||
|
||||
OBJS =command.o common.o help.o input.o stringutils.o mainloop.o copy.o \
|
||||
copy_options.o stage.o \
|
||||
startup.o prompt.o variables.o large_obj.o print.o describe.o \
|
||||
tab-complete.o mbprint.o dumputils.o keywords.o kwlookup.o \
|
||||
sql_help.o \
|
||||
$(WIN32RES)
|
||||
|
||||
PG_LIBS = $(libpq)
|
||||
|
||||
include $(citus_top_builddir)/Makefile.global
|
||||
|
||||
# ensure client includes occur before server
|
||||
client_includes := $(shell $(PG_CONFIG) --includedir)/internal
|
||||
|
||||
override CPPFLAGS := -I$(client_includes) -I$(libpq_srcdir) -I$(citus_abs_top_srcdir)/src/bin/csql $(CPPFLAGS)
|
||||
|
||||
# psqlscan is compiled as part of mainloop
|
||||
mainloop.o: psqlscan.c
|
||||
psqlscan.c: FLEXFLAGS = -Cfe -p -p
|
||||
|
||||
psqlscan.c: psqlscan.l
|
||||
$(FLEX) $(FLEXFLAGS) -o'$@' $<
|
||||
|
||||
clean: csql-clean
|
||||
csql-clean:
|
||||
rm -f csql$(X) $(OBJS) psqlscan.c lex.backup
|
File diff suppressed because it is too large
Load Diff
|
@ -1,43 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/command.h
|
||||
*/
|
||||
#ifndef COMMAND_H
|
||||
#define COMMAND_H
|
||||
|
||||
#include "print.h"
|
||||
#include "psqlscan.h"
|
||||
|
||||
|
||||
typedef enum _backslashResult
|
||||
{
|
||||
PSQL_CMD_UNKNOWN = 0, /* not done parsing yet (internal only) */
|
||||
PSQL_CMD_SEND, /* query complete; send off */
|
||||
PSQL_CMD_SKIP_LINE, /* keep building query */
|
||||
PSQL_CMD_TERMINATE, /* quit program */
|
||||
PSQL_CMD_NEWEDIT, /* query buffer was changed (e.g., via \e) */
|
||||
PSQL_CMD_ERROR /* the execution of the backslash command
|
||||
* resulted in an error */
|
||||
} backslashResult;
|
||||
|
||||
|
||||
extern backslashResult HandleSlashCmds(PsqlScanState scan_state,
|
||||
PQExpBuffer query_buf);
|
||||
|
||||
extern int process_file(char *filename, bool single_txn, bool use_relative_path);
|
||||
|
||||
extern bool do_pset(const char *param,
|
||||
const char *value,
|
||||
printQueryOpt *popt,
|
||||
bool quiet);
|
||||
|
||||
extern void connection_warnings(bool in_startup);
|
||||
|
||||
extern void SyncVariables(void);
|
||||
|
||||
extern void UnsyncVariables(void);
|
||||
|
||||
#endif /* COMMAND_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,52 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/common.h
|
||||
*/
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
#include "postgres_fe.h"
|
||||
#include <setjmp.h>
|
||||
#include "libpq-fe.h"
|
||||
|
||||
#include "print.h"
|
||||
|
||||
#define atooid(x) ((Oid) strtoul((x), NULL, 10))
|
||||
|
||||
extern bool openQueryOutputFile(const char *fname, FILE **fout, bool *is_pipe);
|
||||
extern bool setQFout(const char *fname);
|
||||
|
||||
extern void psql_error(const char *fmt,...) pg_attribute_printf(1, 2);
|
||||
|
||||
extern void NoticeProcessor(void *arg, const char *message);
|
||||
|
||||
extern volatile bool sigint_interrupt_enabled;
|
||||
|
||||
extern sigjmp_buf sigint_interrupt_jmp;
|
||||
|
||||
extern volatile bool cancel_pressed;
|
||||
|
||||
/* Note: cancel_pressed is defined in print.c, see that file for reasons */
|
||||
|
||||
extern void setup_cancel_handler(void);
|
||||
|
||||
extern void SetCancelConn(void);
|
||||
extern void ResetCancelConn(void);
|
||||
|
||||
extern PGresult *PSQLexec(const char *query);
|
||||
extern int PSQLexecWatch(const char *query, const printQueryOpt *opt);
|
||||
|
||||
extern bool SendQuery(const char *query);
|
||||
|
||||
extern bool is_superuser(void);
|
||||
extern bool standard_strings(void);
|
||||
extern const char *session_username(void);
|
||||
|
||||
extern void expand_tilde(char **filename);
|
||||
|
||||
extern bool recognized_connection_string(const char *connstr);
|
||||
|
||||
#endif /* COMMON_H */
|
|
@ -1,595 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/copy.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
#include "copy.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h> /* for isatty */
|
||||
#else
|
||||
#include <io.h> /* I think */
|
||||
#endif
|
||||
|
||||
#include "libpq-fe.h"
|
||||
#include "pqexpbuffer.h"
|
||||
#include "dumputils.h"
|
||||
|
||||
#include "settings.h"
|
||||
#include "common.h"
|
||||
#include "prompt.h"
|
||||
|
||||
|
||||
/*
|
||||
* Execute a \copy command (frontend copy). We have to open a file (or execute
|
||||
* a command), then submit a COPY query to the backend and either feed it data
|
||||
* from the file or route its response into the file.
|
||||
*/
|
||||
bool
|
||||
do_copy(const char *args)
|
||||
{
|
||||
copy_options *options = NULL;
|
||||
PQExpBufferData query = { NULL, 0, 0 };
|
||||
FILE *copystream = NULL;
|
||||
bool success = false;
|
||||
bool fileClosed = false;
|
||||
|
||||
/* parse options */
|
||||
options = parse_slash_copy(args);
|
||||
|
||||
if (!options)
|
||||
return false;
|
||||
|
||||
/* open file stream to copy data into or out of */
|
||||
copystream = OpenCopyStream(options);
|
||||
if (copystream == NULL)
|
||||
{
|
||||
free_copy_options(options);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/* build the command we will send to the backend */
|
||||
initPQExpBuffer(&query);
|
||||
printfPQExpBuffer(&query, "COPY ");
|
||||
appendPQExpBufferStr(&query, options->before_tofrom);
|
||||
if (options->from)
|
||||
appendPQExpBufferStr(&query, " FROM STDIN ");
|
||||
else
|
||||
appendPQExpBufferStr(&query, " TO STDOUT ");
|
||||
if (options->after_tofrom)
|
||||
appendPQExpBufferStr(&query, options->after_tofrom);
|
||||
|
||||
/* run it like a user command, but with copystream as data source/sink */
|
||||
pset.copyStream = copystream;
|
||||
success = SendQuery(query.data);
|
||||
pset.copyStream = NULL;
|
||||
termPQExpBuffer(&query);
|
||||
|
||||
/* close file stream */
|
||||
fileClosed = CloseCopyStream(options, copystream);
|
||||
if (!fileClosed)
|
||||
{
|
||||
success = false;
|
||||
}
|
||||
|
||||
free_copy_options(options);
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* HandleCopyData executes client-side copy data protocols by dispatching the
|
||||
* call to the appropriate copy protocol function. On successful execution of
|
||||
* the protocol, the function returns true. Otherwise, the function returns
|
||||
* false.
|
||||
*
|
||||
* Please note that we refactored this function from a previous version (v9.1)
|
||||
* of PostgreSQL so that copy.c and stage.c could share the same code path. Now
|
||||
* that do_copy uses SendQuery(), we should move or re-refactor this function.
|
||||
*/
|
||||
bool
|
||||
HandleCopyData(PGconn *connection, ExecStatusType copyStatus, bool copyIsBinary,
|
||||
FILE *copyStream, uint64 copySizeLimit)
|
||||
{
|
||||
ExecStatusType drainStatus = 0;
|
||||
PGresult *drainResult = NULL;
|
||||
bool copyOK = true;
|
||||
|
||||
if (copyStatus == PGRES_COPY_OUT)
|
||||
{
|
||||
SetCancelConn();
|
||||
copyOK = handleCopyOut(connection, copyStream, &drainResult);
|
||||
ResetCancelConn();
|
||||
}
|
||||
else if (copyStatus == PGRES_COPY_IN)
|
||||
{
|
||||
SetCancelConn();
|
||||
copyOK = handleCopyIn(connection, copyStream, copyIsBinary,
|
||||
&drainResult, copySizeLimit);
|
||||
ResetCancelConn();
|
||||
}
|
||||
else if (copyStatus == PGRES_BAD_RESPONSE ||
|
||||
copyStatus == PGRES_NONFATAL_ERROR ||
|
||||
copyStatus == PGRES_FATAL_ERROR)
|
||||
{
|
||||
psql_error("\\copy: %s", PQerrorMessage(connection));
|
||||
copyOK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
psql_error("\\copy: unexpected response (%d)\n", copyStatus);
|
||||
copyOK = false;
|
||||
}
|
||||
|
||||
PQclear(drainResult);
|
||||
|
||||
/*
|
||||
* Make sure we drain all results from libpq. Otherwise, the connection may
|
||||
* still be in ASYNC_BUSY state, leading to false readings in get_prompt().
|
||||
*/
|
||||
drainResult = PQgetResult(connection);
|
||||
while (drainResult != NULL)
|
||||
{
|
||||
copyOK = false;
|
||||
|
||||
drainStatus = PQresultStatus(drainResult);
|
||||
psql_error("\\copy: unexpected response (%d)\n", drainStatus);
|
||||
|
||||
/* if we are still in COPY IN state, try to get out of it */
|
||||
if (drainStatus == PGRES_COPY_IN)
|
||||
{
|
||||
PQputCopyEnd(connection, _("trying to exit copy mode"));
|
||||
}
|
||||
|
||||
PQclear(drainResult);
|
||||
drainResult = PQgetResult(connection);
|
||||
}
|
||||
|
||||
return copyOK;
|
||||
}
|
||||
|
||||
|
||||
/* Opens input or output stream to be used during copy command. */
|
||||
FILE *
|
||||
OpenCopyStream(const copy_options *options)
|
||||
{
|
||||
FILE *copyStream = NULL;
|
||||
|
||||
/* prepare to read or write the target file */
|
||||
if (options->file && !options->program)
|
||||
canonicalize_path(options->file);
|
||||
|
||||
if (options->from)
|
||||
{
|
||||
if (options->file)
|
||||
{
|
||||
if (options->program)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
errno = 0;
|
||||
copyStream = popen(options->file, PG_BINARY_R);
|
||||
}
|
||||
else
|
||||
copyStream = fopen(options->file, PG_BINARY_R);
|
||||
}
|
||||
else if (!options->psql_inout)
|
||||
copyStream = pset.cur_cmd_source;
|
||||
else
|
||||
copyStream = stdin;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (options->file)
|
||||
{
|
||||
if (options->program)
|
||||
{
|
||||
fflush(stdout);
|
||||
fflush(stderr);
|
||||
errno = 0;
|
||||
#ifndef WIN32
|
||||
pqsignal(SIGPIPE, SIG_IGN);
|
||||
#endif
|
||||
copyStream = popen(options->file, PG_BINARY_W);
|
||||
}
|
||||
else
|
||||
copyStream = fopen(options->file, PG_BINARY_W);
|
||||
}
|
||||
else if (!options->psql_inout)
|
||||
copyStream = pset.queryFout;
|
||||
else
|
||||
copyStream = stdout;
|
||||
}
|
||||
|
||||
if (!copyStream)
|
||||
{
|
||||
if (options->program)
|
||||
psql_error("could not execute command \"%s\": %s\n",
|
||||
options->file, strerror(errno));
|
||||
else
|
||||
psql_error("%s: %s\n",
|
||||
options->file, strerror(errno));
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!options->program)
|
||||
{
|
||||
struct stat st;
|
||||
int result;
|
||||
|
||||
/* make sure the specified file is not a directory */
|
||||
if ((result = fstat(fileno(copyStream), &st)) < 0)
|
||||
psql_error("could not stat file \"%s\": %s\n",
|
||||
options->file, strerror(errno));
|
||||
|
||||
if (result == 0 && S_ISDIR(st.st_mode))
|
||||
psql_error("%s: cannot copy from/to a directory\n",
|
||||
options->file);
|
||||
|
||||
if (result < 0 || S_ISDIR(st.st_mode))
|
||||
{
|
||||
fclose(copyStream);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return copyStream;
|
||||
}
|
||||
|
||||
|
||||
/* Closes file stream used during copy command, if any. */
|
||||
bool
|
||||
CloseCopyStream(const copy_options *options, FILE *copyStream)
|
||||
{
|
||||
bool success = true;
|
||||
|
||||
if (options->file != NULL)
|
||||
{
|
||||
if (options->program)
|
||||
{
|
||||
int pclose_rc = pclose(copyStream);
|
||||
|
||||
if (pclose_rc != 0)
|
||||
{
|
||||
if (pclose_rc < 0)
|
||||
psql_error("could not close pipe to external command: %s\n",
|
||||
strerror(errno));
|
||||
else
|
||||
{
|
||||
char *reason = wait_result_to_str(pclose_rc);
|
||||
|
||||
psql_error("%s: %s\n", options->file,
|
||||
reason ? reason : "");
|
||||
if (reason)
|
||||
free(reason);
|
||||
}
|
||||
success = false;
|
||||
}
|
||||
#ifndef WIN32
|
||||
pqsignal(SIGPIPE, SIG_DFL);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fclose(copyStream) != 0)
|
||||
{
|
||||
psql_error("%s: %s\n", options->file, strerror(errno));
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Functions for handling COPY IN/OUT data transfer.
|
||||
*
|
||||
* If you want to use COPY TO STDOUT/FROM STDIN in your application,
|
||||
* this is the code to steal ;)
|
||||
*/
|
||||
|
||||
/*
|
||||
* handleCopyOut
|
||||
* receives data as a result of a COPY ... TO STDOUT command
|
||||
*
|
||||
* conn should be a database connection that you just issued COPY TO on
|
||||
* and got back a PGRES_COPY_OUT result.
|
||||
* copystream is the file stream for the data to go to.
|
||||
* The final status for the COPY is returned into *res (but note
|
||||
* we already reported the error, if it's not a success result).
|
||||
*
|
||||
* result is true if successful, false if not.
|
||||
*/
|
||||
bool
|
||||
handleCopyOut(PGconn *conn, FILE *copystream, PGresult **res)
|
||||
{
|
||||
bool OK = true;
|
||||
char *buf;
|
||||
int ret;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
ret = PQgetCopyData(conn, &buf, 0);
|
||||
|
||||
if (ret < 0)
|
||||
break; /* done or server/connection error */
|
||||
|
||||
if (buf)
|
||||
{
|
||||
if (OK && fwrite(buf, 1, ret, copystream) != ret)
|
||||
{
|
||||
psql_error("could not write COPY data: %s\n",
|
||||
strerror(errno));
|
||||
/* complain only once, keep reading data from server */
|
||||
OK = false;
|
||||
}
|
||||
PQfreemem(buf);
|
||||
}
|
||||
}
|
||||
|
||||
if (OK && fflush(copystream))
|
||||
{
|
||||
psql_error("could not write COPY data: %s\n",
|
||||
strerror(errno));
|
||||
OK = false;
|
||||
}
|
||||
|
||||
if (ret == -2)
|
||||
{
|
||||
psql_error("COPY data transfer failed: %s", PQerrorMessage(conn));
|
||||
OK = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check command status and return to normal libpq state.
|
||||
*
|
||||
* If for some reason libpq is still reporting PGRES_COPY_OUT state, we
|
||||
* would like to forcibly exit that state, since our caller would be
|
||||
* unable to distinguish that situation from reaching the next COPY in a
|
||||
* command string that happened to contain two consecutive COPY TO STDOUT
|
||||
* commands. However, libpq provides no API for doing that, and in
|
||||
* principle it's a libpq bug anyway if PQgetCopyData() returns -1 or -2
|
||||
* but hasn't exited COPY_OUT state internally. So we ignore the
|
||||
* possibility here.
|
||||
*/
|
||||
*res = PQgetResult(conn);
|
||||
if (PQresultStatus(*res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
psql_error("%s", PQerrorMessage(conn));
|
||||
OK = false;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* handleCopyIn
|
||||
* sends data to complete a COPY ... FROM STDIN command
|
||||
*
|
||||
* conn should be a database connection that you just issued COPY FROM on
|
||||
* and got back a PGRES_COPY_IN result.
|
||||
* copystream is the file stream to read the data from.
|
||||
* isbinary can be set from PQbinaryTuples().
|
||||
* The final status for the COPY is returned into *res (but note
|
||||
* we already reported the error, if it's not a success result).
|
||||
*
|
||||
* result is true if successful, false if not.
|
||||
*/
|
||||
|
||||
/* read chunk size for COPY IN - size set to double that of Hadoop's default */
|
||||
#define COPYBUFSIZ 32768
|
||||
|
||||
bool
|
||||
handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary,
|
||||
PGresult **res, uint64 copySizeLimit)
|
||||
{
|
||||
bool OK;
|
||||
const char *prompt;
|
||||
char buf[COPYBUFSIZ];
|
||||
uint64 bytesCopied = 0;
|
||||
|
||||
/*
|
||||
* Establish longjmp destination for exiting from wait-for-input. (This is
|
||||
* only effective while sigint_interrupt_enabled is TRUE.)
|
||||
*/
|
||||
if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
|
||||
{
|
||||
/* got here with longjmp */
|
||||
|
||||
/* Terminate data transfer */
|
||||
PQputCopyEnd(conn,
|
||||
(PQprotocolVersion(conn) < 3) ? NULL :
|
||||
_("canceled by user"));
|
||||
|
||||
OK = false;
|
||||
goto copyin_cleanup;
|
||||
}
|
||||
|
||||
/* Prompt if interactive input */
|
||||
if (isatty(fileno(copystream)))
|
||||
{
|
||||
if (!pset.quiet)
|
||||
puts(_("Enter data to be copied followed by a newline.\n"
|
||||
"End with a backslash and a period on a line by itself."));
|
||||
prompt = get_prompt(PROMPT_COPY);
|
||||
}
|
||||
else
|
||||
prompt = NULL;
|
||||
|
||||
OK = true;
|
||||
|
||||
if (isbinary)
|
||||
{
|
||||
/* interactive input probably silly, but give one prompt anyway */
|
||||
if (prompt)
|
||||
{
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int buflen;
|
||||
|
||||
/* enable longjmp while waiting for input */
|
||||
sigint_interrupt_enabled = true;
|
||||
|
||||
buflen = fread(buf, 1, COPYBUFSIZ, copystream);
|
||||
|
||||
sigint_interrupt_enabled = false;
|
||||
|
||||
if (buflen <= 0)
|
||||
break;
|
||||
|
||||
if (PQputCopyData(conn, buf, buflen) <= 0)
|
||||
{
|
||||
OK = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* if size limit is set, copy at most that many bytes*/
|
||||
bytesCopied += buflen;
|
||||
if (copySizeLimit > 0 && bytesCopied >= copySizeLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool copydone = false;
|
||||
|
||||
while (!copydone)
|
||||
{ /* for each input line ... */
|
||||
bool firstload;
|
||||
bool linedone;
|
||||
|
||||
if (prompt)
|
||||
{
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
firstload = true;
|
||||
linedone = false;
|
||||
|
||||
while (!linedone)
|
||||
{ /* for each bufferload in line ... */
|
||||
int linelen = 0;
|
||||
char *fgresult;
|
||||
|
||||
/* enable longjmp while waiting for input */
|
||||
sigint_interrupt_enabled = true;
|
||||
|
||||
fgresult = fgets(buf, sizeof(buf), copystream);
|
||||
|
||||
sigint_interrupt_enabled = false;
|
||||
|
||||
if (!fgresult)
|
||||
{
|
||||
copydone = true;
|
||||
break;
|
||||
}
|
||||
|
||||
linelen = strlen(buf);
|
||||
|
||||
/* current line is done? */
|
||||
if (linelen > 0 && buf[linelen - 1] == '\n')
|
||||
linedone = true;
|
||||
|
||||
/* check for EOF marker, but not on a partial line */
|
||||
if (firstload)
|
||||
{
|
||||
/*
|
||||
* This code erroneously assumes '\.' on a line alone
|
||||
* inside a quoted CSV string terminates the \copy.
|
||||
* http://www.postgresql.org/message-id/E1TdNVQ-0001ju-GO@w
|
||||
* rigleys.postgresql.org
|
||||
*/
|
||||
if (strcmp(buf, "\\.\n") == 0 ||
|
||||
strcmp(buf, "\\.\r\n") == 0)
|
||||
{
|
||||
copydone = true;
|
||||
break;
|
||||
}
|
||||
|
||||
firstload = false;
|
||||
}
|
||||
|
||||
if (PQputCopyData(conn, buf, linelen) <= 0)
|
||||
{
|
||||
OK = false;
|
||||
copydone = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesCopied += linelen;
|
||||
}
|
||||
}
|
||||
|
||||
if (copystream == pset.cur_cmd_source)
|
||||
pset.lineno++;
|
||||
|
||||
/* if size limit is set, copy at most that many bytes */
|
||||
if (copySizeLimit > 0 && bytesCopied >= copySizeLimit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Check for read error */
|
||||
if (ferror(copystream))
|
||||
OK = false;
|
||||
|
||||
/*
|
||||
* Terminate data transfer. We can't send an error message if we're using
|
||||
* protocol version 2.
|
||||
*/
|
||||
if (PQputCopyEnd(conn,
|
||||
(OK || PQprotocolVersion(conn) < 3) ? NULL :
|
||||
_("aborted because of read failure")) <= 0)
|
||||
OK = false;
|
||||
|
||||
copyin_cleanup:
|
||||
|
||||
/*
|
||||
* Check command status and return to normal libpq state.
|
||||
*
|
||||
* We do not want to return with the status still PGRES_COPY_IN: our
|
||||
* caller would be unable to distinguish that situation from reaching the
|
||||
* next COPY in a command string that happened to contain two consecutive
|
||||
* COPY FROM STDIN commands. We keep trying PQputCopyEnd() in the hope
|
||||
* it'll work eventually. (What's actually likely to happen is that in
|
||||
* attempting to flush the data, libpq will eventually realize that the
|
||||
* connection is lost. But that's fine; it will get us out of COPY_IN
|
||||
* state, which is what we need.)
|
||||
*/
|
||||
while (*res = PQgetResult(conn), PQresultStatus(*res) == PGRES_COPY_IN)
|
||||
{
|
||||
OK = false;
|
||||
PQclear(*res);
|
||||
/* We can't send an error message if we're using protocol version 2 */
|
||||
PQputCopyEnd(conn,
|
||||
(PQprotocolVersion(conn) < 3) ? NULL :
|
||||
_("trying to exit copy mode"));
|
||||
}
|
||||
if (PQresultStatus(*res) != PGRES_COMMAND_OK)
|
||||
{
|
||||
psql_error("%s", PQerrorMessage(conn));
|
||||
OK = false;
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/copy.h
|
||||
*/
|
||||
#ifndef COPY_H
|
||||
#define COPY_H
|
||||
|
||||
#include "libpq-fe.h"
|
||||
|
||||
#include "copy_options.h"
|
||||
#include "pqexpbuffer.h"
|
||||
|
||||
|
||||
/* handler for \copy */
|
||||
extern bool do_copy(const char *args);
|
||||
|
||||
/* lower level processors for copy in/out streams */
|
||||
|
||||
extern bool handleCopyOut(PGconn *conn, FILE *copystream,
|
||||
PGresult **res);
|
||||
extern bool handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary,
|
||||
PGresult **res, uint64 copySizeLimit);
|
||||
|
||||
/* Function declarations shared between copy and stage commands */
|
||||
bool HandleCopyData(PGconn *connection, ExecStatusType copyStatus,
|
||||
bool copyIsBinary, FILE *copyStream, uint64 copySizeLimit);
|
||||
FILE * OpenCopyStream(const copy_options *options);
|
||||
bool CloseCopyStream(const copy_options *options, FILE *copyStream);
|
||||
|
||||
#endif
|
|
@ -1,315 +0,0 @@
|
|||
/*
|
||||
* csql - the Citus interactive terminal
|
||||
* copy_options.c
|
||||
* Routines for parsing copy and stage meta commands.
|
||||
*
|
||||
* Copyright (c) 2012-2016, Citus Data, Inc.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#include "postgres_fe.h"
|
||||
#include "copy_options.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "settings.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
|
||||
/* *INDENT-OFF* */
|
||||
void
|
||||
free_copy_options(copy_options * ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
return;
|
||||
free(ptr->before_tofrom);
|
||||
free(ptr->after_tofrom);
|
||||
free(ptr->file);
|
||||
free(ptr->tableName);
|
||||
free(ptr->columnList);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
|
||||
/* concatenate "more" onto "var", freeing the original value of *var */
|
||||
static void
|
||||
xstrcat(char **var, const char *more)
|
||||
{
|
||||
char *newvar;
|
||||
|
||||
newvar = psprintf("%s%s", *var, more);
|
||||
free(*var);
|
||||
*var = newvar;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* parse_slash_copy parses copy options from the given meta-command line. The
|
||||
* function then returns a dynamically allocated structure with the options, or
|
||||
* Null on parsing error.
|
||||
*/
|
||||
copy_options *
|
||||
parse_slash_copy(const char *args)
|
||||
{
|
||||
struct copy_options *result;
|
||||
char *token;
|
||||
const char *whitespace = " \t\n\r";
|
||||
char nonstd_backslash = standard_strings() ? 0 : '\\';
|
||||
|
||||
if (!args)
|
||||
{
|
||||
psql_error("\\copy: arguments required\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = pg_malloc0(sizeof(struct copy_options));
|
||||
|
||||
result->before_tofrom = pg_strdup(""); /* initialize for appending */
|
||||
|
||||
token = strtokx(args, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
|
||||
/* The following can be removed when we drop 7.3 syntax support */
|
||||
if (pg_strcasecmp(token, "binary") == 0)
|
||||
{
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Handle COPY (SELECT) case */
|
||||
if (token[0] == '(')
|
||||
{
|
||||
int parens = 1;
|
||||
|
||||
while (parens > 0)
|
||||
{
|
||||
xstrcat(&result->before_tofrom, " ");
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, "()", "\"'",
|
||||
nonstd_backslash, true, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
if (token[0] == '(')
|
||||
parens++;
|
||||
else if (token[0] == ')')
|
||||
parens--;
|
||||
}
|
||||
}
|
||||
|
||||
xstrcat(&result->before_tofrom, " ");
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* strtokx() will not have returned a multi-character token starting with
|
||||
* '.', so we don't need strcmp() here. Likewise for '(', etc, below.
|
||||
*/
|
||||
if (token[0] == '.')
|
||||
{
|
||||
/* handle schema . table */
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (token[0] == '(')
|
||||
{
|
||||
/* handle parenthesized column list */
|
||||
for (;;)
|
||||
{
|
||||
xstrcat(&result->before_tofrom, " ");
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, "()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
if (token[0] == ')')
|
||||
break;
|
||||
}
|
||||
xstrcat(&result->before_tofrom, " ");
|
||||
xstrcat(&result->before_tofrom, token);
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (pg_strcasecmp(token, "from") == 0)
|
||||
result->from = true;
|
||||
else if (pg_strcasecmp(token, "to") == 0)
|
||||
result->from = false;
|
||||
else
|
||||
goto error;
|
||||
|
||||
/* { 'filename' | PROGRAM 'command' | STDIN | STDOUT | PSTDIN | PSTDOUT } */
|
||||
token = strtokx(NULL, whitespace, ";", "'",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
|
||||
if (pg_strcasecmp(token, "program") == 0)
|
||||
{
|
||||
int toklen;
|
||||
|
||||
token = strtokx(NULL, whitespace, ";", "'",
|
||||
0, false, false, pset.encoding);
|
||||
if (!token)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* The shell command must be quoted. This isn't fool-proof, but
|
||||
* catches most quoting errors.
|
||||
*/
|
||||
toklen = strlen(token);
|
||||
if (token[0] != '\'' || toklen < 2 || token[toklen - 1] != '\'')
|
||||
goto error;
|
||||
|
||||
strip_quotes(token, '\'', 0, pset.encoding);
|
||||
|
||||
result->program = true;
|
||||
result->file = pg_strdup(token);
|
||||
}
|
||||
else if (pg_strcasecmp(token, "stdin") == 0 ||
|
||||
pg_strcasecmp(token, "stdout") == 0)
|
||||
{
|
||||
result->file = NULL;
|
||||
}
|
||||
else if (pg_strcasecmp(token, "pstdin") == 0 ||
|
||||
pg_strcasecmp(token, "pstdout") == 0)
|
||||
{
|
||||
result->psql_inout = true;
|
||||
result->file = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* filename can be optionally quoted */
|
||||
strip_quotes(token, '\'', 0, pset.encoding);
|
||||
result->file = pg_strdup(token);
|
||||
expand_tilde(&result->file);
|
||||
}
|
||||
|
||||
/* Collect the rest of the line (COPY options) */
|
||||
token = strtokx(NULL, "", NULL, NULL,
|
||||
0, false, false, pset.encoding);
|
||||
if (token)
|
||||
result->after_tofrom = pg_strdup(token);
|
||||
|
||||
/* set data staging options to null */
|
||||
result->tableName = NULL;
|
||||
result->columnList = NULL;
|
||||
|
||||
return result;
|
||||
|
||||
error:
|
||||
if (token)
|
||||
psql_error("\\copy: parse error at \"%s\"\n", token);
|
||||
else
|
||||
psql_error("\\copy: parse error at end of line\n");
|
||||
free_copy_options(result);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* *INDENT-ON* */
|
||||
|
||||
/* Frees copy options. */
|
||||
|
||||
/*
|
||||
* ParseStageOptions takes the given copy options, parses the additional options
|
||||
* needed for the \stage command, and sets them in the copy options structure.
|
||||
* The additional parsed options are the table name and the column list.
|
||||
*/
|
||||
copy_options *
|
||||
ParseStageOptions(copy_options *copyOptions)
|
||||
{
|
||||
copy_options *stageOptions = NULL;
|
||||
const char *whitespace = " \t\n\r";
|
||||
char *tableName = NULL;
|
||||
char *columnList = NULL;
|
||||
char *token = NULL;
|
||||
|
||||
const char *beforeToFrom = copyOptions->before_tofrom;
|
||||
Assert(beforeToFrom != NULL);
|
||||
|
||||
token = strtokx(beforeToFrom, whitespace, ".,()", "\"",
|
||||
0, false, false, pset.encoding);
|
||||
|
||||
/*
|
||||
* We should have errored out earlier if the token were null. Similarly, we
|
||||
* should have errored out on the "\stage (select) to" case.
|
||||
*/
|
||||
Assert(token != NULL);
|
||||
Assert(token[0] != '(');
|
||||
|
||||
/* we do not support PostgreSQL's 7.3 syntax */
|
||||
if (pg_strcasecmp(token, "binary") == 0)
|
||||
{
|
||||
psql_error("\\stage: binary keyword before to/from is not supported\n");
|
||||
Assert(false);
|
||||
}
|
||||
|
||||
/* init table name and append either the table name or schema name */
|
||||
tableName = pg_strdup("");
|
||||
xstrcat(&tableName, token);
|
||||
|
||||
/* check for the schema.table use case */
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding);
|
||||
|
||||
if (token != NULL && token[0] == '.')
|
||||
{
|
||||
/* append the dot token */
|
||||
xstrcat(&tableName, token);
|
||||
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding);
|
||||
Assert(token != NULL);
|
||||
|
||||
/* append the table name token */
|
||||
xstrcat(&tableName, token);
|
||||
|
||||
token = strtokx(NULL, whitespace, ".,()", "\"", 0, false, false, pset.encoding);
|
||||
}
|
||||
|
||||
/* check for the column list use case */
|
||||
if (token != NULL && token[0] == '(')
|
||||
{
|
||||
/* init column list, and add columns */
|
||||
columnList = pg_strdup("");
|
||||
for (;;)
|
||||
{
|
||||
xstrcat(&columnList, " ");
|
||||
xstrcat(&columnList, token);
|
||||
|
||||
token = strtokx(NULL, whitespace, "()", "\"", 0, false, false, pset.encoding);
|
||||
Assert(token != NULL);
|
||||
|
||||
if (token[0] == ')')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
xstrcat(&columnList, " ");
|
||||
xstrcat(&columnList, token);
|
||||
}
|
||||
|
||||
/* finally set additional stage options */
|
||||
stageOptions = copyOptions;
|
||||
stageOptions->tableName = tableName;
|
||||
stageOptions->columnList = columnList;
|
||||
|
||||
return stageOptions;
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
/*
|
||||
* csql - the Citus interactive terminal
|
||||
* copy_options.h
|
||||
* Shared declarations for parsing copy and stage meta-commands. The stage
|
||||
* meta-command borrows from copy's syntax, but does not yet support
|
||||
* outputting table data to a file. Further, the stage command reuses copy's
|
||||
* declarations to maintain compatibility with the copy command.
|
||||
*
|
||||
* Copyright (c) 2012-2016, Citus Data, Inc.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef COPY_OPTIONS_H
|
||||
#define COPY_OPTIONS_H
|
||||
|
||||
#include "libpq-fe.h"
|
||||
|
||||
|
||||
/*
|
||||
* The documented syntax is:
|
||||
* \copy tablename [(columnlist)] from|to filename [options]
|
||||
* \copy ( select stmt ) to filename [options]
|
||||
*
|
||||
* where 'filename' can be one of the following:
|
||||
* '<file path>' | PROGRAM '<command>' | stdin | stdout | pstdout | pstdout
|
||||
*
|
||||
* An undocumented fact is that you can still write BINARY before the
|
||||
* tablename; this is a hangover from the pre-7.3 syntax. The options
|
||||
* syntax varies across backend versions, but we avoid all that mess
|
||||
* by just transmitting the stuff after the filename literally.
|
||||
*
|
||||
* table name can be double-quoted and can have a schema part.
|
||||
* column names can be double-quoted.
|
||||
* filename can be single-quoted like SQL literals.
|
||||
* command must be single-quoted like SQL literals.
|
||||
*
|
||||
* returns a malloc'ed structure with the options, or NULL on parsing error
|
||||
*/
|
||||
typedef struct copy_options
|
||||
{
|
||||
char *before_tofrom; /* COPY string before TO/FROM */
|
||||
char *after_tofrom; /* COPY string after TO/FROM filename */
|
||||
char *file; /* NULL = stdin/stdout */
|
||||
bool program; /* is 'file' a program to popen? */
|
||||
bool psql_inout; /* true = use psql stdin/stdout */
|
||||
bool from; /* true = FROM, false = TO */
|
||||
|
||||
char *tableName; /* table name to stage data to */
|
||||
char *columnList; /* optional column list used in staging */
|
||||
} copy_options;
|
||||
|
||||
|
||||
/* Function declarations for parsing and freeing copy options */
|
||||
copy_options * parse_slash_copy(const char *args);
|
||||
void free_copy_options(copy_options * ptr);
|
||||
copy_options * ParseStageOptions(copy_options *copyOptions);
|
||||
|
||||
|
||||
#endif /* COPY_OPTIONS_H */
|
|
@ -1,214 +0,0 @@
|
|||
#! /usr/bin/perl -w
|
||||
|
||||
#################################################################
|
||||
# create_help.pl -- converts SGML docs to internal psql help
|
||||
#
|
||||
# Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
#
|
||||
# src/bin/psql/create_help.pl
|
||||
#################################################################
|
||||
|
||||
#
|
||||
# This script automatically generates the help on SQL in psql from
|
||||
# the SGML docs. So far the format of the docs was consistent
|
||||
# enough that this worked, but this here is by no means an SGML
|
||||
# parser.
|
||||
#
|
||||
# Call: perl create_help.pl docdir sql_help
|
||||
# The name of the header file doesn't matter to this script, but it
|
||||
# sure does matter to the rest of the source.
|
||||
#
|
||||
|
||||
use strict;
|
||||
|
||||
my $docdir = $ARGV[0] or die "$0: missing required argument: docdir\n";
|
||||
my $hfile = $ARGV[1] . '.h'
|
||||
or die "$0: missing required argument: output file\n";
|
||||
my $cfile = $ARGV[1] . '.c';
|
||||
|
||||
my $hfilebasename;
|
||||
if ($hfile =~ m!.*/([^/]+)$!)
|
||||
{
|
||||
$hfilebasename = $1;
|
||||
}
|
||||
else
|
||||
{
|
||||
$hfilebasename = $hfile;
|
||||
}
|
||||
|
||||
my $define = $hfilebasename;
|
||||
$define =~ tr/a-z/A-Z/;
|
||||
$define =~ s/\W/_/g;
|
||||
|
||||
opendir(DIR, $docdir)
|
||||
or die "$0: could not open documentation source dir '$docdir': $!\n";
|
||||
open(HFILE, ">$hfile")
|
||||
or die "$0: could not open output file '$hfile': $!\n";
|
||||
open(CFILE, ">$cfile")
|
||||
or die "$0: could not open output file '$cfile': $!\n";
|
||||
|
||||
print HFILE "/*
|
||||
* *** Do not change this file by hand. It is automatically
|
||||
* *** generated from the DocBook documentation.
|
||||
*
|
||||
* generated by
|
||||
* $^X $0 @ARGV
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef $define
|
||||
#define $define
|
||||
|
||||
#define N_(x) (x) /* gettext noop */
|
||||
|
||||
#include \"postgres_fe.h\"
|
||||
#include \"pqexpbuffer.h\"
|
||||
|
||||
struct _helpStruct
|
||||
{
|
||||
const char *cmd; /* the command name */
|
||||
const char *help; /* the help associated with it */
|
||||
void (*syntaxfunc)(PQExpBuffer); /* function that prints the syntax associated with it */
|
||||
int nl_count; /* number of newlines in syntax (for pager) */
|
||||
};
|
||||
|
||||
";
|
||||
|
||||
print CFILE "/*
|
||||
* *** Do not change this file by hand. It is automatically
|
||||
* *** generated from the DocBook documentation.
|
||||
*
|
||||
* generated by
|
||||
* $^X $0 @ARGV
|
||||
*
|
||||
*/
|
||||
|
||||
#include \"$hfile\"
|
||||
|
||||
";
|
||||
|
||||
my $maxlen = 0;
|
||||
|
||||
my %entries;
|
||||
|
||||
foreach my $file (sort readdir DIR)
|
||||
{
|
||||
my (@cmdnames, $cmddesc, $cmdsynopsis);
|
||||
$file =~ /\.sgml$/ or next;
|
||||
|
||||
open(FILE, "$docdir/$file") or next;
|
||||
my $filecontent = join('', <FILE>);
|
||||
close FILE;
|
||||
|
||||
# Ignore files that are not for SQL language statements
|
||||
$filecontent =~
|
||||
m!<refmiscinfo>\s*SQL - Language Statements\s*</refmiscinfo>!i
|
||||
or next;
|
||||
|
||||
# Collect multiple refnames
|
||||
LOOP:
|
||||
{
|
||||
$filecontent =~ m!\G.*?<refname>\s*([a-z ]+?)\s*</refname>!cgis
|
||||
and push @cmdnames, $1
|
||||
and redo LOOP;
|
||||
}
|
||||
$filecontent =~ m!<refpurpose>\s*(.+?)\s*</refpurpose>!is
|
||||
and $cmddesc = $1;
|
||||
$filecontent =~ m!<synopsis>\s*(.+?)\s*</synopsis>!is
|
||||
and $cmdsynopsis = $1;
|
||||
|
||||
if (@cmdnames && $cmddesc && $cmdsynopsis)
|
||||
{
|
||||
s/\"/\\"/g foreach @cmdnames;
|
||||
|
||||
$cmddesc =~ s/<[^>]+>//g;
|
||||
$cmddesc =~ s/\s+/ /g;
|
||||
$cmddesc =~ s/\"/\\"/g;
|
||||
|
||||
my @params = ();
|
||||
|
||||
my $nl_count = () = $cmdsynopsis =~ /\n/g;
|
||||
|
||||
$cmdsynopsis =~ m!</>!
|
||||
and die "$0:$file: null end tag not supported in synopsis\n";
|
||||
$cmdsynopsis =~ s/%/%%/g;
|
||||
|
||||
while ($cmdsynopsis =~ m!<(\w+)[^>]*>(.+?)</\1[^>]*>!)
|
||||
{
|
||||
my $match = $2;
|
||||
$match =~ s/<[^>]+>//g;
|
||||
$match =~ s/%%/%/g;
|
||||
push @params, $match;
|
||||
$cmdsynopsis =~ s!<(\w+)[^>]*>.+?</\1[^>]*>!%s!;
|
||||
}
|
||||
$cmdsynopsis =~ s/\r?\n/\\n/g;
|
||||
$cmdsynopsis =~ s/\"/\\"/g;
|
||||
|
||||
foreach my $cmdname (@cmdnames)
|
||||
{
|
||||
$entries{$cmdname} = {
|
||||
cmddesc => $cmddesc,
|
||||
cmdsynopsis => $cmdsynopsis,
|
||||
params => \@params,
|
||||
nl_count => $nl_count };
|
||||
$maxlen =
|
||||
($maxlen >= length $cmdname) ? $maxlen : length $cmdname;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
die "$0: parsing file '$file' failed (N='@cmdnames' D='$cmddesc')\n";
|
||||
}
|
||||
}
|
||||
|
||||
foreach (sort keys %entries)
|
||||
{
|
||||
my $prefix = "\t" x 5 . ' ';
|
||||
my $id = $_;
|
||||
$id =~ s/ /_/g;
|
||||
my $synopsis = "\"$entries{$_}{cmdsynopsis}\"";
|
||||
$synopsis =~ s/\\n/\\n"\n$prefix"/g;
|
||||
my @args =
|
||||
("buf", $synopsis, map("_(\"$_\")", @{ $entries{$_}{params} }));
|
||||
print HFILE "extern void sql_help_$id(PQExpBuffer buf);\n";
|
||||
print CFILE "void
|
||||
sql_help_$id(PQExpBuffer buf)
|
||||
{
|
||||
\tappendPQExpBuffer(" . join(",\n$prefix", @args) . ");
|
||||
}
|
||||
|
||||
";
|
||||
}
|
||||
|
||||
print HFILE "
|
||||
|
||||
static const struct _helpStruct QL_HELP[] = {
|
||||
";
|
||||
foreach (sort keys %entries)
|
||||
{
|
||||
my $id = $_;
|
||||
$id =~ s/ /_/g;
|
||||
print HFILE " { \"$_\",
|
||||
N_(\"$entries{$_}{cmddesc}\"),
|
||||
sql_help_$id,
|
||||
$entries{$_}{nl_count} },
|
||||
|
||||
";
|
||||
}
|
||||
|
||||
print HFILE "
|
||||
{ NULL, NULL, NULL } /* End of list marker */
|
||||
};
|
||||
|
||||
|
||||
#define QL_HELP_COUNT "
|
||||
. scalar(keys %entries) . " /* number of help items */
|
||||
#define QL_MAX_CMD_LEN $maxlen /* largest strlen(cmd) */
|
||||
|
||||
|
||||
#endif /* $define */
|
||||
";
|
||||
|
||||
close CFILE;
|
||||
close HFILE;
|
||||
closedir DIR;
|
File diff suppressed because it is too large
Load Diff
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/describe.h
|
||||
*/
|
||||
#ifndef DESCRIBE_H
|
||||
#define DESCRIBE_H
|
||||
|
||||
|
||||
/* \da */
|
||||
extern bool describeAggregates(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \db */
|
||||
extern bool describeTablespaces(const char *pattern, bool verbose);
|
||||
|
||||
/* \df, \dfa, \dfn, \dft, \dfw, etc. */
|
||||
extern bool describeFunctions(const char *functypes, const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dT */
|
||||
extern bool describeTypes(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \do */
|
||||
extern bool describeOperators(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \du, \dg */
|
||||
extern bool describeRoles(const char *pattern, bool verbose);
|
||||
|
||||
/* \drds */
|
||||
extern bool listDbRoleSettings(const char *pattern1, const char *pattern2);
|
||||
|
||||
/* \z (or \dp) */
|
||||
extern bool permissionsList(const char *pattern);
|
||||
|
||||
/* \ddp */
|
||||
extern bool listDefaultACLs(const char *pattern);
|
||||
|
||||
/* \dd */
|
||||
extern bool objectDescription(const char *pattern, bool showSystem);
|
||||
|
||||
/* \d foo */
|
||||
extern bool describeTableDetails(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dF */
|
||||
extern bool listTSConfigs(const char *pattern, bool verbose);
|
||||
|
||||
/* \dFp */
|
||||
extern bool listTSParsers(const char *pattern, bool verbose);
|
||||
|
||||
/* \dFd */
|
||||
extern bool listTSDictionaries(const char *pattern, bool verbose);
|
||||
|
||||
/* \dFt */
|
||||
extern bool listTSTemplates(const char *pattern, bool verbose);
|
||||
|
||||
/* \l */
|
||||
extern bool listAllDbs(const char *pattern, bool verbose);
|
||||
|
||||
/* \dt, \di, \ds, \dS, etc. */
|
||||
extern bool listTables(const char *tabtypes, const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dD */
|
||||
extern bool listDomains(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dc */
|
||||
extern bool listConversions(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dC */
|
||||
extern bool listCasts(const char *pattern, bool verbose);
|
||||
|
||||
/* \dO */
|
||||
extern bool listCollations(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dn */
|
||||
extern bool listSchemas(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dew */
|
||||
extern bool listForeignDataWrappers(const char *pattern, bool verbose);
|
||||
|
||||
/* \des */
|
||||
extern bool listForeignServers(const char *pattern, bool verbose);
|
||||
|
||||
/* \deu */
|
||||
extern bool listUserMappings(const char *pattern, bool verbose);
|
||||
|
||||
/* \det */
|
||||
extern bool listForeignTables(const char *pattern, bool verbose);
|
||||
|
||||
/* \dL */
|
||||
extern bool listLanguages(const char *pattern, bool verbose, bool showSystem);
|
||||
|
||||
/* \dx */
|
||||
extern bool listExtensions(const char *pattern);
|
||||
|
||||
/* \dx+ */
|
||||
extern bool listExtensionContents(const char *pattern);
|
||||
|
||||
/* \dy */
|
||||
extern bool listEventTriggers(const char *pattern, bool verbose);
|
||||
|
||||
#endif /* DESCRIBE_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,572 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/help.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/types.h> /* (ditto) */
|
||||
#include <unistd.h> /* for geteuid() */
|
||||
#else
|
||||
#include <win32.h>
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
#include <sys/ioctl.h> /* for ioctl() */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <termios.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "common/username.h"
|
||||
#include "help.h"
|
||||
#include "input.h"
|
||||
#include "settings.h"
|
||||
#include "sql_help.h"
|
||||
|
||||
|
||||
/*
|
||||
* PLEASE:
|
||||
* If you change something in this file, also make the same changes
|
||||
* in the DocBook documentation, file ref/psql-ref.sgml. If you don't
|
||||
* know how to do it, please find someone who can help you.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* usage
|
||||
*
|
||||
* print out command line arguments
|
||||
*/
|
||||
#define ON(var) (var ? _("on") : _("off"))
|
||||
|
||||
void
|
||||
usage(unsigned short int pager)
|
||||
{
|
||||
const char *env;
|
||||
const char *user;
|
||||
char *errstr;
|
||||
FILE *output;
|
||||
|
||||
/* Find default user, in case we need it. */
|
||||
user = getenv("PGUSER");
|
||||
if (!user)
|
||||
{
|
||||
user = get_user_name(&errstr);
|
||||
if (!user)
|
||||
{
|
||||
psql_error("%s\n", errstr);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
output = PageOutput(59, pager ? &(pset.popt.topt) : NULL);
|
||||
|
||||
printf(_("csql is the Citus interactive terminal.\n\n"));
|
||||
fprintf(output, _("Usage:\n"));
|
||||
printf(_(" csql [OPTION]... [DBNAME [USERNAME]]\n\n"));
|
||||
|
||||
fprintf(output, _("General options:\n"));
|
||||
/* Display default database */
|
||||
env = getenv("PGDATABASE");
|
||||
if (!env)
|
||||
env = user;
|
||||
fprintf(output, _(" -c, --command=COMMAND run only single command (SQL or internal) and exit\n"));
|
||||
fprintf(output, _(" -d, --dbname=DBNAME database name to connect to (default: \"%s\")\n"), env);
|
||||
fprintf(output, _(" -f, --file=FILENAME execute commands from file, then exit\n"));
|
||||
fprintf(output, _(" -l, --list list available databases, then exit\n"));
|
||||
fprintf(output, _(" -v, --set=, --variable=NAME=VALUE\n"
|
||||
" set psql variable NAME to VALUE\n"
|
||||
" (e.g., -v ON_ERROR_STOP=1)\n"));
|
||||
fprintf(output, _(" -V, --version output version information, then exit\n"));
|
||||
fprintf(output, _(" -X, --no-psqlrc do not read startup file (~/.psqlrc)\n"));
|
||||
fprintf(output, _(" -1 (\"one\"), --single-transaction\n"
|
||||
" execute as a single transaction (if non-interactive)\n"));
|
||||
fprintf(output, _(" -?, --help[=options] show this help, then exit\n"));
|
||||
fprintf(output, _(" --help=commands list backslash commands, then exit\n"));
|
||||
fprintf(output, _(" --help=variables list special variables, then exit\n"));
|
||||
|
||||
fprintf(output, _("\nInput and output options:\n"));
|
||||
fprintf(output, _(" -a, --echo-all echo all input from script\n"));
|
||||
fprintf(output, _(" -b, --echo-errors echo failed commands\n"));
|
||||
fprintf(output, _(" -e, --echo-queries echo commands sent to server\n"));
|
||||
fprintf(output, _(" -E, --echo-hidden display queries that internal commands generate\n"));
|
||||
fprintf(output, _(" -L, --log-file=FILENAME send session log to file\n"));
|
||||
fprintf(output, _(" -n, --no-readline disable enhanced command line editing (readline)\n"));
|
||||
fprintf(output, _(" -o, --output=FILENAME send query results to file (or |pipe)\n"));
|
||||
fprintf(output, _(" -q, --quiet run quietly (no messages, only query output)\n"));
|
||||
fprintf(output, _(" -s, --single-step single-step mode (confirm each query)\n"));
|
||||
fprintf(output, _(" -S, --single-line single-line mode (end of line terminates SQL command)\n"));
|
||||
|
||||
fprintf(output, _("\nOutput format options:\n"));
|
||||
fprintf(output, _(" -A, --no-align unaligned table output mode\n"));
|
||||
fprintf(output, _(" -F, --field-separator=STRING\n"
|
||||
" field separator for unaligned output (default: \"%s\")\n"),
|
||||
DEFAULT_FIELD_SEP);
|
||||
fprintf(output, _(" -H, --html HTML table output mode\n"));
|
||||
fprintf(output, _(" -P, --pset=VAR[=ARG] set printing option VAR to ARG (see \\pset command)\n"));
|
||||
fprintf(output, _(" -R, --record-separator=STRING\n"
|
||||
" record separator for unaligned output (default: newline)\n"));
|
||||
fprintf(output, _(" -t, --tuples-only print rows only\n"));
|
||||
fprintf(output, _(" -T, --table-attr=TEXT set HTML table tag attributes (e.g., width, border)\n"));
|
||||
fprintf(output, _(" -x, --expanded turn on expanded table output\n"));
|
||||
fprintf(output, _(" -z, --field-separator-zero\n"
|
||||
" set field separator for unaligned output to zero byte\n"));
|
||||
fprintf(output, _(" -0, --record-separator-zero\n"
|
||||
" set record separator for unaligned output to zero byte\n"));
|
||||
|
||||
fprintf(output, _("\nConnection options:\n"));
|
||||
/* Display default host */
|
||||
env = getenv("PGHOST");
|
||||
fprintf(output, _(" -h, --host=HOSTNAME database server host or socket directory (default: \"%s\")\n"),
|
||||
env ? env : _("local socket"));
|
||||
/* Display default port */
|
||||
env = getenv("PGPORT");
|
||||
fprintf(output, _(" -p, --port=PORT database server port (default: \"%s\")\n"),
|
||||
env ? env : DEF_PGPORT_STR);
|
||||
/* Display default user */
|
||||
env = getenv("PGUSER");
|
||||
if (!env)
|
||||
env = user;
|
||||
fprintf(output, _(" -U, --username=USERNAME database user name (default: \"%s\")\n"), env);
|
||||
fprintf(output, _(" -w, --no-password never prompt for password\n"));
|
||||
fprintf(output, _(" -W, --password force password prompt (should happen automatically)\n"));
|
||||
|
||||
fprintf(output, _("\nFor more information, type \"\\?\" (for internal commands) or \"\\help\" (for SQL\n"
|
||||
"commands) from within psql, or consult the psql section in the PostgreSQL\n"
|
||||
"documentation.\n\n"));
|
||||
fprintf(output, _("Report bugs to <pgsql-bugs@postgresql.org>.\n"));
|
||||
|
||||
ClosePager(output);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* slashUsage
|
||||
*
|
||||
* print out help for the backslash commands
|
||||
*/
|
||||
void
|
||||
slashUsage(unsigned short int pager)
|
||||
{
|
||||
FILE *output;
|
||||
char *currdb;
|
||||
|
||||
currdb = PQdb(pset.db);
|
||||
|
||||
output = PageOutput(103, pager ? &(pset.popt.topt) : NULL);
|
||||
|
||||
/* if you add/remove a line here, change the row count above */
|
||||
|
||||
fprintf(output, _("General\n"));
|
||||
fprintf(output, _(" \\copyright show PostgreSQL usage and distribution terms\n"));
|
||||
fprintf(output, _(" \\g [FILE] or ; execute query (and send results to file or |pipe)\n"));
|
||||
fprintf(output, _(" \\gset [PREFIX] execute query and store results in psql variables\n"));
|
||||
fprintf(output, _(" \\q quit psql\n"));
|
||||
fprintf(output, _(" \\watch [SEC] execute query every SEC seconds\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Help\n"));
|
||||
|
||||
fprintf(output, _(" \\? [commands] show help on backslash commands\n"));
|
||||
fprintf(output, _(" \\? options show help on psql command-line options\n"));
|
||||
fprintf(output, _(" \\? variables show help on special variables\n"));
|
||||
fprintf(output, _(" \\h [NAME] help on syntax of SQL commands, * for all commands\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Query Buffer\n"));
|
||||
fprintf(output, _(" \\e [FILE] [LINE] edit the query buffer (or file) with external editor\n"));
|
||||
fprintf(output, _(" \\ef [FUNCNAME [LINE]] edit function definition with external editor\n"));
|
||||
fprintf(output, _(" \\p show the contents of the query buffer\n"));
|
||||
fprintf(output, _(" \\r reset (clear) the query buffer\n"));
|
||||
#ifdef USE_READLINE
|
||||
fprintf(output, _(" \\s [FILE] display history or save it to file\n"));
|
||||
#endif
|
||||
fprintf(output, _(" \\w FILE write query buffer to file\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Input/Output\n"));
|
||||
fprintf(output, _(" \\copy ... perform SQL COPY with data stream to the client host\n"));
|
||||
fprintf(output, _(" \\echo [STRING] write string to standard output\n"));
|
||||
fprintf(output, _(" \\i FILE execute commands from file\n"));
|
||||
fprintf(output, _(" \\ir FILE as \\i, but relative to location of current script\n"));
|
||||
fprintf(output, _(" \\o [FILE] send all query results to file or |pipe\n"));
|
||||
fprintf(output, _(" \\qecho [STRING] write string to query output stream (see \\o)\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Informational\n"));
|
||||
fprintf(output, _(" (options: S = show system objects, + = additional detail)\n"));
|
||||
fprintf(output, _(" \\d[S+] list tables, views, and sequences\n"));
|
||||
fprintf(output, _(" \\d[S+] NAME describe table, view, sequence, or index\n"));
|
||||
fprintf(output, _(" \\da[S] [PATTERN] list aggregates\n"));
|
||||
fprintf(output, _(" \\db[+] [PATTERN] list tablespaces\n"));
|
||||
fprintf(output, _(" \\dc[S+] [PATTERN] list conversions\n"));
|
||||
fprintf(output, _(" \\dC[+] [PATTERN] list casts\n"));
|
||||
fprintf(output, _(" \\dd[S] [PATTERN] show object descriptions not displayed elsewhere\n"));
|
||||
fprintf(output, _(" \\ddp [PATTERN] list default privileges\n"));
|
||||
fprintf(output, _(" \\dD[S+] [PATTERN] list domains\n"));
|
||||
fprintf(output, _(" \\det[+] [PATTERN] list foreign tables\n"));
|
||||
fprintf(output, _(" \\des[+] [PATTERN] list foreign servers\n"));
|
||||
fprintf(output, _(" \\deu[+] [PATTERN] list user mappings\n"));
|
||||
fprintf(output, _(" \\dew[+] [PATTERN] list foreign-data wrappers\n"));
|
||||
fprintf(output, _(" \\df[antw][S+] [PATRN] list [only agg/normal/trigger/window] functions\n"));
|
||||
fprintf(output, _(" \\dF[+] [PATTERN] list text search configurations\n"));
|
||||
fprintf(output, _(" \\dFd[+] [PATTERN] list text search dictionaries\n"));
|
||||
fprintf(output, _(" \\dFp[+] [PATTERN] list text search parsers\n"));
|
||||
fprintf(output, _(" \\dFt[+] [PATTERN] list text search templates\n"));
|
||||
fprintf(output, _(" \\dg[+] [PATTERN] list roles\n"));
|
||||
fprintf(output, _(" \\di[S+] [PATTERN] list indexes\n"));
|
||||
fprintf(output, _(" \\dl list large objects, same as \\lo_list\n"));
|
||||
fprintf(output, _(" \\dL[S+] [PATTERN] list procedural languages\n"));
|
||||
fprintf(output, _(" \\dm[S+] [PATTERN] list materialized views\n"));
|
||||
fprintf(output, _(" \\dn[S+] [PATTERN] list schemas\n"));
|
||||
fprintf(output, _(" \\do[S] [PATTERN] list operators\n"));
|
||||
fprintf(output, _(" \\dO[S+] [PATTERN] list collations\n"));
|
||||
fprintf(output, _(" \\dp [PATTERN] list table, view, and sequence access privileges\n"));
|
||||
fprintf(output, _(" \\drds [PATRN1 [PATRN2]] list per-database role settings\n"));
|
||||
fprintf(output, _(" \\ds[S+] [PATTERN] list sequences\n"));
|
||||
fprintf(output, _(" \\dt[S+] [PATTERN] list tables\n"));
|
||||
fprintf(output, _(" \\dT[S+] [PATTERN] list data types\n"));
|
||||
fprintf(output, _(" \\du[+] [PATTERN] list roles\n"));
|
||||
fprintf(output, _(" \\dv[S+] [PATTERN] list views\n"));
|
||||
fprintf(output, _(" \\dE[S+] [PATTERN] list foreign tables\n"));
|
||||
fprintf(output, _(" \\dx[+] [PATTERN] list extensions\n"));
|
||||
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
|
||||
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
|
||||
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
|
||||
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Formatting\n"));
|
||||
fprintf(output, _(" \\a toggle between unaligned and aligned output mode\n"));
|
||||
fprintf(output, _(" \\C [STRING] set table title, or unset if none\n"));
|
||||
fprintf(output, _(" \\f [STRING] show or set field separator for unaligned query output\n"));
|
||||
fprintf(output, _(" \\H toggle HTML output mode (currently %s)\n"),
|
||||
ON(pset.popt.topt.format == PRINT_HTML));
|
||||
fprintf(output, _(" \\pset [NAME [VALUE]] set table output option\n"
|
||||
" (NAME := {format|border|expanded|fieldsep|fieldsep_zero|footer|null|\n"
|
||||
" numericlocale|recordsep|recordsep_zero|tuples_only|title|tableattr|pager|\n"
|
||||
" unicode_border_linestyle|unicode_column_linestyle|unicode_header_linestyle})\n"));
|
||||
fprintf(output, _(" \\t [on|off] show only rows (currently %s)\n"),
|
||||
ON(pset.popt.topt.tuples_only));
|
||||
fprintf(output, _(" \\T [STRING] set HTML <table> tag attributes, or unset if none\n"));
|
||||
fprintf(output, _(" \\x [on|off|auto] toggle expanded output (currently %s)\n"),
|
||||
pset.popt.topt.expanded == 2 ? "auto" : ON(pset.popt.topt.expanded));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Connection\n"));
|
||||
if (currdb)
|
||||
fprintf(output, _(" \\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo}\n"
|
||||
" connect to new database (currently \"%s\")\n"),
|
||||
currdb);
|
||||
else
|
||||
fprintf(output, _(" \\c[onnect] {[DBNAME|- USER|- HOST|- PORT|-] | conninfo}\n"
|
||||
" connect to new database (currently no connection)\n"));
|
||||
fprintf(output, _(" \\encoding [ENCODING] show or set client encoding\n"));
|
||||
fprintf(output, _(" \\password [USERNAME] securely change the password for a user\n"));
|
||||
fprintf(output, _(" \\conninfo display information about current connection\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Operating System\n"));
|
||||
fprintf(output, _(" \\cd [DIR] change the current working directory\n"));
|
||||
fprintf(output, _(" \\setenv NAME [VALUE] set or unset environment variable\n"));
|
||||
fprintf(output, _(" \\timing [on|off] toggle timing of commands (currently %s)\n"),
|
||||
ON(pset.timing));
|
||||
fprintf(output, _(" \\! [COMMAND] execute command in shell or start interactive shell\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Variables\n"));
|
||||
fprintf(output, _(" \\prompt [TEXT] NAME prompt user to set internal variable\n"));
|
||||
fprintf(output, _(" \\set [NAME [VALUE]] set internal variable, or list all if no parameters\n"));
|
||||
fprintf(output, _(" \\unset NAME unset (delete) internal variable\n"));
|
||||
fprintf(output, "\n");
|
||||
|
||||
fprintf(output, _("Large Objects\n"));
|
||||
fprintf(output, _(" \\lo_export LOBOID FILE\n"
|
||||
" \\lo_import FILE [COMMENT]\n"
|
||||
" \\lo_list\n"
|
||||
" \\lo_unlink LOBOID large object operations\n"));
|
||||
|
||||
ClosePager(output);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* helpVariables
|
||||
*
|
||||
* show list of available variables (options) from command line
|
||||
*/
|
||||
void
|
||||
helpVariables(unsigned short int pager)
|
||||
{
|
||||
FILE *output;
|
||||
|
||||
output = PageOutput(85, pager ? &(pset.popt.topt) : NULL);
|
||||
|
||||
fprintf(output, _("List of specially treated variables\n\n"));
|
||||
|
||||
fprintf(output, _("psql variables:\n"));
|
||||
fprintf(output, _("Usage:\n"));
|
||||
fprintf(output, _(" psql --set=NAME=VALUE\n or \\set NAME VALUE inside psql\n\n"));
|
||||
|
||||
fprintf(output, _(" AUTOCOMMIT if set, successful SQL commands are automatically committed\n"));
|
||||
fprintf(output, _(" COMP_KEYWORD_CASE determines the case used to complete SQL key words\n"
|
||||
" [lower, upper, preserve-lower, preserve-upper]\n"));
|
||||
fprintf(output, _(" DBNAME the currently connected database name\n"));
|
||||
fprintf(output, _(" ECHO controls what input is written to standard output\n"
|
||||
" [all, errors, none, queries]\n"));
|
||||
fprintf(output, _(" ECHO_HIDDEN if set, display internal queries executed by backslash commands;\n"
|
||||
" if set to \"noexec\", just show without execution\n"));
|
||||
fprintf(output, _(" ENCODING current client character set encoding\n"));
|
||||
fprintf(output, _(" FETCH_COUNT the number of result rows to fetch and display at a time\n"
|
||||
" (default: 0=unlimited)\n"));
|
||||
fprintf(output, _(" HISTCONTROL controls command history [ignorespace, ignoredups, ignoreboth]\n"));
|
||||
fprintf(output, _(" HISTFILE file name used to store the command history\n"));
|
||||
fprintf(output, _(" HISTSIZE the number of commands to store in the command history\n"));
|
||||
fprintf(output, _(" HOST the currently connected database server host\n"));
|
||||
fprintf(output, _(" IGNOREEOF if unset, sending an EOF to interactive session terminates application\n"));
|
||||
fprintf(output, _(" LASTOID value of the last affected OID\n"));
|
||||
fprintf(output, _(" ON_ERROR_ROLLBACK if set, an error doesn't stop a transaction (uses implicit savepoints)\n"));
|
||||
fprintf(output, _(" ON_ERROR_STOP stop batch execution after error\n"));
|
||||
fprintf(output, _(" PORT server port of the current connection\n"));
|
||||
fprintf(output, _(" PROMPT1 specifies the standard psql prompt\n"));
|
||||
fprintf(output, _(" PROMPT2 specifies the prompt used when a statement continues from a previous line\n"));
|
||||
fprintf(output, _(" PROMPT3 specifies the prompt used during COPY ... FROM STDIN\n"));
|
||||
fprintf(output, _(" QUIET run quietly (same as -q option)\n"));
|
||||
fprintf(output, _(" SINGLELINE end of line terminates SQL command mode (same as -S option)\n"));
|
||||
fprintf(output, _(" SINGLESTEP single-step mode (same as -s option)\n"));
|
||||
fprintf(output, _(" USER the currently connected database user\n"));
|
||||
fprintf(output, _(" VERBOSITY controls verbosity of error reports [default, verbose, terse]\n"));
|
||||
|
||||
fprintf(output, _("\nDisplay settings:\n"));
|
||||
fprintf(output, _("Usage:\n"));
|
||||
fprintf(output, _(" psql --pset=NAME[=VALUE]\n or \\pset NAME [VALUE] inside psql\n\n"));
|
||||
|
||||
fprintf(output, _(" border border style (number)\n"));
|
||||
fprintf(output, _(" columns target width for the wrapped format\n"));
|
||||
fprintf(output, _(" expanded (or x) expanded output [on, off, auto]\n"));
|
||||
fprintf(output, _(" fieldsep field separator for unaligned output (default \"%s\")\n"), DEFAULT_FIELD_SEP);
|
||||
fprintf(output, _(" fieldsep_zero set field separator for unaligned output to zero byte\n"));
|
||||
fprintf(output, _(" format set output format [unaligned, aligned, wrapped, html, asciidoc, ...]\n"));
|
||||
fprintf(output, _(" footer enable or disable display of the table footer [on, off]\n"));
|
||||
fprintf(output, _(" linestyle set the border line drawing style [ascii, old-ascii, unicode]\n"));
|
||||
fprintf(output, _(" null set the string to be printed in place of a null value\n"));
|
||||
fprintf(output, _(" numericlocale enable or disable display of a locale-specific character to separate\n"
|
||||
" groups of digits [on, off]\n"));
|
||||
fprintf(output, _(" pager control when an external pager is used [yes, no, always]\n"));
|
||||
fprintf(output, _(" recordsep record (line) separator for unaligned output\n"));
|
||||
fprintf(output, _(" recordsep_zero set record separator for unaligned output to zero byte\n"));
|
||||
fprintf(output, _(" tableattr (or T) specify attributes for table tag in html format or proportional\n"
|
||||
" column widths for left-aligned data types in latex-longtable format\n"));
|
||||
fprintf(output, _(" title set the table title for any subsequently printed tables\n"));
|
||||
fprintf(output, _(" tuples_only if set, only actual table data is shown\n"));
|
||||
fprintf(output, _(" unicode_border_linestyle\n"
|
||||
" unicode_column_linestyle\n"
|
||||
" unicode_header_linestyle\n"
|
||||
" set the style of Unicode line drawing [single, double]\n"));
|
||||
|
||||
fprintf(output, _("\nEnvironment variables:\n"));
|
||||
fprintf(output, _("Usage:\n"));
|
||||
|
||||
#ifndef WIN32
|
||||
fprintf(output, _(" NAME=VALUE [NAME=VALUE] psql ...\n or \\setenv NAME [VALUE] inside psql\n\n"));
|
||||
#else
|
||||
fprintf(output, _(" set NAME=VALUE\n psql ...\n or \\setenv NAME [VALUE] inside psql\n\n"));
|
||||
#endif
|
||||
|
||||
fprintf(output, _(" COLUMNS number of columns for wrapped format\n"));
|
||||
fprintf(output, _(" PAGER name of external pager program\n"));
|
||||
fprintf(output, _(" PGAPPNAME same as the application_name connection parameter\n"));
|
||||
fprintf(output, _(" PGDATABASE same as the dbname connection parameter\n"));
|
||||
fprintf(output, _(" PGHOST same as the host connection parameter\n"));
|
||||
fprintf(output, _(" PGPORT same as the port connection parameter\n"));
|
||||
fprintf(output, _(" PGUSER same as the user connection parameter\n"));
|
||||
fprintf(output, _(" PGPASSWORD connection password (not recommended)\n"));
|
||||
fprintf(output, _(" PGPASSFILE password file name\n"));
|
||||
fprintf(output, _(" PSQL_EDITOR, EDITOR, VISUAL\n"
|
||||
" editor used by the \\e and \\ef commands\n"));
|
||||
fprintf(output, _(" PSQL_EDITOR_LINENUMBER_ARG\n"
|
||||
" how to specify a line number when invoking the editor\n"));
|
||||
fprintf(output, _(" PSQL_HISTORY alternative location for the command history file\n"));
|
||||
fprintf(output, _(" PSQLRC alternative location for the user's .psqlrc file\n"));
|
||||
fprintf(output, _(" SHELL shell used by the \\! command\n"));
|
||||
fprintf(output, _(" TMPDIR directory for temporary files\n"));
|
||||
|
||||
ClosePager(output);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* helpSQL -- help with SQL commands
|
||||
*
|
||||
* Note: we assume caller removed any trailing spaces in "topic".
|
||||
*/
|
||||
void
|
||||
helpSQL(const char *topic, unsigned short int pager)
|
||||
{
|
||||
#define VALUE_OR_NULL(a) ((a) ? (a) : "")
|
||||
|
||||
if (!topic || strlen(topic) == 0)
|
||||
{
|
||||
/* Print all the available command names */
|
||||
int screen_width;
|
||||
int ncolumns;
|
||||
int nrows;
|
||||
FILE *output;
|
||||
int i;
|
||||
int j;
|
||||
|
||||
#ifdef TIOCGWINSZ
|
||||
struct winsize screen_size;
|
||||
|
||||
if (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1)
|
||||
screen_width = 80; /* ioctl failed, assume 80 */
|
||||
else
|
||||
screen_width = screen_size.ws_col;
|
||||
#else
|
||||
screen_width = 80; /* default assumption */
|
||||
#endif
|
||||
|
||||
ncolumns = (screen_width - 3) / (QL_MAX_CMD_LEN + 1);
|
||||
ncolumns = Max(ncolumns, 1);
|
||||
nrows = (QL_HELP_COUNT + (ncolumns - 1)) / ncolumns;
|
||||
|
||||
output = PageOutput(nrows + 1, pager ? &(pset.popt.topt) : NULL);
|
||||
|
||||
fputs(_("Available help:\n"), output);
|
||||
|
||||
for (i = 0; i < nrows; i++)
|
||||
{
|
||||
fprintf(output, " ");
|
||||
for (j = 0; j < ncolumns - 1; j++)
|
||||
fprintf(output, "%-*s",
|
||||
QL_MAX_CMD_LEN + 1,
|
||||
VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd));
|
||||
if (i + j * nrows < QL_HELP_COUNT)
|
||||
fprintf(output, "%s",
|
||||
VALUE_OR_NULL(QL_HELP[i + j * nrows].cmd));
|
||||
fputc('\n', output);
|
||||
}
|
||||
|
||||
ClosePager(output);
|
||||
}
|
||||
else
|
||||
{
|
||||
int i,
|
||||
j,
|
||||
x = 0;
|
||||
bool help_found = false;
|
||||
FILE *output = NULL;
|
||||
size_t len,
|
||||
wordlen;
|
||||
int nl_count = 0;
|
||||
|
||||
/*
|
||||
* We first try exact match, then first + second words, then first
|
||||
* word only.
|
||||
*/
|
||||
len = strlen(topic);
|
||||
|
||||
for (x = 1; x <= 3; x++)
|
||||
{
|
||||
if (x > 1) /* Nothing on first pass - try the opening
|
||||
* word(s) */
|
||||
{
|
||||
wordlen = j = 1;
|
||||
while (topic[j] != ' ' && j++ < len)
|
||||
wordlen++;
|
||||
if (x == 2)
|
||||
{
|
||||
j++;
|
||||
while (topic[j] != ' ' && j++ <= len)
|
||||
wordlen++;
|
||||
}
|
||||
if (wordlen >= len) /* Don't try again if the same word */
|
||||
{
|
||||
if (!output)
|
||||
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
|
||||
break;
|
||||
}
|
||||
len = wordlen;
|
||||
}
|
||||
|
||||
/* Count newlines for pager */
|
||||
for (i = 0; QL_HELP[i].cmd; i++)
|
||||
{
|
||||
if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
|
||||
strcmp(topic, "*") == 0)
|
||||
{
|
||||
nl_count += 5 + QL_HELP[i].nl_count;
|
||||
|
||||
/* If we have an exact match, exit. Fixes \h SELECT */
|
||||
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!output)
|
||||
output = PageOutput(nl_count, pager ? &(pset.popt.topt) : NULL);
|
||||
|
||||
for (i = 0; QL_HELP[i].cmd; i++)
|
||||
{
|
||||
if (pg_strncasecmp(topic, QL_HELP[i].cmd, len) == 0 ||
|
||||
strcmp(topic, "*") == 0)
|
||||
{
|
||||
PQExpBufferData buffer;
|
||||
|
||||
initPQExpBuffer(&buffer);
|
||||
QL_HELP[i].syntaxfunc(&buffer);
|
||||
help_found = true;
|
||||
fprintf(output, _("Command: %s\n"
|
||||
"Description: %s\n"
|
||||
"Syntax:\n%s\n\n"),
|
||||
QL_HELP[i].cmd,
|
||||
_(QL_HELP[i].help),
|
||||
buffer.data);
|
||||
/* If we have an exact match, exit. Fixes \h SELECT */
|
||||
if (pg_strcasecmp(topic, QL_HELP[i].cmd) == 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (help_found) /* Don't keep trying if we got a match */
|
||||
break;
|
||||
}
|
||||
|
||||
if (!help_found)
|
||||
fprintf(output, _("No help available for \"%s\".\nTry \\h with no arguments to see available help.\n"), topic);
|
||||
|
||||
ClosePager(output);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
print_copyright(void)
|
||||
{
|
||||
puts(
|
||||
"PostgreSQL Database Management System\n"
|
||||
"(formerly known as Postgres, then as Postgres95)\n\n"
|
||||
"Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group\n\n"
|
||||
"Portions Copyright (c) 1994, The Regents of the University of California\n\n"
|
||||
"Permission to use, copy, modify, and distribute this software and its\n"
|
||||
"documentation for any purpose, without fee, and without a written agreement\n"
|
||||
"is hereby granted, provided that the above copyright notice and this\n"
|
||||
"paragraph and the following two paragraphs appear in all copies.\n\n"
|
||||
"IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR\n"
|
||||
"DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING\n"
|
||||
"LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS\n"
|
||||
"DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE\n"
|
||||
"POSSIBILITY OF SUCH DAMAGE.\n\n"
|
||||
"THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,\n"
|
||||
"INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY\n"
|
||||
"AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS\n"
|
||||
"ON AN \"AS IS\" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO\n"
|
||||
"PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.\n"
|
||||
);
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/help.h
|
||||
*/
|
||||
#ifndef HELP_H
|
||||
#define HELP_H
|
||||
|
||||
void usage(unsigned short int pager);
|
||||
|
||||
void slashUsage(unsigned short int pager);
|
||||
|
||||
void helpVariables(unsigned short int pager);
|
||||
|
||||
void helpSQL(const char *topic, unsigned short int pager);
|
||||
|
||||
void print_copyright(void);
|
||||
|
||||
#endif
|
|
@ -1,539 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/input.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
|
||||
#include "input.h"
|
||||
#include "settings.h"
|
||||
#include "tab-complete.h"
|
||||
#include "common.h"
|
||||
|
||||
#ifndef WIN32
|
||||
#define PSQLHISTORY ".psql_history"
|
||||
#else
|
||||
#define PSQLHISTORY "psql_history"
|
||||
#endif
|
||||
|
||||
/* 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;
|
||||
|
||||
static char *psql_history;
|
||||
|
||||
static int history_lines_added;
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
#endif
|
||||
|
||||
static void finishInput(void);
|
||||
|
||||
|
||||
/*
|
||||
* gets_interactive()
|
||||
*
|
||||
* Gets a line of interactive input, using readline if desired.
|
||||
* The result is a malloc'd string.
|
||||
*
|
||||
* Caller *must* have set up sigint_interrupt_jmp before calling.
|
||||
*/
|
||||
char *
|
||||
gets_interactive(const char *prompt)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (useReadline)
|
||||
{
|
||||
char *result;
|
||||
|
||||
/*
|
||||
* Some versions of readline don't notice SIGWINCH signals that arrive
|
||||
* when not actively reading input. The simplest fix is to always
|
||||
* re-read the terminal size. This leaves a window for SIGWINCH to be
|
||||
* missed between here and where readline() enables libreadline's
|
||||
* signal handler, but that's probably short enough to be ignored.
|
||||
*/
|
||||
#ifdef HAVE_RL_RESET_SCREEN_SIZE
|
||||
rl_reset_screen_size();
|
||||
#endif
|
||||
|
||||
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
|
||||
sigint_interrupt_enabled = true;
|
||||
|
||||
/* On some platforms, readline is declared as readline(char *) */
|
||||
result = readline((char *) prompt);
|
||||
|
||||
/* Disable SIGINT again */
|
||||
sigint_interrupt_enabled = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
fputs(prompt, stdout);
|
||||
fflush(stdout);
|
||||
return gets_fromFile(stdin);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Append the line to the history buffer, making sure there is a trailing '\n'
|
||||
*/
|
||||
void
|
||||
pg_append_history(const char *s, PQExpBuffer history_buf)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (useHistory && s)
|
||||
{
|
||||
appendPQExpBufferStr(history_buf, s);
|
||||
if (!s[0] || s[strlen(s) - 1] != '\n')
|
||||
appendPQExpBufferChar(history_buf, '\n');
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Emit accumulated history entry to readline's history mechanism,
|
||||
* then reset the buffer to empty.
|
||||
*
|
||||
* Note: we write nothing if history_buf is empty, so extra calls to this
|
||||
* function don't hurt. There must have been at least one line added by
|
||||
* pg_append_history before we'll do anything.
|
||||
*/
|
||||
void
|
||||
pg_send_history(PQExpBuffer history_buf)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
static char *prev_hist = NULL;
|
||||
|
||||
char *s = history_buf->data;
|
||||
int i;
|
||||
|
||||
/* Trim any trailing \n's (OK to scribble on history_buf) */
|
||||
for (i = strlen(s) - 1; i >= 0 && s[i] == '\n'; i--)
|
||||
;
|
||||
s[i + 1] = '\0';
|
||||
|
||||
if (useHistory && s[0])
|
||||
{
|
||||
if (((pset.histcontrol & hctl_ignorespace) &&
|
||||
s[0] == ' ') ||
|
||||
((pset.histcontrol & hctl_ignoredups) &&
|
||||
prev_hist && strcmp(s, prev_hist) == 0))
|
||||
{
|
||||
/* Ignore this line as far as history is concerned */
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Save each previous line for ignoredups processing */
|
||||
if (prev_hist)
|
||||
free(prev_hist);
|
||||
prev_hist = pg_strdup(s);
|
||||
/* And send it to readline */
|
||||
add_history(s);
|
||||
/* Count lines added to history for use later */
|
||||
history_lines_added++;
|
||||
}
|
||||
}
|
||||
|
||||
resetPQExpBuffer(history_buf);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* gets_fromFile
|
||||
*
|
||||
* Gets a line of noninteractive input from a file (which could be stdin).
|
||||
* The result is a malloc'd string, or NULL on EOF or input error.
|
||||
*
|
||||
* Caller *must* have set up sigint_interrupt_jmp before calling.
|
||||
*
|
||||
* Note: we re-use a static PQExpBuffer for each call. This is to avoid
|
||||
* leaking memory if interrupted by SIGINT.
|
||||
*/
|
||||
char *
|
||||
gets_fromFile(FILE *source)
|
||||
{
|
||||
static PQExpBuffer buffer = NULL;
|
||||
|
||||
char line[1024];
|
||||
|
||||
if (buffer == NULL) /* first time through? */
|
||||
buffer = createPQExpBuffer();
|
||||
else
|
||||
resetPQExpBuffer(buffer);
|
||||
|
||||
for (;;)
|
||||
{
|
||||
char *result;
|
||||
|
||||
/* Enable SIGINT to longjmp to sigint_interrupt_jmp */
|
||||
sigint_interrupt_enabled = true;
|
||||
|
||||
/* Get some data */
|
||||
result = fgets(line, sizeof(line), source);
|
||||
|
||||
/* Disable SIGINT again */
|
||||
sigint_interrupt_enabled = false;
|
||||
|
||||
/* EOF or error? */
|
||||
if (result == NULL)
|
||||
{
|
||||
if (ferror(source))
|
||||
{
|
||||
psql_error("could not read from input file: %s\n",
|
||||
strerror(errno));
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
appendPQExpBufferStr(buffer, line);
|
||||
|
||||
if (PQExpBufferBroken(buffer))
|
||||
{
|
||||
psql_error("out of memory\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* EOL? */
|
||||
if (buffer->data[buffer->len - 1] == '\n')
|
||||
{
|
||||
buffer->data[buffer->len - 1] = '\0';
|
||||
return pg_strdup(buffer->data);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer->len > 0) /* EOF after reading some bufferload(s) */
|
||||
return pg_strdup(buffer->data);
|
||||
|
||||
/* EOF, so return null */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
#ifdef USE_READLINE
|
||||
|
||||
/*
|
||||
* Macros to iterate over each element of the history list in order
|
||||
*
|
||||
* You would think this would be simple enough, but in its inimitable fashion
|
||||
* libedit has managed to break it: in libreadline we must use next_history()
|
||||
* to go from oldest to newest, but in libedit we must use previous_history().
|
||||
* To detect what to do, we make a trial call of previous_history(): if it
|
||||
* fails, then either next_history() is what to use, or there's zero or one
|
||||
* history entry so that it doesn't matter which direction we go.
|
||||
*
|
||||
* In case that wasn't disgusting enough: the code below is not as obvious as
|
||||
* it might appear. In some libedit releases history_set_pos(0) fails until
|
||||
* at least one add_history() call has been done. This is not an issue for
|
||||
* printHistory() or encode_history(), which cannot be invoked before that has
|
||||
* happened. In decode_history(), that's not so, and what actually happens is
|
||||
* that we are sitting on the newest entry to start with, previous_history()
|
||||
* fails, and we iterate over all the entries using next_history(). So the
|
||||
* decode_history() loop iterates over the entries in the wrong order when
|
||||
* using such a libedit release, and if there were another attempt to use
|
||||
* BEGIN_ITERATE_HISTORY() before some add_history() call had happened, it
|
||||
* wouldn't work. Fortunately we don't care about either of those things.
|
||||
*
|
||||
* Usage pattern is:
|
||||
*
|
||||
* BEGIN_ITERATE_HISTORY(varname);
|
||||
* {
|
||||
* loop body referencing varname->line;
|
||||
* }
|
||||
* END_ITERATE_HISTORY();
|
||||
*/
|
||||
#define BEGIN_ITERATE_HISTORY(VARNAME) \
|
||||
do { \
|
||||
HIST_ENTRY *VARNAME; \
|
||||
bool use_prev_; \
|
||||
\
|
||||
history_set_pos(0); \
|
||||
use_prev_ = (previous_history() != NULL); \
|
||||
history_set_pos(0); \
|
||||
for (VARNAME = current_history(); VARNAME != NULL; \
|
||||
VARNAME = use_prev_ ? previous_history() : next_history()) \
|
||||
{ \
|
||||
(void) 0
|
||||
|
||||
#define END_ITERATE_HISTORY() \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/*
|
||||
* Convert newlines to NL_IN_HISTORY for safe saving in readline history file
|
||||
*/
|
||||
static void
|
||||
encode_history(void)
|
||||
{
|
||||
BEGIN_ITERATE_HISTORY(cur_hist);
|
||||
{
|
||||
char *cur_ptr;
|
||||
|
||||
/* some platforms declare HIST_ENTRY.line as const char * */
|
||||
for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
|
||||
{
|
||||
if (*cur_ptr == '\n')
|
||||
*cur_ptr = NL_IN_HISTORY;
|
||||
}
|
||||
}
|
||||
END_ITERATE_HISTORY();
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse the above encoding
|
||||
*/
|
||||
static void
|
||||
decode_history(void)
|
||||
{
|
||||
BEGIN_ITERATE_HISTORY(cur_hist);
|
||||
{
|
||||
char *cur_ptr;
|
||||
|
||||
/* some platforms declare HIST_ENTRY.line as const char * */
|
||||
for (cur_ptr = (char *) cur_hist->line; *cur_ptr; cur_ptr++)
|
||||
{
|
||||
if (*cur_ptr == NL_IN_HISTORY)
|
||||
*cur_ptr = '\n';
|
||||
}
|
||||
}
|
||||
END_ITERATE_HISTORY();
|
||||
}
|
||||
#endif /* USE_READLINE */
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
initializeInput(int flags)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (flags & 1)
|
||||
{
|
||||
const char *histfile;
|
||||
char home[MAXPGPATH];
|
||||
|
||||
useReadline = true;
|
||||
|
||||
/* these two things must be done in this order: */
|
||||
initialize_readline();
|
||||
rl_initialize();
|
||||
|
||||
useHistory = true;
|
||||
using_history();
|
||||
history_lines_added = 0;
|
||||
|
||||
histfile = GetVariable(pset.vars, "HISTFILE");
|
||||
|
||||
if (histfile == NULL)
|
||||
{
|
||||
char *envhist;
|
||||
|
||||
envhist = getenv("PSQL_HISTORY");
|
||||
if (envhist != NULL && strlen(envhist) > 0)
|
||||
histfile = envhist;
|
||||
}
|
||||
|
||||
if (histfile == NULL)
|
||||
{
|
||||
if (get_home_path(home))
|
||||
psql_history = psprintf("%s/%s", home, PSQLHISTORY);
|
||||
}
|
||||
else
|
||||
{
|
||||
psql_history = pg_strdup(histfile);
|
||||
expand_tilde(&psql_history);
|
||||
}
|
||||
|
||||
if (psql_history)
|
||||
{
|
||||
read_history(psql_history);
|
||||
decode_history();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
atexit(finishInput);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This function saves the readline history when psql exits.
|
||||
*
|
||||
* fname: pathname of history file. (Should really be "const char *",
|
||||
* but some ancient versions of readline omit the const-decoration.)
|
||||
*
|
||||
* max_lines: if >= 0, limit history file to that many entries.
|
||||
*/
|
||||
#ifdef USE_READLINE
|
||||
static bool
|
||||
saveHistory(char *fname, int max_lines)
|
||||
{
|
||||
int errnum;
|
||||
|
||||
/*
|
||||
* Suppressing the write attempt when HISTFILE is set to /dev/null may
|
||||
* look like a negligible optimization, but it's necessary on e.g. Darwin,
|
||||
* where write_history will fail because it tries to chmod the target
|
||||
* file.
|
||||
*/
|
||||
if (strcmp(fname, DEVNULL) != 0)
|
||||
{
|
||||
/*
|
||||
* Encode \n, since otherwise readline will reload multiline history
|
||||
* entries as separate lines. (libedit doesn't really need this, but
|
||||
* we do it anyway since it's too hard to tell which implementation we
|
||||
* are using.)
|
||||
*/
|
||||
encode_history();
|
||||
|
||||
/*
|
||||
* On newer versions of libreadline, truncate the history file as
|
||||
* needed and then append what we've added. This avoids overwriting
|
||||
* history from other concurrent sessions (although there are still
|
||||
* race conditions when two sessions exit at about the same time). If
|
||||
* we don't have those functions, fall back to write_history().
|
||||
*/
|
||||
#if defined(HAVE_HISTORY_TRUNCATE_FILE) && defined(HAVE_APPEND_HISTORY)
|
||||
{
|
||||
int nlines;
|
||||
int fd;
|
||||
|
||||
/* truncate previous entries if needed */
|
||||
if (max_lines >= 0)
|
||||
{
|
||||
nlines = Max(max_lines - history_lines_added, 0);
|
||||
(void) history_truncate_file(fname, nlines);
|
||||
}
|
||||
/* append_history fails if file doesn't already exist :-( */
|
||||
fd = open(fname, O_CREAT | O_WRONLY | PG_BINARY, 0600);
|
||||
if (fd >= 0)
|
||||
close(fd);
|
||||
/* append the appropriate number of lines */
|
||||
if (max_lines >= 0)
|
||||
nlines = Min(max_lines, history_lines_added);
|
||||
else
|
||||
nlines = history_lines_added;
|
||||
errnum = append_history(nlines, fname);
|
||||
if (errnum == 0)
|
||||
return true;
|
||||
}
|
||||
#else /* don't have append support */
|
||||
{
|
||||
/* truncate what we have ... */
|
||||
if (max_lines >= 0)
|
||||
stifle_history(max_lines);
|
||||
/* ... and overwrite file. Tough luck for concurrent sessions. */
|
||||
errnum = write_history(fname);
|
||||
if (errnum == 0)
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
psql_error("could not save history to file \"%s\": %s\n",
|
||||
fname, strerror(errnum));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Print history to the specified file, or to the console if fname is NULL
|
||||
* (psql \s command)
|
||||
*
|
||||
* We used to use saveHistory() for this purpose, but that doesn't permit
|
||||
* use of a pager; moreover libedit's implementation behaves incompatibly
|
||||
* (preferring to encode its output) and may fail outright when the target
|
||||
* file is specified as /dev/tty.
|
||||
*/
|
||||
bool
|
||||
printHistory(const char *fname, unsigned short int pager)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
FILE *output;
|
||||
bool is_pager;
|
||||
|
||||
if (!useHistory)
|
||||
return false;
|
||||
|
||||
if (fname == NULL)
|
||||
{
|
||||
/* use pager, if enabled, when printing to console */
|
||||
output = PageOutput(INT_MAX, pager ? &(pset.popt.topt) : NULL);
|
||||
is_pager = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
output = fopen(fname, "w");
|
||||
if (output == NULL)
|
||||
{
|
||||
psql_error("could not save history to file \"%s\": %s\n",
|
||||
fname, strerror(errno));
|
||||
return false;
|
||||
}
|
||||
is_pager = false;
|
||||
}
|
||||
|
||||
BEGIN_ITERATE_HISTORY(cur_hist);
|
||||
{
|
||||
fprintf(output, "%s\n", cur_hist->line);
|
||||
}
|
||||
END_ITERATE_HISTORY();
|
||||
|
||||
if (is_pager)
|
||||
ClosePager(output);
|
||||
else
|
||||
fclose(output);
|
||||
|
||||
return true;
|
||||
#else
|
||||
psql_error("history is not supported by this installation\n");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
finishInput(void)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (useHistory && psql_history)
|
||||
{
|
||||
int hist_size;
|
||||
|
||||
hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true);
|
||||
(void) saveHistory(psql_history, hist_size);
|
||||
free(psql_history);
|
||||
psql_history = NULL;
|
||||
}
|
||||
#endif
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/input.h
|
||||
*/
|
||||
#ifndef INPUT_H
|
||||
#define INPUT_H
|
||||
|
||||
/*
|
||||
* If some other file needs to have access to readline/history, include this
|
||||
* file and save yourself all this work.
|
||||
*
|
||||
* USE_READLINE is the definite pointers regarding existence or not.
|
||||
*/
|
||||
#ifdef HAVE_LIBREADLINE
|
||||
#define USE_READLINE 1
|
||||
|
||||
#if defined(HAVE_READLINE_READLINE_H)
|
||||
#include <readline/readline.h>
|
||||
#if defined(HAVE_READLINE_HISTORY_H)
|
||||
#include <readline/history.h>
|
||||
#endif
|
||||
#elif defined(HAVE_EDITLINE_READLINE_H)
|
||||
#include <editline/readline.h>
|
||||
#if defined(HAVE_EDITLINE_HISTORY_H)
|
||||
#include <editline/history.h>
|
||||
#endif
|
||||
#elif defined(HAVE_READLINE_H)
|
||||
#include <readline.h>
|
||||
#if defined(HAVE_HISTORY_H)
|
||||
#include <history.h>
|
||||
#endif
|
||||
#endif /* HAVE_READLINE_READLINE_H, etc */
|
||||
#endif /* HAVE_LIBREADLINE */
|
||||
|
||||
#include "pqexpbuffer.h"
|
||||
|
||||
|
||||
char *gets_interactive(const char *prompt);
|
||||
char *gets_fromFile(FILE *source);
|
||||
|
||||
void initializeInput(int flags);
|
||||
|
||||
bool printHistory(const char *fname, unsigned short int pager);
|
||||
|
||||
void pg_append_history(const char *s, PQExpBuffer history_buf);
|
||||
void pg_send_history(PQExpBuffer history_buf);
|
||||
|
||||
#endif /* INPUT_H */
|
|
@ -1,30 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* keywords.c
|
||||
* lexical token lookup for key words in PostgreSQL
|
||||
*
|
||||
*
|
||||
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/bin/pg_dump/keywords.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include "parser/keywords.h"
|
||||
|
||||
/*
|
||||
* We don't need the token number, so leave it out to avoid requiring other
|
||||
* backend headers.
|
||||
*/
|
||||
#define PG_KEYWORD(a,b,c) {a,0,c},
|
||||
|
||||
const ScanKeyword FEScanKeywords[] = {
|
||||
#include "parser/kwlist.h"
|
||||
};
|
||||
|
||||
const int NumFEScanKeywords = lengthof(FEScanKeywords);
|
|
@ -1,89 +0,0 @@
|
|||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* kwlookup.c
|
||||
* lexical token lookup for key words in PostgreSQL
|
||||
*
|
||||
* NB - this file is also used by ECPG and several frontend programs in
|
||||
* src/bin/ including pg_dump and psql
|
||||
*
|
||||
* Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* src/backend/parser/kwlookup.c
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/* use c.h so this can be built as either frontend or backend */
|
||||
#include "c.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "parser/keywords.h"
|
||||
|
||||
/*
|
||||
* ScanKeywordLookup - see if a given word is a keyword
|
||||
*
|
||||
* Returns a pointer to the ScanKeyword table entry, or NULL if no match.
|
||||
*
|
||||
* The match is done case-insensitively. Note that we deliberately use a
|
||||
* dumbed-down case conversion that will only translate 'A'-'Z' into 'a'-'z',
|
||||
* even if we are in a locale where tolower() would produce more or different
|
||||
* translations. This is to conform to the SQL99 spec, which says that
|
||||
* keywords are to be matched in this way even though non-keyword identifiers
|
||||
* receive a different case-normalization mapping.
|
||||
*/
|
||||
const ScanKeyword *
|
||||
ScanKeywordLookup(const char *text,
|
||||
const ScanKeyword *keywords,
|
||||
int num_keywords)
|
||||
{
|
||||
int len,
|
||||
i;
|
||||
char word[NAMEDATALEN];
|
||||
const ScanKeyword *low;
|
||||
const ScanKeyword *high;
|
||||
|
||||
len = strlen(text);
|
||||
/* We assume all keywords are shorter than NAMEDATALEN. */
|
||||
if (len >= NAMEDATALEN)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Apply an ASCII-only downcasing. We must not use tolower() since it may
|
||||
* produce the wrong translation in some locales (eg, Turkish).
|
||||
*/
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
char ch = text[i];
|
||||
|
||||
if (ch >= 'A' && ch <= 'Z')
|
||||
ch += 'a' - 'A';
|
||||
word[i] = ch;
|
||||
}
|
||||
word[len] = '\0';
|
||||
|
||||
/*
|
||||
* Now do a binary search using plain strcmp() comparison.
|
||||
*/
|
||||
low = keywords;
|
||||
high = keywords + (num_keywords - 1);
|
||||
while (low <= high)
|
||||
{
|
||||
const ScanKeyword *middle;
|
||||
int difference;
|
||||
|
||||
middle = low + (high - low) / 2;
|
||||
difference = strcmp(middle->name, word);
|
||||
if (difference == 0)
|
||||
return middle;
|
||||
else if (difference < 0)
|
||||
low = middle + 1;
|
||||
else
|
||||
high = middle - 1;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
|
@ -1,315 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/large_obj.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
#include "large_obj.h"
|
||||
|
||||
|
||||
#include "settings.h"
|
||||
#include "common.h"
|
||||
|
||||
static void print_lo_result(const char *fmt,...) pg_attribute_printf(1, 2);
|
||||
|
||||
static void
|
||||
print_lo_result(const char *fmt,...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
if (!pset.quiet)
|
||||
{
|
||||
if (pset.popt.topt.format == PRINT_HTML)
|
||||
fputs("<p>", pset.queryFout);
|
||||
|
||||
va_start(ap, fmt);
|
||||
vfprintf(pset.queryFout, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
if (pset.popt.topt.format == PRINT_HTML)
|
||||
fputs("</p>\n", pset.queryFout);
|
||||
else
|
||||
fputs("\n", pset.queryFout);
|
||||
}
|
||||
|
||||
if (pset.logfile)
|
||||
{
|
||||
va_start(ap, fmt);
|
||||
vfprintf(pset.logfile, fmt, ap);
|
||||
va_end(ap);
|
||||
fputs("\n", pset.logfile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Prepare to do a large-object operation. We *must* be inside a transaction
|
||||
* block for all these operations, so start one if needed.
|
||||
*
|
||||
* Returns TRUE if okay, FALSE if failed. *own_transaction is set to indicate
|
||||
* if we started our own transaction or not.
|
||||
*/
|
||||
static bool
|
||||
start_lo_xact(const char *operation, bool *own_transaction)
|
||||
{
|
||||
PGTransactionStatusType tstatus;
|
||||
PGresult *res;
|
||||
|
||||
*own_transaction = false;
|
||||
|
||||
if (!pset.db)
|
||||
{
|
||||
psql_error("%s: not connected to a database\n", operation);
|
||||
return false;
|
||||
}
|
||||
|
||||
tstatus = PQtransactionStatus(pset.db);
|
||||
|
||||
switch (tstatus)
|
||||
{
|
||||
case PQTRANS_IDLE:
|
||||
/* need to start our own xact */
|
||||
if (!(res = PSQLexec("BEGIN")))
|
||||
return false;
|
||||
PQclear(res);
|
||||
*own_transaction = true;
|
||||
break;
|
||||
case PQTRANS_INTRANS:
|
||||
/* use the existing xact */
|
||||
break;
|
||||
case PQTRANS_INERROR:
|
||||
psql_error("%s: current transaction is aborted\n", operation);
|
||||
return false;
|
||||
default:
|
||||
psql_error("%s: unknown transaction status\n", operation);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up after a successful LO operation
|
||||
*/
|
||||
static bool
|
||||
finish_lo_xact(const char *operation, bool own_transaction)
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
if (own_transaction && pset.autocommit)
|
||||
{
|
||||
/* close out our own xact */
|
||||
if (!(res = PSQLexec("COMMIT")))
|
||||
{
|
||||
res = PSQLexec("ROLLBACK");
|
||||
PQclear(res);
|
||||
return false;
|
||||
}
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Clean up after a failed LO operation
|
||||
*/
|
||||
static bool
|
||||
fail_lo_xact(const char *operation, bool own_transaction)
|
||||
{
|
||||
PGresult *res;
|
||||
|
||||
if (own_transaction && pset.autocommit)
|
||||
{
|
||||
/* close out our own xact */
|
||||
res = PSQLexec("ROLLBACK");
|
||||
PQclear(res);
|
||||
}
|
||||
|
||||
return false; /* always */
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* do_lo_export()
|
||||
*
|
||||
* Write a large object to a file
|
||||
*/
|
||||
bool
|
||||
do_lo_export(const char *loid_arg, const char *filename_arg)
|
||||
{
|
||||
int status;
|
||||
bool own_transaction;
|
||||
|
||||
if (!start_lo_xact("\\lo_export", &own_transaction))
|
||||
return false;
|
||||
|
||||
SetCancelConn();
|
||||
status = lo_export(pset.db, atooid(loid_arg), filename_arg);
|
||||
ResetCancelConn();
|
||||
|
||||
/* of course this status is documented nowhere :( */
|
||||
if (status != 1)
|
||||
{
|
||||
psql_error("%s", PQerrorMessage(pset.db));
|
||||
return fail_lo_xact("\\lo_export", own_transaction);
|
||||
}
|
||||
|
||||
if (!finish_lo_xact("\\lo_export", own_transaction))
|
||||
return false;
|
||||
|
||||
print_lo_result("lo_export");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* do_lo_import()
|
||||
*
|
||||
* Copy large object from file to database
|
||||
*/
|
||||
bool
|
||||
do_lo_import(const char *filename_arg, const char *comment_arg)
|
||||
{
|
||||
PGresult *res;
|
||||
Oid loid;
|
||||
char oidbuf[32];
|
||||
bool own_transaction;
|
||||
|
||||
if (!start_lo_xact("\\lo_import", &own_transaction))
|
||||
return false;
|
||||
|
||||
SetCancelConn();
|
||||
loid = lo_import(pset.db, filename_arg);
|
||||
ResetCancelConn();
|
||||
|
||||
if (loid == InvalidOid)
|
||||
{
|
||||
psql_error("%s", PQerrorMessage(pset.db));
|
||||
return fail_lo_xact("\\lo_import", own_transaction);
|
||||
}
|
||||
|
||||
/* insert description if given */
|
||||
if (comment_arg)
|
||||
{
|
||||
char *cmdbuf;
|
||||
char *bufptr;
|
||||
size_t slen = strlen(comment_arg);
|
||||
|
||||
cmdbuf = malloc(slen * 2 + 256);
|
||||
if (!cmdbuf)
|
||||
return fail_lo_xact("\\lo_import", own_transaction);
|
||||
sprintf(cmdbuf, "COMMENT ON LARGE OBJECT %u IS '", loid);
|
||||
bufptr = cmdbuf + strlen(cmdbuf);
|
||||
bufptr += PQescapeStringConn(pset.db, bufptr, comment_arg, slen, NULL);
|
||||
strcpy(bufptr, "'");
|
||||
|
||||
if (!(res = PSQLexec(cmdbuf)))
|
||||
{
|
||||
free(cmdbuf);
|
||||
return fail_lo_xact("\\lo_import", own_transaction);
|
||||
}
|
||||
|
||||
PQclear(res);
|
||||
free(cmdbuf);
|
||||
}
|
||||
|
||||
if (!finish_lo_xact("\\lo_import", own_transaction))
|
||||
return false;
|
||||
|
||||
print_lo_result("lo_import %u", loid);
|
||||
|
||||
sprintf(oidbuf, "%u", loid);
|
||||
SetVariable(pset.vars, "LASTOID", oidbuf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* do_lo_unlink()
|
||||
*
|
||||
* removes a large object out of the database
|
||||
*/
|
||||
bool
|
||||
do_lo_unlink(const char *loid_arg)
|
||||
{
|
||||
int status;
|
||||
Oid loid = atooid(loid_arg);
|
||||
bool own_transaction;
|
||||
|
||||
if (!start_lo_xact("\\lo_unlink", &own_transaction))
|
||||
return false;
|
||||
|
||||
SetCancelConn();
|
||||
status = lo_unlink(pset.db, loid);
|
||||
ResetCancelConn();
|
||||
|
||||
if (status == -1)
|
||||
{
|
||||
psql_error("%s", PQerrorMessage(pset.db));
|
||||
return fail_lo_xact("\\lo_unlink", own_transaction);
|
||||
}
|
||||
|
||||
if (!finish_lo_xact("\\lo_unlink", own_transaction))
|
||||
return false;
|
||||
|
||||
print_lo_result("lo_unlink %u", loid);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* do_lo_list()
|
||||
*
|
||||
* Show all large objects in database with comments
|
||||
*/
|
||||
bool
|
||||
do_lo_list(void)
|
||||
{
|
||||
PGresult *res;
|
||||
char buf[1024];
|
||||
printQueryOpt myopt = pset.popt;
|
||||
|
||||
if (pset.sversion >= 90000)
|
||||
{
|
||||
snprintf(buf, sizeof(buf),
|
||||
"SELECT oid as \"%s\",\n"
|
||||
" pg_catalog.pg_get_userbyid(lomowner) as \"%s\",\n"
|
||||
" pg_catalog.obj_description(oid, 'pg_largeobject') as \"%s\"\n"
|
||||
" FROM pg_catalog.pg_largeobject_metadata "
|
||||
" ORDER BY oid",
|
||||
gettext_noop("ID"),
|
||||
gettext_noop("Owner"),
|
||||
gettext_noop("Description"));
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(buf, sizeof(buf),
|
||||
"SELECT loid as \"%s\",\n"
|
||||
" pg_catalog.obj_description(loid, 'pg_largeobject') as \"%s\"\n"
|
||||
"FROM (SELECT DISTINCT loid FROM pg_catalog.pg_largeobject) x\n"
|
||||
"ORDER BY 1",
|
||||
gettext_noop("ID"),
|
||||
gettext_noop("Description"));
|
||||
}
|
||||
|
||||
res = PSQLexec(buf);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
myopt.topt.tuples_only = false;
|
||||
myopt.nullPrint = NULL;
|
||||
myopt.title = _("Large objects");
|
||||
myopt.translate_header = true;
|
||||
|
||||
printQuery(res, &myopt, pset.queryFout, false, pset.logfile);
|
||||
|
||||
PQclear(res);
|
||||
return true;
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/large_obj.h
|
||||
*/
|
||||
#ifndef LARGE_OBJ_H
|
||||
#define LARGE_OBJ_H
|
||||
|
||||
bool do_lo_export(const char *loid_arg, const char *filename_arg);
|
||||
bool do_lo_import(const char *filename_arg, const char *comment_arg);
|
||||
bool do_lo_unlink(const char *loid_arg);
|
||||
bool do_lo_list(void);
|
||||
|
||||
#endif /* LARGE_OBJ_H */
|
|
@ -1,462 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/mainloop.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
#include "mainloop.h"
|
||||
|
||||
|
||||
#include "command.h"
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include "mb/pg_wchar.h"
|
||||
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
int
|
||||
MainLoop(FILE *source)
|
||||
{
|
||||
PsqlScanState scan_state; /* lexer working state */
|
||||
volatile PQExpBuffer query_buf; /* buffer for query being accumulated */
|
||||
volatile PQExpBuffer previous_buf; /* if there isn't anything in the new
|
||||
* buffer yet, use this one for \e,
|
||||
* etc. */
|
||||
PQExpBuffer history_buf; /* earlier lines of a multi-line command, not
|
||||
* yet saved to readline history */
|
||||
char *line; /* current line of input */
|
||||
int added_nl_pos;
|
||||
bool success;
|
||||
bool line_saved_in_history;
|
||||
volatile int successResult = EXIT_SUCCESS;
|
||||
volatile backslashResult slashCmdStatus = PSQL_CMD_UNKNOWN;
|
||||
volatile promptStatus_t prompt_status = PROMPT_READY;
|
||||
volatile int count_eof = 0;
|
||||
volatile bool die_on_error = false;
|
||||
|
||||
/* Save the prior command source */
|
||||
FILE *prev_cmd_source;
|
||||
bool prev_cmd_interactive;
|
||||
uint64 prev_lineno;
|
||||
|
||||
/* Save old settings */
|
||||
prev_cmd_source = pset.cur_cmd_source;
|
||||
prev_cmd_interactive = pset.cur_cmd_interactive;
|
||||
prev_lineno = pset.lineno;
|
||||
|
||||
/* Establish new source */
|
||||
pset.cur_cmd_source = source;
|
||||
pset.cur_cmd_interactive = ((source == stdin) && !pset.notty);
|
||||
pset.lineno = 0;
|
||||
pset.stmt_lineno = 1;
|
||||
|
||||
/* Create working state */
|
||||
scan_state = psql_scan_create();
|
||||
|
||||
query_buf = createPQExpBuffer();
|
||||
previous_buf = createPQExpBuffer();
|
||||
history_buf = createPQExpBuffer();
|
||||
if (PQExpBufferBroken(query_buf) ||
|
||||
PQExpBufferBroken(previous_buf) ||
|
||||
PQExpBufferBroken(history_buf))
|
||||
{
|
||||
psql_error("out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/* main loop to get queries and execute them */
|
||||
while (successResult == EXIT_SUCCESS)
|
||||
{
|
||||
/*
|
||||
* Clean up after a previous Control-C
|
||||
*/
|
||||
if (cancel_pressed)
|
||||
{
|
||||
if (!pset.cur_cmd_interactive)
|
||||
{
|
||||
/*
|
||||
* You get here if you stopped a script with Ctrl-C.
|
||||
*/
|
||||
successResult = EXIT_USER;
|
||||
break;
|
||||
}
|
||||
|
||||
cancel_pressed = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Establish longjmp destination for exiting from wait-for-input. We
|
||||
* must re-do this each time through the loop for safety, since the
|
||||
* jmpbuf might get changed during command execution.
|
||||
*/
|
||||
if (sigsetjmp(sigint_interrupt_jmp, 1) != 0)
|
||||
{
|
||||
/* got here with longjmp */
|
||||
|
||||
/* reset parsing state */
|
||||
psql_scan_finish(scan_state);
|
||||
psql_scan_reset(scan_state);
|
||||
resetPQExpBuffer(query_buf);
|
||||
resetPQExpBuffer(history_buf);
|
||||
count_eof = 0;
|
||||
slashCmdStatus = PSQL_CMD_UNKNOWN;
|
||||
prompt_status = PROMPT_READY;
|
||||
pset.stmt_lineno = 1;
|
||||
cancel_pressed = false;
|
||||
|
||||
if (pset.cur_cmd_interactive)
|
||||
putc('\n', stdout);
|
||||
else
|
||||
{
|
||||
successResult = EXIT_USER;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
/*
|
||||
* get another line
|
||||
*/
|
||||
if (pset.cur_cmd_interactive)
|
||||
{
|
||||
/* May need to reset prompt, eg after \r command */
|
||||
if (query_buf->len == 0)
|
||||
prompt_status = PROMPT_READY;
|
||||
line = gets_interactive(get_prompt(prompt_status));
|
||||
}
|
||||
else
|
||||
{
|
||||
line = gets_fromFile(source);
|
||||
if (!line && ferror(source))
|
||||
successResult = EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* query_buf holds query already accumulated. line is the malloc'd
|
||||
* new line of input (note it must be freed before looping around!)
|
||||
*/
|
||||
|
||||
/* No more input. Time to quit, or \i done */
|
||||
if (line == NULL)
|
||||
{
|
||||
if (pset.cur_cmd_interactive)
|
||||
{
|
||||
/* This tries to mimic bash's IGNOREEOF feature. */
|
||||
count_eof++;
|
||||
|
||||
if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false))
|
||||
{
|
||||
if (!pset.quiet)
|
||||
printf(_("Use \"\\q\" to leave %s.\n"), pset.progname);
|
||||
continue;
|
||||
}
|
||||
|
||||
puts(pset.quiet ? "" : "\\q");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
count_eof = 0;
|
||||
|
||||
pset.lineno++;
|
||||
|
||||
/* ignore UTF-8 Unicode byte-order mark */
|
||||
if (pset.lineno == 1 && pset.encoding == PG_UTF8 && strncmp(line, "\xef\xbb\xbf", 3) == 0)
|
||||
memmove(line, line + 3, strlen(line + 3) + 1);
|
||||
|
||||
/* Detect attempts to run custom-format dumps as SQL scripts */
|
||||
if (pset.lineno == 1 && !pset.cur_cmd_interactive &&
|
||||
strncmp(line, "PGDMP", 5) == 0)
|
||||
{
|
||||
free(line);
|
||||
puts(_("The input is a PostgreSQL custom-format dump.\n"
|
||||
"Use the pg_restore command-line client to restore this dump to a database.\n"));
|
||||
fflush(stdout);
|
||||
successResult = EXIT_FAILURE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* no further processing of empty lines, unless within a literal */
|
||||
if (line[0] == '\0' && !psql_scan_in_quote(scan_state))
|
||||
{
|
||||
free(line);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* A request for help? Be friendly and give them some guidance */
|
||||
if (pset.cur_cmd_interactive && query_buf->len == 0 &&
|
||||
pg_strncasecmp(line, "help", 4) == 0 &&
|
||||
(line[4] == '\0' || line[4] == ';' || isspace((unsigned char) line[4])))
|
||||
{
|
||||
free(line);
|
||||
puts(_("You are using csql, the command-line interface to Citus."));
|
||||
printf(_("Type: \\copyright for distribution terms\n"
|
||||
" \\h for help with SQL commands\n"
|
||||
" \\? for help with csql commands\n"
|
||||
" \\g or terminate with semicolon to execute query\n"
|
||||
" \\q to quit\n"));
|
||||
|
||||
fflush(stdout);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* echo back if flag is set, unless interactive */
|
||||
if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive)
|
||||
{
|
||||
puts(line);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/* insert newlines into query buffer between source lines */
|
||||
if (query_buf->len > 0)
|
||||
{
|
||||
appendPQExpBufferChar(query_buf, '\n');
|
||||
added_nl_pos = query_buf->len;
|
||||
}
|
||||
else
|
||||
added_nl_pos = -1; /* flag we didn't add one */
|
||||
|
||||
/* Setting this will not have effect until next line. */
|
||||
die_on_error = pset.on_error_stop;
|
||||
|
||||
/*
|
||||
* Parse line, looking for command separators.
|
||||
*/
|
||||
psql_scan_setup(scan_state, line, strlen(line));
|
||||
success = true;
|
||||
line_saved_in_history = false;
|
||||
|
||||
while (success || !die_on_error)
|
||||
{
|
||||
PsqlScanResult scan_result;
|
||||
promptStatus_t prompt_tmp = prompt_status;
|
||||
size_t pos_in_query;
|
||||
char *tmp_line;
|
||||
|
||||
pos_in_query = query_buf->len;
|
||||
scan_result = psql_scan(scan_state, query_buf, &prompt_tmp);
|
||||
prompt_status = prompt_tmp;
|
||||
|
||||
if (PQExpBufferBroken(query_buf))
|
||||
{
|
||||
psql_error("out of memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Increase statement line number counter for each linebreak added
|
||||
* to the query buffer by the last psql_scan() call. There only
|
||||
* will be ones to add when navigating to a statement in
|
||||
* readline's history containing newlines.
|
||||
*/
|
||||
tmp_line = query_buf->data + pos_in_query;
|
||||
while (*tmp_line != '\0')
|
||||
{
|
||||
if (*(tmp_line++) == '\n')
|
||||
pset.stmt_lineno++;
|
||||
}
|
||||
|
||||
if (scan_result == PSCAN_EOL)
|
||||
pset.stmt_lineno++;
|
||||
|
||||
/*
|
||||
* Send command if semicolon found, or if end of line and we're in
|
||||
* single-line mode.
|
||||
*/
|
||||
if (scan_result == PSCAN_SEMICOLON ||
|
||||
(scan_result == PSCAN_EOL && pset.singleline))
|
||||
{
|
||||
/*
|
||||
* Save query in history. We use history_buf to accumulate
|
||||
* multi-line queries into a single history entry.
|
||||
*/
|
||||
if (pset.cur_cmd_interactive && !line_saved_in_history)
|
||||
{
|
||||
pg_append_history(line, history_buf);
|
||||
pg_send_history(history_buf);
|
||||
line_saved_in_history = true;
|
||||
}
|
||||
|
||||
/* execute query */
|
||||
success = SendQuery(query_buf->data);
|
||||
slashCmdStatus = success ? PSQL_CMD_SEND : PSQL_CMD_ERROR;
|
||||
pset.stmt_lineno = 1;
|
||||
|
||||
/* transfer query to previous_buf by pointer-swapping */
|
||||
{
|
||||
PQExpBuffer swap_buf = previous_buf;
|
||||
|
||||
previous_buf = query_buf;
|
||||
query_buf = swap_buf;
|
||||
}
|
||||
resetPQExpBuffer(query_buf);
|
||||
|
||||
added_nl_pos = -1;
|
||||
/* we need not do psql_scan_reset() here */
|
||||
}
|
||||
else if (scan_result == PSCAN_BACKSLASH)
|
||||
{
|
||||
/* handle backslash command */
|
||||
|
||||
/*
|
||||
* If we added a newline to query_buf, and nothing else has
|
||||
* been inserted in query_buf by the lexer, then strip off the
|
||||
* newline again. This avoids any change to query_buf when a
|
||||
* line contains only a backslash command. Also, in this
|
||||
* situation we force out any previous lines as a separate
|
||||
* history entry; we don't want SQL and backslash commands
|
||||
* intermixed in history if at all possible.
|
||||
*/
|
||||
if (query_buf->len == added_nl_pos)
|
||||
{
|
||||
query_buf->data[--query_buf->len] = '\0';
|
||||
pg_send_history(history_buf);
|
||||
}
|
||||
added_nl_pos = -1;
|
||||
|
||||
/* save backslash command in history */
|
||||
if (pset.cur_cmd_interactive && !line_saved_in_history)
|
||||
{
|
||||
pg_append_history(line, history_buf);
|
||||
pg_send_history(history_buf);
|
||||
line_saved_in_history = true;
|
||||
}
|
||||
|
||||
/* execute backslash command */
|
||||
slashCmdStatus = HandleSlashCmds(scan_state,
|
||||
query_buf->len > 0 ?
|
||||
query_buf : previous_buf);
|
||||
|
||||
success = slashCmdStatus != PSQL_CMD_ERROR;
|
||||
pset.stmt_lineno = 1;
|
||||
|
||||
if ((slashCmdStatus == PSQL_CMD_SEND || slashCmdStatus == PSQL_CMD_NEWEDIT) &&
|
||||
query_buf->len == 0)
|
||||
{
|
||||
/* copy previous buffer to current for handling */
|
||||
appendPQExpBufferStr(query_buf, previous_buf->data);
|
||||
}
|
||||
|
||||
if (slashCmdStatus == PSQL_CMD_SEND)
|
||||
{
|
||||
success = SendQuery(query_buf->data);
|
||||
|
||||
/* transfer query to previous_buf by pointer-swapping */
|
||||
{
|
||||
PQExpBuffer swap_buf = previous_buf;
|
||||
|
||||
previous_buf = query_buf;
|
||||
query_buf = swap_buf;
|
||||
}
|
||||
resetPQExpBuffer(query_buf);
|
||||
|
||||
/* flush any paren nesting info after forced send */
|
||||
psql_scan_reset(scan_state);
|
||||
}
|
||||
else if (slashCmdStatus == PSQL_CMD_NEWEDIT)
|
||||
{
|
||||
/* rescan query_buf as new input */
|
||||
psql_scan_finish(scan_state);
|
||||
free(line);
|
||||
line = pg_strdup(query_buf->data);
|
||||
resetPQExpBuffer(query_buf);
|
||||
/* reset parsing state since we are rescanning whole line */
|
||||
psql_scan_reset(scan_state);
|
||||
psql_scan_setup(scan_state, line, strlen(line));
|
||||
line_saved_in_history = false;
|
||||
prompt_status = PROMPT_READY;
|
||||
}
|
||||
else if (slashCmdStatus == PSQL_CMD_TERMINATE)
|
||||
break;
|
||||
}
|
||||
|
||||
/* fall out of loop if lexer reached EOL */
|
||||
if (scan_result == PSCAN_INCOMPLETE ||
|
||||
scan_result == PSCAN_EOL)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add line to pending history if we didn't execute anything yet */
|
||||
if (pset.cur_cmd_interactive && !line_saved_in_history)
|
||||
pg_append_history(line, history_buf);
|
||||
|
||||
psql_scan_finish(scan_state);
|
||||
free(line);
|
||||
|
||||
if (slashCmdStatus == PSQL_CMD_TERMINATE)
|
||||
{
|
||||
successResult = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!pset.cur_cmd_interactive)
|
||||
{
|
||||
if (!success && die_on_error)
|
||||
successResult = EXIT_USER;
|
||||
/* Have we lost the db connection? */
|
||||
else if (!pset.db)
|
||||
successResult = EXIT_BADCONN;
|
||||
}
|
||||
} /* while !endoffile/session */
|
||||
|
||||
/*
|
||||
* Process query at the end of file without a semicolon
|
||||
*/
|
||||
if (query_buf->len > 0 && !pset.cur_cmd_interactive &&
|
||||
successResult == EXIT_SUCCESS)
|
||||
{
|
||||
/* save query in history */
|
||||
if (pset.cur_cmd_interactive)
|
||||
pg_send_history(history_buf);
|
||||
|
||||
/* execute query */
|
||||
success = SendQuery(query_buf->data);
|
||||
|
||||
if (!success && die_on_error)
|
||||
successResult = EXIT_USER;
|
||||
else if (pset.db == NULL)
|
||||
successResult = EXIT_BADCONN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Let's just make real sure the SIGINT handler won't try to use
|
||||
* sigint_interrupt_jmp after we exit this routine. If there is an outer
|
||||
* MainLoop instance, it will reset sigint_interrupt_jmp to point to
|
||||
* itself at the top of its loop, before any further interactive input
|
||||
* happens.
|
||||
*/
|
||||
sigint_interrupt_enabled = false;
|
||||
|
||||
destroyPQExpBuffer(query_buf);
|
||||
destroyPQExpBuffer(previous_buf);
|
||||
destroyPQExpBuffer(history_buf);
|
||||
|
||||
psql_scan_destroy(scan_state);
|
||||
|
||||
pset.cur_cmd_source = prev_cmd_source;
|
||||
pset.cur_cmd_interactive = prev_cmd_interactive;
|
||||
pset.lineno = prev_lineno;
|
||||
|
||||
return successResult;
|
||||
} /* MainLoop() */
|
||||
|
||||
|
||||
/*
|
||||
* psqlscan.c is #include'd here instead of being compiled on its own.
|
||||
* This is because we need postgres_fe.h to be read before any system
|
||||
* include files, else things tend to break on platforms that have
|
||||
* multiple infrastructures for stdio.h and so on. flex is absolutely
|
||||
* uncooperative about that, so we can't compile psqlscan.c on its own.
|
||||
*/
|
||||
#include "psqlscan.c"
|
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/mainloop.h
|
||||
*/
|
||||
#ifndef MAINLOOP_H
|
||||
#define MAINLOOP_H
|
||||
|
||||
#include "postgres_fe.h"
|
||||
|
||||
int MainLoop(FILE *source);
|
||||
|
||||
#endif /* MAINLOOP_H */
|
|
@ -1,398 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/mbprint.c
|
||||
*
|
||||
* XXX this file does not really belong in psql/. Perhaps move to libpq?
|
||||
* It also seems that the mbvalidate function is redundant with existing
|
||||
* functionality.
|
||||
*/
|
||||
|
||||
#include "postgres_fe.h"
|
||||
#include "mbprint.h"
|
||||
#ifndef PGSCRIPTS
|
||||
#include "settings.h"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* To avoid version-skew problems, this file must not use declarations
|
||||
* from pg_wchar.h: the encoding IDs we are dealing with are determined
|
||||
* by the libpq.so we are linked with, and that might not match the
|
||||
* numbers we see at compile time. (If this file were inside libpq,
|
||||
* the problem would go away...)
|
||||
*
|
||||
* Hence, we have our own definition of pg_wchar, and we get the values
|
||||
* of any needed encoding IDs on-the-fly.
|
||||
*/
|
||||
|
||||
typedef unsigned int pg_wchar;
|
||||
|
||||
static int
|
||||
pg_get_utf8_id(void)
|
||||
{
|
||||
static int utf8_id = -1;
|
||||
|
||||
if (utf8_id < 0)
|
||||
utf8_id = pg_char_to_encoding("utf8");
|
||||
return utf8_id;
|
||||
}
|
||||
|
||||
#define PG_UTF8 pg_get_utf8_id()
|
||||
|
||||
|
||||
/*
|
||||
* Convert a UTF-8 character to a Unicode code point.
|
||||
* This is a one-character version of pg_utf2wchar_with_len.
|
||||
*
|
||||
* No error checks here, c must point to a long-enough string.
|
||||
*/
|
||||
static pg_wchar
|
||||
utf8_to_unicode(const unsigned char *c)
|
||||
{
|
||||
if ((*c & 0x80) == 0)
|
||||
return (pg_wchar) c[0];
|
||||
else if ((*c & 0xe0) == 0xc0)
|
||||
return (pg_wchar) (((c[0] & 0x1f) << 6) |
|
||||
(c[1] & 0x3f));
|
||||
else if ((*c & 0xf0) == 0xe0)
|
||||
return (pg_wchar) (((c[0] & 0x0f) << 12) |
|
||||
((c[1] & 0x3f) << 6) |
|
||||
(c[2] & 0x3f));
|
||||
else if ((*c & 0xf8) == 0xf0)
|
||||
return (pg_wchar) (((c[0] & 0x07) << 18) |
|
||||
((c[1] & 0x3f) << 12) |
|
||||
((c[2] & 0x3f) << 6) |
|
||||
(c[3] & 0x3f));
|
||||
else
|
||||
/* that is an invalid code on purpose */
|
||||
return 0xffffffff;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Unicode 3.1 compliant validation : for each category, it checks the
|
||||
* combination of each byte to make sure it maps to a valid range. It also
|
||||
* returns -1 for the following UCS values: ucs > 0x10ffff ucs & 0xfffe =
|
||||
* 0xfffe 0xfdd0 < ucs < 0xfdef ucs & 0xdb00 = 0xd800 (surrogates)
|
||||
*/
|
||||
static int
|
||||
utf_charcheck(const unsigned char *c)
|
||||
{
|
||||
if ((*c & 0x80) == 0)
|
||||
return 1;
|
||||
else if ((*c & 0xe0) == 0xc0)
|
||||
{
|
||||
/* two-byte char */
|
||||
if (((c[1] & 0xc0) == 0x80) && ((c[0] & 0x1f) > 0x01))
|
||||
return 2;
|
||||
return -1;
|
||||
}
|
||||
else if ((*c & 0xf0) == 0xe0)
|
||||
{
|
||||
/* three-byte char */
|
||||
if (((c[1] & 0xc0) == 0x80) &&
|
||||
(((c[0] & 0x0f) != 0x00) || ((c[1] & 0x20) == 0x20)) &&
|
||||
((c[2] & 0xc0) == 0x80))
|
||||
{
|
||||
int z = c[0] & 0x0f;
|
||||
int yx = ((c[1] & 0x3f) << 6) | (c[0] & 0x3f);
|
||||
int lx = yx & 0x7f;
|
||||
|
||||
/* check 0xfffe/0xffff, 0xfdd0..0xfedf range, surrogates */
|
||||
if (((z == 0x0f) &&
|
||||
(((yx & 0xffe) == 0xffe) ||
|
||||
(((yx & 0xf80) == 0xd80) && (lx >= 0x30) && (lx <= 0x4f)))) ||
|
||||
((z == 0x0d) && ((yx & 0xb00) == 0x800)))
|
||||
return -1;
|
||||
return 3;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
else if ((*c & 0xf8) == 0xf0)
|
||||
{
|
||||
int u = ((c[0] & 0x07) << 2) | ((c[1] & 0x30) >> 4);
|
||||
|
||||
/* four-byte char */
|
||||
if (((c[1] & 0xc0) == 0x80) &&
|
||||
(u > 0x00) && (u <= 0x10) &&
|
||||
((c[2] & 0xc0) == 0x80) && ((c[3] & 0xc0) == 0x80))
|
||||
{
|
||||
/* test for 0xzzzzfffe/0xzzzzfffff */
|
||||
if (((c[1] & 0x0f) == 0x0f) && ((c[2] & 0x3f) == 0x3f) &&
|
||||
((c[3] & 0x3e) == 0x3e))
|
||||
return -1;
|
||||
return 4;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
mb_utf_validate(unsigned char *pwcs)
|
||||
{
|
||||
unsigned char *p = pwcs;
|
||||
|
||||
while (*pwcs)
|
||||
{
|
||||
int len;
|
||||
|
||||
if ((len = utf_charcheck(pwcs)) > 0)
|
||||
{
|
||||
if (p != pwcs)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < len; i++)
|
||||
*p++ = *pwcs++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pwcs += len;
|
||||
p += len;
|
||||
}
|
||||
}
|
||||
else
|
||||
/* we skip the char */
|
||||
pwcs++;
|
||||
}
|
||||
if (p != pwcs)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
/*
|
||||
* public functions : wcswidth and mbvalidate
|
||||
*/
|
||||
|
||||
/*
|
||||
* pg_wcswidth is the dumb display-width function.
|
||||
* It assumes that everything will appear on one line.
|
||||
* OTOH it is easier to use than pg_wcssize if this applies to you.
|
||||
*/
|
||||
int
|
||||
pg_wcswidth(const char *pwcs, size_t len, int encoding)
|
||||
{
|
||||
int width = 0;
|
||||
|
||||
while (len > 0)
|
||||
{
|
||||
int chlen,
|
||||
chwidth;
|
||||
|
||||
chlen = PQmblen(pwcs, encoding);
|
||||
if (len < (size_t) chlen)
|
||||
break; /* Invalid string */
|
||||
|
||||
chwidth = PQdsplen(pwcs, encoding);
|
||||
if (chwidth > 0)
|
||||
width += chwidth;
|
||||
|
||||
pwcs += chlen;
|
||||
len -= chlen;
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_wcssize takes the given string in the given encoding and returns three
|
||||
* values:
|
||||
* result_width: Width in display characters of the longest line in string
|
||||
* result_height: Number of lines in display output
|
||||
* result_format_size: Number of bytes required to store formatted
|
||||
* representation of string
|
||||
*
|
||||
* This MUST be kept in sync with pg_wcsformat!
|
||||
*/
|
||||
void
|
||||
pg_wcssize(const unsigned char *pwcs, size_t len, int encoding,
|
||||
int *result_width, int *result_height, int *result_format_size)
|
||||
{
|
||||
int w,
|
||||
chlen = 0,
|
||||
linewidth = 0;
|
||||
int width = 0;
|
||||
int height = 1;
|
||||
int format_size = 0;
|
||||
|
||||
for (; *pwcs && len > 0; pwcs += chlen)
|
||||
{
|
||||
chlen = PQmblen((const char *) pwcs, encoding);
|
||||
if (len < (size_t) chlen)
|
||||
break;
|
||||
w = PQdsplen((const char *) pwcs, encoding);
|
||||
|
||||
if (chlen == 1) /* single-byte char */
|
||||
{
|
||||
if (*pwcs == '\n') /* Newline */
|
||||
{
|
||||
if (linewidth > width)
|
||||
width = linewidth;
|
||||
linewidth = 0;
|
||||
height += 1;
|
||||
format_size += 1; /* For NUL char */
|
||||
}
|
||||
else if (*pwcs == '\r') /* Linefeed */
|
||||
{
|
||||
linewidth += 2;
|
||||
format_size += 2;
|
||||
}
|
||||
else if (*pwcs == '\t') /* Tab */
|
||||
{
|
||||
do
|
||||
{
|
||||
linewidth++;
|
||||
format_size++;
|
||||
} while (linewidth % 8 != 0);
|
||||
}
|
||||
else if (w < 0) /* Other control char */
|
||||
{
|
||||
linewidth += 4;
|
||||
format_size += 4;
|
||||
}
|
||||
else /* Output it as-is */
|
||||
{
|
||||
linewidth += w;
|
||||
format_size += 1;
|
||||
}
|
||||
}
|
||||
else if (w < 0) /* Non-ascii control char */
|
||||
{
|
||||
linewidth += 6; /* \u0000 */
|
||||
format_size += 6;
|
||||
}
|
||||
else /* All other chars */
|
||||
{
|
||||
linewidth += w;
|
||||
format_size += chlen;
|
||||
}
|
||||
len -= chlen;
|
||||
}
|
||||
if (linewidth > width)
|
||||
width = linewidth;
|
||||
format_size += 1; /* For NUL char */
|
||||
|
||||
/* Set results */
|
||||
if (result_width)
|
||||
*result_width = width;
|
||||
if (result_height)
|
||||
*result_height = height;
|
||||
if (result_format_size)
|
||||
*result_format_size = format_size;
|
||||
}
|
||||
|
||||
/*
|
||||
* Format a string into one or more "struct lineptr" lines.
|
||||
* lines[i].ptr == NULL indicates the end of the array.
|
||||
*
|
||||
* This MUST be kept in sync with pg_wcssize!
|
||||
*/
|
||||
void
|
||||
pg_wcsformat(const unsigned char *pwcs, size_t len, int encoding,
|
||||
struct lineptr * lines, int count)
|
||||
{
|
||||
int w,
|
||||
chlen = 0;
|
||||
int linewidth = 0;
|
||||
unsigned char *ptr = lines->ptr; /* Pointer to data area */
|
||||
|
||||
for (; *pwcs && len > 0; pwcs += chlen)
|
||||
{
|
||||
chlen = PQmblen((const char *) pwcs, encoding);
|
||||
if (len < (size_t) chlen)
|
||||
break;
|
||||
w = PQdsplen((const char *) pwcs, encoding);
|
||||
|
||||
if (chlen == 1) /* single-byte char */
|
||||
{
|
||||
if (*pwcs == '\n') /* Newline */
|
||||
{
|
||||
*ptr++ = '\0';
|
||||
lines->width = linewidth;
|
||||
linewidth = 0;
|
||||
lines++;
|
||||
count--;
|
||||
if (count <= 0)
|
||||
exit(1); /* Screwup */
|
||||
|
||||
/* make next line point to remaining memory */
|
||||
lines->ptr = ptr;
|
||||
}
|
||||
else if (*pwcs == '\r') /* Linefeed */
|
||||
{
|
||||
strcpy((char *) ptr, "\\r");
|
||||
linewidth += 2;
|
||||
ptr += 2;
|
||||
}
|
||||
else if (*pwcs == '\t') /* Tab */
|
||||
{
|
||||
do
|
||||
{
|
||||
*ptr++ = ' ';
|
||||
linewidth++;
|
||||
} while (linewidth % 8 != 0);
|
||||
}
|
||||
else if (w < 0) /* Other control char */
|
||||
{
|
||||
sprintf((char *) ptr, "\\x%02X", *pwcs);
|
||||
linewidth += 4;
|
||||
ptr += 4;
|
||||
}
|
||||
else /* Output it as-is */
|
||||
{
|
||||
linewidth += w;
|
||||
*ptr++ = *pwcs;
|
||||
}
|
||||
}
|
||||
else if (w < 0) /* Non-ascii control char */
|
||||
{
|
||||
if (encoding == PG_UTF8)
|
||||
sprintf((char *) ptr, "\\u%04X", utf8_to_unicode(pwcs));
|
||||
else
|
||||
{
|
||||
/*
|
||||
* This case cannot happen in the current code because only
|
||||
* UTF-8 signals multibyte control characters. But we may need
|
||||
* to support it at some stage
|
||||
*/
|
||||
sprintf((char *) ptr, "\\u????");
|
||||
}
|
||||
ptr += 6;
|
||||
linewidth += 6;
|
||||
}
|
||||
else /* All other chars */
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < chlen; i++)
|
||||
*ptr++ = pwcs[i];
|
||||
linewidth += w;
|
||||
}
|
||||
len -= chlen;
|
||||
}
|
||||
lines->width = linewidth;
|
||||
*ptr++ = '\0'; /* Terminate formatted string */
|
||||
|
||||
if (count <= 0)
|
||||
exit(1); /* Screwup */
|
||||
|
||||
(lines + 1)->ptr = NULL; /* terminate line array */
|
||||
}
|
||||
|
||||
unsigned char *
|
||||
mbvalidate(unsigned char *pwcs, int encoding)
|
||||
{
|
||||
if (encoding == PG_UTF8)
|
||||
mb_utf_validate(pwcs);
|
||||
else
|
||||
{
|
||||
/*
|
||||
* other encodings needing validation should add their own routines
|
||||
* here
|
||||
*/
|
||||
}
|
||||
|
||||
return pwcs;
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
/* src/bin/psql/mbprint.h */
|
||||
#ifndef MBPRINT_H
|
||||
#define MBPRINT_H
|
||||
|
||||
|
||||
struct lineptr
|
||||
{
|
||||
unsigned char *ptr;
|
||||
int width;
|
||||
};
|
||||
|
||||
extern unsigned char *mbvalidate(unsigned char *pwcs, int encoding);
|
||||
extern int pg_wcswidth(const char *pwcs, size_t len, int encoding);
|
||||
extern void pg_wcsformat(const unsigned char *pwcs, size_t len, int encoding, struct lineptr * lines, int count);
|
||||
extern void pg_wcssize(const unsigned char *pwcs, size_t len, int encoding,
|
||||
int *width, int *height, int *format_size);
|
||||
|
||||
#endif /* MBPRINT_H */
|
3495
src/bin/csql/print.c
3495
src/bin/csql/print.c
File diff suppressed because it is too large
Load Diff
|
@ -1,206 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/print.h
|
||||
*/
|
||||
#ifndef PRINT_H
|
||||
#define PRINT_H
|
||||
|
||||
#include "libpq-fe.h"
|
||||
|
||||
|
||||
enum printFormat
|
||||
{
|
||||
PRINT_NOTHING = 0, /* to make sure someone initializes this */
|
||||
PRINT_UNALIGNED,
|
||||
PRINT_ALIGNED,
|
||||
PRINT_WRAPPED,
|
||||
PRINT_HTML,
|
||||
PRINT_ASCIIDOC,
|
||||
PRINT_LATEX,
|
||||
PRINT_LATEX_LONGTABLE,
|
||||
PRINT_TROFF_MS
|
||||
/* add your favourite output format here ... */
|
||||
};
|
||||
|
||||
typedef struct printTextLineFormat
|
||||
{
|
||||
/* Line drawing characters to be used in various contexts */
|
||||
const char *hrule; /* horizontal line character */
|
||||
const char *leftvrule; /* left vertical line (+horizontal) */
|
||||
const char *midvrule; /* intra-column vertical line (+horizontal) */
|
||||
const char *rightvrule; /* right vertical line (+horizontal) */
|
||||
} printTextLineFormat;
|
||||
|
||||
typedef enum printTextRule
|
||||
{
|
||||
/* Additional context for selecting line drawing characters */
|
||||
PRINT_RULE_TOP, /* top horizontal line */
|
||||
PRINT_RULE_MIDDLE, /* intra-data horizontal line */
|
||||
PRINT_RULE_BOTTOM, /* bottom horizontal line */
|
||||
PRINT_RULE_DATA /* data line (hrule is unused here) */
|
||||
} printTextRule;
|
||||
|
||||
typedef enum printTextLineWrap
|
||||
{
|
||||
/* Line wrapping conditions */
|
||||
PRINT_LINE_WRAP_NONE, /* No wrapping */
|
||||
PRINT_LINE_WRAP_WRAP, /* Wraparound due to overlength line */
|
||||
PRINT_LINE_WRAP_NEWLINE /* Newline in data */
|
||||
} printTextLineWrap;
|
||||
|
||||
typedef struct printTextFormat
|
||||
{
|
||||
/* A complete line style */
|
||||
const char *name; /* for display purposes */
|
||||
printTextLineFormat lrule[4]; /* indexed by enum printTextRule */
|
||||
const char *midvrule_nl; /* vertical line for continue after newline */
|
||||
const char *midvrule_wrap; /* vertical line for wrapped data */
|
||||
const char *midvrule_blank; /* vertical line for blank data */
|
||||
const char *header_nl_left; /* left mark after newline */
|
||||
const char *header_nl_right; /* right mark for newline */
|
||||
const char *nl_left; /* left mark after newline */
|
||||
const char *nl_right; /* right mark for newline */
|
||||
const char *wrap_left; /* left mark after wrapped data */
|
||||
const char *wrap_right; /* right mark for wrapped data */
|
||||
bool wrap_right_border; /* use right-hand border for wrap
|
||||
* marks when border=0? */
|
||||
} printTextFormat;
|
||||
|
||||
typedef enum unicode_linestyle
|
||||
{
|
||||
UNICODE_LINESTYLE_SINGLE = 0,
|
||||
UNICODE_LINESTYLE_DOUBLE
|
||||
} unicode_linestyle;
|
||||
|
||||
struct separator
|
||||
{
|
||||
char *separator;
|
||||
bool separator_zero;
|
||||
};
|
||||
|
||||
typedef struct printTableOpt
|
||||
{
|
||||
enum printFormat format; /* see enum above */
|
||||
unsigned short int expanded;/* expanded/vertical output (if supported by
|
||||
* output format); 0=no, 1=yes, 2=auto */
|
||||
unsigned short int border; /* Print a border around the table. 0=none,
|
||||
* 1=dividing lines, 2=full */
|
||||
unsigned short int pager; /* use pager for output (if to stdout and
|
||||
* stdout is a tty) 0=off 1=on 2=always */
|
||||
int pager_min_lines;/* don't use pager unless there are at least
|
||||
* this many lines */
|
||||
bool tuples_only; /* don't output headers, row counts, etc. */
|
||||
bool start_table; /* print start decoration, eg <table> */
|
||||
bool stop_table; /* print stop decoration, eg </table> */
|
||||
bool default_footer; /* allow "(xx rows)" default footer */
|
||||
unsigned long prior_records; /* start offset for record counters */
|
||||
const printTextFormat *line_style; /* line style (NULL for default) */
|
||||
struct separator fieldSep; /* field separator for unaligned text mode */
|
||||
struct separator recordSep; /* record separator for unaligned text mode */
|
||||
bool numericLocale; /* locale-aware numeric units separator and
|
||||
* decimal marker */
|
||||
char *tableAttr; /* attributes for HTML <table ...> */
|
||||
int encoding; /* character encoding */
|
||||
int env_columns; /* $COLUMNS on psql start, 0 is unset */
|
||||
int columns; /* target width for wrapped format */
|
||||
unicode_linestyle unicode_border_linestyle;
|
||||
unicode_linestyle unicode_column_linestyle;
|
||||
unicode_linestyle unicode_header_linestyle;
|
||||
} printTableOpt;
|
||||
|
||||
/*
|
||||
* Table footers are implemented as a singly-linked list.
|
||||
*
|
||||
* This is so that you don't need to know the number of footers in order to
|
||||
* initialise the printTableContent struct, which is very convenient when
|
||||
* preparing complex footers (as in describeOneTableDetails).
|
||||
*/
|
||||
typedef struct printTableFooter
|
||||
{
|
||||
char *data;
|
||||
struct printTableFooter *next;
|
||||
} printTableFooter;
|
||||
|
||||
/*
|
||||
* The table content struct holds all the information which will be displayed
|
||||
* by printTable().
|
||||
*/
|
||||
typedef struct printTableContent
|
||||
{
|
||||
const printTableOpt *opt;
|
||||
const char *title; /* May be NULL */
|
||||
int ncolumns; /* Specified in Init() */
|
||||
int nrows; /* Specified in Init() */
|
||||
const char **headers; /* NULL-terminated array of header strings */
|
||||
const char **header; /* Pointer to the last added header */
|
||||
const char **cells; /* NULL-terminated array of cell content
|
||||
* strings */
|
||||
const char **cell; /* Pointer to the last added cell */
|
||||
long cellsadded; /* Number of cells added this far */
|
||||
bool *cellmustfree; /* true for cells that need to be free()d */
|
||||
printTableFooter *footers; /* Pointer to the first footer */
|
||||
printTableFooter *footer; /* Pointer to the last added footer */
|
||||
char *aligns; /* Array of alignment specifiers; 'l' or 'r',
|
||||
* one per column */
|
||||
char *align; /* Pointer to the last added alignment */
|
||||
} printTableContent;
|
||||
|
||||
typedef struct printQueryOpt
|
||||
{
|
||||
printTableOpt topt; /* the options above */
|
||||
char *nullPrint; /* how to print null entities */
|
||||
bool quote; /* quote all values as much as possible */
|
||||
char *title; /* override title */
|
||||
char **footers; /* override footer (default is "(xx rows)") */
|
||||
bool translate_header; /* do gettext on column headers */
|
||||
const bool *translate_columns; /* translate_columns[i-1] => do
|
||||
* gettext on col i */
|
||||
int n_translate_columns; /* length of translate_columns[] */
|
||||
} printQueryOpt;
|
||||
|
||||
|
||||
extern const printTextFormat pg_asciiformat;
|
||||
extern const printTextFormat pg_asciiformat_old;
|
||||
extern const printTextFormat pg_utf8format;
|
||||
|
||||
|
||||
extern void disable_sigpipe_trap(void);
|
||||
extern void restore_sigpipe_trap(void);
|
||||
extern void set_sigpipe_trap_state(bool ignore);
|
||||
|
||||
extern FILE *PageOutput(int lines, const printTableOpt *topt);
|
||||
extern void ClosePager(FILE *pagerpipe);
|
||||
|
||||
extern void html_escaped_print(const char *in, FILE *fout);
|
||||
|
||||
extern void printTableInit(printTableContent *const content,
|
||||
const printTableOpt *opt, const char *title,
|
||||
const int ncolumns, const int nrows);
|
||||
extern void printTableAddHeader(printTableContent *const content,
|
||||
char *header, const bool translate, const char align);
|
||||
extern void printTableAddCell(printTableContent *const content,
|
||||
char *cell, const bool translate, const bool mustfree);
|
||||
extern void printTableAddFooter(printTableContent *const content,
|
||||
const char *footer);
|
||||
extern void printTableSetFooter(printTableContent *const content,
|
||||
const char *footer);
|
||||
extern void printTableCleanup(printTableContent *const content);
|
||||
extern void printTable(const printTableContent *cont,
|
||||
FILE *fout, bool is_pager, FILE *flog);
|
||||
extern void printQuery(const PGresult *result, const printQueryOpt *opt,
|
||||
FILE *fout, bool is_pager, FILE *flog);
|
||||
|
||||
extern void setDecimalLocale(void);
|
||||
extern const printTextFormat *get_line_style(const printTableOpt *opt);
|
||||
extern void refresh_utf8format(const printTableOpt *opt);
|
||||
|
||||
#ifndef __CYGWIN__
|
||||
#define DEFAULT_PAGER "more"
|
||||
#else
|
||||
#define DEFAULT_PAGER "less"
|
||||
#endif
|
||||
|
||||
#endif /* PRINT_H */
|
|
@ -1,325 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/prompt.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#ifdef WIN32
|
||||
#include <io.h>
|
||||
#include <win32.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
#include <unistd.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
#include "common.h"
|
||||
#include "input.h"
|
||||
#include "prompt.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
/*--------------------------
|
||||
* get_prompt
|
||||
*
|
||||
* Returns a statically allocated prompt made by interpolating certain
|
||||
* tcsh style escape sequences into pset.vars "PROMPT1|2|3".
|
||||
* (might not be completely multibyte safe)
|
||||
*
|
||||
* Defined interpolations are:
|
||||
* %M - database server "hostname.domainname", "[local]" for AF_UNIX
|
||||
* sockets, "[local:/dir/name]" if not default
|
||||
* %m - like %M, but hostname only (before first dot), or always "[local]"
|
||||
* %> - database server port number
|
||||
* %n - database user name
|
||||
* %/ - current database
|
||||
* %~ - like %/ but "~" when database name equals user name
|
||||
* %# - "#" if superuser, ">" otherwise
|
||||
* %R - in prompt1 normally =, or ^ if single line mode,
|
||||
* or a ! if session is not connected to a database;
|
||||
* in prompt2 -, *, ', or ";
|
||||
* in prompt3 nothing
|
||||
* %x - transaction status: empty, *, !, ? (unknown or no connection)
|
||||
* %l - The line number inside the current statement, starting from 1.
|
||||
* %? - the error code of the last query (not yet implemented)
|
||||
* %% - a percent sign
|
||||
*
|
||||
* %[0-9] - the character with the given decimal code
|
||||
* %0[0-7] - the character with the given octal code
|
||||
* %0x[0-9A-Fa-f] - the character with the given hexadecimal code
|
||||
*
|
||||
* %`command` - The result of executing command in /bin/sh with trailing
|
||||
* newline stripped.
|
||||
* %:name: - The value of the psql variable 'name'
|
||||
* (those will not be rescanned for more escape sequences!)
|
||||
*
|
||||
* %[ ... %] - tell readline that the contained text is invisible
|
||||
*
|
||||
* If the application-wide prompts become NULL somehow, the returned string
|
||||
* will be empty (not NULL!).
|
||||
*--------------------------
|
||||
*/
|
||||
|
||||
char *
|
||||
get_prompt(promptStatus_t status)
|
||||
{
|
||||
#define MAX_PROMPT_SIZE 256
|
||||
static char destination[MAX_PROMPT_SIZE + 1];
|
||||
char buf[MAX_PROMPT_SIZE + 1];
|
||||
bool esc = false;
|
||||
const char *p;
|
||||
const char *prompt_string = "? ";
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case PROMPT_READY:
|
||||
prompt_string = pset.prompt1;
|
||||
break;
|
||||
|
||||
case PROMPT_CONTINUE:
|
||||
case PROMPT_SINGLEQUOTE:
|
||||
case PROMPT_DOUBLEQUOTE:
|
||||
case PROMPT_DOLLARQUOTE:
|
||||
case PROMPT_COMMENT:
|
||||
case PROMPT_PAREN:
|
||||
prompt_string = pset.prompt2;
|
||||
break;
|
||||
|
||||
case PROMPT_COPY:
|
||||
prompt_string = pset.prompt3;
|
||||
break;
|
||||
}
|
||||
|
||||
destination[0] = '\0';
|
||||
|
||||
for (p = prompt_string;
|
||||
*p && strlen(destination) < sizeof(destination) - 1;
|
||||
p++)
|
||||
{
|
||||
memset(buf, 0, sizeof(buf));
|
||||
if (esc)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
/* Current database */
|
||||
case '/':
|
||||
if (pset.db)
|
||||
strlcpy(buf, PQdb(pset.db), sizeof(buf));
|
||||
break;
|
||||
case '~':
|
||||
if (pset.db)
|
||||
{
|
||||
const char *var;
|
||||
|
||||
if (strcmp(PQdb(pset.db), PQuser(pset.db)) == 0 ||
|
||||
((var = getenv("PGDATABASE")) && strcmp(var, PQdb(pset.db)) == 0))
|
||||
strlcpy(buf, "~", sizeof(buf));
|
||||
else
|
||||
strlcpy(buf, PQdb(pset.db), sizeof(buf));
|
||||
}
|
||||
break;
|
||||
|
||||
/* DB server hostname (long/short) */
|
||||
case 'M':
|
||||
case 'm':
|
||||
if (pset.db)
|
||||
{
|
||||
const char *host = PQhost(pset.db);
|
||||
|
||||
/* INET socket */
|
||||
if (host && host[0] && !is_absolute_path(host))
|
||||
{
|
||||
strlcpy(buf, host, sizeof(buf));
|
||||
if (*p == 'm')
|
||||
buf[strcspn(buf, ".")] = '\0';
|
||||
}
|
||||
#ifdef HAVE_UNIX_SOCKETS
|
||||
/* UNIX socket */
|
||||
else
|
||||
{
|
||||
if (!host
|
||||
|| strcmp(host, DEFAULT_PGSOCKET_DIR) == 0
|
||||
|| *p == 'm')
|
||||
strlcpy(buf, "[local]", sizeof(buf));
|
||||
else
|
||||
snprintf(buf, sizeof(buf), "[local:%s]", host);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
/* DB server port number */
|
||||
case '>':
|
||||
if (pset.db && PQport(pset.db))
|
||||
strlcpy(buf, PQport(pset.db), sizeof(buf));
|
||||
break;
|
||||
/* DB server user name */
|
||||
case 'n':
|
||||
if (pset.db)
|
||||
strlcpy(buf, session_username(), sizeof(buf));
|
||||
break;
|
||||
|
||||
case '0':
|
||||
case '1':
|
||||
case '2':
|
||||
case '3':
|
||||
case '4':
|
||||
case '5':
|
||||
case '6':
|
||||
case '7':
|
||||
*buf = (char) strtol(p, (char **) &p, 8);
|
||||
--p;
|
||||
break;
|
||||
case 'R':
|
||||
switch (status)
|
||||
{
|
||||
case PROMPT_READY:
|
||||
if (!pset.db)
|
||||
buf[0] = '!';
|
||||
else if (!pset.singleline)
|
||||
buf[0] = '=';
|
||||
else
|
||||
buf[0] = '^';
|
||||
break;
|
||||
case PROMPT_CONTINUE:
|
||||
buf[0] = '-';
|
||||
break;
|
||||
case PROMPT_SINGLEQUOTE:
|
||||
buf[0] = '\'';
|
||||
break;
|
||||
case PROMPT_DOUBLEQUOTE:
|
||||
buf[0] = '"';
|
||||
break;
|
||||
case PROMPT_DOLLARQUOTE:
|
||||
buf[0] = '$';
|
||||
break;
|
||||
case PROMPT_COMMENT:
|
||||
buf[0] = '*';
|
||||
break;
|
||||
case PROMPT_PAREN:
|
||||
buf[0] = '(';
|
||||
break;
|
||||
default:
|
||||
buf[0] = '\0';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
if (!pset.db)
|
||||
buf[0] = '?';
|
||||
else
|
||||
switch (PQtransactionStatus(pset.db))
|
||||
{
|
||||
case PQTRANS_IDLE:
|
||||
buf[0] = '\0';
|
||||
break;
|
||||
case PQTRANS_ACTIVE:
|
||||
case PQTRANS_INTRANS:
|
||||
buf[0] = '*';
|
||||
break;
|
||||
case PQTRANS_INERROR:
|
||||
buf[0] = '!';
|
||||
break;
|
||||
default:
|
||||
buf[0] = '?';
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
snprintf(buf, sizeof(buf), UINT64_FORMAT, pset.stmt_lineno);
|
||||
break;
|
||||
|
||||
case '?':
|
||||
/* not here yet */
|
||||
break;
|
||||
|
||||
case '#':
|
||||
if (is_superuser())
|
||||
buf[0] = '#';
|
||||
else
|
||||
buf[0] = '>';
|
||||
break;
|
||||
|
||||
/* execute command */
|
||||
case '`':
|
||||
{
|
||||
FILE *fd;
|
||||
char *file = pg_strdup(p + 1);
|
||||
int cmdend;
|
||||
|
||||
cmdend = strcspn(file, "`");
|
||||
file[cmdend] = '\0';
|
||||
fd = popen(file, "r");
|
||||
if (fd)
|
||||
{
|
||||
if (fgets(buf, sizeof(buf), fd) == NULL)
|
||||
buf[0] = '\0';
|
||||
pclose(fd);
|
||||
}
|
||||
if (strlen(buf) > 0 && buf[strlen(buf) - 1] == '\n')
|
||||
buf[strlen(buf) - 1] = '\0';
|
||||
free(file);
|
||||
p += cmdend + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
/* interpolate variable */
|
||||
case ':':
|
||||
{
|
||||
char *name;
|
||||
const char *val;
|
||||
int nameend;
|
||||
|
||||
name = pg_strdup(p + 1);
|
||||
nameend = strcspn(name, ":");
|
||||
name[nameend] = '\0';
|
||||
val = GetVariable(pset.vars, name);
|
||||
if (val)
|
||||
strlcpy(buf, val, sizeof(buf));
|
||||
free(name);
|
||||
p += nameend + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case '[':
|
||||
case ']':
|
||||
#if defined(USE_READLINE) && defined(RL_PROMPT_START_IGNORE)
|
||||
|
||||
/*
|
||||
* readline >=4.0 undocumented feature: non-printing
|
||||
* characters in prompt strings must be marked as such, in
|
||||
* order to properly display the line during editing.
|
||||
*/
|
||||
buf[0] = (*p == '[') ? RL_PROMPT_START_IGNORE : RL_PROMPT_END_IGNORE;
|
||||
buf[1] = '\0';
|
||||
#endif /* USE_READLINE */
|
||||
break;
|
||||
|
||||
default:
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
break;
|
||||
|
||||
}
|
||||
esc = false;
|
||||
}
|
||||
else if (*p == '%')
|
||||
esc = true;
|
||||
else
|
||||
{
|
||||
buf[0] = *p;
|
||||
buf[1] = '\0';
|
||||
esc = false;
|
||||
}
|
||||
|
||||
if (!esc)
|
||||
strlcat(destination, buf, sizeof(destination));
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/prompt.h
|
||||
*/
|
||||
#ifndef PROMPT_H
|
||||
#define PROMPT_H
|
||||
|
||||
typedef enum _promptStatus
|
||||
{
|
||||
PROMPT_READY,
|
||||
PROMPT_CONTINUE,
|
||||
PROMPT_COMMENT,
|
||||
PROMPT_SINGLEQUOTE,
|
||||
PROMPT_DOUBLEQUOTE,
|
||||
PROMPT_DOLLARQUOTE,
|
||||
PROMPT_PAREN,
|
||||
PROMPT_COPY
|
||||
} promptStatus_t;
|
||||
|
||||
char *get_prompt(promptStatus_t status);
|
||||
|
||||
#endif /* PROMPT_H */
|
|
@ -1,8 +0,0 @@
|
|||
--
|
||||
-- system-wide psql configuration file
|
||||
--
|
||||
-- This file is read before the .psqlrc file in the user's home directory.
|
||||
--
|
||||
-- Copy this to your installation's sysconf directory and rename it psqlrc.
|
||||
-- The sysconf directory can be identified via "pg_config --sysconfdir".
|
||||
--
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/psqlscan.h
|
||||
*/
|
||||
#ifndef PSQLSCAN_H
|
||||
#define PSQLSCAN_H
|
||||
|
||||
#include "pqexpbuffer.h"
|
||||
|
||||
#include "prompt.h"
|
||||
|
||||
|
||||
/* Abstract type for lexer's internal state */
|
||||
typedef struct PsqlScanStateData *PsqlScanState;
|
||||
|
||||
/* Termination states for psql_scan() */
|
||||
typedef enum
|
||||
{
|
||||
PSCAN_SEMICOLON, /* found command-ending semicolon */
|
||||
PSCAN_BACKSLASH, /* found backslash command */
|
||||
PSCAN_INCOMPLETE, /* end of line, SQL statement incomplete */
|
||||
PSCAN_EOL /* end of line, SQL possibly complete */
|
||||
} PsqlScanResult;
|
||||
|
||||
/* Different ways for scan_slash_option to handle parameter words */
|
||||
enum slash_option_type
|
||||
{
|
||||
OT_NORMAL, /* normal case */
|
||||
OT_SQLID, /* treat as SQL identifier */
|
||||
OT_SQLIDHACK, /* SQL identifier, but don't downcase */
|
||||
OT_FILEPIPE, /* it's a filename or pipe */
|
||||
OT_WHOLE_LINE, /* just snarf the rest of the line */
|
||||
OT_NO_EVAL /* no expansion of backticks or variables */
|
||||
};
|
||||
|
||||
|
||||
extern PsqlScanState psql_scan_create(void);
|
||||
extern void psql_scan_destroy(PsqlScanState state);
|
||||
|
||||
extern void psql_scan_setup(PsqlScanState state,
|
||||
const char *line, int line_len);
|
||||
extern void psql_scan_finish(PsqlScanState state);
|
||||
|
||||
extern PsqlScanResult psql_scan(PsqlScanState state,
|
||||
PQExpBuffer query_buf,
|
||||
promptStatus_t *prompt);
|
||||
|
||||
extern void psql_scan_reset(PsqlScanState state);
|
||||
|
||||
extern bool psql_scan_in_quote(PsqlScanState state);
|
||||
|
||||
extern char *psql_scan_slash_command(PsqlScanState state);
|
||||
|
||||
extern char *psql_scan_slash_option(PsqlScanState state,
|
||||
enum slash_option_type type,
|
||||
char *quote,
|
||||
bool semicolon);
|
||||
|
||||
extern void psql_scan_slash_command_end(PsqlScanState state);
|
||||
|
||||
#endif /* PSQLSCAN_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,149 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/settings.h
|
||||
*/
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
|
||||
|
||||
#include "variables.h"
|
||||
#include "print.h"
|
||||
|
||||
#define DEFAULT_FIELD_SEP "|"
|
||||
#define DEFAULT_RECORD_SEP "\n"
|
||||
|
||||
#if defined(WIN32) || defined(__CYGWIN__)
|
||||
#define DEFAULT_EDITOR "notepad.exe"
|
||||
/* no DEFAULT_EDITOR_LINENUMBER_ARG for Notepad */
|
||||
#else
|
||||
#define DEFAULT_EDITOR "vi"
|
||||
#define DEFAULT_EDITOR_LINENUMBER_ARG "+"
|
||||
#endif
|
||||
|
||||
#define DEFAULT_PROMPT1 "%/%R%# "
|
||||
#define DEFAULT_PROMPT2 "%/%R%# "
|
||||
#define DEFAULT_PROMPT3 ">> "
|
||||
|
||||
/*
|
||||
* Note: these enums should generally be chosen so that zero corresponds
|
||||
* to the default behavior.
|
||||
*/
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PSQL_ECHO_NONE,
|
||||
PSQL_ECHO_QUERIES,
|
||||
PSQL_ECHO_ERRORS,
|
||||
PSQL_ECHO_ALL
|
||||
} PSQL_ECHO;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PSQL_ECHO_HIDDEN_OFF,
|
||||
PSQL_ECHO_HIDDEN_ON,
|
||||
PSQL_ECHO_HIDDEN_NOEXEC
|
||||
} PSQL_ECHO_HIDDEN;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PSQL_ERROR_ROLLBACK_OFF,
|
||||
PSQL_ERROR_ROLLBACK_INTERACTIVE,
|
||||
PSQL_ERROR_ROLLBACK_ON
|
||||
} PSQL_ERROR_ROLLBACK;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
PSQL_COMP_CASE_PRESERVE_UPPER,
|
||||
PSQL_COMP_CASE_PRESERVE_LOWER,
|
||||
PSQL_COMP_CASE_UPPER,
|
||||
PSQL_COMP_CASE_LOWER
|
||||
} PSQL_COMP_CASE;
|
||||
|
||||
typedef enum
|
||||
{
|
||||
hctl_none = 0,
|
||||
hctl_ignorespace = 1,
|
||||
hctl_ignoredups = 2,
|
||||
hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups
|
||||
} HistControl;
|
||||
|
||||
enum trivalue
|
||||
{
|
||||
TRI_DEFAULT,
|
||||
TRI_NO,
|
||||
TRI_YES
|
||||
};
|
||||
|
||||
typedef struct _psqlSettings
|
||||
{
|
||||
PGconn *db; /* connection to backend */
|
||||
int encoding; /* client_encoding */
|
||||
FILE *queryFout; /* where to send the query results */
|
||||
bool queryFoutPipe; /* queryFout is from a popen() */
|
||||
|
||||
FILE *copyStream; /* Stream to read/write for \copy command */
|
||||
|
||||
printQueryOpt popt;
|
||||
|
||||
char *gfname; /* one-shot file output argument for \g */
|
||||
char *gset_prefix; /* one-shot prefix argument for \gset */
|
||||
|
||||
bool notty; /* stdin or stdout is not a tty (as determined
|
||||
* on startup) */
|
||||
enum trivalue getPassword; /* prompt the user for a username and password */
|
||||
FILE *cur_cmd_source; /* describe the status of the current main
|
||||
* loop */
|
||||
bool cur_cmd_interactive;
|
||||
int sversion; /* backend server version */
|
||||
const char *progname; /* in case you renamed psql */
|
||||
char *inputfile; /* file being currently processed, if any */
|
||||
uint64 lineno; /* also for error reporting */
|
||||
uint64 stmt_lineno; /* line number inside the current statement */
|
||||
|
||||
bool timing; /* enable timing of all queries */
|
||||
|
||||
FILE *logfile; /* session log file handle */
|
||||
|
||||
VariableSpace vars; /* "shell variable" repository */
|
||||
|
||||
/*
|
||||
* The remaining fields are set by assign hooks associated with entries in
|
||||
* "vars". They should not be set directly except by those hook
|
||||
* functions.
|
||||
*/
|
||||
bool autocommit;
|
||||
bool on_error_stop;
|
||||
bool quiet;
|
||||
bool singleline;
|
||||
bool singlestep;
|
||||
int fetch_count;
|
||||
PSQL_ECHO echo;
|
||||
PSQL_ECHO_HIDDEN echo_hidden;
|
||||
PSQL_ERROR_ROLLBACK on_error_rollback;
|
||||
PSQL_COMP_CASE comp_case;
|
||||
HistControl histcontrol;
|
||||
const char *prompt1;
|
||||
const char *prompt2;
|
||||
const char *prompt3;
|
||||
PGVerbosity verbosity; /* current error verbosity level */
|
||||
} PsqlSettings;
|
||||
|
||||
extern PsqlSettings pset;
|
||||
|
||||
|
||||
#ifndef EXIT_SUCCESS
|
||||
#define EXIT_SUCCESS 0
|
||||
#endif
|
||||
|
||||
#ifndef EXIT_FAILURE
|
||||
#define EXIT_FAILURE 1
|
||||
#endif
|
||||
|
||||
#define EXIT_BADCONN 2
|
||||
|
||||
#define EXIT_USER 3
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
1787
src/bin/csql/stage.c
1787
src/bin/csql/stage.c
File diff suppressed because it is too large
Load Diff
|
@ -1,131 +0,0 @@
|
|||
/*
|
||||
* csql - the Citus interactive terminal
|
||||
* stage.h
|
||||
* Declarations for the csql meta-command \stage. These declarations define a
|
||||
* protocol for the client to communicate to the master and worker nodes.
|
||||
*
|
||||
* Copyright (c) 2012-2016, Citus Data, Inc.
|
||||
*
|
||||
* $Id$
|
||||
*/
|
||||
|
||||
#ifndef STAGE_H
|
||||
#define STAGE_H
|
||||
|
||||
#include "postgres_fe.h"
|
||||
|
||||
|
||||
/* Server returned values never equal to zero. */
|
||||
#define INVALID_UINT64 0
|
||||
#define MAX_SHARD_UPLOADS 256
|
||||
#define SHARD_NAME_SEPARATOR '_'
|
||||
|
||||
/* Connection parameters set to enable connect timeouts in seconds. */
|
||||
#define CONN_INFO_TEMPLATE "dbname=%s connect_timeout=%u"
|
||||
#define CLIENT_CONNECT_TIMEOUT 20
|
||||
|
||||
/* Transaction related commands used in talking to the master and primary. */
|
||||
#define BEGIN_COMMAND "BEGIN"
|
||||
#define COMMIT_COMMAND "COMMIT"
|
||||
#define ROLLBACK_COMMAND "ROLLBACK"
|
||||
|
||||
/* Names of remote function calls to execute on the master. */
|
||||
#define MASTER_GET_TABLE_METADATA "SELECT * FROM master_get_table_metadata($1::text)"
|
||||
#define MASTER_GET_TABLE_DDL_EVENTS "SELECT * FROM master_get_table_ddl_events($1::text)"
|
||||
#define MASTER_GET_NEW_SHARDID "SELECT * FROM master_get_new_shardid()"
|
||||
#define MASTER_GET_LOCAL_FIRST_CANDIDATE_NODES \
|
||||
"SELECT * FROM master_get_local_first_candidate_nodes()"
|
||||
#define MASTER_GET_ROUND_ROBIN_CANDIDATE_NODES \
|
||||
"SELECT * FROM master_get_round_robin_candidate_nodes($1::int8)"
|
||||
|
||||
#define MASTER_INSERT_SHARD_ROW \
|
||||
"SELECT master_stage_shard_row(\
|
||||
$1::oid, $2::int8, $3::\"char\", $4::text, $5::text)"
|
||||
#define MASTER_INSERT_PLACEMENT_ROW \
|
||||
"SELECT master_stage_shard_placement_row(\
|
||||
$1::int8, $2::int4, $3::int8, $4::text, $5::int4)"
|
||||
|
||||
/* Column names used to identify response fields as returned from the master. */
|
||||
#define LOGICAL_RELID_FIELD "logical_relid"
|
||||
#define PART_STORAGE_TYPE_FIELD "part_storage_type"
|
||||
#define PART_METHOD_FIELD "part_method"
|
||||
#define PART_KEY_FIELD "part_key"
|
||||
#define PART_REPLICA_COUNT_FIELD "part_replica_count"
|
||||
#define PART_MAX_SIZE_FIELD "part_max_size"
|
||||
#define PART_PLACEMENT_POLICY_FIELD "part_placement_policy"
|
||||
#define NODE_NAME_FIELD "node_name"
|
||||
#define NODE_PORT_FIELD "node_port"
|
||||
|
||||
/* the tablename in the overloaded COPY statement is the to-be-transferred file */
|
||||
#define TRANSMIT_REGULAR_COMMAND "COPY \"%s\" FROM STDIN WITH (format 'transmit')"
|
||||
#define SHARD_MIN_MAX_COMMAND "SELECT min(%s), max(%s) FROM %s"
|
||||
#define SHARD_TABLE_SIZE_COMMAND "SELECT pg_table_size('%s')"
|
||||
#define SET_FOREIGN_TABLE_FILENAME "ALTER FOREIGN TABLE %s OPTIONS (SET filename '%s')"
|
||||
#define GET_COLUMNAR_TABLE_FILENAME_OPTION \
|
||||
"SELECT * FROM (SELECT (pg_options_to_table(ftoptions)).* FROM pg_foreign_table " \
|
||||
"WHERE ftrelid = %u) AS Q WHERE option_name = 'filename';"
|
||||
#define APPLY_SHARD_DDL_COMMAND \
|
||||
"SELECT * FROM worker_apply_shard_ddl_command ($1::int8, $2::text)"
|
||||
#define REMOTE_FILE_SIZE_COMMAND "SELECT size FROM pg_stat_file('%s')"
|
||||
#define SHARD_COLUMNAR_TABLE_SIZE_COMMAND "SELECT cstore_table_size('%s')"
|
||||
|
||||
/* Types that define table storage type and shard state. */
|
||||
#define STORAGE_TYPE_TABLE 't'
|
||||
#define STORAGE_TYPE_FOREIGN 'f'
|
||||
#define STORAGE_TYPE_COLUMNAR 'c'
|
||||
#define FILE_FINALIZED "1"
|
||||
|
||||
/* Shard placement policy types. */
|
||||
#define SHARD_PLACEMENT_LOCAL_NODE_FIRST 1
|
||||
#define SHARD_PLACEMENT_ROUND_ROBIN 2
|
||||
|
||||
/* Directory to put foreign table files on secondary nodes. */
|
||||
#define FOREIGN_CACHED_DIR "pg_foreign_file/cached"
|
||||
|
||||
|
||||
/*
|
||||
* TableMetadata keeps table related metadata to which the user requested to
|
||||
* stage data. These metadata are retrieved from the master as read-only; and
|
||||
* are cached and reused as new shards are created.
|
||||
*/
|
||||
typedef struct TableMetadata
|
||||
{
|
||||
uint32 logicalRelid; /* table's relationId on the master */
|
||||
char tableStorageType; /* relay file, foreign table, or table */
|
||||
char partitionMethod; /* table's partition method */
|
||||
char *partitionKey; /* partition key expression */
|
||||
uint32 shardReplicaCount; /* shard replication factor */
|
||||
uint64 shardMaxSize; /* create new shard when shard reaches max size */
|
||||
uint32 shardPlacementPolicy; /* policy to use when choosing nodes to place shards */
|
||||
|
||||
char **ddlEventList; /* DDL statements used for creating new shard */
|
||||
uint32 ddlEventCount; /* DDL statement count; statement list size */
|
||||
} TableMetadata;
|
||||
|
||||
|
||||
/*
|
||||
* ShardMetadata keeps metadata related to one shard for which we are currently
|
||||
* staging data. Some parts of these metadata are retrieved from the master as
|
||||
* read-only (shardId, node names and port numbers); and other parts are updated
|
||||
* by the client as we stage data to clients (shard size and stage statuses).
|
||||
*/
|
||||
typedef struct ShardMetadata
|
||||
{
|
||||
uint64 shardId; /* global shardId; created on the master node */
|
||||
|
||||
char **nodeNameList; /* candidate node name list for shard uploading */
|
||||
uint32 *nodePortList; /* candidate node port list for shard uploading */
|
||||
uint32 nodeCount; /* candidate node count; node list size */
|
||||
bool *nodeStageList; /* shard uploaded to corresponding candidate node? */
|
||||
|
||||
char *shardMinValue; /* partition key's minimum value in shard */
|
||||
char *shardMaxValue; /* partition key's maximum value in shard */
|
||||
uint64 shardSize; /* shard size; updated during staging */
|
||||
} ShardMetadata;
|
||||
|
||||
|
||||
/* Function declaration for staging data to remote nodes */
|
||||
bool DoStageData(const char *stageCommand);
|
||||
|
||||
|
||||
#endif /* STAGE_H */
|
|
@ -1,896 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/startup.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#else /* WIN32 */
|
||||
#include <io.h>
|
||||
#include <win32.h>
|
||||
#endif /* WIN32 */
|
||||
|
||||
#include "getopt_long.h"
|
||||
|
||||
#include <locale.h>
|
||||
|
||||
#include "command.h"
|
||||
#include "common.h"
|
||||
#include "describe.h"
|
||||
#include "help.h"
|
||||
#include "input.h"
|
||||
#include "mainloop.h"
|
||||
#include "print.h"
|
||||
#include "settings.h"
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Global psql options
|
||||
*/
|
||||
PsqlSettings pset;
|
||||
|
||||
#ifndef WIN32
|
||||
#define SYSPSQLRC "psqlrc"
|
||||
#define PSQLRC ".psqlrc"
|
||||
#else
|
||||
#define SYSPSQLRC "psqlrc"
|
||||
#define PSQLRC "psqlrc.conf"
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Structures to pass information between the option parsing routine
|
||||
* and the main function
|
||||
*/
|
||||
enum _actions
|
||||
{
|
||||
ACT_NOTHING = 0,
|
||||
ACT_SINGLE_SLASH,
|
||||
ACT_LIST_DB,
|
||||
ACT_SINGLE_QUERY,
|
||||
ACT_FILE
|
||||
};
|
||||
|
||||
struct adhoc_opts
|
||||
{
|
||||
char *dbname;
|
||||
char *host;
|
||||
char *port;
|
||||
char *username;
|
||||
char *logfilename;
|
||||
enum _actions action;
|
||||
char *action_string;
|
||||
bool no_readline;
|
||||
bool no_psqlrc;
|
||||
bool single_txn;
|
||||
};
|
||||
|
||||
static void parse_psql_options(int argc, char *argv[],
|
||||
struct adhoc_opts * options);
|
||||
static void process_psqlrc(char *argv0);
|
||||
static void process_psqlrc_file(char *filename);
|
||||
static void showVersion(void);
|
||||
static void EstablishVariableSpace(void);
|
||||
|
||||
#define NOPAGER 0
|
||||
|
||||
/*
|
||||
*
|
||||
* main
|
||||
*
|
||||
*/
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
struct adhoc_opts options;
|
||||
int successResult;
|
||||
char *password = NULL;
|
||||
char *password_prompt = NULL;
|
||||
bool new_pass;
|
||||
|
||||
set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("psql"));
|
||||
|
||||
if (argc > 1)
|
||||
{
|
||||
if ((strcmp(argv[1], "-?") == 0) || (argc == 2 && (strcmp(argv[1], "--help") == 0)))
|
||||
{
|
||||
usage(NOPAGER);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
|
||||
{
|
||||
showVersion();
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
setvbuf(stderr, NULL, _IONBF, 0);
|
||||
#endif
|
||||
|
||||
pset.progname = get_progname(argv[0]);
|
||||
|
||||
pset.db = NULL;
|
||||
setDecimalLocale();
|
||||
pset.encoding = PQenv2encoding();
|
||||
pset.queryFout = stdout;
|
||||
pset.queryFoutPipe = false;
|
||||
pset.copyStream = NULL;
|
||||
pset.cur_cmd_source = stdin;
|
||||
pset.cur_cmd_interactive = false;
|
||||
|
||||
/* We rely on unmentioned fields of pset.popt to start out 0/false/NULL */
|
||||
pset.popt.topt.format = PRINT_ALIGNED;
|
||||
pset.popt.topt.border = 1;
|
||||
pset.popt.topt.pager = 1;
|
||||
pset.popt.topt.pager_min_lines = 0;
|
||||
pset.popt.topt.start_table = true;
|
||||
pset.popt.topt.stop_table = true;
|
||||
pset.popt.topt.default_footer = true;
|
||||
|
||||
pset.popt.topt.unicode_border_linestyle = UNICODE_LINESTYLE_SINGLE;
|
||||
pset.popt.topt.unicode_column_linestyle = UNICODE_LINESTYLE_SINGLE;
|
||||
pset.popt.topt.unicode_header_linestyle = UNICODE_LINESTYLE_SINGLE;
|
||||
|
||||
refresh_utf8format(&(pset.popt.topt));
|
||||
|
||||
/* We must get COLUMNS here before readline() sets it */
|
||||
pset.popt.topt.env_columns = getenv("COLUMNS") ? atoi(getenv("COLUMNS")) : 0;
|
||||
|
||||
pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout)));
|
||||
|
||||
pset.getPassword = TRI_DEFAULT;
|
||||
|
||||
EstablishVariableSpace();
|
||||
|
||||
SetVariable(pset.vars, "VERSION", PG_VERSION_STR);
|
||||
|
||||
/* Default values for variables */
|
||||
SetVariableBool(pset.vars, "AUTOCOMMIT");
|
||||
SetVariable(pset.vars, "VERBOSITY", "default");
|
||||
SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1);
|
||||
SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
|
||||
SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
|
||||
|
||||
parse_psql_options(argc, argv, &options);
|
||||
|
||||
/*
|
||||
* If no action was specified and we're in non-interactive mode, treat it
|
||||
* as if the user had specified "-f -". This lets single-transaction mode
|
||||
* work in this case.
|
||||
*/
|
||||
if (options.action == ACT_NOTHING && pset.notty)
|
||||
{
|
||||
options.action = ACT_FILE;
|
||||
options.action_string = NULL;
|
||||
}
|
||||
|
||||
/* Bail out if -1 was specified but will be ignored. */
|
||||
if (options.single_txn && options.action != ACT_FILE && options.action == ACT_NOTHING)
|
||||
{
|
||||
fprintf(stderr, _("%s: -1 can only be used in non-interactive mode\n"), pset.progname);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (!pset.popt.topt.fieldSep.separator &&
|
||||
!pset.popt.topt.fieldSep.separator_zero)
|
||||
{
|
||||
pset.popt.topt.fieldSep.separator = pg_strdup(DEFAULT_FIELD_SEP);
|
||||
pset.popt.topt.fieldSep.separator_zero = false;
|
||||
}
|
||||
if (!pset.popt.topt.recordSep.separator &&
|
||||
!pset.popt.topt.recordSep.separator_zero)
|
||||
{
|
||||
pset.popt.topt.recordSep.separator = pg_strdup(DEFAULT_RECORD_SEP);
|
||||
pset.popt.topt.recordSep.separator_zero = false;
|
||||
}
|
||||
|
||||
if (options.username == NULL)
|
||||
password_prompt = pg_strdup(_("Password: "));
|
||||
else
|
||||
password_prompt = psprintf(_("Password for user %s: "),
|
||||
options.username);
|
||||
|
||||
if (pset.getPassword == TRI_YES)
|
||||
password = simple_prompt(password_prompt, 100, false);
|
||||
|
||||
/* loop until we have a password if requested by backend */
|
||||
do
|
||||
{
|
||||
#define PARAMS_ARRAY_SIZE 8
|
||||
const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords));
|
||||
const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values));
|
||||
|
||||
keywords[0] = "host";
|
||||
values[0] = options.host;
|
||||
keywords[1] = "port";
|
||||
values[1] = options.port;
|
||||
keywords[2] = "user";
|
||||
values[2] = options.username;
|
||||
keywords[3] = "password";
|
||||
values[3] = password;
|
||||
keywords[4] = "dbname";
|
||||
values[4] = (options.action == ACT_LIST_DB &&
|
||||
options.dbname == NULL) ?
|
||||
"postgres" : options.dbname;
|
||||
keywords[5] = "fallback_application_name";
|
||||
values[5] = pset.progname;
|
||||
keywords[6] = "client_encoding";
|
||||
values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto";
|
||||
keywords[7] = NULL;
|
||||
values[7] = NULL;
|
||||
|
||||
new_pass = false;
|
||||
pset.db = PQconnectdbParams(keywords, values, true);
|
||||
free(keywords);
|
||||
free(values);
|
||||
|
||||
if (PQstatus(pset.db) == CONNECTION_BAD &&
|
||||
PQconnectionNeedsPassword(pset.db) &&
|
||||
password == NULL &&
|
||||
pset.getPassword != TRI_NO)
|
||||
{
|
||||
PQfinish(pset.db);
|
||||
password = simple_prompt(password_prompt, 100, false);
|
||||
new_pass = true;
|
||||
}
|
||||
} while (new_pass);
|
||||
|
||||
free(password);
|
||||
free(password_prompt);
|
||||
|
||||
if (PQstatus(pset.db) == CONNECTION_BAD)
|
||||
{
|
||||
fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db));
|
||||
PQfinish(pset.db);
|
||||
exit(EXIT_BADCONN);
|
||||
}
|
||||
|
||||
setup_cancel_handler();
|
||||
|
||||
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
|
||||
|
||||
SyncVariables();
|
||||
|
||||
if (options.action == ACT_LIST_DB)
|
||||
{
|
||||
int success;
|
||||
|
||||
if (!options.no_psqlrc)
|
||||
process_psqlrc(argv[0]);
|
||||
|
||||
success = listAllDbs(NULL, false);
|
||||
PQfinish(pset.db);
|
||||
exit(success ? EXIT_SUCCESS : EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (options.logfilename)
|
||||
{
|
||||
pset.logfile = fopen(options.logfilename, "a");
|
||||
if (!pset.logfile)
|
||||
{
|
||||
fprintf(stderr, _("%s: could not open log file \"%s\": %s\n"),
|
||||
pset.progname, options.logfilename, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Now find something to do
|
||||
*/
|
||||
|
||||
/*
|
||||
* process file given by -f
|
||||
*/
|
||||
if (options.action == ACT_FILE)
|
||||
{
|
||||
if (!options.no_psqlrc)
|
||||
process_psqlrc(argv[0]);
|
||||
|
||||
successResult = process_file(options.action_string, options.single_txn, false);
|
||||
}
|
||||
|
||||
/*
|
||||
* process slash command if one was given to -c
|
||||
*/
|
||||
else if (options.action == ACT_SINGLE_SLASH)
|
||||
{
|
||||
PsqlScanState scan_state;
|
||||
|
||||
if (pset.echo == PSQL_ECHO_ALL)
|
||||
puts(options.action_string);
|
||||
|
||||
scan_state = psql_scan_create();
|
||||
psql_scan_setup(scan_state,
|
||||
options.action_string,
|
||||
strlen(options.action_string));
|
||||
|
||||
successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR
|
||||
? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
|
||||
psql_scan_destroy(scan_state);
|
||||
}
|
||||
|
||||
/*
|
||||
* If the query given to -c was a normal one, send it
|
||||
*/
|
||||
else if (options.action == ACT_SINGLE_QUERY)
|
||||
{
|
||||
if (pset.echo == PSQL_ECHO_ALL)
|
||||
puts(options.action_string);
|
||||
|
||||
successResult = SendQuery(options.action_string)
|
||||
? EXIT_SUCCESS : EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/*
|
||||
* or otherwise enter interactive main loop
|
||||
*/
|
||||
else
|
||||
{
|
||||
if (!options.no_psqlrc)
|
||||
process_psqlrc(argv[0]);
|
||||
|
||||
connection_warnings(true);
|
||||
if (!pset.quiet)
|
||||
printf(_("Type \"help\" for help.\n\n"));
|
||||
initializeInput(options.no_readline ? 0 : 1);
|
||||
successResult = MainLoop(stdin);
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
if (pset.logfile)
|
||||
fclose(pset.logfile);
|
||||
PQfinish(pset.db);
|
||||
setQFout(NULL);
|
||||
|
||||
return successResult;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Parse command line options
|
||||
*/
|
||||
|
||||
static void
|
||||
parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
|
||||
{
|
||||
static struct option long_options[] =
|
||||
{
|
||||
{"echo-all", no_argument, NULL, 'a'},
|
||||
{"no-align", no_argument, NULL, 'A'},
|
||||
{"command", required_argument, NULL, 'c'},
|
||||
{"dbname", required_argument, NULL, 'd'},
|
||||
{"echo-queries", no_argument, NULL, 'e'},
|
||||
{"echo-errors", no_argument, NULL, 'b'},
|
||||
{"echo-hidden", no_argument, NULL, 'E'},
|
||||
{"file", required_argument, NULL, 'f'},
|
||||
{"field-separator", required_argument, NULL, 'F'},
|
||||
{"field-separator-zero", no_argument, NULL, 'z'},
|
||||
{"host", required_argument, NULL, 'h'},
|
||||
{"html", no_argument, NULL, 'H'},
|
||||
{"list", no_argument, NULL, 'l'},
|
||||
{"log-file", required_argument, NULL, 'L'},
|
||||
{"no-readline", no_argument, NULL, 'n'},
|
||||
{"single-transaction", no_argument, NULL, '1'},
|
||||
{"output", required_argument, NULL, 'o'},
|
||||
{"port", required_argument, NULL, 'p'},
|
||||
{"pset", required_argument, NULL, 'P'},
|
||||
{"quiet", no_argument, NULL, 'q'},
|
||||
{"record-separator", required_argument, NULL, 'R'},
|
||||
{"record-separator-zero", no_argument, NULL, '0'},
|
||||
{"single-step", no_argument, NULL, 's'},
|
||||
{"single-line", no_argument, NULL, 'S'},
|
||||
{"tuples-only", no_argument, NULL, 't'},
|
||||
{"table-attr", required_argument, NULL, 'T'},
|
||||
{"username", required_argument, NULL, 'U'},
|
||||
{"set", required_argument, NULL, 'v'},
|
||||
{"variable", required_argument, NULL, 'v'},
|
||||
{"version", no_argument, NULL, 'V'},
|
||||
{"no-password", no_argument, NULL, 'w'},
|
||||
{"password", no_argument, NULL, 'W'},
|
||||
{"expanded", no_argument, NULL, 'x'},
|
||||
{"no-psqlrc", no_argument, NULL, 'X'},
|
||||
{"help", optional_argument, NULL, 1},
|
||||
{NULL, 0, NULL, 0}
|
||||
};
|
||||
|
||||
int optindex;
|
||||
int c;
|
||||
|
||||
memset(options, 0, sizeof *options);
|
||||
|
||||
while ((c = getopt_long(argc, argv, "aAbc:d:eEf:F:h:HlL:no:p:P:qR:sStT:U:v:VwWxXz?01",
|
||||
long_options, &optindex)) != -1)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case 'a':
|
||||
SetVariable(pset.vars, "ECHO", "all");
|
||||
break;
|
||||
case 'A':
|
||||
pset.popt.topt.format = PRINT_UNALIGNED;
|
||||
break;
|
||||
case 'b':
|
||||
SetVariable(pset.vars, "ECHO", "errors");
|
||||
break;
|
||||
case 'c':
|
||||
options->action_string = pg_strdup(optarg);
|
||||
if (optarg[0] == '\\')
|
||||
{
|
||||
options->action = ACT_SINGLE_SLASH;
|
||||
options->action_string++;
|
||||
}
|
||||
else
|
||||
options->action = ACT_SINGLE_QUERY;
|
||||
break;
|
||||
case 'd':
|
||||
options->dbname = pg_strdup(optarg);
|
||||
break;
|
||||
case 'e':
|
||||
SetVariable(pset.vars, "ECHO", "queries");
|
||||
break;
|
||||
case 'E':
|
||||
SetVariableBool(pset.vars, "ECHO_HIDDEN");
|
||||
break;
|
||||
case 'f':
|
||||
options->action = ACT_FILE;
|
||||
options->action_string = pg_strdup(optarg);
|
||||
break;
|
||||
case 'F':
|
||||
pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
|
||||
pset.popt.topt.fieldSep.separator_zero = false;
|
||||
break;
|
||||
case 'h':
|
||||
options->host = pg_strdup(optarg);
|
||||
break;
|
||||
case 'H':
|
||||
pset.popt.topt.format = PRINT_HTML;
|
||||
break;
|
||||
case 'l':
|
||||
options->action = ACT_LIST_DB;
|
||||
break;
|
||||
case 'L':
|
||||
options->logfilename = pg_strdup(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
options->no_readline = true;
|
||||
break;
|
||||
case 'o':
|
||||
if (!setQFout(optarg))
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
case 'p':
|
||||
options->port = pg_strdup(optarg);
|
||||
break;
|
||||
case 'P':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
bool result;
|
||||
|
||||
value = pg_strdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc)
|
||||
result = do_pset(value, NULL, &pset.popt, true);
|
||||
else
|
||||
{
|
||||
*equal_loc = '\0';
|
||||
result = do_pset(value, equal_loc + 1, &pset.popt, true);
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
fprintf(stderr, _("%s: could not set printing parameter \"%s\"\n"), pset.progname, value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
free(value);
|
||||
break;
|
||||
}
|
||||
case 'q':
|
||||
SetVariableBool(pset.vars, "QUIET");
|
||||
break;
|
||||
case 'R':
|
||||
pset.popt.topt.recordSep.separator = pg_strdup(optarg);
|
||||
pset.popt.topt.recordSep.separator_zero = false;
|
||||
break;
|
||||
case 's':
|
||||
SetVariableBool(pset.vars, "SINGLESTEP");
|
||||
break;
|
||||
case 'S':
|
||||
SetVariableBool(pset.vars, "SINGLELINE");
|
||||
break;
|
||||
case 't':
|
||||
pset.popt.topt.tuples_only = true;
|
||||
break;
|
||||
case 'T':
|
||||
pset.popt.topt.tableAttr = pg_strdup(optarg);
|
||||
break;
|
||||
case 'U':
|
||||
options->username = pg_strdup(optarg);
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
char *value;
|
||||
char *equal_loc;
|
||||
|
||||
value = pg_strdup(optarg);
|
||||
equal_loc = strchr(value, '=');
|
||||
if (!equal_loc)
|
||||
{
|
||||
if (!DeleteVariable(pset.vars, value))
|
||||
{
|
||||
fprintf(stderr, _("%s: could not delete variable \"%s\"\n"),
|
||||
pset.progname, value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*equal_loc = '\0';
|
||||
if (!SetVariable(pset.vars, value, equal_loc + 1))
|
||||
{
|
||||
fprintf(stderr, _("%s: could not set variable \"%s\"\n"),
|
||||
pset.progname, value);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
|
||||
free(value);
|
||||
break;
|
||||
}
|
||||
case 'V':
|
||||
showVersion();
|
||||
exit(EXIT_SUCCESS);
|
||||
case 'w':
|
||||
pset.getPassword = TRI_NO;
|
||||
break;
|
||||
case 'W':
|
||||
pset.getPassword = TRI_YES;
|
||||
break;
|
||||
case 'x':
|
||||
pset.popt.topt.expanded = true;
|
||||
break;
|
||||
case 'X':
|
||||
options->no_psqlrc = true;
|
||||
break;
|
||||
case 'z':
|
||||
pset.popt.topt.fieldSep.separator_zero = true;
|
||||
break;
|
||||
case '0':
|
||||
pset.popt.topt.recordSep.separator_zero = true;
|
||||
break;
|
||||
case '1':
|
||||
options->single_txn = true;
|
||||
break;
|
||||
case '?':
|
||||
/* Actual help option given */
|
||||
if (strcmp(argv[optind - 1], "-?") == 0)
|
||||
{
|
||||
usage(NOPAGER);
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
/* unknown option reported by getopt */
|
||||
else
|
||||
goto unknown_option;
|
||||
break;
|
||||
case 1:
|
||||
{
|
||||
if (!optarg || strcmp(optarg, "options") == 0)
|
||||
usage(NOPAGER);
|
||||
else if (optarg && strcmp(optarg, "commands") == 0)
|
||||
slashUsage(NOPAGER);
|
||||
else if (optarg && strcmp(optarg, "variables") == 0)
|
||||
helpVariables(NOPAGER);
|
||||
else
|
||||
goto unknown_option;
|
||||
|
||||
exit(EXIT_SUCCESS);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unknown_option:
|
||||
fprintf(stderr, _("Try \"%s --help\" for more information.\n"),
|
||||
pset.progname);
|
||||
exit(EXIT_FAILURE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* if we still have arguments, use it as the database name and username
|
||||
*/
|
||||
while (argc - optind >= 1)
|
||||
{
|
||||
if (!options->dbname)
|
||||
options->dbname = argv[optind];
|
||||
else if (!options->username)
|
||||
options->username = argv[optind];
|
||||
else if (!pset.quiet)
|
||||
fprintf(stderr, _("%s: warning: extra command-line argument \"%s\" ignored\n"),
|
||||
pset.progname, argv[optind]);
|
||||
|
||||
optind++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load .psqlrc file, if found.
|
||||
*/
|
||||
static void
|
||||
process_psqlrc(char *argv0)
|
||||
{
|
||||
char home[MAXPGPATH];
|
||||
char rc_file[MAXPGPATH];
|
||||
char my_exec_path[MAXPGPATH];
|
||||
char etc_path[MAXPGPATH];
|
||||
char *envrc = getenv("PSQLRC");
|
||||
|
||||
if (find_my_exec(argv0, my_exec_path) < 0)
|
||||
{
|
||||
fprintf(stderr, _("%s: could not find own program executable\n"), argv0);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
get_etc_path(my_exec_path, etc_path);
|
||||
|
||||
snprintf(rc_file, MAXPGPATH, "%s/%s", etc_path, SYSPSQLRC);
|
||||
process_psqlrc_file(rc_file);
|
||||
|
||||
if (envrc != NULL && strlen(envrc) > 0)
|
||||
{
|
||||
/* might need to free() this */
|
||||
char *envrc_alloc = pstrdup(envrc);
|
||||
|
||||
expand_tilde(&envrc_alloc);
|
||||
process_psqlrc_file(envrc_alloc);
|
||||
}
|
||||
else if (get_home_path(home))
|
||||
{
|
||||
snprintf(rc_file, MAXPGPATH, "%s/%s", home, PSQLRC);
|
||||
process_psqlrc_file(rc_file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
process_psqlrc_file(char *filename)
|
||||
{
|
||||
char *psqlrc_minor,
|
||||
*psqlrc_major;
|
||||
|
||||
#if defined(WIN32) && (!defined(__MINGW32__))
|
||||
#define R_OK 4
|
||||
#endif
|
||||
|
||||
psqlrc_minor = psprintf("%s-%s", filename, PG_VERSION);
|
||||
psqlrc_major = psprintf("%s-%s", filename, PG_MAJORVERSION);
|
||||
|
||||
/* check for minor version first, then major, then no version */
|
||||
if (access(psqlrc_minor, R_OK) == 0)
|
||||
(void) process_file(psqlrc_minor, false, false);
|
||||
else if (access(psqlrc_major, R_OK) == 0)
|
||||
(void) process_file(psqlrc_major, false, false);
|
||||
else if (access(filename, R_OK) == 0)
|
||||
(void) process_file(filename, false, false);
|
||||
|
||||
free(psqlrc_minor);
|
||||
free(psqlrc_major);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* showVersion
|
||||
*
|
||||
* This output format is intended to match GNU standards.
|
||||
*/
|
||||
static void
|
||||
showVersion(void)
|
||||
{
|
||||
puts("psql (PostgreSQL) " PG_VERSION);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Assign hooks for psql variables.
|
||||
*
|
||||
* This isn't an amazingly good place for them, but neither is anywhere else.
|
||||
*/
|
||||
|
||||
static void
|
||||
autocommit_hook(const char *newval)
|
||||
{
|
||||
pset.autocommit = ParseVariableBool(newval, "AUTOCOMMIT");
|
||||
}
|
||||
|
||||
static void
|
||||
on_error_stop_hook(const char *newval)
|
||||
{
|
||||
pset.on_error_stop = ParseVariableBool(newval, "ON_ERROR_STOP");
|
||||
}
|
||||
|
||||
static void
|
||||
quiet_hook(const char *newval)
|
||||
{
|
||||
pset.quiet = ParseVariableBool(newval, "QUIET");
|
||||
}
|
||||
|
||||
static void
|
||||
singleline_hook(const char *newval)
|
||||
{
|
||||
pset.singleline = ParseVariableBool(newval, "SINGLELINE");
|
||||
}
|
||||
|
||||
static void
|
||||
singlestep_hook(const char *newval)
|
||||
{
|
||||
pset.singlestep = ParseVariableBool(newval, "SINGLESTEP");
|
||||
}
|
||||
|
||||
static void
|
||||
fetch_count_hook(const char *newval)
|
||||
{
|
||||
pset.fetch_count = ParseVariableNum(newval, -1, -1, false);
|
||||
}
|
||||
|
||||
static void
|
||||
echo_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.echo = PSQL_ECHO_NONE;
|
||||
else if (pg_strcasecmp(newval, "queries") == 0)
|
||||
pset.echo = PSQL_ECHO_QUERIES;
|
||||
else if (pg_strcasecmp(newval, "errors") == 0)
|
||||
pset.echo = PSQL_ECHO_ERRORS;
|
||||
else if (pg_strcasecmp(newval, "all") == 0)
|
||||
pset.echo = PSQL_ECHO_ALL;
|
||||
else if (pg_strcasecmp(newval, "none") == 0)
|
||||
pset.echo = PSQL_ECHO_NONE;
|
||||
else
|
||||
{
|
||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
||||
newval, "ECHO", "none");
|
||||
pset.echo = PSQL_ECHO_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
echo_hidden_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
||||
else if (pg_strcasecmp(newval, "noexec") == 0)
|
||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC;
|
||||
else if (ParseVariableBool(newval, "ECHO_HIDDEN"))
|
||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_ON;
|
||||
else /* ParseVariableBool printed msg if needed */
|
||||
pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF;
|
||||
}
|
||||
|
||||
static void
|
||||
on_error_rollback_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
||||
else if (pg_strcasecmp(newval, "interactive") == 0)
|
||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE;
|
||||
else if (ParseVariableBool(newval, "ON_ERROR_ROLLBACK"))
|
||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON;
|
||||
else /* ParseVariableBool printed msg if needed */
|
||||
pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF;
|
||||
}
|
||||
|
||||
static void
|
||||
comp_keyword_case_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
|
||||
else if (pg_strcasecmp(newval, "preserve-upper") == 0)
|
||||
pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
|
||||
else if (pg_strcasecmp(newval, "preserve-lower") == 0)
|
||||
pset.comp_case = PSQL_COMP_CASE_PRESERVE_LOWER;
|
||||
else if (pg_strcasecmp(newval, "upper") == 0)
|
||||
pset.comp_case = PSQL_COMP_CASE_UPPER;
|
||||
else if (pg_strcasecmp(newval, "lower") == 0)
|
||||
pset.comp_case = PSQL_COMP_CASE_LOWER;
|
||||
else
|
||||
{
|
||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
||||
newval, "COMP_KEYWORD_CASE", "preserve-upper");
|
||||
pset.comp_case = PSQL_COMP_CASE_PRESERVE_UPPER;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
histcontrol_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.histcontrol = hctl_none;
|
||||
else if (pg_strcasecmp(newval, "ignorespace") == 0)
|
||||
pset.histcontrol = hctl_ignorespace;
|
||||
else if (pg_strcasecmp(newval, "ignoredups") == 0)
|
||||
pset.histcontrol = hctl_ignoredups;
|
||||
else if (pg_strcasecmp(newval, "ignoreboth") == 0)
|
||||
pset.histcontrol = hctl_ignoreboth;
|
||||
else if (pg_strcasecmp(newval, "none") == 0)
|
||||
pset.histcontrol = hctl_none;
|
||||
else
|
||||
{
|
||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
||||
newval, "HISTCONTROL", "none");
|
||||
pset.histcontrol = hctl_none;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
prompt1_hook(const char *newval)
|
||||
{
|
||||
pset.prompt1 = newval ? newval : "";
|
||||
}
|
||||
|
||||
static void
|
||||
prompt2_hook(const char *newval)
|
||||
{
|
||||
pset.prompt2 = newval ? newval : "";
|
||||
}
|
||||
|
||||
static void
|
||||
prompt3_hook(const char *newval)
|
||||
{
|
||||
pset.prompt3 = newval ? newval : "";
|
||||
}
|
||||
|
||||
static void
|
||||
verbosity_hook(const char *newval)
|
||||
{
|
||||
if (newval == NULL)
|
||||
pset.verbosity = PQERRORS_DEFAULT;
|
||||
else if (pg_strcasecmp(newval, "default") == 0)
|
||||
pset.verbosity = PQERRORS_DEFAULT;
|
||||
else if (pg_strcasecmp(newval, "terse") == 0)
|
||||
pset.verbosity = PQERRORS_TERSE;
|
||||
else if (pg_strcasecmp(newval, "verbose") == 0)
|
||||
pset.verbosity = PQERRORS_VERBOSE;
|
||||
else
|
||||
{
|
||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
||||
newval, "VERBOSITY", "default");
|
||||
pset.verbosity = PQERRORS_DEFAULT;
|
||||
}
|
||||
|
||||
if (pset.db)
|
||||
PQsetErrorVerbosity(pset.db, pset.verbosity);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
EstablishVariableSpace(void)
|
||||
{
|
||||
pset.vars = CreateVariableSpace();
|
||||
|
||||
SetVariableAssignHook(pset.vars, "AUTOCOMMIT", autocommit_hook);
|
||||
SetVariableAssignHook(pset.vars, "ON_ERROR_STOP", on_error_stop_hook);
|
||||
SetVariableAssignHook(pset.vars, "QUIET", quiet_hook);
|
||||
SetVariableAssignHook(pset.vars, "SINGLELINE", singleline_hook);
|
||||
SetVariableAssignHook(pset.vars, "SINGLESTEP", singlestep_hook);
|
||||
SetVariableAssignHook(pset.vars, "FETCH_COUNT", fetch_count_hook);
|
||||
SetVariableAssignHook(pset.vars, "ECHO", echo_hook);
|
||||
SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook);
|
||||
SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook);
|
||||
SetVariableAssignHook(pset.vars, "COMP_KEYWORD_CASE", comp_keyword_case_hook);
|
||||
SetVariableAssignHook(pset.vars, "HISTCONTROL", histcontrol_hook);
|
||||
SetVariableAssignHook(pset.vars, "PROMPT1", prompt1_hook);
|
||||
SetVariableAssignHook(pset.vars, "PROMPT2", prompt2_hook);
|
||||
SetVariableAssignHook(pset.vars, "PROMPT3", prompt3_hook);
|
||||
SetVariableAssignHook(pset.vars, "VERBOSITY", verbosity_hook);
|
||||
}
|
|
@ -1,340 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/stringutils.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
#include "common.h"
|
||||
#include "stringutils.h"
|
||||
|
||||
|
||||
/*
|
||||
* Replacement for strtok() (a.k.a. poor man's flex)
|
||||
*
|
||||
* Splits a string into tokens, returning one token per call, then NULL
|
||||
* when no more tokens exist in the given string.
|
||||
*
|
||||
* The calling convention is similar to that of strtok, but with more
|
||||
* frammishes.
|
||||
*
|
||||
* s - string to parse, if NULL continue parsing the last string
|
||||
* whitespace - set of whitespace characters that separate tokens
|
||||
* delim - set of non-whitespace separator characters (or NULL)
|
||||
* quote - set of characters that can quote a token (NULL if none)
|
||||
* escape - character that can quote quotes (0 if none)
|
||||
* e_strings - if TRUE, treat E'...' syntax as a valid token
|
||||
* del_quotes - if TRUE, strip quotes from the returned token, else return
|
||||
* it exactly as found in the string
|
||||
* encoding - the active character-set encoding
|
||||
*
|
||||
* Characters in 'delim', if any, will be returned as single-character
|
||||
* tokens unless part of a quoted token.
|
||||
*
|
||||
* Double occurrences of the quoting character are always taken to represent
|
||||
* a single quote character in the data. If escape isn't 0, then escape
|
||||
* followed by anything (except \0) is a data character too.
|
||||
*
|
||||
* The combination of e_strings and del_quotes both TRUE is not currently
|
||||
* handled. This could be fixed but it's not needed anywhere at the moment.
|
||||
*
|
||||
* Note that the string s is _not_ overwritten in this implementation.
|
||||
*
|
||||
* NB: it's okay to vary delim, quote, and escape from one call to the
|
||||
* next on a single source string, but changing whitespace is a bad idea
|
||||
* since you might lose data.
|
||||
*/
|
||||
char *
|
||||
strtokx(const char *s,
|
||||
const char *whitespace,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
bool e_strings,
|
||||
bool del_quotes,
|
||||
int encoding)
|
||||
{
|
||||
static char *storage = NULL;/* store the local copy of the users string
|
||||
* here */
|
||||
static char *string = NULL; /* pointer into storage where to continue on
|
||||
* next call */
|
||||
|
||||
/* variously abused variables: */
|
||||
unsigned int offset;
|
||||
char *start;
|
||||
char *p;
|
||||
|
||||
if (s)
|
||||
{
|
||||
free(storage);
|
||||
|
||||
/*
|
||||
* We may need extra space to insert delimiter nulls for adjacent
|
||||
* tokens. 2X the space is a gross overestimate, but it's unlikely
|
||||
* that this code will be used on huge strings anyway.
|
||||
*/
|
||||
storage = pg_malloc(2 * strlen(s) + 1);
|
||||
strcpy(storage, s);
|
||||
string = storage;
|
||||
}
|
||||
|
||||
if (!storage)
|
||||
return NULL;
|
||||
|
||||
/* skip leading whitespace */
|
||||
offset = strspn(string, whitespace);
|
||||
start = &string[offset];
|
||||
|
||||
/* end of string reached? */
|
||||
if (*start == '\0')
|
||||
{
|
||||
/* technically we don't need to free here, but we're nice */
|
||||
free(storage);
|
||||
storage = NULL;
|
||||
string = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* test if delimiter character */
|
||||
if (delim && strchr(delim, *start))
|
||||
{
|
||||
/*
|
||||
* If not at end of string, we need to insert a null to terminate the
|
||||
* returned token. We can just overwrite the next character if it
|
||||
* happens to be in the whitespace set ... otherwise move over the
|
||||
* rest of the string to make room. (This is why we allocated extra
|
||||
* space above).
|
||||
*/
|
||||
p = start + 1;
|
||||
if (*p != '\0')
|
||||
{
|
||||
if (!strchr(whitespace, *p))
|
||||
memmove(p + 1, p, strlen(p) + 1);
|
||||
*p = '\0';
|
||||
string = p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* at end of string, so no extra work */
|
||||
string = p;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
/* check for E string */
|
||||
p = start;
|
||||
if (e_strings &&
|
||||
(*p == 'E' || *p == 'e') &&
|
||||
p[1] == '\'')
|
||||
{
|
||||
quote = "'";
|
||||
escape = '\\'; /* if std strings before, not any more */
|
||||
p++;
|
||||
}
|
||||
|
||||
/* test if quoting character */
|
||||
if (quote && strchr(quote, *p))
|
||||
{
|
||||
/* okay, we have a quoted token, now scan for the closer */
|
||||
char thisquote = *p++;
|
||||
|
||||
for (; *p; p += PQmblen(p, encoding))
|
||||
{
|
||||
if (*p == escape && p[1] != '\0')
|
||||
p++; /* process escaped anything */
|
||||
else if (*p == thisquote && p[1] == thisquote)
|
||||
p++; /* process doubled quote */
|
||||
else if (*p == thisquote)
|
||||
{
|
||||
p++; /* skip trailing quote */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If not at end of string, we need to insert a null to terminate the
|
||||
* returned token. See notes above.
|
||||
*/
|
||||
if (*p != '\0')
|
||||
{
|
||||
if (!strchr(whitespace, *p))
|
||||
memmove(p + 1, p, strlen(p) + 1);
|
||||
*p = '\0';
|
||||
string = p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* at end of string, so no extra work */
|
||||
string = p;
|
||||
}
|
||||
|
||||
/* Clean up the token if caller wants that */
|
||||
if (del_quotes)
|
||||
strip_quotes(start, thisquote, escape, encoding);
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Otherwise no quoting character. Scan till next whitespace, delimiter
|
||||
* or quote. NB: at this point, *start is known not to be '\0',
|
||||
* whitespace, delim, or quote, so we will consume at least one character.
|
||||
*/
|
||||
offset = strcspn(start, whitespace);
|
||||
|
||||
if (delim)
|
||||
{
|
||||
unsigned int offset2 = strcspn(start, delim);
|
||||
|
||||
if (offset > offset2)
|
||||
offset = offset2;
|
||||
}
|
||||
|
||||
if (quote)
|
||||
{
|
||||
unsigned int offset2 = strcspn(start, quote);
|
||||
|
||||
if (offset > offset2)
|
||||
offset = offset2;
|
||||
}
|
||||
|
||||
p = start + offset;
|
||||
|
||||
/*
|
||||
* If not at end of string, we need to insert a null to terminate the
|
||||
* returned token. See notes above.
|
||||
*/
|
||||
if (*p != '\0')
|
||||
{
|
||||
if (!strchr(whitespace, *p))
|
||||
memmove(p + 1, p, strlen(p) + 1);
|
||||
*p = '\0';
|
||||
string = p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* at end of string, so no extra work */
|
||||
string = p;
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* strip_quotes
|
||||
*
|
||||
* Remove quotes from the string at *source. Leading and trailing occurrences
|
||||
* of 'quote' are removed; embedded double occurrences of 'quote' are reduced
|
||||
* to single occurrences; if 'escape' is not 0 then 'escape' removes special
|
||||
* significance of next character.
|
||||
*
|
||||
* Note that the source string is overwritten in-place.
|
||||
*/
|
||||
void
|
||||
strip_quotes(char *source, char quote, char escape, int encoding)
|
||||
{
|
||||
char *src;
|
||||
char *dst;
|
||||
|
||||
Assert(source != NULL);
|
||||
Assert(quote != '\0');
|
||||
|
||||
src = dst = source;
|
||||
|
||||
if (*src && *src == quote)
|
||||
src++; /* skip leading quote */
|
||||
|
||||
while (*src)
|
||||
{
|
||||
char c = *src;
|
||||
int i;
|
||||
|
||||
if (c == quote && src[1] == '\0')
|
||||
break; /* skip trailing quote */
|
||||
else if (c == quote && src[1] == quote)
|
||||
src++; /* process doubled quote */
|
||||
else if (c == escape && src[1] != '\0')
|
||||
src++; /* process escaped character */
|
||||
|
||||
i = PQmblen(src, encoding);
|
||||
while (i--)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
*dst = '\0';
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* quote_if_needed
|
||||
*
|
||||
* Opposite of strip_quotes(). If "source" denotes itself literally without
|
||||
* quoting or escaping, returns NULL. Otherwise, returns a malloc'd copy with
|
||||
* quoting and escaping applied:
|
||||
*
|
||||
* source - string to parse
|
||||
* entails_quote - any of these present? need outer quotes
|
||||
* quote - doubled within string, affixed to both ends
|
||||
* escape - doubled within string
|
||||
* encoding - the active character-set encoding
|
||||
*
|
||||
* Do not use this as a substitute for PQescapeStringConn(). Use it for
|
||||
* strings to be parsed by strtokx() or psql_scan_slash_option().
|
||||
*/
|
||||
char *
|
||||
quote_if_needed(const char *source, const char *entails_quote,
|
||||
char quote, char escape, int encoding)
|
||||
{
|
||||
const char *src;
|
||||
char *ret;
|
||||
char *dst;
|
||||
bool need_quotes = false;
|
||||
|
||||
Assert(source != NULL);
|
||||
Assert(quote != '\0');
|
||||
|
||||
src = source;
|
||||
dst = ret = pg_malloc(2 * strlen(src) + 3); /* excess */
|
||||
|
||||
*dst++ = quote;
|
||||
|
||||
while (*src)
|
||||
{
|
||||
char c = *src;
|
||||
int i;
|
||||
|
||||
if (c == quote)
|
||||
{
|
||||
need_quotes = true;
|
||||
*dst++ = quote;
|
||||
}
|
||||
else if (c == escape)
|
||||
{
|
||||
need_quotes = true;
|
||||
*dst++ = escape;
|
||||
}
|
||||
else if (strchr(entails_quote, c))
|
||||
need_quotes = true;
|
||||
|
||||
i = PQmblen(src, encoding);
|
||||
while (i--)
|
||||
*dst++ = *src++;
|
||||
}
|
||||
|
||||
*dst++ = quote;
|
||||
*dst = '\0';
|
||||
|
||||
if (!need_quotes)
|
||||
{
|
||||
free(ret);
|
||||
ret = NULL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/stringutils.h
|
||||
*/
|
||||
#ifndef STRINGUTILS_H
|
||||
#define STRINGUTILS_H
|
||||
|
||||
/* The cooler version of strtok() which knows about quotes and doesn't
|
||||
* overwrite your input */
|
||||
extern char *strtokx(const char *s,
|
||||
const char *whitespace,
|
||||
const char *delim,
|
||||
const char *quote,
|
||||
char escape,
|
||||
bool e_strings,
|
||||
bool del_quotes,
|
||||
int encoding);
|
||||
|
||||
extern void strip_quotes(char *source, char quote, char escape, int encoding);
|
||||
|
||||
extern char *quote_if_needed(const char *source, const char *entails_quote,
|
||||
char quote, char escape, int encoding);
|
||||
|
||||
#endif /* STRINGUTILS_H */
|
File diff suppressed because it is too large
Load Diff
|
@ -1,15 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/tab-complete.h
|
||||
*/
|
||||
#ifndef TAB_COMPLETE_H
|
||||
#define TAB_COMPLETE_H
|
||||
|
||||
#include "postgres_fe.h"
|
||||
|
||||
void initialize_readline(void);
|
||||
|
||||
#endif
|
|
@ -1,305 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/variables.c
|
||||
*/
|
||||
#include "postgres_fe.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "variables.h"
|
||||
|
||||
|
||||
/*
|
||||
* Check whether a variable's name is allowed.
|
||||
*
|
||||
* We allow any non-ASCII character, as well as ASCII letters, digits, and
|
||||
* underscore. Keep this in sync with the definition of variable_char in
|
||||
* psqlscan.l.
|
||||
*/
|
||||
static bool
|
||||
valid_variable_name(const char *name)
|
||||
{
|
||||
const unsigned char *ptr = (const unsigned char *) name;
|
||||
|
||||
/* Mustn't be zero-length */
|
||||
if (*ptr == '\0')
|
||||
return false;
|
||||
|
||||
while (*ptr)
|
||||
{
|
||||
if (IS_HIGHBIT_SET(*ptr) ||
|
||||
strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
|
||||
"_0123456789", *ptr) != NULL)
|
||||
ptr++;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* A "variable space" is represented by an otherwise-unused struct _variable
|
||||
* that serves as list header.
|
||||
*/
|
||||
VariableSpace
|
||||
CreateVariableSpace(void)
|
||||
{
|
||||
struct _variable *ptr;
|
||||
|
||||
ptr = pg_malloc(sizeof *ptr);
|
||||
ptr->name = NULL;
|
||||
ptr->value = NULL;
|
||||
ptr->assign_hook = NULL;
|
||||
ptr->next = NULL;
|
||||
|
||||
return ptr;
|
||||
}
|
||||
|
||||
const char *
|
||||
GetVariable(VariableSpace space, const char *name)
|
||||
{
|
||||
struct _variable *current;
|
||||
|
||||
if (!space)
|
||||
return NULL;
|
||||
|
||||
for (current = space->next; current; current = current->next)
|
||||
{
|
||||
if (strcmp(current->name, name) == 0)
|
||||
{
|
||||
/* this is correct answer when value is NULL, too */
|
||||
return current->value;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to interpret "value" as boolean value.
|
||||
*
|
||||
* Valid values are: true, false, yes, no, on, off, 1, 0; as well as unique
|
||||
* prefixes thereof.
|
||||
*
|
||||
* "name" is the name of the variable we're assigning to, to use in error
|
||||
* report if any. Pass name == NULL to suppress the error report.
|
||||
*/
|
||||
bool
|
||||
ParseVariableBool(const char *value, const char *name)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
if (value == NULL)
|
||||
return false; /* not set -> assume "off" */
|
||||
|
||||
len = strlen(value);
|
||||
|
||||
if (pg_strncasecmp(value, "true", len) == 0)
|
||||
return true;
|
||||
else if (pg_strncasecmp(value, "false", len) == 0)
|
||||
return false;
|
||||
else if (pg_strncasecmp(value, "yes", len) == 0)
|
||||
return true;
|
||||
else if (pg_strncasecmp(value, "no", len) == 0)
|
||||
return false;
|
||||
/* 'o' is not unique enough */
|
||||
else if (pg_strncasecmp(value, "on", (len > 2 ? len : 2)) == 0)
|
||||
return true;
|
||||
else if (pg_strncasecmp(value, "off", (len > 2 ? len : 2)) == 0)
|
||||
return false;
|
||||
else if (pg_strcasecmp(value, "1") == 0)
|
||||
return true;
|
||||
else if (pg_strcasecmp(value, "0") == 0)
|
||||
return false;
|
||||
else
|
||||
{
|
||||
/* NULL is treated as false, so a non-matching value is 'true' */
|
||||
if (name)
|
||||
psql_error("unrecognized value \"%s\" for \"%s\"; assuming \"%s\"\n",
|
||||
value, name, "on");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Read numeric variable, or defaultval if it is not set, or faultval if its
|
||||
* value is not a valid numeric string. If allowtrail is false, this will
|
||||
* include the case where there are trailing characters after the number.
|
||||
*/
|
||||
int
|
||||
ParseVariableNum(const char *val,
|
||||
int defaultval,
|
||||
int faultval,
|
||||
bool allowtrail)
|
||||
{
|
||||
int result;
|
||||
|
||||
if (!val)
|
||||
result = defaultval;
|
||||
else if (!val[0])
|
||||
result = faultval;
|
||||
else
|
||||
{
|
||||
char *end;
|
||||
|
||||
result = strtol(val, &end, 0);
|
||||
if (!allowtrail && *end)
|
||||
result = faultval;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int
|
||||
GetVariableNum(VariableSpace space,
|
||||
const char *name,
|
||||
int defaultval,
|
||||
int faultval,
|
||||
bool allowtrail)
|
||||
{
|
||||
const char *val;
|
||||
|
||||
val = GetVariable(space, name);
|
||||
return ParseVariableNum(val, defaultval, faultval, allowtrail);
|
||||
}
|
||||
|
||||
void
|
||||
PrintVariables(VariableSpace space)
|
||||
{
|
||||
struct _variable *ptr;
|
||||
|
||||
if (!space)
|
||||
return;
|
||||
|
||||
for (ptr = space->next; ptr; ptr = ptr->next)
|
||||
{
|
||||
if (ptr->value)
|
||||
printf("%s = '%s'\n", ptr->name, ptr->value);
|
||||
if (cancel_pressed)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
SetVariable(VariableSpace space, const char *name, const char *value)
|
||||
{
|
||||
struct _variable *current,
|
||||
*previous;
|
||||
|
||||
if (!space)
|
||||
return false;
|
||||
|
||||
if (!valid_variable_name(name))
|
||||
return false;
|
||||
|
||||
if (!value)
|
||||
return DeleteVariable(space, name);
|
||||
|
||||
for (previous = space, current = space->next;
|
||||
current;
|
||||
previous = current, current = current->next)
|
||||
{
|
||||
if (strcmp(current->name, name) == 0)
|
||||
{
|
||||
/* found entry, so update */
|
||||
if (current->value)
|
||||
free(current->value);
|
||||
current->value = pg_strdup(value);
|
||||
if (current->assign_hook)
|
||||
(*current->assign_hook) (current->value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* not present, make new entry */
|
||||
current = pg_malloc(sizeof *current);
|
||||
current->name = pg_strdup(name);
|
||||
current->value = pg_strdup(value);
|
||||
current->assign_hook = NULL;
|
||||
current->next = NULL;
|
||||
previous->next = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* This both sets a hook function, and calls it on the current value (if any)
|
||||
*/
|
||||
bool
|
||||
SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook)
|
||||
{
|
||||
struct _variable *current,
|
||||
*previous;
|
||||
|
||||
if (!space)
|
||||
return false;
|
||||
|
||||
if (!valid_variable_name(name))
|
||||
return false;
|
||||
|
||||
for (previous = space, current = space->next;
|
||||
current;
|
||||
previous = current, current = current->next)
|
||||
{
|
||||
if (strcmp(current->name, name) == 0)
|
||||
{
|
||||
/* found entry, so update */
|
||||
current->assign_hook = hook;
|
||||
(*hook) (current->value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* not present, make new entry */
|
||||
current = pg_malloc(sizeof *current);
|
||||
current->name = pg_strdup(name);
|
||||
current->value = NULL;
|
||||
current->assign_hook = hook;
|
||||
current->next = NULL;
|
||||
previous->next = current;
|
||||
(*hook) (NULL);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SetVariableBool(VariableSpace space, const char *name)
|
||||
{
|
||||
return SetVariable(space, name, "on");
|
||||
}
|
||||
|
||||
bool
|
||||
DeleteVariable(VariableSpace space, const char *name)
|
||||
{
|
||||
struct _variable *current,
|
||||
*previous;
|
||||
|
||||
if (!space)
|
||||
return false;
|
||||
|
||||
for (previous = space, current = space->next;
|
||||
current;
|
||||
previous = current, current = current->next)
|
||||
{
|
||||
if (strcmp(current->name, name) == 0)
|
||||
{
|
||||
if (current->value)
|
||||
free(current->value);
|
||||
current->value = NULL;
|
||||
/* Physically delete only if no hook function to remember */
|
||||
if (current->assign_hook)
|
||||
(*current->assign_hook) (NULL);
|
||||
else
|
||||
{
|
||||
previous->next = current->next;
|
||||
free(current->name);
|
||||
free(current);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* psql - the PostgreSQL interactive terminal
|
||||
*
|
||||
* Copyright (c) 2000-2015, PostgreSQL Global Development Group
|
||||
*
|
||||
* src/bin/psql/variables.h
|
||||
*/
|
||||
#ifndef VARIABLES_H
|
||||
#define VARIABLES_H
|
||||
|
||||
/*
|
||||
* This implements a sort of variable repository. One could also think of it
|
||||
* as a cheap version of an associative array. In each one of these
|
||||
* datastructures you can store name/value pairs. There can also be an
|
||||
* "assign hook" function that is called whenever the variable's value is
|
||||
* changed.
|
||||
*
|
||||
* An "unset" operation causes the hook to be called with newval == NULL.
|
||||
*
|
||||
* Note: if value == NULL then the variable is logically unset, but we are
|
||||
* keeping the struct around so as not to forget about its hook function.
|
||||
*/
|
||||
typedef void (*VariableAssignHook) (const char *newval);
|
||||
|
||||
struct _variable
|
||||
{
|
||||
char *name;
|
||||
char *value;
|
||||
VariableAssignHook assign_hook;
|
||||
struct _variable *next;
|
||||
};
|
||||
|
||||
typedef struct _variable *VariableSpace;
|
||||
|
||||
VariableSpace CreateVariableSpace(void);
|
||||
const char *GetVariable(VariableSpace space, const char *name);
|
||||
|
||||
bool ParseVariableBool(const char *value, const char *name);
|
||||
int ParseVariableNum(const char *val,
|
||||
int defaultval,
|
||||
int faultval,
|
||||
bool allowtrail);
|
||||
int GetVariableNum(VariableSpace space,
|
||||
const char *name,
|
||||
int defaultval,
|
||||
int faultval,
|
||||
bool allowtrail);
|
||||
|
||||
void PrintVariables(VariableSpace space);
|
||||
|
||||
bool SetVariable(VariableSpace space, const char *name, const char *value);
|
||||
bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
|
||||
bool SetVariableBool(VariableSpace space, const char *name);
|
||||
bool DeleteVariable(VariableSpace space, const char *name);
|
||||
|
||||
#endif /* VARIABLES_H */
|
Loading…
Reference in New Issue