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

Reply via email to