On Sun, 2006-03-12 at 19:59 +0200, Volkan YAZICI wrote:
> I've written do_connect() and \c handling part from scratch in the
> attached patch.
Attached is a revised version of this patch. I rewrote most of the code,
because the existing stuff was in pretty bad style IMHO. I haven't
updated the documentation yet, but I'll do that if no one objects to
this version of the patch.
One question about behavior: in the attached patch, omitting an argument
to \connect or specifying "-" are treated equivalently -- the value for
that parameter from the previous connection is used, otherwise NULL (for
the libpq default). Is this what people want? (One possible complaint is
that once you specify a parameter, you can't get back to the libpq
default without specifying it explicitly.)
-Neil
============================================================
*** src/bin/psql/command.c b1305764d53ec9138de3c55ec3475a4d8a3190d9
--- src/bin/psql/command.c e570b6eb0b6ade25461b88aca5bbef2e75d27c00
***************
*** 55,61 ****
PsqlScanState scan_state,
PQExpBuffer query_buf);
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
! static bool do_connect(const char *new_dbname, const char *new_user, const char *new_host, const char *new_port);
static bool do_shell(const char *command);
--- 55,61 ----
PsqlScanState scan_state,
PQExpBuffer query_buf);
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);
***************
*** 154,159 ****
--- 154,192 ----
}
/*
+ * Read and interpret an argument to the \connect slash command.
+ */
+ static char *
+ read_connect_arg(PsqlScanState scan_state)
+ {
+ char *result;
+ char quote;
+
+ /*
+ * Ideally we should treat the arguments as SQL identifiers. But
+ * for backwards compatibility with 7.2 and older pg_dump files,
+ * we have to take unquoted arguments verbatim (don't downcase
+ * them). For now, double-quoted arguments may be stripped of
+ * double quotes (as if SQL identifiers). By 7.4 or so, pg_dump
+ * files can be expected to double-quote all mixed-case \connect
+ * arguments, and then we can get rid of OT_SQLIDHACK.
+ */
+ result = psql_scan_slash_option(scan_state, OT_SQLIDHACK, "e, true);
+
+ if (!result)
+ return NULL;
+
+ if (quote)
+ return result;
+
+ if (*result == '\0' || strcmp(result, "-") == 0)
+ return NULL;
+
+ return result;
+ }
+
+
+ /*
* Subroutine to actually try to execute a backslash command.
*/
static backslashResult
***************
*** 188,204 ****
free(opt);
}
! /*----------
! * \c or \connect -- connect to new database or as different user,
! * and/or new host and/or port
*
! * \c foo bar [-] [-] connect to db "foo" as user "bar" on current host and port
! * \c foo [-] [-] [-] connect to db "foo" as current user on current host and port
! * \c - bar [-] [-] connect to current db as user "bar" on current host and port
! * \c - - host.domain.tld [-] connect to default db as default user on host.domain.tld on default port
! * \c - - - 5555 connect to default db as default user on default host at port 5555
! * \c connect to default db as default user
! *----------
*/
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
{
--- 221,242 ----
free(opt);
}
! /*
! * \c or \connect -- connect to database using the specified parameters.
*
! * \c dbname user host port
! *
! * If any of these parameters are omitted or specified as '-', the
! * current value of the parameter will be used instead. If the
! * parameter has no current value, the default value for that
! * parameter will be used. Some examples:
! *
! * \c - - hst Connect to current database on current port of
! * host "hst" as current user.
! * \c - usr - prt Connect to current database on "prt" port of current
! * host as user "usr".
! * \c dbs Connect to "dbs" database on current port of current
! * host as current user.
*/
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
{
***************
*** 206,233 ****
*opt2,
*opt3,
*opt4;
- char opt1q,
- opt2q,
- opt3q,
- opt4q;
! /*
! * Ideally we should treat the arguments as SQL identifiers. But for
! * backwards compatibility with 7.2 and older pg_dump files, we have
! * to take unquoted arguments verbatim (don't downcase them). For now,
! * double-quoted arguments may be stripped of double quotes (as if SQL
! * identifiers). By 7.4 or so, pg_dump files can be expected to
! * double-quote all mixed-case \connect arguments, and then we can get
! * rid of OT_SQLIDHACK.
! */
! opt1 = psql_scan_slash_option(scan_state,
! OT_SQLIDHACK, &opt1q, true);
! opt2 = psql_scan_slash_option(scan_state,
! OT_SQLIDHACK, &opt2q, true);
! opt3 = psql_scan_slash_option(scan_state,
! OT_SQLIDHACK, &opt3q, true);
! opt4 = psql_scan_slash_option(scan_state,
! OT_SQLIDHACK, &opt4q, true);
if (opt4)
/* gave port */
--- 244,254 ----
*opt2,
*opt3,
*opt4;
! opt1 = read_connect_arg(scan_state);
! opt2 = read_connect_arg(scan_state);
! opt3 = read_connect_arg(scan_state);
! opt4 = read_connect_arg(scan_state);
success = do_connect(opt1, opt2, opt3, opt4);
***************
*** 229,271 ****
opt4 = psql_scan_slash_option(scan_state,
OT_SQLIDHACK, &opt4q, true);
! if (opt4)
! /* gave port */
! success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 ||
! strcmp(opt1, "") == 0) ? "" : opt1,
! !opt2q && (strcmp(opt2, "-") == 0 ||
! strcmp(opt2, "") == 0) ? "" : opt2,
! !opt3q && (strcmp(opt3, "-") == 0 ||
! strcmp(opt3, "") == 0) ? "" : opt3,
! !opt3q && (strcmp(opt3, "-") == 0 ||
! strcmp(opt3, "") == 0) ? "" : opt3);
! if (opt3)
! /* gave host */
! success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 ||
! strcmp(opt1, "") == 0) ? "" : opt1,
! !opt2q && (strcmp(opt2, "-") == 0 ||
! strcmp(opt2, "") == 0) ? "" : opt2,
! !opt3q && (strcmp(opt3, "-") == 0 ||
! strcmp(opt3, "") == 0) ? "" : opt3,
! NULL);
! if (opt2)
! /* gave username */
! success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 ||
! strcmp(opt1, "") == 0) ? "" : opt1,
! !opt2q && (strcmp(opt2, "-") == 0 ||
! strcmp(opt2, "") == 0) ? "" : opt2,
! NULL,
! NULL);
! else if (opt1)
! /* gave database name */
! success = do_connect(!opt1q && (strcmp(opt1, "-") == 0 ||
! strcmp(opt1, "") == 0) ? "" : opt1,
! "",
! NULL,
! NULL);
! else
! /* connect to default db as default user */
! success = do_connect(NULL, NULL, NULL, NULL);
free(opt1);
free(opt2);
--- 250,256 ----
opt4 = psql_scan_slash_option(scan_state,
OT_SQLIDHACK, &opt4q, true);
! success = do_connect(opt1, opt2, opt3, opt4);
free(opt1);
free(opt2);
***************
*** 985,1001 ****
return status;
}
!
!
! /* do_connect
! * -- handler for \connect
! *
! * Connects to a database (new_dbname) as a certain user (new_user).
! * The new user can be NULL. A db name of "-" is the same as the old one.
! * (That is, the one currently in pset. But pset.db can also be NULL. A NULL
! * dbname is handled by libpq.)
! * Returns true if all ok, false if the new connection couldn't be established.
! * The old connection will be kept if the session is interactive.
*/
static bool
do_connect(const char *new_dbname, const char *new_user, const char *new_host, const char *new_port)
--- 970,979 ----
return status;
}
! /*
! * Ask the user for a password; 'username' is the username the
! * password is for, if one has been explicitly specified. Returns a
! * malloc'd string.
*/
static char *
prompt_for_password(const char *username)
***************
*** 997,1015 ****
* Returns true if all ok, false if the new connection couldn't be established.
* The old connection will be kept if the session is interactive.
*/
! static bool
! do_connect(const char *new_dbname, const char *new_user, const char *new_host, const char *new_port)
{
! PGconn *oldconn = pset.db;
! const char *dbparam = NULL;
! const char *userparam = NULL;
! const char *hostparam = NULL;
! const char *portparam = NULL;
! const char *pwparam = NULL;
! char *password_prompt = NULL;
! char *prompted_password = NULL;
! bool need_pass;
! bool success = false;
/* Delete variables (in case we fail before setting them anew) */
UnsyncVariables();
--- 975,984 ----
* Returns true if all ok, false if the new connection couldn't be established.
* The old connection will be kept if the session is interactive.
*/
! static char *
! prompt_for_password(const char *username)
{
! char *result;
if (username == NULL)
result = simple_prompt("Password: ", 100, false);
***************
*** 1011,1051 ****
bool need_pass;
bool success = false;
! /* Delete variables (in case we fail before setting them anew) */
! UnsyncVariables();
! /* If dbname is "" then use old name, else new one (even if NULL) */
! if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "") == 0)
! dbparam = PQdb(oldconn);
! else
! dbparam = new_dbname;
! /* If user is "" then use the old one */
! if (new_user && PQuser(oldconn) && strcmp(new_user, "") == 0)
! userparam = PQuser(oldconn);
! else
! userparam = new_user;
! /* If host is "" then use the old one */
! if (new_host && PQhost(oldconn) && strcmp(new_host, "") == 0)
! hostparam = PQhost(oldconn);
! else
! hostparam = new_host;
! /* If port is "" then use the old one */
! if (new_port && PQport(oldconn) && strcmp(new_port, "") == 0)
! portparam = PQport(oldconn);
! else
! portparam = new_port;
! if (userparam == NULL)
! password_prompt = strdup("Password: ");
! else
! {
! password_prompt = malloc(strlen(_("Password for user %s: ")) - 2 +
! strlen(userparam) + 1);
! sprintf(password_prompt, _("Password for user %s: "), userparam);
! }
/* need to prompt for password? */
if (pset.getPassword)
--- 980,1011 ----
bool need_pass;
bool success = false;
! if (username == NULL)
! result = simple_prompt("Password: ", 100, false);
! else
! {
! char *prompt_text;
! prompt_text = malloc(strlen(username) + 32);
! sprintf(prompt_text, "Password for user \"%s\": ", username);
! result = simple_prompt(prompt_text, 100, false);
! free(prompt_text);
! }
! return result;
! }
! static bool
! param_is_newly_set(const char *old_val, const char *new_val)
! {
! if (new_val == NULL)
! return false;
! if (old_val == NULL || strcmp(old_val, new_val) != 0)
! return true;
! return false;
! }
/*
* do_connect -- handler for \connect
***************
*** 1047,1091 ****
sprintf(password_prompt, _("Password for user %s: "), userparam);
}
! /* need to prompt for password? */
! if (pset.getPassword)
! pwparam = prompted_password = simple_prompt(password_prompt, 100, false);
/*
! * Use old password (if any) if no new one given and we are reconnecting
! * as same user
*/
! if (!pwparam && oldconn && PQuser(oldconn) && userparam &&
! strcmp(PQuser(oldconn), userparam) == 0)
! pwparam = PQpass(oldconn);
! do
{
! need_pass = false;
! pset.db = PQsetdbLogin(hostparam, portparam,
! NULL, NULL, dbparam, userparam, pwparam);
! if (PQstatus(pset.db) == CONNECTION_BAD &&
! strcmp(PQerrorMessage(pset.db), PQnoPasswordSupplied) == 0 &&
! !feof(stdin))
{
! PQfinish(pset.db);
! need_pass = true;
! free(prompted_password);
! prompted_password = NULL;
! pwparam = prompted_password = simple_prompt(password_prompt, 100, false);
}
- } while (need_pass);
! free(prompted_password);
! free(password_prompt);
!
! /*
! * If connection failed, try at least keep the old one. That's probably
! * more convenient than just kicking you out of the program.
! */
! if (!pset.db || PQstatus(pset.db) == CONNECTION_BAD)
! {
if (pset.cur_cmd_interactive)
{
psql_error("%s", PQerrorMessage(pset.db));
--- 1007,1087 ----
sprintf(password_prompt, _("Password for user %s: "), userparam);
}
! /*
! * do_connect -- handler for \connect
! *
! * Connects to a database with given parameters. If there exists an
! * established connection, NULL values will be replaced with the ones
! * in the current connection. Otherwise NULL will be passed for that
! * parameter to PQsetdbLogin(), so the libpq defaults will be used.
! *
! * In interactive mode, if connection fails with the given parameters,
! * the old connection will be kept.
! */
! static bool
! do_connect(char *dbname, char *user, char *host, char *port)
! {
! PGconn *o_conn = pset.db,
! *n_conn;
! char *password = NULL;
+ if (!dbname)
+ dbname = PQdb(o_conn);
+ if (!user)
+ user = PQuser(o_conn);
+ if (!host)
+ host = PQhost(o_conn);
+ if (!port)
+ port = PQport(o_conn);
+
/*
! * If the user asked to be prompted for a password, ask for one
! * now. If not, use the password from the old connection, provided
! * the username has not changed. Otherwise, try to connect without
! * a password first, and then ask for a password if we got the
! * appropriate error message.
! *
! * XXX: this behavior is broken. It leads to spurious connection
! * attempts in the postmaster's log, and doing a string comparison
! * against the returned error message is pretty fragile.
*/
! if (pset.getPassword)
! {
! password = prompt_for_password(user);
! }
! else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0)
! {
! password = strdup(PQpass(o_conn));
! }
! while (true)
{
! n_conn = PQsetdbLogin(host, port, NULL, NULL,
! dbname, user, password);
! /* We can immediately discard the password -- no longer needed */
! if (password)
! free(password);
!
! if (PQstatus(n_conn) == CONNECTION_OK)
! break;
!
! /*
! * Connection attempt failed; either retry the connection
! * attempt with a new password, or give up.
! */
! if (strcmp(PQerrorMessage(n_conn), PQnoPasswordSupplied) == 0)
{
! PQfinish(n_conn);
! password = prompt_for_password(user);
! continue;
}
! /*
! * Failed to connect to the database. In interactive mode,
! * keep the previous connection to the DB; in scripting mode,
! * close our previous connection as well.
! */
if (pset.cur_cmd_interactive)
{
psql_error("%s", PQerrorMessage(n_conn));
***************
*** 1088,1102 ****
{
if (pset.cur_cmd_interactive)
{
! psql_error("%s", PQerrorMessage(pset.db));
! PQfinish(pset.db);
! if (oldconn)
! {
! fputs(_("Previous connection kept\n"), stderr);
! pset.db = oldconn;
! }
! else
! pset.db = NULL;
}
else
{
--- 1084,1094 ----
{
if (pset.cur_cmd_interactive)
{
! psql_error("%s", PQerrorMessage(n_conn));
!
! /* pset.db is left unmodified */
! if (o_conn)
! fputs(_("Previous connection kept.\n"), stderr);
}
else
{
***************
*** 1100,1113 ****
}
else
{
! /*
! * we don't want unpredictable things to happen in scripting mode
! */
! psql_error("\\connect: %s", PQerrorMessage(pset.db));
! PQfinish(pset.db);
! if (oldconn)
! PQfinish(oldconn);
! pset.db = NULL;
}
}
else
--- 1092,1103 ----
}
else
{
! psql_error("\\connect: %s", PQerrorMessage(n_conn));
! if (o_conn)
! {
! PQfinish(o_conn);
! pset.db = NULL;
! }
}
PQfinish(n_conn);
***************
*** 1109,1138 ****
PQfinish(oldconn);
pset.db = NULL;
}
}
! else
{
! if (!QUIET())
! {
! if ((hostparam == new_host) && (portparam == new_port)) /* no new host or port */
! {
! if (userparam != new_user) /* no new user */
! printf(_("You are now connected to database \"%s\".\n"), dbparam);
! else if (dbparam != new_dbname) /* no new db */
! printf(_("You are now connected as new user \"%s\".\n"), new_user);
! else
! /* both new */
! printf(_("You are now connected to database \"%s\" as user \"%s\".\n"),
! PQdb(pset.db), PQuser(pset.db));
! }
! else /* At least one of host and port are new */
! {
! printf(
! _("You are now connected to database \"%s\" as user \"%s\" on host \"%s\" at port %s.\n"),
! PQdb(pset.db), PQuser(pset.db), PQhost(pset.db),
! PQport(pset.db));
! }
! }
if (oldconn)
PQfinish(oldconn);
--- 1099,1121 ----
PQfinish(oldconn);
pset.db = NULL;
}
+
+ PQfinish(n_conn);
+ return false;
}
!
! /*
! * Replace the old connection with the new one, and update
! * connection-dependent variables.
! */
! PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL);
! pset.db = n_conn;
! SyncVariables();
!
! /* Tell the user about the new connection */
! if (!QUIET())
{
! printf(_("You are now connected to database \"%s\""), PQdb(pset.db));
if (param_is_newly_set(PQuser(o_conn), PQuser(pset.db)))
printf(_(" as user \"%s\""), PQuser(pset.db));
***************
*** 1134,1151 ****
}
}
! if (oldconn)
! PQfinish(oldconn);
! success = true;
! }
! PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
! /* Update variables */
! SyncVariables();
! return success;
}
--- 1117,1137 ----
}
}
! if (param_is_newly_set(PQuser(o_conn), PQuser(pset.db)))
! printf(_(" as user \"%s\""), PQuser(pset.db));
! if (param_is_newly_set(PQhost(o_conn), PQhost(pset.db)))
! printf(_(" on host \"%s\""), PQhost(pset.db));
! if (param_is_newly_set(PQport(o_conn), PQport(pset.db)))
! printf(_(" at port \"%s\""), PQport(pset.db));
! printf(".\n");
! }
! if (o_conn)
! PQfinish(o_conn);
! return true;
}
---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?
http://www.postgresql.org/docs/faq