diff options
Diffstat (limited to 'src/bin/psql/psql.c')
| -rw-r--r-- | src/bin/psql/psql.c | 3297 |
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); - } -} |
