diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 971ad0c8161..28d5cef0536 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.171 2006/07/18 17:42:01 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.172 2006/08/29 15:19:50 tgl Exp $ */ #include "postgres_fe.h" #include "command.h" @@ -55,8 +55,6 @@ static backslashResult exec_command(const char *cmd, static bool do_edit(const char *filename_arg, PQExpBuffer query_buf); static bool do_connect(char *dbname, char *user, char *host, char *port); static bool do_shell(const char *command); -static void SyncVerbosityVariable(void); - /*---------- @@ -196,7 +194,6 @@ exec_command(const char *cmd, { bool success = true; /* indicate here if the command ran ok or * failed */ - bool quiet = QUIET(); backslashResult status = PSQL_CMD_SKIP_LINE; /* @@ -206,9 +203,9 @@ exec_command(const char *cmd, if (strcmp(cmd, "a") == 0) { if (pset.popt.topt.format != PRINT_ALIGNED) - success = do_pset("format", "aligned", &pset.popt, quiet); + success = do_pset("format", "aligned", &pset.popt, pset.quiet); else - success = do_pset("format", "unaligned", &pset.popt, quiet); + success = do_pset("format", "unaligned", &pset.popt, pset.quiet); } /* \C -- override table title (formerly change HTML caption) */ @@ -217,7 +214,7 @@ exec_command(const char *cmd, char *opt = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, true); - success = do_pset("title", opt, &pset.popt, quiet); + success = do_pset("title", opt, &pset.popt, pset.quiet); free(opt); } @@ -493,7 +490,7 @@ exec_command(const char *cmd, char *fname = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); - success = do_pset("fieldsep", fname, &pset.popt, quiet); + success = do_pset("fieldsep", fname, &pset.popt, pset.quiet); free(fname); } @@ -528,9 +525,9 @@ exec_command(const char *cmd, else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0) { if (pset.popt.topt.format != PRINT_HTML) - success = do_pset("format", "html", &pset.popt, quiet); + success = do_pset("format", "html", &pset.popt, pset.quiet); else - success = do_pset("format", "aligned", &pset.popt, quiet); + success = do_pset("format", "aligned", &pset.popt, pset.quiet); } @@ -638,7 +635,7 @@ exec_command(const char *cmd, { if (query_buf && query_buf->len > 0) puts(query_buf->data); - else if (!quiet) + else if (!pset.quiet) puts(_("Query buffer is empty.")); fflush(stdout); } @@ -712,7 +709,7 @@ exec_command(const char *cmd, success = false; } else - success = do_pset(opt0, opt1, &pset.popt, quiet); + success = do_pset(opt0, opt1, &pset.popt, pset.quiet); free(opt0); free(opt1); @@ -727,7 +724,7 @@ exec_command(const char *cmd, { resetPQExpBuffer(query_buf); psql_scan_reset(scan_state); - if (!quiet) + if (!pset.quiet) puts(_("Query buffer reset (cleared).")); } @@ -740,7 +737,7 @@ exec_command(const char *cmd, expand_tilde(&fname); /* This scrolls off the screen when using /dev/tty */ success = saveHistory(fname ? fname : DEVTTY, false); - if (success && !quiet && fname) + if (success && !pset.quiet && fname) printf(gettext("Wrote history to file \"%s/%s\".\n"), pset.dirname ? pset.dirname : ".", fname); if (!fname) @@ -786,13 +783,7 @@ exec_command(const char *cmd, free(opt); } - if (SetVariable(pset.vars, opt0, newval)) - { - /* Check for special variables */ - if (strcmp(opt0, "VERBOSITY") == 0) - SyncVerbosityVariable(); - } - else + if (!SetVariable(pset.vars, opt0, newval)) { psql_error("\\%s: error\n", cmd); success = false; @@ -804,7 +795,7 @@ exec_command(const char *cmd, /* \t -- turn off headers and row count */ else if (strcmp(cmd, "t") == 0) - success = do_pset("tuples_only", NULL, &pset.popt, quiet); + success = do_pset("tuples_only", NULL, &pset.popt, pset.quiet); /* \T -- define html attributes */ @@ -813,7 +804,7 @@ exec_command(const char *cmd, char *value = psql_scan_slash_option(scan_state, OT_NORMAL, NULL, false); - success = do_pset("tableattr", value, &pset.popt, quiet); + success = do_pset("tableattr", value, &pset.popt, pset.quiet); free(value); } @@ -821,7 +812,7 @@ exec_command(const char *cmd, else if (strcmp(cmd, "timing") == 0) { pset.timing = !pset.timing; - if (!quiet) + if (!pset.quiet) { if (pset.timing) puts(_("Timing is on.")); @@ -916,7 +907,7 @@ exec_command(const char *cmd, /* \x -- toggle expanded table representation */ else if (strcmp(cmd, "x") == 0) - success = do_pset("expanded", NULL, &pset.popt, quiet); + success = do_pset("expanded", NULL, &pset.popt, pset.quiet); /* \z -- list table rights (equivalent to \dp) */ else if (strcmp(cmd, "z") == 0) @@ -1114,7 +1105,7 @@ do_connect(char *dbname, char *user, char *host, char *port) SyncVariables(); /* Tell the user about the new connection */ - if (!QUIET()) + if (!pset.quiet) { printf(_("You are now connected to database \"%s\""), PQdb(pset.db)); @@ -1148,6 +1139,7 @@ SyncVariables(void) /* get stuff from connection */ pset.encoding = PQclientEncoding(pset.db); pset.popt.topt.encoding = pset.encoding; + pset.sversion = PQserverVersion(pset.db); SetVariable(pset.vars, "DBNAME", PQdb(pset.db)); SetVariable(pset.vars, "USER", PQuser(pset.db)); @@ -1156,7 +1148,7 @@ SyncVariables(void) SetVariable(pset.vars, "ENCODING", pg_encoding_to_char(pset.encoding)); /* send stuff to it, too */ - SyncVerbosityVariable(); + PQsetErrorVerbosity(pset.db, pset.verbosity); } /* @@ -1174,32 +1166,6 @@ UnsyncVariables(void) SetVariable(pset.vars, "ENCODING", NULL); } -/* - * Update connection state from VERBOSITY variable - */ -static void -SyncVerbosityVariable(void) -{ - switch (SwitchVariable(pset.vars, "VERBOSITY", - "default", "terse", "verbose", NULL)) - { - case 1: /* default */ - pset.verbosity = PQERRORS_DEFAULT; - break; - case 2: /* terse */ - pset.verbosity = PQERRORS_TERSE; - break; - case 3: /* verbose */ - pset.verbosity = PQERRORS_VERBOSE; - break; - default: /* not set or unrecognized value */ - pset.verbosity = PQERRORS_DEFAULT; - break; - } - - PQsetErrorVerbosity(pset.db, pset.verbosity); -} - /* * do_edit -- handler for \e diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 7bdf6d15dcf..3e9d23d1956 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.125 2006/08/25 04:06:54 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/common.c,v 1.126 2006/08/29 15:19:50 tgl Exp $ */ #include "postgres_fe.h" #include "common.h" @@ -515,7 +515,6 @@ PGresult * PSQLexec(const char *query, bool start_xact) { PGresult *res; - int echo_hidden; if (!pset.db) { @@ -523,8 +522,7 @@ PSQLexec(const char *query, bool start_xact) return NULL; } - echo_hidden = SwitchVariable(pset.vars, "ECHO_HIDDEN", "noexec", NULL); - if (echo_hidden != VAR_NOTSET) + if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF) { printf(_("********* QUERY **********\n" "%s\n" @@ -539,14 +537,15 @@ PSQLexec(const char *query, bool start_xact) fflush(pset.logfile); } - if (echo_hidden == 1) /* noexec? */ + if (pset.echo_hidden == PSQL_ECHO_HIDDEN_NOEXEC) return NULL; } SetCancelConn(); - if (start_xact && PQtransactionStatus(pset.db) == PQTRANS_IDLE && - !GetVariableBool(pset.vars, "AUTOCOMMIT")) + if (start_xact && + !pset.autocommit && + PQtransactionStatus(pset.db) == PQTRANS_IDLE) { res = PQexec(pset.db, "BEGIN"); if (PQresultStatus(res) != PGRES_COMMAND_OK) @@ -693,7 +692,7 @@ PrintQueryStatus(PGresult *results) { char buf[16]; - if (!QUIET()) + if (!pset.quiet) { if (pset.popt.topt.format == PRINT_HTML) { @@ -789,7 +788,6 @@ SendQuery(const char *query) on_error_rollback_savepoint = false; PGTransactionStatusType transaction_status; static bool on_error_rollback_warning = false; - const char *rollback_str; if (!pset.db) { @@ -797,7 +795,7 @@ SendQuery(const char *query) return false; } - if (GetVariableBool(pset.vars, "SINGLESTEP")) + if (pset.singlestep) { char buf[3]; @@ -810,7 +808,7 @@ SendQuery(const char *query) if (buf[0] == 'x') return false; } - else if (VariableEquals(pset.vars, "ECHO", "queries")) + else if (pset.echo == PSQL_ECHO_QUERIES) { puts(query); fflush(stdout); @@ -830,7 +828,7 @@ SendQuery(const char *query) transaction_status = PQtransactionStatus(pset.db); if (transaction_status == PQTRANS_IDLE && - !GetVariableBool(pset.vars, "AUTOCOMMIT") && + !pset.autocommit && !command_no_begin(query)) { results = PQexec(pset.db, "BEGIN"); @@ -846,11 +844,9 @@ SendQuery(const char *query) } if (transaction_status == PQTRANS_INTRANS && - (rollback_str = GetVariable(pset.vars, "ON_ERROR_ROLLBACK")) != NULL && - /* !off and !interactive is 'on' */ - pg_strcasecmp(rollback_str, "off") != 0 && + pset.on_error_rollback != PSQL_ERROR_ROLLBACK_OFF && (pset.cur_cmd_interactive || - pg_strcasecmp(rollback_str, "interactive") != 0)) + pset.on_error_rollback == PSQL_ERROR_ROLLBACK_ON)) { if (on_error_rollback_warning == false && pset.sversion < 80000) { diff --git a/src/bin/psql/copy.c b/src/bin/psql/copy.c index 2ec01af186b..0d7cb5b4e00 100644 --- a/src/bin/psql/copy.c +++ b/src/bin/psql/copy.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.66 2006/06/14 16:49:02 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/copy.c,v 1.67 2006/08/29 15:19:50 tgl Exp $ */ #include "postgres_fe.h" #include "copy.h" @@ -704,7 +704,7 @@ handleCopyIn(PGconn *conn, FILE *copystream, bool isbinary) /* Prompt if interactive input */ if (isatty(fileno(copystream))) { - if (!QUIET()) + if (!pset.quiet) puts(_("Enter data to be copied followed by a newline.\n" "End with a backslash and a period on a line by itself.")); prompt = get_prompt(PROMPT_COPY); diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index 49db39074d1..b6fbc135806 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.143 2006/08/25 04:06:54 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/describe.c,v 1.144 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" #include "describe.h" @@ -673,7 +673,7 @@ describeTableDetails(const char *pattern, bool verbose) if (PQntuples(res) == 0) { - if (!QUIET()) + if (!pset.quiet) fprintf(stderr, _("Did not find any relation named \"%s\".\n"), pattern); PQclear(res); @@ -768,7 +768,7 @@ describeOneTableDetails(const char *schemaname, /* Did we get anything? */ if (PQntuples(res) == 0) { - if (!QUIET()) + if (!pset.quiet) fprintf(stderr, _("Did not find any relation with OID %s.\n"), oid); goto error_return; @@ -1582,7 +1582,7 @@ listTables(const char *tabtypes, const char *pattern, bool verbose) if (!res) return false; - if (PQntuples(res) == 0 && !QUIET()) + if (PQntuples(res) == 0 && !pset.quiet) { if (pattern) fprintf(pset.queryFout, _("No matching relations found.\n")); diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 3441d7813e2..323ef573b40 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.58 2006/08/27 15:05:20 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/input.c,v 1.59 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" @@ -34,14 +34,6 @@ char *psql_history; * for this purpose. */ #define NL_IN_HISTORY 0x01 - -enum histcontrol -{ - hctl_none = 0, - hctl_ignorespace = 1, - hctl_ignoredups = 2, - hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups -}; #endif #ifdef HAVE_ATEXIT @@ -52,31 +44,6 @@ static void finishInput(int, void *); #endif -#ifdef USE_READLINE -static enum histcontrol -GetHistControlConfig(void) -{ - enum histcontrol HC; - const char *var; - - var = GetVariable(pset.vars, "HISTCONTROL"); - - if (!var) - HC = hctl_none; - else if (strcmp(var, "ignorespace") == 0) - HC = hctl_ignorespace; - else if (strcmp(var, "ignoredups") == 0) - HC = hctl_ignoredups; - else if (strcmp(var, "ignoreboth") == 0) - HC = hctl_ignoreboth; - else - HC = hctl_none; - - return HC; -} -#endif - - /* * gets_interactive() * @@ -147,10 +114,10 @@ pg_send_history(PQExpBuffer history_buf) if (useHistory && s[0]) { - enum histcontrol HC = GetHistControlConfig(); - - if (((HC & hctl_ignorespace) && s[0] == ' ') || - ((HC & hctl_ignoredups) && prev_hist && strcmp(s, prev_hist) == 0)) + if (((pset.histcontrol & hctl_ignorespace) && + s[0] == ' ') || + ((pset.histcontrol & hctl_ignoredups) && + prev_hist && strcmp(s, prev_hist) == 0)) { /* Ignore this line as far as history is concerned */ } @@ -287,17 +254,17 @@ initializeInput(int flags) #ifdef USE_READLINE if (flags & 1) { + const char *histfile; char home[MAXPGPATH]; useReadline = true; initialize_readline(); useHistory = true; - if (GetVariable(pset.vars, "HISTSIZE") == NULL) - SetVariable(pset.vars, "HISTSIZE", "500"); using_history(); - if (GetVariable(pset.vars, "HISTFILE") == NULL) + histfile = GetVariable(pset.vars, "HISTFILE"); + if (histfile == NULL) { if (get_home_path(home)) { @@ -308,7 +275,7 @@ initializeInput(int flags) } else { - psql_history = pg_strdup(GetVariable(pset.vars, "HISTFILE")); + psql_history = pg_strdup(histfile); expand_tilde(&psql_history); } @@ -386,7 +353,7 @@ finishInput(int exitstatus, void *arg) { int hist_size; - hist_size = GetVariableNum(pset.vars, "HISTSIZE", -1, -1, true); + hist_size = GetVariableNum(pset.vars, "HISTSIZE", 500, -1, true); if (hist_size >= 0) stifle_history(hist_size); diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 778606c5f0e..48d8931e9fc 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.45 2006/07/14 14:52:26 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/large_obj.c,v 1.46 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" #include "large_obj.h" @@ -67,8 +67,7 @@ finish_lo_xact(const char *operation, bool own_transaction) { PGresult *res; - if (own_transaction && - GetVariableBool(pset.vars, "AUTOCOMMIT")) + if (own_transaction && pset.autocommit) { /* close out our own xact */ if (!(res = PSQLexec("COMMIT", false))) @@ -91,8 +90,7 @@ fail_lo_xact(const char *operation, bool own_transaction) { PGresult *res; - if (own_transaction && - GetVariableBool(pset.vars, "AUTOCOMMIT")) + if (own_transaction && pset.autocommit) { /* close out our own xact */ res = PSQLexec("ROLLBACK", false); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index 730210b20c9..519230b28ac 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.82 2006/08/11 19:20:59 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/mainloop.c,v 1.83 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" #include "mainloop.h" @@ -146,12 +146,12 @@ MainLoop(FILE *source) if (count_eof < GetVariableNum(pset.vars, "IGNOREEOF", 0, 10, false)) { - if (!QUIET()) + if (!pset.quiet) printf(_("Use \"\\q\" to leave %s.\n"), pset.progname); continue; } - puts(QUIET() ? "" : "\\q"); + puts(pset.quiet ? "" : "\\q"); } break; } @@ -168,8 +168,7 @@ MainLoop(FILE *source) } /* echo back if flag is set */ - if (!pset.cur_cmd_interactive && - VariableEquals(pset.vars, "ECHO", "all")) + if (pset.echo == PSQL_ECHO_ALL && !pset.cur_cmd_interactive) puts(line); fflush(stdout); @@ -183,7 +182,7 @@ MainLoop(FILE *source) added_nl_pos = -1; /* flag we didn't add one */ /* Setting this will not have effect until next line. */ - die_on_error = GetVariableBool(pset.vars, "ON_ERROR_STOP"); + die_on_error = pset.on_error_stop; /* * Parse line, looking for command separators. @@ -205,8 +204,7 @@ MainLoop(FILE *source) * single-line mode. */ if (scan_result == PSCAN_SEMICOLON || - (scan_result == PSCAN_EOL && - GetVariableBool(pset.vars, "SINGLELINE"))) + (scan_result == PSCAN_EOL && pset.singleline)) { /* * Save query in history. We use history_buf to accumulate diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index 981e8b6b58e..6bf86d1108c 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.47 2006/07/15 03:35:21 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/prompt.c,v 1.48 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" @@ -72,12 +72,11 @@ get_prompt(promptStatus_t status) bool esc = false; const char *p; const char *prompt_string = "? "; - const char *prompt_name = NULL; switch (status) { case PROMPT_READY: - prompt_name = "PROMPT1"; + prompt_string = pset.prompt1; break; case PROMPT_CONTINUE: @@ -86,21 +85,18 @@ get_prompt(promptStatus_t status) case PROMPT_DOLLARQUOTE: case PROMPT_COMMENT: case PROMPT_PAREN: - prompt_name = "PROMPT2"; + prompt_string = pset.prompt2; break; case PROMPT_COPY: - prompt_name = "PROMPT3"; + prompt_string = pset.prompt3; break; } - if (prompt_name) - prompt_string = GetVariable(pset.vars, prompt_name); - destination[0] = '\0'; for (p = prompt_string; - p && *p && strlen(destination) < MAX_PROMPT_SIZE; + *p && strlen(destination) < MAX_PROMPT_SIZE; p++) { memset(buf, 0, MAX_PROMPT_SIZE + 1); @@ -182,7 +178,7 @@ get_prompt(promptStatus_t status) case PROMPT_READY: if (!pset.db) buf[0] = '!'; - else if (!GetVariableBool(pset.vars, "SINGLELINE")) + else if (!pset.singleline) buf[0] = '='; else buf[0] = '^'; diff --git a/src/bin/psql/settings.h b/src/bin/psql/settings.h index a5c4a02be9e..9dc41e3bdfb 100644 --- a/src/bin/psql/settings.h +++ b/src/bin/psql/settings.h @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.28 2006/08/11 19:20:59 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/settings.h,v 1.29 2006/08/29 15:19:51 tgl Exp $ */ #ifndef SETTINGS_H #define SETTINGS_H @@ -26,16 +26,44 @@ #define DEFAULT_PROMPT2 "%/%R%# " #define DEFAULT_PROMPT3 ">> " +typedef enum +{ + PSQL_ECHO_NONE, + PSQL_ECHO_QUERIES, + PSQL_ECHO_ALL +} PSQL_ECHO; + +typedef enum +{ + PSQL_ECHO_HIDDEN_OFF, + PSQL_ECHO_HIDDEN_ON, + PSQL_ECHO_HIDDEN_NOEXEC +} PSQL_ECHO_HIDDEN; + +typedef enum +{ + PSQL_ERROR_ROLLBACK_OFF, + PSQL_ERROR_ROLLBACK_INTERACTIVE, + PSQL_ERROR_ROLLBACK_ON +} PSQL_ERROR_ROLLBACK; + +typedef enum +{ + hctl_none = 0, + hctl_ignorespace = 1, + hctl_ignoredups = 2, + hctl_ignoreboth = hctl_ignorespace | hctl_ignoredups +} HistControl; + typedef struct _psqlSettings { PGconn *db; /* connection to backend */ - int encoding; + int encoding; /* client_encoding */ FILE *queryFout; /* where to send the query results */ bool queryFoutPipe; /* queryFout is from a popen() */ printQueryOpt popt; - VariableSpace vars; /* "shell variable" repository */ char *gfname; /* one-shot file output argument for \g */ @@ -54,16 +82,33 @@ typedef struct _psqlSettings bool timing; /* enable timing of all queries */ - PGVerbosity verbosity; /* current error verbosity level */ FILE *logfile; /* session log file handle */ + + VariableSpace vars; /* "shell variable" repository */ + + /* + * The remaining fields are set by assign hooks associated with + * entries in "vars". They should not be set directly except by + * those hook functions. + */ + bool autocommit; + bool on_error_stop; + bool quiet; + bool singleline; + bool singlestep; + PSQL_ECHO echo; + PSQL_ECHO_HIDDEN echo_hidden; + PSQL_ERROR_ROLLBACK on_error_rollback; + HistControl histcontrol; + const char *prompt1; + const char *prompt2; + const char *prompt3; + PGVerbosity verbosity; /* current error verbosity level */ } PsqlSettings; extern PsqlSettings pset; -#define QUIET() (GetVariableBool(pset.vars, "QUIET")) - - #ifndef EXIT_SUCCESS #define EXIT_SUCCESS 0 #endif diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 1a937e1eb47..8d3409bd194 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,7 +3,7 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.135 2006/07/14 14:52:26 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/startup.c,v 1.136 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" @@ -84,14 +84,14 @@ static void parse_psql_options(int argc, char *argv[], static void process_psqlrc(char *argv0); static void process_psqlrc_file(char *filename); static void showVersion(void); +static void EstablishVariableSpace(void); #ifdef USE_SSL static void printSSLInfo(void); #endif #ifdef WIN32 -static void - checkWin32Codepage(void); +static void checkWin32Codepage(void); #endif /* @@ -134,34 +134,19 @@ main(int argc, char *argv[]) pset.progname = get_progname(argv[0]); + pset.db = NULL; setDecimalLocale(); + pset.encoding = PQenv2encoding(); + pset.queryFout = stdout; + pset.queryFoutPipe = false; pset.cur_cmd_source = stdin; pset.cur_cmd_interactive = false; - pset.encoding = PQenv2encoding(); - pset.vars = CreateVariableSpace(); - if (!pset.vars) - { - fprintf(stderr, _("%s: out of memory\n"), pset.progname); - exit(EXIT_FAILURE); - } pset.popt.topt.format = PRINT_ALIGNED; - pset.queryFout = stdout; pset.popt.topt.border = 1; pset.popt.topt.pager = 1; pset.popt.default_footer = true; - SetVariable(pset.vars, "VERSION", PG_VERSION_STR); - - /* Default values for variables */ - SetVariableBool(pset.vars, "AUTOCOMMIT"); - SetVariable(pset.vars, "VERBOSITY", "default"); - SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); - SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); - SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); - - pset.verbosity = PQERRORS_DEFAULT; - pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); /* This is obsolete and should be removed sometime. */ @@ -171,6 +156,17 @@ main(int argc, char *argv[]) pset.getPassword = false; #endif + EstablishVariableSpace(); + + SetVariable(pset.vars, "VERSION", PG_VERSION_STR); + + /* Default values for variables */ + SetVariableBool(pset.vars, "AUTOCOMMIT"); + SetVariable(pset.vars, "VERBOSITY", "default"); + SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); + SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); + SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); + parse_psql_options(argc, argv, &options); if (!pset.popt.topt.fieldSep) @@ -239,9 +235,6 @@ main(int argc, char *argv[]) SyncVariables(); - /* Grab the backend server version */ - pset.sversion = PQserverVersion(pset.db); - if (options.action == ACT_LIST_DB) { int success = listAllDbs(false); @@ -280,7 +273,7 @@ main(int argc, char *argv[]) { PsqlScanState scan_state; - if (VariableEquals(pset.vars, "ECHO", "all")) + if (pset.echo == PSQL_ECHO_ALL) puts(options.action_string); scan_state = psql_scan_create(); @@ -299,7 +292,7 @@ main(int argc, char *argv[]) */ else if (options.action == ACT_SINGLE_QUERY) { - if (VariableEquals(pset.vars, "ECHO", "all")) + if (pset.echo == PSQL_ECHO_ALL) puts(options.action_string); successResult = SendQuery(options.action_string) @@ -314,7 +307,7 @@ main(int argc, char *argv[]) if (!options.no_psqlrc) process_psqlrc(argv[0]); - if (!QUIET() && !pset.notty) + if (!pset.quiet && !pset.notty) { int client_ver = parse_version(PG_VERSION); @@ -644,14 +637,14 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts * options) options->dbname = argv[optind]; else if (!options->username) options->username = argv[optind]; - else if (!QUIET()) + else if (!pset.quiet) fprintf(stderr, _("%s: warning: extra command-line argument \"%s\" ignored\n"), pset.progname, argv[optind]); optind++; } - if (used_old_u_option && !QUIET()) + if (used_old_u_option && !pset.quiet) fprintf(stderr, _("%s: Warning: The -u option is deprecated. Use -U.\n"), pset.progname); } @@ -743,7 +736,6 @@ printSSLInfo(void) #endif - /* * checkWin32Codepage * @@ -768,3 +760,151 @@ checkWin32Codepage(void) } #endif + + +/* + * Assign hooks for psql variables. + * + * This isn't an amazingly good place for them, but neither is anywhere else. + */ + +static void +autocommit_hook(const char *newval) +{ + pset.autocommit = ParseVariableBool(newval); +} + +static void +on_error_stop_hook(const char *newval) +{ + pset.on_error_stop = ParseVariableBool(newval); +} + +static void +quiet_hook(const char *newval) +{ + pset.quiet = ParseVariableBool(newval); +} + +static void +singleline_hook(const char *newval) +{ + pset.singleline = ParseVariableBool(newval); +} + +static void +singlestep_hook(const char *newval) +{ + pset.singlestep = ParseVariableBool(newval); +} + +static void +echo_hook(const char *newval) +{ + if (newval == NULL) + pset.echo = PSQL_ECHO_NONE; + else if (strcmp(newval, "queries") == 0) + pset.echo = PSQL_ECHO_QUERIES; + else if (strcmp(newval, "all") == 0) + pset.echo = PSQL_ECHO_ALL; + else + pset.echo = PSQL_ECHO_NONE; +} + +static void +echo_hidden_hook(const char *newval) +{ + if (newval == NULL) + pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; + else if (strcmp(newval, "noexec") == 0) + pset.echo_hidden = PSQL_ECHO_HIDDEN_NOEXEC; + else if (pg_strcasecmp(newval, "off") == 0) + pset.echo_hidden = PSQL_ECHO_HIDDEN_OFF; + else + pset.echo_hidden = PSQL_ECHO_HIDDEN_ON; +} + +static void +on_error_rollback_hook(const char *newval) +{ + if (newval == NULL) + pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; + else if (pg_strcasecmp(newval, "interactive") == 0) + pset.on_error_rollback = PSQL_ERROR_ROLLBACK_INTERACTIVE; + else if (pg_strcasecmp(newval, "off") == 0) + pset.on_error_rollback = PSQL_ERROR_ROLLBACK_OFF; + else + pset.on_error_rollback = PSQL_ERROR_ROLLBACK_ON; +} + +static void +histcontrol_hook(const char *newval) +{ + if (newval == NULL) + pset.histcontrol = hctl_none; + else if (strcmp(newval, "ignorespace") == 0) + pset.histcontrol = hctl_ignorespace; + else if (strcmp(newval, "ignoredups") == 0) + pset.histcontrol = hctl_ignoredups; + else if (strcmp(newval, "ignoreboth") == 0) + pset.histcontrol = hctl_ignoreboth; + else + pset.histcontrol = hctl_none; +} + +static void +prompt1_hook(const char *newval) +{ + pset.prompt1 = newval ? newval : ""; +} + +static void +prompt2_hook(const char *newval) +{ + pset.prompt2 = newval ? newval : ""; +} + +static void +prompt3_hook(const char *newval) +{ + pset.prompt3 = newval ? newval : ""; +} + +static void +verbosity_hook(const char *newval) +{ + if (newval == NULL) + pset.verbosity = PQERRORS_DEFAULT; + else if (strcmp(newval, "default") == 0) + pset.verbosity = PQERRORS_DEFAULT; + else if (strcmp(newval, "terse") == 0) + pset.verbosity = PQERRORS_TERSE; + else if (strcmp(newval, "verbose") == 0) + pset.verbosity = PQERRORS_VERBOSE; + else + pset.verbosity = PQERRORS_DEFAULT; + + if (pset.db) + PQsetErrorVerbosity(pset.db, pset.verbosity); +} + + +static void +EstablishVariableSpace(void) +{ + pset.vars = CreateVariableSpace(); + + SetVariableAssignHook(pset.vars, "AUTOCOMMIT", autocommit_hook); + SetVariableAssignHook(pset.vars, "ON_ERROR_STOP", on_error_stop_hook); + SetVariableAssignHook(pset.vars, "QUIET", quiet_hook); + SetVariableAssignHook(pset.vars, "SINGLELINE", singleline_hook); + SetVariableAssignHook(pset.vars, "SINGLESTEP", singlestep_hook); + SetVariableAssignHook(pset.vars, "ECHO", echo_hook); + SetVariableAssignHook(pset.vars, "ECHO_HIDDEN", echo_hidden_hook); + SetVariableAssignHook(pset.vars, "ON_ERROR_ROLLBACK", on_error_rollback_hook); + SetVariableAssignHook(pset.vars, "HISTCONTROL", histcontrol_hook); + SetVariableAssignHook(pset.vars, "PROMPT1", prompt1_hook); + SetVariableAssignHook(pset.vars, "PROMPT2", prompt2_hook); + SetVariableAssignHook(pset.vars, "PROMPT3", prompt3_hook); + SetVariableAssignHook(pset.vars, "VERBOSITY", verbosity_hook); +} diff --git a/src/bin/psql/variables.c b/src/bin/psql/variables.c index e0e416e9e5a..cac638e0393 100644 --- a/src/bin/psql/variables.c +++ b/src/bin/psql/variables.c @@ -3,20 +3,27 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/variables.c,v 1.25 2006/06/21 16:05:11 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/psql/variables.c,v 1.26 2006/08/29 15:19:51 tgl Exp $ */ #include "postgres_fe.h" #include "common.h" #include "variables.h" + +/* + * A "variable space" is represented by an otherwise-unused struct _variable + * that serves as list header. + */ VariableSpace CreateVariableSpace(void) { struct _variable *ptr; - ptr = pg_calloc(1, sizeof *ptr); - ptr->name = pg_strdup("@"); - ptr->value = pg_strdup(""); + ptr = pg_malloc(sizeof *ptr); + ptr->name = NULL; + ptr->value = NULL; + ptr->assign_hook = NULL; + ptr->next = NULL; return ptr; } @@ -33,7 +40,7 @@ GetVariable(VariableSpace space, const char *name) { if (strcmp(current->name, name) == 0) { - psql_assert(current->value); + /* this is correct answer when value is NULL, too */ return current->value; } } @@ -42,11 +49,8 @@ GetVariable(VariableSpace space, const char *name) } bool -GetVariableBool(VariableSpace space, const char *name) +ParseVariableBool(const char *val) { - const char *val; - - val = GetVariable(space, name); if (val == NULL) return false; /* not set -> assume "off" */ if (pg_strcasecmp(val, "off") == 0) @@ -59,35 +63,28 @@ GetVariableBool(VariableSpace space, const char *name) return true; } -bool -VariableEquals(VariableSpace space, const char name[], const char value[]) -{ - const char *var; - - var = GetVariable(space, name); - return var && (strcmp(var, value) == 0); -} - +/* + * Read numeric variable, or defaultval if it is not set, or faultval if its + * value is not a valid numeric string. If allowtrail is false, this will + * include the case where there are trailing characters after the number. + */ int -GetVariableNum(VariableSpace space, - const char name[], - int defaultval, - int faultval, - bool allowtrail) +ParseVariableNum(const char *val, + int defaultval, + int faultval, + bool allowtrail) { - const char *var; int result; - var = GetVariable(space, name); - if (!var) + if (!val) result = defaultval; - else if (!var[0]) + else if (!val[0]) result = faultval; else { char *end; - result = strtol(var, &end, 0); + result = strtol(val, &end, 0); if (!allowtrail && *end) result = faultval; } @@ -96,27 +93,16 @@ GetVariableNum(VariableSpace space, } int -SwitchVariable(VariableSpace space, const char name[], const char *opt,...) +GetVariableNum(VariableSpace space, + const char *name, + int defaultval, + int faultval, + bool allowtrail) { - int result; - const char *var; + const char *val; - var = GetVariable(space, name); - if (var) - { - va_list args; - - va_start(args, opt); - for (result = 1; opt && (strcmp(var, opt) != 0); result++) - opt = va_arg(args, const char *); - if (!opt) - result = VAR_NOTFOUND; - va_end(args); - } - else - result = VAR_NOTSET; - - return result; + val = GetVariable(space, name); + return ParseVariableNum(val, defaultval, faultval, allowtrail); } void @@ -129,7 +115,8 @@ PrintVariables(VariableSpace space) for (ptr = space->next; ptr; ptr = ptr->next) { - printf("%s = '%s'\n", ptr->name, ptr->value); + if (ptr->value) + printf("%s = '%s'\n", ptr->name, ptr->value); if (cancel_pressed) break; } @@ -156,16 +143,62 @@ SetVariable(VariableSpace space, const char *name, const char *value) { if (strcmp(current->name, name) == 0) { - psql_assert(current->value); - free(current->value); + /* found entry, so update */ + if (current->value) + free(current->value); current->value = pg_strdup(value); + if (current->assign_hook) + (*current->assign_hook) (current->value); return true; } } - previous->next = pg_calloc(1, sizeof *(previous->next)); - previous->next->name = pg_strdup(name); - previous->next->value = pg_strdup(value); + /* not present, make new entry */ + current = pg_malloc(sizeof *current); + current->name = pg_strdup(name); + current->value = pg_strdup(value); + current->assign_hook = NULL; + current->next = NULL; + previous->next = current; + return true; +} + +/* + * This both sets a hook function, and calls it on the current value (if any) + */ +bool +SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook) +{ + struct _variable *current, + *previous; + + if (!space) + return false; + + if (strspn(name, VALID_VARIABLE_CHARS) != strlen(name)) + return false; + + for (previous = space, current = space->next; + current; + previous = current, current = current->next) + { + if (strcmp(current->name, name) == 0) + { + /* found entry, so update */ + current->assign_hook = hook; + (*hook) (current->value); + return true; + } + } + + /* not present, make new entry */ + current = pg_malloc(sizeof *current); + current->name = pg_strdup(name); + current->value = NULL; + current->assign_hook = hook; + current->next = NULL; + previous->next = current; + (*hook) (NULL); return true; } @@ -190,11 +223,18 @@ DeleteVariable(VariableSpace space, const char *name) { if (strcmp(current->name, name) == 0) { - psql_assert(current->value); - previous->next = current->next; - free(current->name); - free(current->value); - free(current); + if (current->value) + free(current->value); + current->value = NULL; + /* Physically delete only if no hook function to remember */ + if (current->assign_hook) + (*current->assign_hook) (NULL); + else + { + previous->next = current->next; + free(current->name); + free(current); + } return true; } } diff --git a/src/bin/psql/variables.h b/src/bin/psql/variables.h index 37abadb66ca..e4dce97a0a7 100644 --- a/src/bin/psql/variables.h +++ b/src/bin/psql/variables.h @@ -3,62 +3,57 @@ * * Copyright (c) 2000-2006, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/bin/psql/variables.h,v 1.18 2006/03/05 15:58:52 momjian Exp $ + * $PostgreSQL: pgsql/src/bin/psql/variables.h,v 1.19 2006/08/29 15:19:51 tgl Exp $ */ - -/* - * This implements a sort of variable repository. One could also think of it - * as cheap version of an associative array. In each one of these - * datastructures you can store name/value pairs. - */ - #ifndef VARIABLES_H #define VARIABLES_H -#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\ - "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_" +/* + * This implements a sort of variable repository. One could also think of it + * as a cheap version of an associative array. In each one of these + * datastructures you can store name/value pairs. There can also be an + * "assign hook" function that is called whenever the variable's value is + * changed. + * + * An "unset" operation causes the hook to be called with newval == NULL. + * + * Note: if value == NULL then the variable is logically unset, but we are + * keeping the struct around so as not to forget about its hook function. + */ +typedef void (*VariableAssignHook) (const char *newval); struct _variable { char *name; char *value; + VariableAssignHook assign_hook; struct _variable *next; }; typedef struct _variable *VariableSpace; +/* Allowed chars in a variable's name */ +#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\ + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_" VariableSpace CreateVariableSpace(void); const char *GetVariable(VariableSpace space, const char *name); -bool GetVariableBool(VariableSpace space, const char *name); -bool VariableEquals(VariableSpace space, const char name[], const char *opt); -/* Read numeric variable, or defaultval if it is not set, or faultval if its - * value is not a valid numeric string. If allowtrail is false, this will - * include the case where there are trailing characters after the number. - */ +bool ParseVariableBool(const char *val); +int ParseVariableNum(const char *val, + int defaultval, + int faultval, + bool allowtrail); int GetVariableNum(VariableSpace space, - const char name[], + const char *name, int defaultval, int faultval, bool allowtrail); - -/* Find value of variable among NULL-terminated list of alternative - * options. Returns VAR_NOTSET if the variable was not set, VAR_NOTFOUND - * if its value did not occur in the list of options, or the number of the - * matching option. The first option is 1, the second is 2 and so on. - */ -enum -{ -VAR_NOTSET = 0, VAR_NOTFOUND = -1}; -int -SwitchVariable(VariableSpace space, const char name[], - const char *opt,...); - void PrintVariables(VariableSpace space); bool SetVariable(VariableSpace space, const char *name, const char *value); +bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook); bool SetVariableBool(VariableSpace space, const char *name); bool DeleteVariable(VariableSpace space, const char *name);