Hi, I've written do_connect() and \c handling part from scratch in the attached patch. Here are some features introduced:
- \c syntax is extended. (See comment lines just above \c stuff.) - do_connect() AI (like used for prompting password or informing client about connect attempt's result.) improved. - Some code clean up. If you'd agree with the style, I'll add patch for the documentation and PO files too. Regards. P.S. Patch passed regressions tests on CVS tip. On Dec 13 04:29, David Fetter wrote: > Please find enclosed a patch that lets you use \c to connect > (optionally) to a new host and port without exiting psql. This > eliminates, IMHO, a surprise in that you can now connect to PostgreSQL > on a differnt machine from the one where you started your session. > This should help people who use psql as an administrative tool.
Index: src/bin/psql/command.c =================================================================== RCS file: /projects/cvsroot/pgsql/src/bin/psql/command.c,v retrieving revision 1.164 diff -c -r1.164 command.c *** src/bin/psql/command.c 5 Mar 2006 15:58:51 -0000 1.164 --- src/bin/psql/command.c 12 Mar 2006 17:42:10 -0000 *************** *** 188,215 **** 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) { ! char *opt1, ! *opt2, ! *opt3, ! *opt4; ! char opt1q, ! opt2q, ! opt3q, ! opt4q; /* * Ideally we should treat the arguments as SQL identifiers. But for --- 188,221 ---- free(opt); } ! /* ! * \c or \connect -- connect to database with using specified parameters. ! * ! * \c dbname user host port ! * ! * Place a `-' instead of the related parameter to make it use its current ! * value. (If there doesn't exist any current value for the specified ! * parameter, it won't get included in the connection string.) * ! * Here are 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) { ! char *opt1, ! *opt2, ! *opt3, ! *opt4; ! char opt1q, ! opt2q, ! opt3q, ! opt4q; /* * Ideally we should treat the arguments as SQL identifiers. But for *************** *** 220,272 **** * 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 */ ! 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); free(opt3); --- 226,250 ---- * double-quote all mixed-case \connect arguments, and then we can get * rid of OT_SQLIDHACK. */ ! #define PARSE_OPT(optq_addr) \ ! psql_scan_slash_option(scan_state, OT_SQLIDHACK, optq_addr, true); + opt1 = PARSE_OPT(&opt1q); + opt2 = PARSE_OPT(&opt2q); + opt3 = PARSE_OPT(&opt3q); + opt4 = PARSE_OPT(&opt4q); + + #define VALIDATE_OPT(oq, o) \ + ((!oq && (*o == '\0' || strcmp(o, "-") == 0)) ? NULL : o) + + #define CHECK_OPT(o, oq) \ + (o ? VALIDATE_OPT(oq, o) : NULL) + + success = do_connect(CHECK_OPT(opt1, opt1q), + CHECK_OPT(opt2, opt2q), + CHECK_OPT(opt3, opt3q), + CHECK_OPT(opt4, opt4q)); + free(opt1); free(opt2); free(opt3); *************** *** 987,1151 **** ! /* 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) { ! 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(); ! ! /* 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) ! 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)); ! PQfinish(pset.db); ! if (oldconn) ! { fputs(_("Previous connection kept\n"), stderr); - pset.db = oldconn; - } - else - pset.db = NULL; } 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 { ! 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); ! ! success = true; } ! PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); ! /* Update variables */ ! SyncVariables(); ! return success; } --- 965,1178 ---- ! /* ! * 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, related parameter won't get added to ! * connection string. ! * ! * If connection fails with the given parameters, old connection will be kept. */ static bool ! do_connect(const char *n_dbname, const char *n_user, const char *n_host, const char *n_port) { ! PGconn *o_conn = pset.db, ! *n_conn = NULL; ! const char *dbname, ! *user, ! *host, ! *port; ! int sz; ! char *conn_param, *c, ! *pw_prompt = NULL; ! bool done = false; ! ! dbname = (n_dbname ? n_dbname : ((o_conn && PQdb(o_conn)) ? PQdb(o_conn) : NULL)); ! user = (n_user ? n_user : ((o_conn && PQuser(o_conn)) ? PQuser(o_conn) : NULL)); ! host = (n_host ? n_host : ((o_conn && PQhost(o_conn)) ? PQhost(o_conn) : NULL)); ! port = (n_port ? n_port : ((o_conn && PQport(o_conn)) ? PQport(o_conn) : NULL)); ! ! sz = (dbname ? (strlen(dbname) + 8) : 0) /* strlen("dbname= ") = 8 */ ! + (user ? (strlen(user) + 6) : 0) ! + (host ? (strlen(host) + 6) : 0) ! + (port ? (strlen(port) + 6) : 0) ! + 1; ! c = conn_param = malloc(sz); ! if (!c) ! goto OutOfMemory; ! ! /* Form connection parameter string */ ! if (dbname) ! { ! sprintf(c, "dbname=%s ", dbname); ! c += strlen(c); ! } ! if (user) ! { ! sprintf(c, "user=%s ", user); ! c += strlen(c); ! } ! if (host) ! { ! sprintf(c, "host=%s ", host); ! c += strlen(c); ! } ! if (port) ! sprintf(c, "port=%s ", port); ! ! /* First try with these settings without a password */ ! n_conn = PQconnectdb(conn_param); ! if (PQstatus(n_conn) == CONNECTION_OK) ! done = true; ! ! /* Do we need a password? */ ! else if (strcmp(PQerrorMessage(n_conn), PQnoPasswordSupplied) == 0 || ! pset.getPassword) ! { ! bool first = true; ! int n_sz; ! char *password; ! ! /* Free previous failed one */ ! PQfinish(n_conn); ! ! /* First, use password of current connection - if any. */ ! password = ((o_conn && PQpass(o_conn)) ? strdup(PQpass(o_conn)) : NULL); ! ! for (;;) ! { ! /* In the second turn try with prompted password. */ ! if (!first) ! { ! if (!user) ! pw_prompt = strdup("Password: "); ! if (!pw_prompt) ! goto OutOfMemory; ! else ! { ! pw_prompt = malloc(strlen(_("Password for user %s: ")) - 2 ! + strlen(user) + 1); ! if (!pw_prompt) ! goto OutOfMemory; ! sprintf(pw_prompt, _("Password for user %s: "), user); ! } ! ! password = simple_prompt(pw_prompt, 100, false); ! free(pw_prompt); ! if (!password) ! goto OutOfMemory; ! } ! ! /* This is our first turn and we're lacking of old password! */ ! else if (!password) ! continue; ! ! n_sz = sz + 9 + strlen(password); /* strlen("Password=") = 9 */ ! c = realloc(conn_param, n_sz); ! if (!c) ! goto OutOfMemory; ! conn_param = c; ! c = conn_param + sz - 1; ! sprintf(c, "password=%s", password); ! ! /* Password is allocated, not returned from PQpass(). */ ! if (!first) ! free(password); ! ! /* Try again with supplied connection parameter. */ ! n_conn = PQconnectdb(conn_param); ! if (PQstatus(n_conn) == CONNECTION_OK) ! { ! done = true; ! break; ! } ! ! if (first) ! first = false; ! else ! break; } ! } ! /* None of above methods worked, giving up. */ ! if (!done) { if (pset.cur_cmd_interactive) { ! psql_error("%s", PQerrorMessage(n_conn)); ! ! /* Not touching to pset.db */ ! if (o_conn) fputs(_("Previous connection kept\n"), stderr); } else ! psql_error("\\connect: %s", PQerrorMessage(n_conn)); ! ! PQfinish(n_conn); ! free(conn_param); ! ! return false; } ! ! /* Shout new connection. */ ! if (!QUIET()) { ! /* At least one of host and port are new */ ! if ((host && strcmp(host, PQhost(o_conn))) || ! (port && strcmp(port, PQport(o_conn)))) ! printf(_("You are now connected to database \"%s\" as user " ! "\"%s\" on host \"%s\" at port %s.\n"), ! PQdb(n_conn), PQuser(n_conn), PQhost(n_conn), ! PQport(n_conn)); ! ! /* Now new host or port. */ ! else { ! /* Both are new. */ ! if (dbname && strcmp(dbname, PQdb(o_conn)) && ! user && strcmp(user, PQuser(o_conn))) ! printf(_("You are now connected to database \"%s\" as" ! "user \"%s\".\n"), ! PQdb(n_conn), PQuser(n_conn)); ! ! /* Database is new. */ ! else if (dbname && strcmp(dbname, PQdb(o_conn))) ! printf(_("You are now connected to database \"%s\".\n"), ! PQdb(n_conn)); ! ! /* User is new. */ ! else if (user && strcmp(user, PQuser(o_conn))) ! printf(_("You are now connected as new user \"%s\".\n"), ! PQuser(n_conn)); ! /* No parameter changings. Seems like a connection reset. */ ! else ! printf(_("Connection is reseted.\n")); ! } } ! ! /* Replacing old connection structure with the new one. */ ! UnsyncVariables(); ! if (pset.db) ! PQfinish(pset.db); ! pset.db = n_conn; ! SyncVariables(); ! PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL); ! return true; ! OutOfMemory: ! psql_error("out of memory\n"); ! ! if (conn_param) ! free(conn_param); ! if (n_conn) ! PQfinish(n_conn); ! ! return false; }
---------------------------(end of broadcast)--------------------------- TIP 3: Have you checked our extensive FAQ? http://www.postgresql.org/docs/faq