summaryrefslogtreecommitdiff
path: root/src/bin/psql/psql.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>1999-11-04 23:14:30 +0000
committerBruce Momjian <bruce@momjian.us>1999-11-04 23:14:30 +0000
commit0e6652e67357354e01f2466184343d0bc0ee2cab (patch)
tree0c06450508417e033f8a2b90241a7126cd54c4fe /src/bin/psql/psql.c
parent2323b63631080b7d4c287872a83b0ea30bd7874a (diff)
psql cleanup
Diffstat (limited to 'src/bin/psql/psql.c')
-rw-r--r--src/bin/psql/psql.c3297
1 files changed, 0 insertions, 3297 deletions
diff --git a/src/bin/psql/psql.c b/src/bin/psql/psql.c
deleted file mode 100644
index ca2fc6f27c1..00000000000
--- a/src/bin/psql/psql.c
+++ /dev/null
@@ -1,3297 +0,0 @@
-/*-------------------------------------------------------------------------
- *
- * psql.c
- * an interactive front-end to postgreSQL
- *
- * Copyright (c) 1996, Regents of the University of California
- *
- *
- * IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/bin/psql/Attic/psql.c,v 1.195 1999/10/26 04:40:58 momjian Exp $
- *
- *-------------------------------------------------------------------------
- */
-#include <signal.h>
-#include <errno.h>
-#include <sys/types.h>
-#ifdef WIN32
-#define WIN32_LEAN_AND_MEAN
-#include <windows.h>
-#include <io.h>
-#else
-#include <sys/ioctl.h>
-#include <unistd.h>
-#endif
-#include <sys/stat.h>
-#include <fcntl.h>
-#include <ctype.h>
-
-#include "postgres.h"
-#include "libpq-fe.h"
-#include "pqexpbuffer.h"
-#include "pqsignal.h"
-#include "stringutils.h"
-#include "psqlHelp.h"
-
-#ifndef HAVE_STRDUP
-#include "strdup.h"
-#endif
-
-#ifdef HAVE_TERMIOS_H
-#include <termios.h>
-#endif
-
-#ifdef HAVE_GETOPT_H
-#include <getopt.h>
-#endif
-
-#ifdef HAVE_LIBREADLINE
-#ifdef HAVE_READLINE_H
-#include <readline.h>
-#define USE_READLINE 1
-#if defined(HAVE_HISTORY_H)
-#include <history.h>
-#define USE_HISTORY 1
-#endif
-#else
-#if defined(HAVE_READLINE_READLINE_H)
-#include <readline/readline.h>
-#define USE_READLINE 1
-#if defined(HAVE_READLINE_HISTORY_H)
-#include <readline/history.h>
-#define USE_HISTORY 1
-#endif
-#endif
-#endif
-#if defined(HAVE_HISTORY) && !defined(USE_HISTORY)
-#define USE_HISTORY 1
-#endif
-#endif
-
-
-#ifdef WIN32
-#define popen(x,y) _popen(x,y)
-#define pclose(x) _pclose(x)
-#define open(x,y,z) _open(x,y,z)
-#define strcasecmp(x,y) stricmp(x,y)
-#define pqsignal(x,y)
-#define R_OK 0
-
-/* getopt is not in the standard includes on Win32 */
-extern char *optarg;
-extern int optind,
- opterr,
- optopt;
-int getopt(int, char *const[], const char *);
-char *__progname = "psql";
-
-#endif
-
-#ifdef MULTIBYTE
-/* flag to indicate if PGCLIENTENCODING has been set by a user */
-static char *has_client_encoding = 0;
-
-#endif
-
-/* This prompt string is assumed to have at least 3 characters by code in MainLoop().
- * A character two characters from the end is replaced each time by a mode character.
- */
-#define PROMPT "=> "
-
-#define PROMPT_READY '='
-#define PROMPT_CONTINUE '-'
-#define PROMPT_COMMENT '*'
-#define PROMPT_SINGLEQUOTE '\''
-#define PROMPT_DOUBLEQUOTE '"'
-
-/* Backslash command handling:
- * 0 - send currently constructed query to backend (i.e. we got a \g)
- * 1 - skip processing of this line, continue building up query
- * 2 - terminate processing (i.e. we got a \q)
- * 3 - new query supplied by edit
- */
-#define CMD_UNKNOWN -1
-#define CMD_SEND 0
-#define CMD_SKIP_LINE 1
-#define CMD_TERMINATE 2
-#define CMD_NEWEDIT 3
-
-#define COPYBUFSIZ 8192
-
-#define DEFAULT_FIELD_SEP "|"
-#define DEFAULT_EDITOR "vi"
-#define DEFAULT_SHELL "/bin/sh"
-
-typedef struct _psqlSettings
-{
- PGconn *db; /* connection to backend */
- FILE *queryFout; /* where to send the query results */
- PQprintOpt opt; /* options to be passed to PQprint */
- char *prompt; /* prompt to display */
- char *gfname; /* one-shot file output argument for \g */
- bool notty; /* input or output is not a tty */
- bool pipe; /* queryFout is from a popen() */
- bool echoQuery; /* echo the query before sending it */
- bool echoAllQueries; /* echo all queries before sending it */
- bool quiet; /* run quietly, no messages, no promt */
- bool singleStep; /* prompt before for each query */
- bool singleLineMode; /* query terminated by newline */
- bool useReadline; /* use libreadline routines */
- bool getPassword; /* prompt the user for a username and
- * password */
-} PsqlSettings;
-
-/*
- * cur_cmd_source and cur_cmd_interactive are the top of a stack of
- * source files (one stack level per recursive invocation of MainLoop).
- * It's kinda grotty to make these global variables, but the alternative
- * of passing them around through many function parameter lists seems
- * worse.
- */
-static FILE *cur_cmd_source = NULL; /* current source of command input */
-static bool cur_cmd_interactive = false; /* is it an interactive
- * source? */
-
-
-#ifdef TIOCGWINSZ
-struct winsize screen_size;
-
-#else
-struct winsize
-{
- int ws_row;
- int ws_col;
-} screen_size;
-
-#endif
-
-/* declarations for functions in this file */
-static void usage(char *progname);
-static void slashUsage();
-static bool handleCopyOut(PGconn *conn, FILE *copystream);
-static bool handleCopyIn(PGconn *conn, const bool mustprompt,
- FILE *copystream);
-static int tableList(PsqlSettings *pset, bool deep_tablelist,
- char info_type, bool system_tables);
-static int tableDesc(PsqlSettings *pset, char *table, FILE *fout);
-static int objectDescription(PsqlSettings *pset, char *object);
-static int rightsList(PsqlSettings *pset);
-static void emitNtimes(FILE *fout, const char *str, int N);
-static void prompt_for_password(char *username, char *password);
-
-static char *gets_noreadline(char *prompt, FILE *source);
-static char *gets_readline(char *prompt, FILE *source);
-static char *gets_fromFile(char *prompt, FILE *source);
-static int listAllDbs(PsqlSettings *pset);
-static bool SendQuery(PsqlSettings *pset, const char *query,
- FILE *copy_in_stream, FILE *copy_out_stream);
-static int HandleSlashCmds(PsqlSettings *pset, char *line,
- PQExpBuffer query_buf);
-static int MainLoop(PsqlSettings *pset, FILE *source);
-static FILE *setFout(PsqlSettings *pset, char *fname);
-
-static char *selectVersion(PsqlSettings *pset);
-
-/*
- * usage print out usage for command line arguments
- */
-
-static void
-usage(char *progname)
-{
- fprintf(stderr, "Usage: %s [options] [dbname]\n", progname);
- fprintf(stderr, "\t -a authsvc set authentication service\n");
- fprintf(stderr, "\t -A turn off alignment when printing out attributes\n");
- fprintf(stderr, "\t -c query run single query (slash commands too)\n");
- fprintf(stderr, "\t -d dbName specify database name\n");
- fprintf(stderr, "\t -e echo the query sent to the backend\n");
- fprintf(stderr, "\t -E echo all queries sent to the backend\n");
- fprintf(stderr, "\t -f filename use file as a source of queries\n");
- fprintf(stderr, "\t -F sep set the field separator (default is '|')\n");
- fprintf(stderr, "\t -h host set database server host\n");
- fprintf(stderr, "\t -H turn on html3.0 table output\n");
- fprintf(stderr, "\t -l list available databases\n");
- fprintf(stderr, "\t -n don't use readline library\n");
- fprintf(stderr, "\t -o filename send output to filename or (|pipe)\n");
- fprintf(stderr, "\t -p port set port number\n");
- fprintf(stderr, "\t -q run quietly (no messages, no prompts)\n");
- fprintf(stderr, "\t -s single step mode (prompts for each query)\n");
- fprintf(stderr, "\t -S single line mode (i.e. query terminated by newline)\n");
- fprintf(stderr, "\t -t turn off printing of headings and row count\n");
- fprintf(stderr, "\t -T html set html3.0 table command options (cf. -H)\n");
- fprintf(stderr, "\t -u ask for a username and password for authentication\n");
- fprintf(stderr, "\t -x turn on expanded output (field names on left)\n");
- exit(1);
-}
-
-/*
- * slashUsage print out usage for the backslash commands
- */
-
-static char *
-on(bool f)
-{
- return f ? "on" : "off";
-}
-
-static void
-slashUsage(PsqlSettings *pset)
-{
- int usePipe = 0;
- char *pagerenv;
- FILE *fout;
-
-#ifdef TIOCGWINSZ
- if (pset->notty == 0 &&
- (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0))
- {
-#endif
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#ifdef TIOCGWINSZ
- }
-#endif
-
- if (pset->notty == 0 &&
- (pagerenv = getenv("PAGER")) &&
- (pagerenv[0] != '\0') &&
- screen_size.ws_row <= 35 &&
- (fout = popen(pagerenv, "w")))
- {
- usePipe = 1;
- pqsignal(SIGPIPE, SIG_IGN);
- }
- else
- fout = stdout;
-
- /* if you add/remove a line here, change the row test above */
- fprintf(fout, " \\? -- help\n");
- fprintf(fout, " \\a -- toggle field-alignment (currently %s)\n", on(pset->opt.align));
- fprintf(fout, " \\C [<captn>] -- set html3 caption (currently '%s')\n", pset->opt.caption ? pset->opt.caption : "");
- fprintf(fout, " \\connect <dbname|-> <user> -- connect to new database (currently '%s')\n", PQdb(pset->db));
- fprintf(fout, " \\copy table {from | to} <fname>\n");
- fprintf(fout, " \\d [<table>] -- list tables and indices, columns in <table>, or * for all\n");
- fprintf(fout, " \\da -- list aggregates\n");
- fprintf(fout, " \\dd [<object>]- list comment an object.\n");
- fprintf(fout, " \\df -- list functions\n");
- fprintf(fout, " \\di -- list only indices\n");
- fprintf(fout, " \\do -- list operators\n");
- fprintf(fout, " \\ds -- list only sequences\n");
- fprintf(fout, " \\dS -- list system tables and indexes\n");
- fprintf(fout, " \\dt -- list only tables\n");
- fprintf(fout, " \\dT -- list types\n");
- fprintf(fout, " \\e [<fname>] -- edit the current query buffer or <fname>\n");
- fprintf(fout, " \\E [<fname>] -- edit the current query buffer or <fname>, and execute\n");
- fprintf(fout, " \\f [<sep>] -- change field separater (currently '%s')\n", pset->opt.fieldSep);
- fprintf(fout, " \\g [<fname>] [|<cmd>] -- send query to backend [and results in <fname> or pipe]\n");
- fprintf(fout, " \\h [<cmd>] -- help on syntax of sql commands, * for all commands\n");
- fprintf(fout, " \\H -- toggle html3 output (currently %s)\n", on(pset->opt.html3));
- fprintf(fout, " \\i <fname> -- read and execute queries from filename\n");
- fprintf(fout, " \\l -- list all databases\n");
- fprintf(fout, " \\m -- toggle monitor-like table display (currently %s)\n", on(pset->opt.standard));
- fprintf(fout, " \\o [<fname>] [|<cmd>] -- send all query results to stdout, <fname>, or pipe\n");
- fprintf(fout, " \\p -- print the current query buffer\n");
- fprintf(fout, " \\q -- quit\n");
- fprintf(fout, " \\r -- reset(clear) the query buffer\n");
- fprintf(fout, " \\s [<fname>] -- print history or save it in <fname>\n");
- fprintf(fout, " \\t -- toggle table headings and row count (currently %s)\n", on(pset->opt.header));
- fprintf(fout, " \\T [<html>] -- set html3.0 <table ...> options (currently '%s')\n", pset->opt.tableOpt ? pset->opt.tableOpt : "");
- fprintf(fout, " \\x -- toggle expanded output (currently %s)\n", on(pset->opt.expanded));
- fprintf(fout, " \\w <fname> -- write current buffer to a file\n");
- fprintf(fout, " \\z -- list current grant/revoke permissions\n");
- fprintf(fout, " \\! [<cmd>] -- shell escape or command\n");
-
- if (usePipe)
- {
- pclose(fout);
- pqsignal(SIGPIPE, SIG_DFL);
- }
-}
-
-static PGresult *
-PSQLexec(PsqlSettings *pset, char *query)
-{
- PGresult *res;
-
- if (pset->echoAllQueries)
- {
- fprintf(stderr, "QUERY: %s\n", query);
- fprintf(stderr, "\n");
- fflush(stderr);
- }
-
- res = PQexec(pset->db, query);
- if (!res)
- fputs(PQerrorMessage(pset->db), stderr);
- else
- {
- if (PQresultStatus(res) == PGRES_COMMAND_OK ||
- PQresultStatus(res) == PGRES_TUPLES_OK)
- return res;
- if (!pset->quiet)
- fputs(PQerrorMessage(pset->db), stderr);
- PQclear(res);
- }
- return NULL;
-}
-
-/*
- * Code to support command cancellation.
- * If interactive, we enable a SIGINT signal catcher that sends
- * a cancel request to the backend.
- * Note that sending the cancel directly from the signal handler
- * is safe only because PQrequestCancel is carefully written to
- * make it so. We have to be very careful what else we do in the
- * signal handler.
- * Writing on stderr is potentially dangerous, if the signal interrupted
- * some stdio operation on stderr. On Unix we can avoid trouble by using
- * write() instead; on Windows that's probably not workable, but we can
- * at least avoid trusting printf by using the more primitive fputs.
- */
-
-static PGconn *cancelConn = NULL; /* connection to try cancel on */
-
-static void
-safe_write_stderr(const char *s)
-{
-#ifdef WIN32
- fputs(s, stderr);
-#else
- write(fileno(stderr), s, strlen(s));
-#endif
-}
-
-static void
-handle_sigint(SIGNAL_ARGS)
-{
- if (cancelConn == NULL)
- exit(1); /* accept signal if no connection */
- /* Try to send cancel request */
- if (PQrequestCancel(cancelConn))
- safe_write_stderr("\nCANCEL request sent\n");
- else
- {
- safe_write_stderr("\nCannot send cancel request:\n");
- safe_write_stderr(PQerrorMessage(cancelConn));
- }
-}
-
-
-/*
- * listAllDbs
- *
- * list all the databases in the system returns 0 if all went well
- *
- *
- */
-
-static int
-listAllDbs(PsqlSettings *pset)
-{
- PGresult *results;
- char *query = "select * from pg_database;";
-
- if (!(results = PSQLexec(pset, query)))
- return 1;
- else
- {
- PQprint(pset->queryFout,
- results,
- &pset->opt);
- PQclear(results);
- return 0;
- }
-}
-
-/*
- * List The Database Tables returns 0 if all went well
- *
- */
-static int
-tableList(PsqlSettings *pset, bool deep_tablelist, char info_type,
- bool system_tables)
-{
- char listbuf[512];
- int nColumns;
- int i;
- char *rk;
- char *rr;
- PGresult *res;
- int usePipe = 0;
- bool haveIndexes = false;
- char *pagerenv;
- FILE *fout;
-
-#ifdef TIOCGWINSZ
- if (pset->notty == 0 &&
- (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0))
- {
-#endif
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#ifdef TIOCGWINSZ
- }
-#endif
-
- listbuf[0] = '\0';
- strcat(listbuf, "SELECT usename, relname, relkind, relhasrules ");
- strcat(listbuf, "FROM pg_class, pg_user ");
- strcat(listbuf, "WHERE usesysid = relowner ");
- switch (info_type)
- {
- case 't':
- strcat(listbuf, "and ( relkind = 'r') ");
- break;
- case 'i':
- strcat(listbuf, "and ( relkind = 'i') ");
- haveIndexes = true;
- break;
- case 'S':
- strcat(listbuf, "and ( relkind = 'S') ");
- break;
- case 'b':
- default:
- strcat(listbuf, "and ( relkind = 'r' OR relkind = 'i' OR relkind = 'S') ");
- haveIndexes = true;
- break;
- }
- if (!system_tables)
- strcat(listbuf, "and relname !~ '^pg_' ");
- else
- strcat(listbuf, "and relname ~ '^pg_' ");
- /*
- * Large-object relations are automatically ignored because they have
- * relkind 'l'. However, we want to ignore their indexes as well.
- * The clean way to do that would be to do a join to find out which
- * table each index is for. The ugly but fast way is to know that
- * large object indexes have names starting with 'xinx'.
- */
- if (haveIndexes)
- strcat(listbuf, "and (relkind != 'i' OR relname !~ '^xinx') ");
-
- strcat(listbuf, " ORDER BY relname ");
- if (!(res = PSQLexec(pset, listbuf)))
- return -1;
- /* first, print out the attribute names */
- nColumns = PQntuples(res);
- if (nColumns > 0)
- {
- if (pset->notty == 0 &&
- (pagerenv = getenv("PAGER")) &&
- pagerenv[0] != '\0' &&
- (deep_tablelist ||
- screen_size.ws_row <= nColumns + 7) &&
- (fout = popen(pagerenv, "w")))
- {
- usePipe = 1;
- pqsignal(SIGPIPE, SIG_IGN);
- }
- else
- fout = stdout;
-
- if (deep_tablelist)
- {
- /* describe everything here */
- char **table;
-
- table = (char **) malloc(nColumns * sizeof(char *));
- if (table == NULL)
- perror("malloc");
-
- /* load table table */
-
- /*
- * Put double quotes around the table name to allow for
- * mixed-case and whitespaces in the table name. - BGA
- * 1998-11-14
- */
- for (i = 0; i < nColumns; i++)
- {
- table[i] = (char *) malloc(PQgetlength(res, i, 1) * sizeof(char) + 3);
- if (table[i] == NULL)
- perror("malloc");
- strcpy(table[i], "\"");
- strcat(table[i], PQgetvalue(res, i, 1));
- strcat(table[i], "\"");
- }
-
- PQclear(res);
- for (i = 0; i < nColumns; i++)
- tableDesc(pset, table[i], fout);
- free(table);
- }
- else
- {
- /* Display the information */
-
- fprintf(fout, "Database = %s\n", PQdb(pset->db));
- fprintf(fout, " +------------------+----------------------------------+----------+\n");
- fprintf(fout, " | Owner | Relation | Type |\n");
- fprintf(fout, " +------------------+----------------------------------+----------+\n");
-
- /* next, print out the instances */
- for (i = 0; i < PQntuples(res); i++)
- {
- fprintf(fout, " | %-16.16s", PQgetvalue(res, i, 0));
- fprintf(fout, " | %-32.32s | ", PQgetvalue(res, i, 1));
- rk = PQgetvalue(res, i, 2);
- rr = PQgetvalue(res, i, 3);
- if (strcmp(rk, "r") == 0)
- fprintf(fout, "%-8.8s |", (rr[0] == 't') ? "view?" : "table");
- else if (strcmp(rk, "i") == 0)
- fprintf(fout, "%-8.8s |", "index");
- else
- fprintf(fout, "%-8.8s |", "sequence");
- fprintf(fout, "\n");
- }
- fprintf(fout, " +------------------+----------------------------------+----------+\n");
- fprintf(fout, "\n");
- PQclear(res);
- }
- if (usePipe)
- {
- pclose(fout);
- pqsignal(SIGPIPE, SIG_DFL);
- }
- return 0;
-
- }
- else
- {
- PQclear(res);
- switch (info_type)
- {
- case 't':
- fprintf(stderr, "Couldn't find any tables!\n");
- break;
- case 'i':
- fprintf(stderr, "Couldn't find any indices!\n");
- break;
- case 'S':
- fprintf(stderr, "Couldn't find any sequences!\n");
- break;
- case 'b':
- default:
- fprintf(stderr, "Couldn't find any tables, sequences or indices!\n");
- break;
- }
- return -1;
- }
-}
-
-/*
- * List Tables Grant/Revoke Permissions returns 0 if all went well
- *
- */
-static int
-rightsList(PsqlSettings *pset)
-{
- char listbuf[512];
- int nColumns;
- int i;
- int maxCol1Len;
- int maxCol2Len;
- int usePipe = 0;
- char *pagerenv;
- FILE *fout;
- PGresult *res;
-
-#ifdef TIOCGWINSZ
- if (pset->notty == 0 &&
- (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0))
- {
-#endif
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#ifdef TIOCGWINSZ
- }
-#endif
-
- listbuf[0] = '\0';
- strcat(listbuf, "SELECT relname, relacl ");
- strcat(listbuf, "FROM pg_class ");
- /* Currently, we ignore indexes since they have no meaningful rights */
- strcat(listbuf, "WHERE ( relkind = 'r' OR relkind = 'S') ");
- strcat(listbuf, " and relname !~ '^pg_'");
- strcat(listbuf, " ORDER BY relname ");
- if (!(res = PSQLexec(pset, listbuf)))
- return -1;
- /* first, print out the attribute names */
- nColumns = PQntuples(res);
- if (nColumns > 0)
- {
- if (pset->notty == 0 &&
- (pagerenv = getenv("PAGER")) &&
- pagerenv[0] != '\0' &&
- screen_size.ws_row <= nColumns + 7 &&
- (fout = popen(pagerenv, "w")))
- {
- usePipe = 1;
- pqsignal(SIGPIPE, SIG_IGN);
- }
- else
- fout = stdout;
-
- /* choose column widths */
- maxCol1Len = strlen("Relation");
- maxCol2Len = strlen("Grant/Revoke Permissions");
- for (i = 0; i < PQntuples(res); i++)
- {
- int l = strlen(PQgetvalue(res, i, 0));
-
- if (l > maxCol1Len)
- maxCol1Len = l;
- l = strlen(PQgetvalue(res, i, 1));
- if (l > maxCol2Len)
- maxCol2Len = l;
- }
-
- /* Display the information */
-
- fprintf(fout, "Database = %s\n", PQdb(pset->db));
- fprintf(fout, " +");
- emitNtimes(fout, "-", maxCol1Len + 2);
- fprintf(fout, "+");
- emitNtimes(fout, "-", maxCol2Len + 2);
- fprintf(fout, "+\n");
- fprintf(fout, " | %-*s | %-*s |\n",
- maxCol1Len, "Relation",
- maxCol2Len, "Grant/Revoke Permissions");
- fprintf(fout, " +");
- emitNtimes(fout, "-", maxCol1Len + 2);
- fprintf(fout, "+");
- emitNtimes(fout, "-", maxCol2Len + 2);
- fprintf(fout, "+\n");
-
- /* next, print out the instances */
- for (i = 0; i < PQntuples(res); i++)
- {
- fprintf(fout, " | %-*s | %-*s |\n",
- maxCol1Len, PQgetvalue(res, i, 0),
- maxCol2Len, PQgetvalue(res, i, 1));
- }
-
- fprintf(fout, " +");
- emitNtimes(fout, "-", maxCol1Len + 2);
- fprintf(fout, "+");
- emitNtimes(fout, "-", maxCol2Len + 2);
- fprintf(fout, "+\n");
-
- PQclear(res);
- if (usePipe)
- {
- pclose(fout);
- pqsignal(SIGPIPE, SIG_DFL);
- }
- return 0;
- }
- else
- {
- PQclear(res);
- fprintf(stderr, "Couldn't find any tables!\n");
- return -1;
- }
-}
-
-static void
-emitNtimes(FILE *fout, const char *str, int N)
-{
- int i;
-
- for (i = 0; i < N; i++)
- fputs(str, fout);
-}
-
-/*
- * Describe a table
- *
- * Describe the columns in a database table. returns 0 if all went well
- *
- *
- */
-static int
-tableDesc(PsqlSettings *pset, char *table, FILE *fout)
-{
- char descbuf[512];
- int nColumns,
- nIndices;
- char *rtype;
- char *rnotnull;
- char *rhasdef;
- int i;
- int attlen,
- atttypmod;
- PGresult *res,
- *res2;
- int usePipe = 0;
- char *pagerenv;
-
-#ifdef TIOCGWINSZ
- if (pset->notty == 0 &&
- (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0))
- {
-#endif
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#ifdef TIOCGWINSZ
- }
-#endif
-
- /* Build the query */
-
- /*
- * if the table name is surrounded by double-quotes, then don't
- * convert case
- */
- if (*table == '"')
- {
- table++;
- if (*(table + strlen(table) - 1) == '"')
- *(table + strlen(table) - 1) = '\0';
- }
- else
- {
-#ifdef MULTIBYTE
- for (i = 0; table[i]; i += PQmblen(table + i))
-#else
- for (i = 0; table[i]; i++)
-#endif
- if (isascii((unsigned char) table[i]) &&
- isupper(table[i]))
- table[i] = tolower(table[i]);
- }
-
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT a.attnum, a.attname, t.typname, a.attlen, ");
- strcat(descbuf, "a.atttypmod, a.attnotnull, a.atthasdef ");
- strcat(descbuf, "FROM pg_class c, pg_attribute a, pg_type t ");
- strcat(descbuf, "WHERE c.relname = '");
- strcat(descbuf, table);
- strcat(descbuf, "'");
- strcat(descbuf, " and a.attnum > 0 ");
- strcat(descbuf, " and a.attrelid = c.oid ");
- strcat(descbuf, " and a.atttypid = t.oid ");
- strcat(descbuf, " ORDER BY attnum ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- /* first, print out the attribute names */
- nColumns = PQntuples(res);
- if (nColumns > 0)
- {
- if (fout == NULL)
- {
- if (pset->notty == 0 &&
- (pagerenv = getenv("PAGER")) &&
- pagerenv[0] != '\0' &&
- screen_size.ws_row <= nColumns + 7 &&
- (fout = popen(pagerenv, "w")))
- {
- usePipe = 1;
- pqsignal(SIGPIPE, SIG_IGN);
- }
- else
- fout = stdout;
- }
-
- /*
- * Extract the veiw name and veiw definition from pg_views. -Ryan
- * 2/14/99
- */
-
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT viewname, definition ");
- strcat(descbuf, "FROM pg_views ");
- strcat(descbuf, "WHERE viewname like '");
- strcat(descbuf, table);
- strcat(descbuf, "' ");
- if (!(res2 = PSQLexec(pset, descbuf)))
- return -1;
-
- /*
- * Display the information
- */
- if (PQntuples(res2))
- {
-
- /*
- * display the query. o * -Ryan 2/14/99
- */
- fprintf(fout, "View = %s\n", table);
- fprintf(fout, "Query = %s\n", PQgetvalue(res2, 0, 1));
- }
- else
- fprintf(fout, "Table = %s\n", table);
- PQclear(res2);
-
- fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
- fprintf(fout, "| Field | Type | Length|\n");
- fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
-
- /* next, print out the instances */
- for (i = 0; i < PQntuples(res); i++)
- {
- char type_str[33];
-
- fprintf(fout, "| %-32.32s | ", PQgetvalue(res, i, 1));
- rtype = PQgetvalue(res, i, 2);
- attlen = atoi(PQgetvalue(res, i, 3));
- atttypmod = atoi(PQgetvalue(res, i, 4));
- rnotnull = PQgetvalue(res, i, 5);
- rhasdef = PQgetvalue(res, i, 6);
-
- strcpy(type_str, rtype);
- if (strcmp(rtype, "bpchar") == 0)
- strcpy(type_str, "char()");
- else if (strcmp(rtype, "varchar") == 0)
- strcpy(type_str, "varchar()");
- else if (rtype[0] == '_')
- {
- strcpy(type_str, rtype + 1);
- strncat(type_str, "[]", 32 - strlen(type_str));
- type_str[32] = '\0';
- }
-
- if (rnotnull[0] == 't')
- {
- strncat(type_str, " not null", 32 - strlen(type_str));
- type_str[32] = '\0';
- }
- if (rhasdef[0] == 't')
- {
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT d.adsrc ");
- strcat(descbuf, "FROM pg_attrdef d, pg_class c ");
- strcat(descbuf, "WHERE c.relname = '");
- strcat(descbuf, table);
- strcat(descbuf, "'");
- strcat(descbuf, " and c.oid = d.adrelid ");
- strcat(descbuf, " and d.adnum = ");
- strcat(descbuf, PQgetvalue(res, i, 0));
- if (!(res2 = PSQLexec(pset, descbuf)))
- return -1;
- strcat(type_str, " default ");
- strncat(type_str, PQgetvalue(res2, 0, 0), 32 - strlen(type_str));
- type_str[32] = '\0';
- }
- fprintf(fout, "%-32.32s |", type_str);
-
- if (strcmp(rtype, "text") == 0)
- fprintf(fout, "%6s |", "var");
- else if (strcmp(rtype, "bpchar") == 0 ||
- strcmp(rtype, "varchar") == 0)
- fprintf(fout, "%6i |", atttypmod != -1 ? atttypmod - VARHDRSZ : 0);
- else if (strcmp(rtype, "numeric") == 0)
- fprintf(fout, "%3i.%-2i |",
- ((atttypmod - VARHDRSZ) >> 16) & 0xffff,
- (atttypmod - VARHDRSZ) & 0xffff);
- else
- {
- if (attlen > 0)
- fprintf(fout, "%6i |", attlen);
- else
- fprintf(fout, "%6s |", "var");
- }
- fprintf(fout, "\n");
- }
- fprintf(fout, "+----------------------------------+----------------------------------+-------+\n");
- PQclear(res);
-
- /* display defined indexes for this table */
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT c2.relname ");
- strcat(descbuf, "FROM pg_class c, pg_class c2, pg_index i ");
- strcat(descbuf, "WHERE c.relname = '");
- strcat(descbuf, table);
- strcat(descbuf, "'");
- strcat(descbuf, " and c.oid = i.indrelid ");
- strcat(descbuf, " and i.indexrelid = c2.oid ");
- strcat(descbuf, " ORDER BY c2.relname ");
- if ((res = PSQLexec(pset, descbuf)))
- {
- nIndices = PQntuples(res);
- if (nIndices > 0)
- {
-
- /*
- * Display the information
- */
-
- if (nIndices == 1)
- fprintf(fout, "Index: ");
- else
- fprintf(fout, "Indices: ");
-
- /* next, print out the instances */
- for (i = 0; i < PQntuples(res); i++)
- if (i == 0)
- fprintf(fout, "%s\n", PQgetvalue(res, i, 0));
- else
- fprintf(fout, " %s\n", PQgetvalue(res, i, 0));
- fprintf(fout, "\n");
- }
- PQclear(res);
- }
- if (usePipe)
- {
- pclose(fout);
- pqsignal(SIGPIPE, SIG_DFL);
- }
- return 0;
- }
- else
- {
- PQclear(res);
- fprintf(stderr, "Couldn't find table %s!\n", table);
- return -1;
- }
-}
-
-/*
- * Get object comments
- *
- * Describe the columns in a database table. returns 0 if all went well
- *
- *
- */
-static int
-objectDescription(PsqlSettings *pset, char *object)
-{
- char descbuf[512];
- PGresult *res;
- int i;
- bool success;
-
- /* Build the query */
-
- while (isspace(*object))
- object++;
-
- /*
- * if the object name is surrounded by double-quotes, then don't
- * convert case
- */
- if (*object == '"')
- {
- object++;
- if (*(object + strlen(object) - 1) == '"')
- *(object + strlen(object) - 1) = '\0';
- }
- else
- {
-#ifdef MULTIBYTE
- for (i = 0; object[i]; i += PQmblen(object + i))
-#else
- for (i = 0; object[i]; i++)
-#endif
- if (isupper(object[i]))
- object[i] = tolower(object[i]);
- }
-
- descbuf[0] = '\0';
- if (strchr(object, '.') != NULL)
- {
- char table[NAMEDATALEN],
- column[NAMEDATALEN];
-
- StrNCpy(table, object,
- ((strchr(object, '.') - object + 1) < NAMEDATALEN) ?
- (strchr(object, '.') - object + 1) : NAMEDATALEN);
- StrNCpy(column, strchr(object, '.') + 1, NAMEDATALEN);
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_class, pg_attribute, pg_description ");
- strcat(descbuf, "WHERE pg_class.relname = '");
- strcat(descbuf, table);
- strcat(descbuf, "' and ");
- strcat(descbuf, "pg_class.oid = pg_attribute.attrelid and ");
- strcat(descbuf, "pg_attribute.attname = '");
- strcat(descbuf, column);
- strcat(descbuf, "' and ");
- strcat(descbuf, " pg_attribute.oid = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- }
- else
- {
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_class, pg_description ");
- strcat(descbuf, "WHERE pg_class.relname ~ '^");
- strcat(descbuf, object);
- strcat(descbuf, "'");
- strcat(descbuf, " and pg_class.oid = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- else if (PQntuples(res) <= 0)
- {
- PQclear(res);
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_type, pg_description ");
- strcat(descbuf, "WHERE pg_type.typname ~ '^");
- strcat(descbuf, object);
- strcat(descbuf, "' and ");
- strcat(descbuf, " pg_type.oid = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- else if (PQntuples(res) <= 0)
- {
- PQclear(res);
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_proc, pg_description ");
- strcat(descbuf, "WHERE pg_proc.proname ~ '^");
- strcat(descbuf, object);
- strcat(descbuf, "'");
- strcat(descbuf, " and pg_proc.oid = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- else if (PQntuples(res) <= 0)
- {
- PQclear(res);
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_operator, pg_description ");
- strcat(descbuf, "WHERE pg_operator.oprname ~ '^");
- strcat(descbuf, object);
- strcat(descbuf, "'");
- /* operator descriptions are attached to the proc */
- strcat(descbuf, " and RegprocToOid(pg_operator.oprcode) = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- else if (PQntuples(res) <= 0)
- {
- PQclear(res);
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT DISTINCT description ");
- strcat(descbuf, "FROM pg_aggregate, pg_description ");
- strcat(descbuf, "WHERE pg_aggregate.aggname ~ '^");
- strcat(descbuf, object);
- strcat(descbuf, "'");
- strcat(descbuf, " and pg_aggregate.oid = pg_description.objoid ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- else if (PQntuples(res) <= 0)
- {
- PQclear(res);
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT 'no description' as description ");
- if (!(res = PSQLexec(pset, descbuf)))
- return -1;
- }
- }
- }
- }
- }
- }
-
- PQclear(res);
-
- success = SendQuery(pset, descbuf, NULL, NULL);
-
- return 0;
-}
-
-/*
- * Basic routines to read a line of input
- *
- * All three routines will return a malloc'd string of indefinite size.
- */
-typedef char *(*READ_ROUTINE) (char *prompt, FILE *source);
-
-/*
- * gets_noreadline
- * gets a line of interactive input (without using readline)
- *
- * the source is ignored
- */
-static char *
-gets_noreadline(char *prompt, FILE *source)
-{
- fputs(prompt, stdout);
- fflush(stdout);
- return gets_fromFile(prompt, stdin);
-}
-
-/*
- * gets_readline
- * gets a line of interactive input using readline library
- *
- * the source is ignored
- */
-static char *
-gets_readline(char *prompt, FILE *source)
-{
- char *s;
-
-#ifdef USE_READLINE
- s = readline(prompt);
-#else
- s = gets_noreadline(prompt, source);
-#endif
- fputc('\r', stdout);
- fflush(stdout);
- return s;
-}
-
-/*
- * gets_fromFile
- * gets a line of noninteractive input from a file
- *
- * the prompt is ignored
- */
-static char *
-gets_fromFile(char *prompt, FILE *source)
-{
- PQExpBufferData buffer;
- char line[COPYBUFSIZ];
-
- initPQExpBuffer(&buffer);
-
- while (fgets(line, COPYBUFSIZ, source) != NULL)
- {
- appendPQExpBufferStr(&buffer, line);
- if (buffer.data[buffer.len-1] == '\n')
- return buffer.data;
- }
-
- if (buffer.len > 0)
- return buffer.data; /* EOF after reading some bufferload(s) */
-
- /* EOF, so return null */
- termPQExpBuffer(&buffer);
- return NULL;
-}
-
-/*
- * SendQuery: send the query string to the backend.
- *
- * Return true if the query executed successfully, false otherwise.
- *
- * If not NULL, copy_in_stream and copy_out_stream are files to redirect
- * copy in/out data to.
- */
-static bool
-SendQuery(PsqlSettings *pset, const char *query,
- FILE *copy_in_stream, FILE *copy_out_stream)
-{
- bool success = false;
- PGresult *results;
- PGnotify *notify;
-
- if (pset->singleStep)
- fprintf(stdout, "\n**************************************"
- "*****************************************\n");
-
- if (pset->echoQuery || pset->singleStep)
- {
- fprintf(stderr, "QUERY: %s\n", query);
- fflush(stderr);
- }
- if (pset->singleStep)
- {
- fprintf(stdout, "\n**************************************"
- "*****************************************\n");
- fflush(stdout);
- printf("\npress return to continue ..\n");
- gets_fromFile("", stdin);
- }
- results = PQexec(pset->db, query);
- if (results == NULL)
- {
- fprintf(stderr, "%s", PQerrorMessage(pset->db));
- success = false;
- }
- else
- {
- switch (PQresultStatus(results))
- {
- case PGRES_TUPLES_OK:
- if (pset->gfname)
- {
- PsqlSettings settings_copy = *pset;
- FILE *fp;
-
- settings_copy.queryFout = stdout;
- fp = setFout(&settings_copy, pset->gfname);
- if (!fp || fp == stdout)
- {
- success = false;
- break;
- }
- PQprint(fp,
- results,
- &pset->opt);
- if (settings_copy.pipe)
- pclose(fp);
- else
- fclose(fp);
- free(pset->gfname);
- pset->gfname = NULL;
- success = true;
- break;
- }
- else
- {
- success = true;
- PQprint(pset->queryFout,
- results,
- &(pset->opt));
- fflush(pset->queryFout);
- }
- break;
- case PGRES_EMPTY_QUERY:
- success = true;
- break;
- case PGRES_COMMAND_OK:
- success = true;
- if (!pset->quiet)
- printf("%s\n", PQcmdStatus(results));
- break;
- case PGRES_COPY_OUT:
- if (copy_out_stream)
- success = handleCopyOut(pset->db, copy_out_stream);
- else
- {
- if (pset->queryFout == stdout && !pset->quiet)
- printf("Copy command returns...\n");
-
- success = handleCopyOut(pset->db, pset->queryFout);
- }
- break;
- case PGRES_COPY_IN:
- if (copy_in_stream)
- success = handleCopyIn(pset->db, false, copy_in_stream);
- else
- success = handleCopyIn(pset->db,
- cur_cmd_interactive && !pset->quiet,
- cur_cmd_source);
- break;
- case PGRES_NONFATAL_ERROR:
- case PGRES_FATAL_ERROR:
- case PGRES_BAD_RESPONSE:
- success = false;
- fprintf(stderr, "%s", PQerrorMessage(pset->db));
- break;
- }
-
- if (PQstatus(pset->db) == CONNECTION_BAD)
- {
- fprintf(stderr,
- "We have lost the connection to the backend, so "
- "further processing is impossible. "
- "Terminating.\n");
- exit(2); /* we are out'ta here */
- }
- /* check for asynchronous returns */
- while ((notify = PQnotifies(pset->db)) != NULL)
- {
- fprintf(stderr,
- "ASYNC NOTIFY of '%s' from backend pid '%d' received\n",
- notify->relname, notify->be_pid);
- free(notify);
- }
- if (results)
- PQclear(results);
- }
- return success;
-}
-
-
-
-static void
-editFile(char *fname)
-{
- char *editorName;
- char *sys;
-
- editorName = getenv("EDITOR");
- if (!editorName)
- editorName = DEFAULT_EDITOR;
- sys = malloc(strlen(editorName) + strlen(fname) + 32 + 1);
- if (!sys)
- {
- perror("malloc");
- exit(1);
- }
- sprintf(sys, "exec '%s' '%s'", editorName, fname);
- system(sys);
- free(sys);
-}
-
-static bool
-toggle(PsqlSettings *pset, bool *sw, char *msg)
-{
- *sw = !*sw;
- if (!pset->quiet)
- printf("turned %s %s\n", on(*sw), msg);
- return *sw;
-}
-
-
-
-static void
-unescape(char *dest, const char *source)
-{
- /*-----------------------------------------------------------------------------
- Return as the string <dest> the value of string <source> with escape
- sequences turned into the bytes they represent.
- -----------------------------------------------------------------------------*/
- char *p;
- bool esc; /* Last character we saw was the escape
- * character (/) */
-
- esc = false; /* Haven't seen escape character yet */
- for (p = (char *) source; *p; p++)
- {
- char c; /* Our output character */
-
- if (esc)
- {
- switch (*p)
- {
- case 'n':
- c = '\n';
- break;
- case 'r':
- c = '\r';
- break;
- case 't':
- c = '\t';
- break;
- case 'f':
- c = '\f';
- break;
- case '\\':
- c = '\\';
- break;
- default:
- c = *p;
- }
- esc = false;
- }
- else if (*p == '\\')
- {
- esc = true;
- c = ' '; /* meaningless, but compiler doesn't know
- * that */
- }
- else
- {
- c = *p;
- esc = false;
- }
- if (!esc)
- *dest++ = c;
- }
- *dest = '\0'; /* Terminating null character */
-}
-
-
-
-static void
-parse_slash_copy(const char *args, char *table, const int table_len,
- char *file, const int file_len,
- bool *from_p, bool *error_p)
-{
-
- char work_args[200];
-
- /*
- * A copy of the \copy command arguments, except that we modify it as
- * we parse to suit our parsing needs.
- */
- char *table_tok,
- *fromto_tok;
-
- strncpy(work_args, args, sizeof(work_args));
- work_args[sizeof(work_args) - 1] = '\0';
-
- *error_p = false; /* initial assumption */
-
- table_tok = strtok(work_args, " ");
- if (table_tok == NULL)
- {
- fprintf(stderr, "\\copy needs arguments.\n");
- *error_p = true;
- }
- else
- {
- strncpy(table, table_tok, table_len);
- file[table_len - 1] = '\0';
-
- fromto_tok = strtok(NULL, " ");
- if (fromto_tok == NULL)
- {
- fprintf(stderr, "'FROM' or 'TO' must follow table name.\n");
- *error_p = true;
- }
- else
- {
- if (strcasecmp(fromto_tok, "from") == 0)
- *from_p = true;
- else if (strcasecmp(fromto_tok, "to") == 0)
- *from_p = false;
- else
- {
- fprintf(stderr,
- "Unrecognized token found where "
- "'FROM' or 'TO' expected: '%s'.\n",
- fromto_tok);
- *error_p = true;
- }
- if (!*error_p)
- {
- char *file_tok;
-
- file_tok = strtok(NULL, " ");
- if (file_tok == NULL)
- {
- fprintf(stderr, "A file pathname must follow '%s'.\n",
- fromto_tok);
- *error_p = true;
- }
- else
- {
- strncpy(file, file_tok, file_len);
- file[file_len - 1] = '\0';
- if (strtok(NULL, " ") != NULL)
- {
- fprintf(stderr,
- "You have extra tokens after the filename.\n");
- *error_p = true;
- }
- }
- }
- }
- }
-}
-
-
-
-static void
-do_copy(const char *args, PsqlSettings *pset)
-{
- /*---------------------------------------------------------------------------
- Execute a \copy command (frontend copy). We have to open a file, then
- submit a COPY query to the backend and either feed it data from the
- file or route its response into the file.
-
- We do a text copy with default (tab) column delimiters. Some day, we
- should do all the things a backend copy can do.
-
- ----------------------------------------------------------------------------*/
- char query[200];
-
- /* The COPY command we send to the back end */
- bool from;
-
- /* The direction of the copy is from a file to a table. */
- char file[MAXPGPATH];
-
- /* The pathname of the file from/to which we copy */
- char table[NAMEDATALEN];
-
- /* The name of the table from/to which we copy */
- bool syntax_error;
-
- /* The \c command has invalid syntax */
- FILE *copystream;
-
- parse_slash_copy(args, table, sizeof(table), file, sizeof(file),
- &from, &syntax_error);
-
- if (!syntax_error)
- {
- strcpy(query, "COPY ");
- strcat(query, table);
-
- if (from)
- strcat(query, " FROM stdin");
- else
- strcat(query, " TO stdout");
-
- if (from)
-#ifndef __CYGWIN32__
- copystream = fopen(file, "r");
-#else
- copystream = fopen(file, "rb");
-#endif
- else
-#ifndef __CYGWIN32__
- copystream = fopen(file, "w");
-#else
- copystream = fopen(file, "wb");
-#endif
- if (copystream == NULL)
- fprintf(stderr,
- "Unable to open file %s which to copy, errno = %s (%d).",
- from ? "from" : "to", strerror(errno), errno);
- else
- {
- bool success;/* The query succeeded at the backend */
-
- success = SendQuery(pset, query,
- from ? copystream : (FILE *) NULL,
- !from ? copystream : (FILE *) NULL);
- fclose(copystream);
- if (!pset->quiet)
- {
- if (success)
- printf("Successfully copied.\n");
- else
- printf("Copy failed.\n");
- }
- }
- }
-}
-
-
-static void
-do_connect(const char *new_dbname,
- const char *new_user,
- PsqlSettings *pset)
-{
- if (!new_dbname)
- fprintf(stderr, "\\connect must be followed by a database name\n");
- else if (new_user != NULL && pset->getPassword)
- fprintf(stderr, "You can't specify a username when using passwords.\n");
- else
- {
- PGconn *olddb = pset->db;
- const char *dbparam;
- const char *userparam;
- const char *pwparam;
-
- if (strcmp(new_dbname, "-") != 0)
- dbparam = new_dbname;
- else
- dbparam = PQdb(olddb);
-
- if (new_user != NULL && strcmp(new_user, "-") != 0)
- userparam = new_user;
- else
- userparam = PQuser(olddb);
-
- /* FIXME: if changing user, ought to prompt for a new password? */
- pwparam = PQpass(olddb);
-
-#ifdef MULTIBYTE
-
- /*
- * PGCLIENTENCODING may be set by the previous connection. if a
- * user does not explicitly set PGCLIENTENCODING, we should
- * discard PGCLIENTENCODING so that libpq could get the backend
- * encoding as the default PGCLIENTENCODING value. -- 1998/12/12
- * Tatsuo Ishii
- */
-
- if (!has_client_encoding)
- {
- static const char ev[] = "PGCLIENTENCODING=";
-
- putenv(ev);
- }
-#endif
-
- pset->db = PQsetdbLogin(PQhost(olddb), PQport(olddb),
- NULL, NULL, dbparam, userparam, pwparam);
-
- if (!pset->quiet)
- {
- if (!new_user)
- printf("connecting to new database: %s\n", dbparam);
- else if (dbparam != new_dbname)
- printf("connecting as new user: %s\n", new_user);
- else
- printf("connecting to new database: %s as user: %s\n",
- dbparam, new_user);
- }
-
- if (PQstatus(pset->db) == CONNECTION_BAD)
- {
- fprintf(stderr, "%s\n", PQerrorMessage(pset->db));
- fprintf(stderr, "Could not connect to new database. exiting\n");
- exit(2);
- }
- else
- {
- cancelConn = pset->db; /* redirect sigint's loving
- * attentions */
- PQfinish(olddb);
- free(pset->prompt);
- pset->prompt = malloc(strlen(PQdb(pset->db)) + 10);
- sprintf(pset->prompt, "%s%s", PQdb(pset->db), PROMPT);
- }
- }
-}
-
-
-static void
-do_edit(const char *filename_arg, PQExpBuffer query_buf, int *status_p)
-{
- int fd;
- char fnametmp[64];
- char *fname;
- int cc;
- int ql = query_buf->len;
- bool error;
- char line[COPYBUFSIZ+1];
-
- if (filename_arg)
- {
- fname = (char *) filename_arg;
- error = false;
- }
- else
- {
-#ifndef WIN32
- sprintf(fnametmp, "/tmp/psql.%ld.%ld",
- (long) geteuid(), (long) getpid());
-#else
- GetTempFileName(".", "psql", 0, fnametmp);
-#endif
- fname = fnametmp;
- unlink(fname);
- if ((fd = open(fname, O_EXCL | O_CREAT | O_WRONLY, 0600)) < 0)
- {
- perror(fname);
- error = true;
- }
- else
- {
- if (ql == 0 || query_buf->data[ql - 1] != '\n')
- {
- appendPQExpBufferChar(query_buf, '\n');
- ql++;
- }
- if (write(fd, query_buf->data, ql) != ql)
- {
- perror(fname);
- close(fd);
- unlink(fname);
- error = true;
- }
- else
- {
- close(fd);
- error = false;
- }
- }
- }
-
- if (error)
- *status_p = CMD_SKIP_LINE;
- else
- {
- editFile(fname);
- if ((fd = open(fname, O_RDONLY, 0)) < 0)
- {
- perror(fname);
- *status_p = CMD_SKIP_LINE;
- }
- else
- {
- resetPQExpBuffer(query_buf);
- while ((cc = (int) read(fd, line, COPYBUFSIZ)) > 0)
- {
- line[cc] = '\0';
- appendPQExpBufferStr(query_buf, line);
- }
- close(fd);
- rightTrim(query_buf->data);
- query_buf->len = strlen(query_buf->data);
- *status_p = CMD_NEWEDIT;
- }
- if (!filename_arg)
- unlink(fname);
- }
-}
-
-
-static void
-do_help(PsqlSettings *pset, const char *topic)
-{
-
- if (!topic)
- {
- char left_center_right; /* Which column we're displaying */
- int i; /* Index into QL_HELP[] */
-
- printf("type \\h <cmd> where <cmd> is one of the following:\n");
-
- left_center_right = 'L';/* Start with left column */
- i = 0;
- while (QL_HELP[i].cmd != NULL)
- {
- switch (left_center_right)
- {
- case 'L':
- printf(" %-25s", QL_HELP[i].cmd);
- left_center_right = 'C';
- break;
- case 'C':
- printf("%-25s", QL_HELP[i].cmd);
- left_center_right = 'R';
- break;
- case 'R':
- printf("%-25s\n", QL_HELP[i].cmd);
- left_center_right = 'L';
- break;
- }
- i++;
- }
- if (left_center_right != 'L')
- puts("\n");
- printf("type \\h * for a complete description of all commands\n");
- }
- else
- {
- int i; /* Index into QL_HELP[] */
- bool help_found; /* We found the help he asked for */
-
- int usePipe = 0;
- char *pagerenv;
- FILE *fout;
-
- if (strcmp(topic, "*") == 0 &&
- (pset->notty == 0) &&
- (pagerenv = getenv("PAGER")) &&
- (pagerenv[0] != '\0') &&
- (fout = popen(pagerenv, "w")))
- {
- usePipe = 1;
- pqsignal(SIGPIPE, SIG_IGN);
- }
- else
- fout = stdout;
-
- help_found = false; /* Haven't found it yet */
- for (i = 0; QL_HELP[i].cmd; i++)
- {
- if (strcasecmp(QL_HELP[i].cmd, topic) == 0 ||
- strcmp(topic, "*") == 0)
- {
- help_found = true;
- fprintf(fout, "Command: %s\n", QL_HELP[i].cmd);
- fprintf(fout, "Description: %s\n", QL_HELP[i].help);
- fprintf(fout, "Syntax:\n");
- fprintf(fout, "%s\n", QL_HELP[i].syntax);
- fprintf(fout, "\n");
- }
- }
-
- if (usePipe)
- {
- pclose(fout);
- pqsignal(SIGPIPE, SIG_DFL);
- }
-
- if (!help_found)
- fprintf(stderr, "command not found, "
- "try \\h with no arguments to see available help\n");
- }
-}
-
-
-
-static void
-do_shell(const char *command)
-{
-
- if (!command)
- {
- char *sys;
- char *shellName;
-
- shellName = getenv("SHELL");
- if (shellName == NULL)
- shellName = DEFAULT_SHELL;
- sys = malloc(strlen(shellName) + 16);
- if (!sys)
- {
- perror("malloc");
- exit(1);
- }
- sprintf(sys, "exec %s", shellName);
- system(sys);
- free(sys);
- }
- else
- system(command);
-}
-
-
-
-/*----------
- * HandleSlashCmds:
- *
- * Handles all the different commands that start with \
- *
- * 'line' is the current input line (ie, the backslash command)
- * 'query_buf' contains the query-so-far, which may be modified by
- * execution of the backslash command (for example, \r clears it)
- *
- * query_buf can be NULL if there is no query-so-far.
- *
- * Returns a status code:
- * 0 - send currently constructed query to backend (i.e. we got a \g)
- * 1 - skip processing of this line, continue building up query
- * 2 - terminate processing (i.e. we got a \q)
- * 3 - new query supplied by edit
- *----------
- */
-static int
-HandleSlashCmds(PsqlSettings *pset,
- char *line,
- PQExpBuffer query_buf)
-{
- int status = CMD_SKIP_LINE;
- bool success;
- char *cmd;
- /*
- * String: value of the slash command, less the slash and with escape
- * sequences decoded.
- */
- char *optarg;
- /*
- * Pointer inside the <cmd> string to the argument of the slash
- * command, assuming it is a one-character slash command. If it's not
- * a one-character command, this is meaningless.
- */
- char *optarg2;
- /*
- * Pointer inside the <cmd> string to the argument of the slash
- * command assuming it's not a one-character command. If it's a
- * one-character command, this is meaningless.
- */
- int blank_loc;
- /* Offset within <cmd> of first blank */
-
- cmd = malloc(strlen(line)); /* unescaping better not make string grow. */
-
- unescape(cmd, line + 1); /* sets cmd string */
-
- if (strlen(cmd) >= 1 && cmd[strlen(cmd) - 1] == ';') /* strip trailing ; */
- cmd[strlen(cmd) - 1] = '\0';
-
- /*
- * Originally, there were just single character commands. Now, we
- * define some longer, friendly commands, but we have to keep the old
- * single character commands too. \c used to be what \connect is now.
- * Complicating matters is the fact that with the single-character
- * commands, you can start the argument right after the single
- * character, so "\copy" would mean "connect to database named 'opy'".
- */
-
- if (strlen(cmd) > 1)
- optarg = cmd + 1 + strspn(cmd + 1, " \t");
- else
- optarg = NULL;
-
- blank_loc = strcspn(cmd, " \t");
- if (blank_loc == 0 || !cmd[blank_loc])
- optarg2 = NULL;
- else
- optarg2 = cmd + blank_loc + strspn(cmd + blank_loc, " \t");
-
- switch (cmd[0])
- {
- case 'a': /* toggles to align fields on output */
- toggle(pset, &pset->opt.align, "field alignment");
- break;
-
- case 'C': /* define new caption */
- if (pset->opt.caption)
- {
- free(pset->opt.caption);
- pset->opt.caption = NULL;
- }
- if (optarg && !(pset->opt.caption = strdup(optarg)))
- {
- perror("malloc");
- exit(CMD_TERMINATE);
- }
- break;
-
- case 'c':
- {
- if (strncmp(cmd, "copy ", strlen("copy ")) == 0 ||
- strncmp(cmd, "copy ", strlen("copy ")) == 0)
- do_copy(optarg2, pset);
- else if (strcmp(cmd, "copy") == 0)
- {
- fprintf(stderr, "See \\? for help\n");
- break;
- }
- else if (strncmp(cmd, "connect ", strlen("connect ")) == 0 ||
- strcmp(cmd, "connect") == 0 /* issue error message */ )
- {
- char *optarg3 = NULL;
- int blank_loc2;
-
- if (optarg2)
- {
- blank_loc2 = strcspn(optarg2, " \t");
- if (blank_loc2 == 0 || *(optarg2 + blank_loc2) == '\0')
- optarg3 = NULL;
- else
- {
- optarg3 = optarg2 + blank_loc2 +
- strspn(optarg2 + blank_loc2, " \t");
- *(optarg2 + blank_loc2) = '\0';
- }
- }
- do_connect(optarg2, optarg3, pset);
- }
- else
- {
- char *optarg3 = NULL;
- int blank_loc2;
-
- if (optarg)
- {
- blank_loc2 = strcspn(optarg, " \t");
- if (blank_loc2 == 0 || *(optarg + blank_loc2) == '\0')
- optarg3 = NULL;
- else
- {
- optarg3 = optarg + blank_loc2 +
- strspn(optarg + blank_loc2, " \t");
- *(optarg + blank_loc2) = '\0';
- }
- }
- do_connect(optarg, optarg3, pset);
- }
- }
- break;
-
- case 'd': /* \d describe database information */
-
- /*
- * if the optarg2 name is surrounded by double-quotes, then
- * don't convert case
- */
- if (optarg2)
- {
- if (*optarg2 == '"')
- {
- optarg2++;
- if (*(optarg2 + strlen(optarg2) - 1) == '"')
- *(optarg2 + strlen(optarg2) - 1) = '\0';
- }
- else
- {
- int i;
-
-#ifdef MULTIBYTE
- for (i = 0; optarg2[i]; i += PQmblen(optarg2 + i))
-#else
- for (i = 0; optarg2[i]; i++)
-#endif
- if (isupper(optarg2[i]))
- optarg2[i] = tolower(optarg2[i]);
- }
- }
-
-#ifdef TIOCGWINSZ
- if (pset->notty == 0 &&
- (ioctl(fileno(stdout), TIOCGWINSZ, &screen_size) == -1 ||
- screen_size.ws_col == 0 ||
- screen_size.ws_row == 0))
- {
-#endif
- screen_size.ws_row = 24;
- screen_size.ws_col = 80;
-#ifdef TIOCGWINSZ
- }
-#endif
- if (strncmp(cmd, "da", 2) == 0)
- {
- char descbuf[4096];
-
- /* aggregates */
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT a.aggname AS aggname, ");
- strcat(descbuf, " t.typname AS type, ");
- strcat(descbuf, " obj_description(a.oid) as description ");
- strcat(descbuf, "FROM pg_aggregate a, pg_type t ");
- strcat(descbuf, "WHERE a.aggbasetype = t.oid ");
- if (optarg2)
- {
- strcat(descbuf, "AND a.aggname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "UNION ");
- strcat(descbuf, "SELECT a.aggname AS aggname, ");
- strcat(descbuf, " 'all types' as type, ");
- strcat(descbuf, " obj_description(a.oid) as description ");
- strcat(descbuf, "FROM pg_aggregate a ");
- strcat(descbuf, "WHERE a.aggbasetype = 0 ");
- if (optarg2)
- {
- strcat(descbuf, "AND a.aggname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "ORDER BY aggname, type;");
- success = SendQuery(pset, descbuf, NULL, NULL);
- }
- else if (strncmp(cmd, "dd", 2) == 0)
- /* descriptions */
- objectDescription(pset, optarg + 1);
- else if (strncmp(cmd, "df", 2) == 0)
- {
- char descbuf[4096];
-
- /* functions/procedures */
-
- /*
- * we skip in/out funcs by excluding functions that take
- * some arguments, but have no types defined for those
- * arguments
- */
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT t.typname as result, ");
- strcat(descbuf, " p.proname as function, ");
- if (screen_size.ws_col <= 80)
- strcat(descbuf, " substr(oid8types(p.proargtypes),1,14) as arguments, ");
- else
- strcat(descbuf, " oid8types(p.proargtypes) as arguments, ");
- if (screen_size.ws_col <= 80)
- strcat(descbuf, " substr(obj_description(p.oid),1,34) as description ");
- else
- strcat(descbuf, " obj_description(p.oid) as description ");
- strcat(descbuf, "FROM pg_proc p, pg_type t ");
- strcat(descbuf, "WHERE p.prorettype = t.oid and ");
- strcat(descbuf, "(pronargs = 0 or oid8types(p.proargtypes) != '') ");
- if (optarg2)
- {
- strcat(descbuf, "AND p.proname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "ORDER BY result, function, arguments;");
- success = SendQuery(pset, descbuf, NULL, NULL);
- }
- else if (strncmp(cmd, "di", 2) == 0)
- /* only indices */
- tableList(pset, false, 'i', false);
- else if (strncmp(cmd, "do", 2) == 0)
- {
- char descbuf[4096];
-
- /* operators */
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT o.oprname AS op, ");
- strcat(descbuf, " t1.typname AS left_arg, ");
- strcat(descbuf, " t2.typname AS right_arg, ");
- strcat(descbuf, " t0.typname AS result, ");
- if (screen_size.ws_col <= 80)
- strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
- else
- strcat(descbuf, " obj_description(p.oid) as description ");
- strcat(descbuf, "FROM pg_proc p, pg_type t0, ");
- strcat(descbuf, " pg_type t1, pg_type t2, ");
- strcat(descbuf, " pg_operator o ");
- strcat(descbuf, "WHERE p.prorettype = t0.oid AND ");
- strcat(descbuf, " RegprocToOid(o.oprcode) = p.oid AND ");
- strcat(descbuf, " p.pronargs = 2 AND ");
- strcat(descbuf, " o.oprleft = t1.oid AND ");
- strcat(descbuf, " o.oprright = t2.oid ");
- if (optarg2)
- {
- strcat(descbuf, "AND o.oprname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "UNION ");
- strcat(descbuf, "SELECT o.oprname as op, ");
- strcat(descbuf, " ''::name AS left_arg, ");
- strcat(descbuf, " t1.typname AS right_arg, ");
- strcat(descbuf, " t0.typname AS result, ");
- if (screen_size.ws_col <= 80)
- strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
- else
- strcat(descbuf, " obj_description(p.oid) as description ");
- strcat(descbuf, "FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 ");
- strcat(descbuf, "WHERE RegprocToOid(o.oprcode) = p.oid AND ");
- strcat(descbuf, " o.oprresult = t0.oid AND ");
- strcat(descbuf, " o.oprkind = 'l' AND ");
- strcat(descbuf, " o.oprright = t1.oid ");
- if (optarg2)
- {
- strcat(descbuf, "AND o.oprname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "UNION ");
- strcat(descbuf, "SELECT o.oprname as op, ");
- strcat(descbuf, " t1.typname AS left_arg, ");
- strcat(descbuf, " ''::name AS right_arg, ");
- strcat(descbuf, " t0.typname AS result, ");
- if (screen_size.ws_col <= 80)
- strcat(descbuf, " substr(obj_description(p.oid),1,41) as description ");
- else
- strcat(descbuf, " obj_description(p.oid) as description ");
- strcat(descbuf, "FROM pg_operator o, pg_proc p, pg_type t0, pg_type t1 ");
- strcat(descbuf, "WHERE RegprocToOid(o.oprcode) = p.oid AND ");
- strcat(descbuf, " o.oprresult = t0.oid AND ");
- strcat(descbuf, " o.oprkind = 'r' AND ");
- strcat(descbuf, " o.oprleft = t1.oid ");
- if (optarg2)
- {
- strcat(descbuf, "AND o.oprname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- strcat(descbuf, "ORDER BY op, left_arg, right_arg, result;");
- success = SendQuery(pset, descbuf, NULL, NULL);
- }
- else if (strncmp(cmd, "ds", 2) == 0)
- /* only sequences */
- tableList(pset, false, 'S', false);
- else if (strncmp(cmd, "dS", 2) == 0)
- /* system tables */
- tableList(pset, false, 'b', true);
- else if (strncmp(cmd, "dt", 2) == 0)
- /* only tables */
- tableList(pset, false, 't', false);
- else if (strncmp(cmd, "dT", 2) == 0)
- {
- char descbuf[4096];
-
- /* types */
- descbuf[0] = '\0';
- strcat(descbuf, "SELECT typname AS type, ");
- strcat(descbuf, " obj_description(oid) as description ");
- strcat(descbuf, "FROM pg_type ");
- strcat(descbuf, "WHERE typrelid = 0 AND ");
- strcat(descbuf, " typname !~ '^_.*' ");
- strcat(descbuf, "ORDER BY type;");
- if (optarg2)
- {
- strcat(descbuf, "AND typname ~ '^");
- strcat(descbuf, optarg2);
- strcat(descbuf, "' ");
- }
- success = SendQuery(pset, descbuf, NULL, NULL);
- }
- else if (!optarg)
- /* show tables, sequences and indices */
- tableList(pset, false, 'b', false);
- else if (strcmp(optarg, "*") == 0)
- { /* show everything */
- if (tableList(pset, false, 'b', false) == 0)
- tableList(pset, true, 'b', false);
- }
- else if (strncmp(cmd, "d ", 2) == 0)
- /* describe the specified table */
- tableDesc(pset, optarg, NULL);
- else
- slashUsage(pset);
- break;
-
- case 'e': /* edit */
- if (query_buf)
- do_edit(optarg, query_buf, &status);
- break;
-
- case 'E':
- {
- FILE *fd;
- static char *lastfile;
- struct stat st,
- st2;
-
- if (optarg)
- {
- if (lastfile)
- free(lastfile);
- lastfile = malloc(strlen(optarg + 1));
- if (!lastfile)
- {
- perror("malloc");
- exit(CMD_TERMINATE);
- }
- strcpy(lastfile, optarg);
- }
- else if (!lastfile)
- {
- fprintf(stderr, "\\r must be followed by a file name initially\n");
- break;
- }
- stat(lastfile, &st);
- editFile(lastfile);
-#ifndef __CYGWIN32__
- if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "r")) == NULL))
-#else
- if ((stat(lastfile, &st2) == -1) || ((fd = fopen(lastfile, "rb")) == NULL))
-#endif
- {
- perror(lastfile);
- break;
- }
- if (st2.st_mtime == st.st_mtime)
- {
- if (!pset->quiet)
- fprintf(stderr, "warning: %s not modified. query not executed\n", lastfile);
- fclose(fd);
- break;
- }
- MainLoop(pset, fd);
- fclose(fd);
- break;
- }
-
- case 'f':
- {
- char *fs = DEFAULT_FIELD_SEP;
-
- if (optarg)
- fs = optarg;
- /* handle \f \{space} */
- if (optarg && !*optarg && strlen(cmd) > 1)
- {
- int i;
-
- /* line and cmd match until the first blank space */
- for (i = 2; isspace(line[i]); i++)
- ;
- fs = cmd + i - 1;
- }
- if (pset->opt.fieldSep)
- free(pset->opt.fieldSep);
- if (!(pset->opt.fieldSep = strdup(fs)))
- {
- perror("malloc");
- exit(CMD_TERMINATE);
- }
- if (!pset->quiet)
- printf("field separator changed to '%s'\n", pset->opt.fieldSep);
- break;
- }
- case 'g': /* \g means send query */
- if (!optarg)
- pset->gfname = NULL;
- else if (!(pset->gfname = strdup(optarg)))
- {
- perror("malloc");
- exit(CMD_TERMINATE);
- }
- status = CMD_SEND;
- break;
-
- case 'h': /* help */
- {
- do_help(pset, optarg);
- break;
- }
-
- case 'i': /* \i is include file */
- {
- FILE *fd;
-
- if (!optarg)
- {
- fprintf(stderr, "\\i must be followed by a file name\n");
- break;
- }
-#ifndef __CYGWIN32__
- if ((fd = fopen(optarg, "r")) == NULL)
-#else
- if ((fd = fopen(optarg, "rb")) == NULL)
-#endif
- {
- fprintf(stderr, "file named %s could not be opened\n", optarg);
- break;
- }
- MainLoop(pset, fd);
- fclose(fd);
- break;
- }
-
- case 'H':
- if (toggle(pset, &pset->opt.html3, "HTML3.0 tabular output"))
- pset->opt.standard = 0;
- break;
-
- case 'l': /* \l is list database */
- listAllDbs(pset);
- break;
-
- case 'm': /* monitor like type-setting */
- if (toggle(pset, &pset->opt.standard, "standard SQL separaters and padding"))
- {
- pset->opt.html3 = pset->opt.expanded = 0;
- pset->opt.align = pset->opt.header = 1;
- if (pset->opt.fieldSep)
- free(pset->opt.fieldSep);
- pset->opt.fieldSep = strdup("|");
- if (!pset->quiet)
- printf("field separator changed to '%s'\n", pset->opt.fieldSep);
- }
- else
- {
- if (pset->opt.fieldSep)
- free(pset->opt.fieldSep);
- pset->opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
- if (!pset->quiet)
- printf("field separator changed to '%s'\n", pset->opt.fieldSep);
- }
- break;
-
- case 'o':
- setFout(pset, optarg);
- break;
-
- case 'p':
- if (query_buf && query_buf->len > 0)
- {
- fputs(query_buf->data, stdout);
- fputc('\n', stdout);
- }
- break;
-
- case 'q': /* \q is quit */
- status = CMD_TERMINATE;
- break;
-
- case 'r': /* reset(clear) the buffer */
- if (query_buf)
- {
- resetPQExpBuffer(query_buf);
- if (!pset->quiet)
- printf("buffer reset(cleared)\n");
- }
- break;
-
- case 's': /* \s is save history to a file */
- if (!optarg)
- optarg = "/dev/tty";
-#ifdef USE_HISTORY
- if (write_history(optarg) != 0)
- fprintf(stderr, "cannot write history to %s\n", optarg);
-#endif
- break;
-
- case 't': /* toggle headers */
- toggle(pset, &pset->opt.header, "output headings and row count");
- break;
-
- case 'T': /* define html <table ...> option */
- if (pset->opt.tableOpt)
- free(pset->opt.tableOpt);
- if (!optarg)
- pset->opt.tableOpt = NULL;
- else if (!(pset->opt.tableOpt = strdup(optarg)))
- {
- perror("malloc");
- exit(CMD_TERMINATE);
- }
- break;
-
- case 'w':
- {
- FILE *fd;
-
- if (!optarg)
- {
- fprintf(stderr, "\\w must be followed by a file name\n");
- break;
- }
-#ifndef __CYGWIN32__
- if ((fd = fopen(optarg, "w")) == NULL)
-#else
- if ((fd = fopen(optarg, "w")) == NULL)
-#endif
- {
- fprintf(stderr, "file named %s could not be opened\n", optarg);
- break;
- }
- if (query_buf)
- fputs(query_buf->data, fd);
- fputc('\n', fd);
- fclose(fd);
- break;
- }
-
- case 'x':
- toggle(pset, &pset->opt.expanded, "expanded table representation");
- break;
-
- case 'z': /* list table rights (grant/revoke) */
- rightsList(pset);
- break;
-
- case '!':
- do_shell(optarg);
- break;
- default:
-
- case '?': /* \? is help */
- slashUsage(pset);
- break;
- }
- free(cmd);
- return status;
-}
-
-/* MainLoop()
- * Main processing loop for reading lines of input
- * and sending them to the backend.
- *
- * This loop is re-entrant. May be called by \i command
- * which reads input from a file.
- * db_ptr must be initialized and set.
- */
-
-static int
-MainLoop(PsqlSettings *pset, FILE *source)
-{
- PQExpBuffer query_buf; /* buffer for query being accumulated */
- char *line; /* current line of input */
- char *xcomment; /* start of extended comment */
- int len; /* length of the line */
- int successResult = 1;
- int slashCmdStatus = CMD_SEND;
-
- /*--------------------------------------------------------------
- * slashCmdStatus can be:
- * CMD_UNKNOWN - send currently constructed query to backend
- * (i.e. we got a \g)
- * CMD_SEND - send currently constructed query to backend
- * (i.e. we got a \g)
- * CMD_SKIP_LINE - skip processing of this line, continue building
- * up query
- * CMD_TERMINATE - terminate processing of this query entirely
- * CMD_NEWEDIT - new query supplied by edit
- *---------------------------------------------------------------
- */
-
- bool querySent = false;
- READ_ROUTINE GetNextLine;
- bool eof = false; /* end of our command input? */
- bool success;
- char in_quote; /* == 0 for no in_quote */
- bool was_bslash; /* backslash */
- int paren_level;
- char *query_start;
-
- /* Stack the prior command source */
- FILE *prev_cmd_source = cur_cmd_source;
- bool prev_cmd_interactive = cur_cmd_interactive;
-
- /* Establish new source */
- cur_cmd_source = source;
- cur_cmd_interactive = ((source == stdin) && !pset->notty);
-
- if (cur_cmd_interactive)
- {
- if (pset->prompt)
- free(pset->prompt);
- pset->prompt = malloc(strlen(PQdb(pset->db)) + strlen(PROMPT) + 1);
- if (pset->quiet)
- pset->prompt[0] = '\0';
- else
- sprintf(pset->prompt, "%s%s", PQdb(pset->db), PROMPT);
- if (pset->useReadline)
- {
-#ifdef USE_HISTORY
- using_history();
-#endif
- GetNextLine = gets_readline;
- }
- else
- GetNextLine = gets_noreadline;
- }
- else
- GetNextLine = gets_fromFile;
-
- query_buf = createPQExpBuffer();
- xcomment = NULL;
- in_quote = false;
- paren_level = 0;
- slashCmdStatus = CMD_UNKNOWN; /* set default */
-
- /* main loop to get queries and execute them */
- while (!eof)
- {
- if (slashCmdStatus == CMD_NEWEDIT)
- {
- /*
- * just returned from editing the line? then just copy to the
- * input buffer
- */
- line = strdup(query_buf->data);
- resetPQExpBuffer(query_buf);
- /* reset parsing state since we are rescanning whole query */
- xcomment = NULL;
- in_quote = false;
- paren_level = 0;
- }
- else
- {
- /*
- * otherwise, set interactive prompt if necessary
- * and get another line
- */
- if (cur_cmd_interactive && !pset->quiet)
- {
- if (in_quote && in_quote == PROMPT_SINGLEQUOTE)
- pset->prompt[strlen(pset->prompt) - 3] = PROMPT_SINGLEQUOTE;
- else if (in_quote && in_quote == PROMPT_DOUBLEQUOTE)
- pset->prompt[strlen(pset->prompt) - 3] = PROMPT_DOUBLEQUOTE;
- else if (xcomment != NULL)
- pset->prompt[strlen(pset->prompt) - 3] = PROMPT_COMMENT;
- else if (query_buf->len > 0 && !querySent)
- pset->prompt[strlen(pset->prompt) - 3] = PROMPT_CONTINUE;
- else
- pset->prompt[strlen(pset->prompt) - 3] = PROMPT_READY;
- }
- line = GetNextLine(pset->prompt, source);
-#ifdef USE_HISTORY
- if (cur_cmd_interactive && pset->useReadline && line != NULL)
- add_history(line); /* save non-empty lines in history */
-#endif
- }
-
- /*
- * query_buf holds query already accumulated. line is the malloc'd
- * new line of input (note it must be freed before looping around!)
- * query_start is the next command start location within the line.
- */
- if (line == NULL || (!cur_cmd_interactive && *line == '\0'))
- { /* No more input. Time to quit, or \i
- * done */
- if (!pset->quiet)
- printf("EOF\n");/* Goes on prompt line */
- eof = true;
- continue;
- }
-
- /* not currently inside an extended comment? */
- if (xcomment == NULL)
- {
- query_start = line;
- }
- else
- {
- /* otherwise, continue the extended comment... */
- query_start = line;
- xcomment = line;
- }
-
- /* remove whitespaces on the right, incl. \n's */
- line = rightTrim(line);
-
- /* echo back if input is from file */
- if (!cur_cmd_interactive && !pset->singleStep && !pset->quiet)
- fprintf(stderr, "%s\n", line);
-
- slashCmdStatus = CMD_UNKNOWN;
- /* nothing on line after trimming? then ignore */
- if (line[0] == '\0')
- {
- free(line);
- continue;
- }
-
- len = strlen(line);
-
- if (pset->singleLineMode)
- {
- success = SendQuery(pset, line, NULL, NULL);
- successResult &= success;
- querySent = true;
- }
- else
- {
- int i;
-
- /*
- * Parse line, looking for command separators.
- *
- * The current character is at line[i], the prior character at
- * line[i - prevlen], the next character at line[i + thislen].
- */
-#ifdef MULTIBYTE
- int prevlen = 0;
- int thislen = (len > 0) ? PQmblen(line) : 0;
-
-#define ADVANCE_I (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
-#else
-#define prevlen 1
-#define thislen 1
-#define ADVANCE_I (i++)
-#endif
-
- was_bslash = false;
- for (i = 0; i < len; ADVANCE_I)
- {
- if (line[i] == '\\' && !in_quote)
- {
- /* backslash command. Copy whatever is before \ to
- * query_buf.
- */
- char hold_char = line[i];
-
- line[i] = '\0';
- if (query_start[0] != '\0')
- {
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- appendPQExpBufferStr(query_buf, query_start);
- }
- line[i] = hold_char;
- query_start = line + i;
- break; /* go handle backslash command */
- }
-
- if (querySent &&
- isascii((unsigned char) (line[i])) &&
- !isspace(line[i]))
- {
- resetPQExpBuffer(query_buf);
- querySent = false;
- }
-
- if (was_bslash)
- was_bslash = false;
- else if (i > 0 && line[i - prevlen] == '\\')
- was_bslash = true;
-
- /* inside a quote? */
- if (in_quote && (line[i] != in_quote || was_bslash))
- /* do nothing */ ;
- /* inside an extended comment? */
- else if (xcomment != NULL)
- {
- if (line[i] == '*' && line[i + thislen] == '/')
- {
- xcomment = NULL;
- ADVANCE_I;
- }
- }
- /* start of extended comment? */
- else if (line[i] == '/' && line[i + thislen] == '*')
- {
- xcomment = line + i;
- ADVANCE_I;
- }
- /* single-line comment? truncate line */
- else if ((line[i] == '-' && line[i + thislen] == '-') ||
- (line[i] == '/' && line[i + thislen] == '/'))
- {
- /* print comment at top of query */
- if (pset->singleStep)
- fprintf(stdout, "%s\n", line + i);
- line[i] = '\0'; /* remove comment */
- break;
- }
- else if (in_quote && line[i] == in_quote)
- in_quote = false;
- else if (!in_quote && (line[i] == '\'' || line[i] == '"'))
- in_quote = line[i];
- /* semi-colon? then send query now */
- else if (!paren_level && line[i] == ';')
- {
- char hold_char = line[i + thislen];
-
- line[i + thislen] = '\0';
- if (query_start[0] != '\0')
- {
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- appendPQExpBufferStr(query_buf, query_start);
- }
- success = SendQuery(pset, query_buf->data, NULL, NULL);
- successResult &= success;
- line[i + thislen] = hold_char;
- query_start = line + i + thislen;
- /* sometimes, people do ';\g', don't execute twice */
- if (*query_start == '\\' &&
- *(query_start + 1) == 'g')
- query_start += 2;
- querySent = true;
- }
- else if (line[i] == '(')
- {
- paren_level++;
-
- }
- else if (paren_level && line[i] == ')')
- paren_level--;
- }
- }
-
- /* nothing on line after trimming? then ignore */
- if (line[0] == '\0')
- {
- free(line);
- continue;
- }
-
- if (!in_quote && query_start[0] == '\\')
- {
- /* loop to handle \p\g and other backslash combinations */
- while (query_start[0] != '\0')
- {
- char hold_char;
-
-#ifndef WIN32
- /* I believe \w \dos\system\x would cause a problem */
- /* do we have '\p\g' or '\p \g' ? */
- if (strlen(query_start) > 2 &&
- query_start[2 + strspn(query_start + 2, " \t")] == '\\')
- {
- hold_char = query_start[2 + strspn(query_start + 2, " \t")];
- query_start[2 + strspn(query_start + 2, " \t")] = '\0';
- }
- else
-/* spread over #endif */
-#endif
- hold_char = '\0';
-
- slashCmdStatus = HandleSlashCmds(pset,
- query_start,
- query_buf);
-
- if (slashCmdStatus == CMD_SKIP_LINE && !hold_char)
- {
- if (query_buf->len == 0)
- paren_level = 0;
- break;
- }
- if (slashCmdStatus == CMD_TERMINATE)
- break;
-
- query_start += strlen(query_start);
- if (hold_char)
- query_start[0] = hold_char;
- }
- free(line);
- if (slashCmdStatus == CMD_TERMINATE)
- break; /* They did \q, leave the loop */
- }
- else
- {
- if (query_start[0] != '\0')
- {
- querySent = false;
- if (query_buf->len > 0)
- appendPQExpBufferChar(query_buf, '\n');
- appendPQExpBufferStr(query_buf, query_start);
- }
- free(line);
- }
-
- /* had a backslash-g? force the query to be sent */
- if (slashCmdStatus == CMD_SEND)
- {
- success = SendQuery(pset, query_buf->data, NULL, NULL);
- successResult &= success;
- xcomment = NULL;
- in_quote = false;
- paren_level = 0;
- querySent = true;
- }
- } /* while */
-
- destroyPQExpBuffer(query_buf);
-
- cur_cmd_source = prev_cmd_source;
- cur_cmd_interactive = prev_cmd_interactive;
-
- return successResult;
-} /* MainLoop() */
-
-int
-main(int argc, char **argv)
-{
- extern char *optarg;
- extern int optind;
-
- char *dbname = NULL;
- char *host = NULL;
- char *port = NULL;
- char *qfilename = NULL;
-
- PsqlSettings settings;
-
- char *singleQuery = NULL;
-
- bool listDatabases = 0;
- int successResult = 1;
- bool singleSlashCmd = 0;
- int c;
-
- char *home = NULL; /* Used to store $HOME */
- char *version = NULL; /* PostgreSQL version */
-
- /*
- * initialize cur_cmd_source in case we do not use MainLoop ... some
- * systems fail if we try to use a static initializer for this :-(
- */
- cur_cmd_source = stdin;
- cur_cmd_interactive = false;
-
- MemSet(&settings, 0, sizeof settings);
- settings.opt.align = 1;
- settings.opt.header = 1;
- settings.queryFout = stdout;
- settings.opt.fieldSep = strdup(DEFAULT_FIELD_SEP);
- settings.opt.pager = 1;
- if (!isatty(0) || !isatty(1))
- {
- /* Noninteractive defaults */
- settings.notty = 1;
- }
- else
- {
- /* Interactive defaults */
- pqsignal(SIGINT, handle_sigint); /* control-C => cancel */
-#ifdef USE_READLINE
- settings.useReadline = 1;
- {
- /* Set the application name, used for parsing .inputrc */
- char *progname = strrchr(argv[0], SEP_CHAR);
- rl_readline_name = (progname ? progname+1 : argv[0]);
- }
-#endif
- }
-#ifdef PSQL_ALWAYS_GET_PASSWORDS
- settings.getPassword = 1;
-#else
- settings.getPassword = 0;
-#endif
-
-#ifdef MULTIBYTE
- has_client_encoding = getenv("PGCLIENTENCODING");
-#endif
-
- while ((c = getopt(argc, argv, "Aa:c:d:eEf:F:lh:Hnso:p:qStT:ux")) != EOF)
- {
- switch (c)
- {
- case 'A':
- settings.opt.align = 0;
- break;
- case 'a':
-#ifdef NOT_USED /* this no longer does anything */
- fe_setauthsvc(optarg, errbuf);
-#endif
- break;
- case 'c':
- singleQuery = strdup(optarg);
- if (singleQuery[0] == '\\')
- singleSlashCmd = 1;
- break;
- case 'd':
- dbname = optarg;
- break;
- case 'e':
- settings.echoQuery = 1;
- break;
- case 'E':
- settings.echoAllQueries = 1;
- settings.echoQuery = 1;
- break;
- case 'f':
- qfilename = optarg;
- break;
- case 'F':
- settings.opt.fieldSep = strdup(optarg);
- break;
- case 'l':
- listDatabases = 1;
- break;
- case 'h':
- host = optarg;
- break;
- case 'H':
- settings.opt.html3 = 1;
- break;
- case 'n':
- settings.useReadline = 0;
- break;
- case 'o':
- setFout(&settings, optarg);
- break;
- case 'p':
- port = optarg;
- break;
- case 'q':
- settings.quiet = 1;
- break;
- case 's':
- settings.singleStep = 1;
- break;
- case 'S':
- settings.singleLineMode = 1;
- break;
- case 't':
- settings.opt.header = 0;
- break;
- case 'T':
- settings.opt.tableOpt = strdup(optarg);
- break;
- case 'u':
- settings.getPassword = 1;
- break;
- case 'x':
- settings.opt.expanded = 1;
- break;
- default:
- usage(argv[0]);
- break;
- }
- }
- /* if we still have an argument, use it as the database name */
- if (argc - optind == 1)
- dbname = argv[optind];
-
- if (listDatabases)
- dbname = "template1";
-
- if (settings.getPassword)
- {
- char username[100];
- char password[100];
-
- prompt_for_password(username, password);
-
- settings.db = PQsetdbLogin(host, port, NULL, NULL, dbname,
- username, password);
- }
- else
- settings.db = PQsetdb(host, port, NULL, NULL, dbname);
-
- dbname = PQdb(settings.db);
-
- if (PQstatus(settings.db) == CONNECTION_BAD)
- {
- fprintf(stderr, "Connection to database '%s' failed.\n", dbname);
- fprintf(stderr, "%s\n", PQerrorMessage(settings.db));
- PQfinish(settings.db);
- exit(1);
- }
-
- cancelConn = settings.db; /* enable SIGINT to send cancel */
-
- if (listDatabases)
- exit(listAllDbs(&settings));
- if (!settings.quiet && !settings.notty && !singleQuery && !qfilename)
- {
- printf("Welcome to the POSTGRESQL interactive sql monitor:\n");
- printf(" Please read the file COPYRIGHT for copyright terms "
- "of POSTGRESQL\n");
-
- if ((version = selectVersion(&settings)) != NULL)
- printf("[%s]\n", version);
-
- printf("\n");
- printf(" type \\? for help on slash commands\n");
- printf(" type \\q to quit\n");
- printf(" type \\g or terminate with semicolon to execute query\n");
- printf(" You are currently connected to the database: %s\n\n", dbname);
- }
-
- /*
- * 20.06.97 ACRM See if we've got a /etc/psqlrc or .psqlrc file
- */
- if (!access("/etc/psqlrc", R_OK))
- HandleSlashCmds(&settings, "\\i /etc/psqlrc", NULL);
- if ((home = getenv("HOME")) != NULL)
- {
- char *psqlrc = NULL,
- *line = NULL;
-
- if ((psqlrc = (char *) malloc(strlen(home) + 10)) != NULL)
- {
- sprintf(psqlrc, "%s/.psqlrc", home);
- if (!access(psqlrc, R_OK))
- {
- if ((line = (char *) malloc(strlen(psqlrc) + 5)) != NULL)
- {
- sprintf(line, "\\i %s", psqlrc);
- HandleSlashCmds(&settings, line, NULL);
- free(line);
- }
- }
- free(psqlrc);
- }
- }
- /* End of check for psqlrc files */
-
- if (qfilename || singleSlashCmd)
- {
-
- /*
- * read in a file full of queries instead of reading in queries
- * interactively
- */
- char *line;
-
- if (singleSlashCmd)
- {
- /* Not really a query, but "Do what I mean, not what I say." */
- line = singleQuery;
- }
- else
- {
- line = malloc(strlen(qfilename) + 5);
- sprintf(line, "\\i %s", qfilename);
- }
- HandleSlashCmds(&settings, line, NULL);
- free(line);
- }
- else
- {
- if (singleQuery)
- successResult = SendQuery(&settings, singleQuery, NULL, NULL);
- else
- successResult = MainLoop(&settings, stdin);
- }
-
- PQfinish(settings.db);
- free(settings.opt.fieldSep);
- if (settings.prompt)
- free(settings.prompt);
-
- return !successResult;
-}
-
-static bool
-handleCopyOut(PGconn *conn, FILE *copystream)
-{
- bool copydone;
- char copybuf[COPYBUFSIZ];
- int ret;
-
- copydone = false; /* Can't be done; haven't started. */
-
- while (!copydone)
- {
- ret = PQgetline(conn, copybuf, COPYBUFSIZ);
-
- if (copybuf[0] == '\\' &&
- copybuf[1] == '.' &&
- copybuf[2] == '\0')
- {
- copydone = true; /* don't print this... */
- }
- else
- {
- fputs(copybuf, copystream);
- switch (ret)
- {
- case EOF:
- copydone = true;
- /* FALLTHROUGH */
- case 0:
- fputc('\n', copystream);
- break;
- case 1:
- break;
- }
- }
- }
- fflush(copystream);
- return !PQendcopy(conn);
-}
-
-
-
-static bool
-handleCopyIn(PGconn *conn, const bool mustprompt, FILE *copystream)
-{
- bool copydone = false;
- bool firstload;
- bool linedone;
- char copybuf[COPYBUFSIZ];
- char *s;
- int buflen;
- int c = 0;
-
- if (mustprompt)
- {
- fputs("Enter info followed by a newline\n", stdout);
- fputs("End with a backslash and a "
- "period on a line by itself.\n", stdout);
- }
- while (!copydone)
- { /* for each input line ... */
- if (mustprompt)
- {
- fputs(">> ", stdout);
- fflush(stdout);
- }
- firstload = true;
- linedone = false;
- while (!linedone)
- { /* for each buffer ... */
- s = copybuf;
- for (buflen = COPYBUFSIZ; buflen > 1; buflen--)
- {
- c = getc(copystream);
- if (c == '\n' || c == EOF)
- {
- linedone = true;
- break;
- }
- *s++ = c;
- }
- *s = '\0';
- if (c == EOF)
- {
- PQputline(conn, "\\.");
- copydone = true;
- break;
- }
- PQputline(conn, copybuf);
- if (firstload)
- {
- if (!strcmp(copybuf, "\\."))
- copydone = true;
- firstload = false;
- }
- }
- PQputline(conn, "\n");
- }
- return !PQendcopy(conn);
-}
-
-
-
-/*
- * try to open fname and return a FILE *, if it fails, use stdout, instead
- */
-
-static FILE *
-setFout(PsqlSettings *pset, char *fname)
-{
- if (pset->queryFout && pset->queryFout != stdout)
- {
- if (pset->pipe)
- pclose(pset->queryFout);
- else
- fclose(pset->queryFout);
- }
- if (!fname)
- {
- pset->queryFout = stdout;
- pqsignal(SIGPIPE, SIG_DFL);
- }
- else
- {
- if (*fname == '|')
- {
- pqsignal(SIGPIPE, SIG_IGN);
-#ifndef __CYGWIN32__
- pset->queryFout = popen(fname + 1, "w");
-#else
- pset->queryFout = popen(fname + 1, "wb");
-#endif
- pset->pipe = 1;
- }
- else
- {
- pset->queryFout = fopen(fname, "w");
- pqsignal(SIGPIPE, SIG_DFL);
- pset->pipe = 0;
- }
- if (!pset->queryFout)
- {
- perror(fname);
- pset->queryFout = stdout;
- }
- }
- return pset->queryFout;
-}
-
-static void
-prompt_for_password(char *username, char *password)
-{
- char buf[512];
- int length;
-
-#ifdef HAVE_TERMIOS_H
- struct termios t_orig,
- t;
-
-#endif
-
- printf("Username: ");
- fgets(username, 100, stdin);
- length = strlen(username);
- /* skip rest of the line */
- if (length > 0 && username[length - 1] != '\n')
- {
- do
- {
- fgets(buf, 512, stdin);
- } while (buf[strlen(buf) - 1] != '\n');
- }
- if (length > 0 && username[length - 1] == '\n')
- username[length - 1] = '\0';
-
- printf("Password: ");
-#ifdef HAVE_TERMIOS_H
- tcgetattr(0, &t);
- t_orig = t;
- t.c_lflag &= ~ECHO;
- tcsetattr(0, TCSADRAIN, &t);
-#endif
- fgets(password, 100, stdin);
-#ifdef HAVE_TERMIOS_H
- tcsetattr(0, TCSADRAIN, &t_orig);
-#endif
-
- length = strlen(password);
- /* skip rest of the line */
- if (length > 0 && password[length - 1] != '\n')
- {
- do
- {
- fgets(buf, 512, stdin);
- } while (buf[strlen(buf) - 1] != '\n');
- }
- if (length > 0 && password[length - 1] == '\n')
- password[length - 1] = '\0';
-
- printf("\n\n");
-}
-
-static char *
-selectVersion(PsqlSettings *pset)
-{
-#define PGVERSIONBUFSZ 128
- static char version[PGVERSIONBUFSZ + 1];
- PGresult *res;
- char *query = "select version();";
-
- if (!(res = PQexec(pset->db, query)))
- return (NULL);
-
- if (PQresultStatus(res) == PGRES_COMMAND_OK ||
- PQresultStatus(res) == PGRES_TUPLES_OK)
- {
- strncpy(version, PQgetvalue(res, 0, 0), PGVERSIONBUFSZ);
- version[PGVERSIONBUFSZ] = '\0';
- PQclear(res);
- return (version);
- }
- else
- {
- PQclear(res);
- return (NULL);
- }
-}