On Mon, Feb 23, 2015 at 05:56:12PM -0300, Alvaro Herrera wrote: > > David Fetter wrote: > > > My thinking behind this was that the patch is a bug fix and intended > > to be back-patched, so I wanted to mess with as little infrastructure > > as possible. A new version of libpq seems like a very big ask for > > such a case. You'll recall that the original problem was that > > > > \c service=foo > > > > only worked accidentally for some pretty narrow use cases and broke > > without much of a clue for the rest. It turned out that the general > > problem was that options given to psql on the command line were not > > even remotely equivalent to \c, even though they were documented to > > be. > > So, in view of these arguments and those put forward by Pavel > downthread, I think the attached is an acceptable patch for the master > branch. It doesn't apply to back branches though; 9.4 and 9.3 have a > conflict in tab-complete.c, 9.2 has additional conflicts in command.c, > and 9.1 and 9.0 are problematic all over because they don't have > src/common. Could you please submit patches adapted for each group of > branches?
Please find patches attached for each live branch. Cheers, David. -- David Fetter <da...@fetter.org> http://fetter.org/ Phone: +1 415 235 3778 AIM: dfetter666 Yahoo!: dfetter Skype: davidfetter XMPP: david.fet...@gmail.com Remember to vote! Consider donating to Postgres: http://www.postgresql.org/about/donate
>From 64c7d3bcfad80dc80857b2ea06c9f2b7fff8f2a5 Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera Resolved conflicts: src/bin/psql/tab-complete.c Resolved conflicts: src/bin/psql/command.c Resolved conflicts: doc/src/sgml/ref/psql-ref.sgml src/bin/psql/command.c --- doc/src/sgml/ref/psql-ref.sgml | 61 ++++++++++++++++++++++----------- src/bin/psql/command.c | 77 ++++++++++++++++++++++++++++++++---------- src/bin/psql/common.c | 36 ++++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 12 ++++++- 5 files changed, 150 insertions(+), 38 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 15bbd7a..b7f0601 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -751,33 +751,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\C [ <replaceable class="parameter">title</replaceable> ]</literal></term> - <listitem> - <para> - Sets the title of any tables being printed as the result of a - query or unset any such title. This command is equivalent to - <literal>\pset title <replaceable - class="parameter">title</replaceable></literal>. (The name of - this command derives from <quote>caption</quote>, as it was - previously only used to set the caption in an - <acronym>HTML</acronym> table.) - </para> - </listitem> - </varlistentry> - - <varlistentry> - <term><literal>\connect</literal> (or <literal>\c</literal>) <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -792,6 +779,42 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> + </listitem> + </varlistentry> + + <varlistentry> + <term><literal>\C [ <replaceable class="parameter">title</replaceable> ]</literal></term> + <listitem> + <para> + Sets the title of any tables being printed as the result of a + query or unset any such title. This command is equivalent to + <literal>\pset title <replaceable + class="parameter">title</replaceable></literal>. (The name of + this command derives from <quote>caption</quote>, as it was + previously only used to set the caption in an + <acronym>HTML</acronym> table.) + </para> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index d2967ed..49c20bd 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1240,16 +1240,46 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; + + if (!o_conn && (!dbname || !user || !host || !port)) + { + /* + * We don't know the supplied connection parameters and don't want to + * connect to the wrong database by using defaults, so require all + * parameters to be specified. + */ + psql_error("All connection parameters must be supplied because no " + "database connection exists\n"); + return false; + } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1264,9 +1294,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1274,21 +1308,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 7 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = NULL; - values[6] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 5013ad3..6938d08 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1514,6 +1514,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index 838a552..155771c 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -63,4 +63,6 @@ extern const char *session_username(void); extern char *expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index feeb76f..98e23b9 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -2451,7 +2455,13 @@ psql_completion(char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
>From 968ca9febefe48b2424049a21a9cf51b331defcd Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera Resolved conflicts: src/bin/psql/tab-complete.c Resolved conflicts: src/bin/psql/command.c --- doc/src/sgml/ref/psql-ref.sgml | 31 ++++++++++++++--- src/bin/psql/command.c | 79 +++++++++++++++++++++++++++++++----------- src/bin/psql/common.c | 36 +++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 12 ++++++- 5 files changed, 135 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 6b64cd5..54bde8d 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -745,18 +745,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -771,6 +773,27 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form detailed in <xref linkend="LIBPQ-CONNSTRING"> takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index fdb2404..adfc60f 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1467,16 +1467,46 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; + + if (!o_conn && (!dbname || !user || !host || !port)) + { + /* + * We don't know the supplied connection parameters and don't want to + * connect to the wrong database by using defaults, so require all + * parameters to be specified. + */ + psql_error("All connection parameters must be supplied because no " + "database connection exists\n"); + return false; + } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1491,9 +1521,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1501,23 +1535,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index abb1082..82f2320 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1514,6 +1514,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index a26b868..20951d7 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -63,4 +63,6 @@ extern const char *session_username(void); extern char *expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index d90d281..b5316fa 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -2776,7 +2780,13 @@ psql_completion(char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
>From 8c21e0d49c0ce8c4b21bd14d13b681f9148d68da Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera Resolved conflicts: src/bin/psql/tab-complete.c Resolved conflicts: src/bin/psql/command.c --- doc/src/sgml/ref/psql-ref.sgml | 31 ++++++++++++++--- src/bin/psql/command.c | 79 +++++++++++++++++++++++++++++++----------- src/bin/psql/common.c | 36 +++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 12 ++++++- 5 files changed, 135 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 9d9c573..7d741ed 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -771,18 +771,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -797,6 +799,27 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form detailed in <xref linkend="LIBPQ-CONNSTRING"> takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index b74db20..5a8b24d 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1506,16 +1506,46 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; + + if (!o_conn && (!dbname || !user || !host || !port)) + { + /* + * We don't know the supplied connection parameters and don't want to + * connect to the wrong database by using defaults, so require all + * parameters to be specified. + */ + psql_error("All connection parameters must be supplied because no " + "database connection exists\n"); + return false; + } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1530,9 +1560,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1540,23 +1574,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 49ee47a..8b13a8f 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1626,6 +1626,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index cfe0dad..7364908 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -63,4 +63,6 @@ extern const char *session_username(void); extern void expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 870dd6f..ae55189 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -2938,7 +2942,13 @@ psql_completion(char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
>From 2ab01b469297e0dd0eeae8b227924667e9aad814 Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera Resolved conflicts: src/bin/psql/tab-complete.c --- doc/src/sgml/ref/psql-ref.sgml | 31 ++++++++++++++++--- src/bin/psql/command.c | 67 +++++++++++++++++++++++++++++------------- src/bin/psql/common.c | 36 +++++++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 12 +++++++- 5 files changed, 123 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index ca1daea..a8403cb 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -772,18 +772,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -798,6 +800,27 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form detailed in <xref linkend="LIBPQ-CONNSTRING"> takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 0152a76..8786a1a 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1559,6 +1559,8 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; if (!o_conn && (!dbname || !user || !host || !port)) { @@ -1572,15 +1574,31 @@ do_connect(char *dbname, char *user, char *host, char *port) return false; } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1595,9 +1613,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = pg_strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1605,23 +1627,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 6f16a5c..2d98d44 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1654,6 +1654,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index db645da..dd2fdcf 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -46,4 +46,6 @@ extern const char *session_username(void); extern void expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 0a210cb..a4e5a6e 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -3256,7 +3260,13 @@ psql_completion(char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
>From 86f7d6549dc74c1baa58cf9d66f2f2cf4df32666 Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera Resolved conflicts: src/bin/psql/tab-complete.c --- doc/src/sgml/ref/psql-ref.sgml | 31 ++++++++++++++++--- src/bin/psql/command.c | 67 +++++++++++++++++++++++++++++------------- src/bin/psql/common.c | 36 +++++++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 12 +++++++- 5 files changed, 123 insertions(+), 25 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 1d034df..d985201 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -778,18 +778,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -804,6 +806,27 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form detailed in <xref linkend="LIBPQ-CONNSTRING"> takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 8f8c785..391f60b 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1595,6 +1595,8 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; if (!o_conn && (!dbname || !user || !host || !port)) { @@ -1608,15 +1610,31 @@ do_connect(char *dbname, char *user, char *host, char *port) return false; } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1631,9 +1649,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = pg_strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1641,23 +1663,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index c08c813..da4f414 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1691,6 +1691,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index f58c545..069c1b8 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -46,4 +46,6 @@ extern const char *session_username(void); extern void expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 9841c1a..3e51ecb 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -3413,7 +3417,13 @@ psql_completion(const char *text, int start, int end) /* Backslash commands */ /* TODO: \dc \dd \dl */ else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
>From 1fcca51973cdd91b1620fdf6502f658f6b557c16 Mon Sep 17 00:00:00 2001 From: David Fetter <da...@fetter.org> Date: Wed, 25 Feb 2015 13:51:17 -0800 Subject: [PATCH] From Alvaro Herrera --- doc/src/sgml/ref/psql-ref.sgml | 31 ++++++++++++++++--- src/bin/psql/command.c | 67 +++++++++++++++++++++++++++++------------- src/bin/psql/common.c | 36 +++++++++++++++++++++++ src/bin/psql/common.h | 2 ++ src/bin/psql/tab-complete.c | 20 +++++++++++-- 5 files changed, 130 insertions(+), 26 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index a637001..340a5d5 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -796,18 +796,20 @@ testdb=> </varlistentry> <varlistentry> - <term><literal>\c</literal> or <literal>\connect</literal> <literal>[ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ]</literal></term> + <term><literal>\c</literal> or <literal>\connect</literal> <literal> [ { [ <replaceable class="parameter">dbname</replaceable> [ <replaceable class="parameter">username</replaceable> ] [ <replaceable class="parameter">host</replaceable> ] [ <replaceable class="parameter">port</replaceable> ] ] | <replaceable class="parameter">conninfo</replaceable> string | <replaceable class="parameter">URI</replaceable> } ] </literal></term> <listitem> <para> Establishes a new connection to a <productname>PostgreSQL</> - server. If the new connection is successfully made, the - previous connection is closed. If any of <replaceable + server using positional parameters as described below, a + <parameter>conninfo</parameter> string, or a <acronym>URI</acronym>. If the new connection is + successfully made, the + previous connection is closed. When using positional parameters, if any of <replaceable class="parameter">dbname</replaceable>, <replaceable class="parameter">username</replaceable>, <replaceable class="parameter">host</replaceable> or <replaceable class="parameter">port</replaceable> are omitted or specified as <literal>-</literal>, the value of that parameter from the - previous connection is used. If there is no previous + previous connection is used. If using positional parameters and there is no previous connection, the <application>libpq</application> default for the parameter's value is used. </para> @@ -822,6 +824,27 @@ testdb=> mechanism that scripts are not accidentally acting on the wrong database on the other hand. </para> + + <para> + Positional syntax: +<programlisting> +=> \c mydb myuser host.dom 6432 +</programlisting> + </para> + + <para> + The conninfo form detailed in <xref linkend="LIBPQ-CONNSTRING"> takes two forms: keyword-value pairs + </para> +<programlisting> +=> \c service=foo +=> \c "host=localhost port=5432 dbname=mydb connect_timeout=10 sslmode=disable" +</programlisting> + <para> + and URIs: + </para> +<programlisting> +=> \c postgresql://tom@localhost/mydb?application_name=myapp +</programlisting> </listitem> </varlistentry> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 7c9f28d..dd9350e 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -1607,6 +1607,8 @@ do_connect(char *dbname, char *user, char *host, char *port) PGconn *o_conn = pset.db, *n_conn; char *password = NULL; + bool keep_password = true; + bool has_connection_string = false; if (!o_conn && (!dbname || !user || !host || !port)) { @@ -1620,15 +1622,31 @@ do_connect(char *dbname, char *user, char *host, char *port) return false; } - 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 (dbname) + has_connection_string = recognized_connection_string(dbname); + + keep_password = ( + (strcmp(user, PQuser(o_conn)) == 0) && + (!host || strcmp(host, PQhost(o_conn)) == 0) && + (strcmp(port, PQport(o_conn)) == 0) && + !has_connection_string); + + /* + * Unlike the previous stanzas, changing only the dbname shouldn't + * trigger throwing away the password. + */ + if (!dbname) + dbname = PQdb(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 @@ -1643,9 +1661,13 @@ do_connect(char *dbname, char *user, char *host, char *port) { password = prompt_for_password(user); } - else if (o_conn && user && strcmp(PQuser(o_conn), user) == 0) + else if (o_conn && keep_password) { - password = pg_strdup(PQpass(o_conn)); + password = PQpass(o_conn); + if (password && *password) + password = pg_strdup(password); + else + password = NULL; } while (true) @@ -1653,23 +1675,28 @@ do_connect(char *dbname, char *user, char *host, char *port) #define PARAMS_ARRAY_SIZE 8 const char **keywords = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*keywords)); const char **values = pg_malloc(PARAMS_ARRAY_SIZE * sizeof(*values)); + int paramnum = 0; - keywords[0] = "host"; - values[0] = host; - keywords[1] = "port"; - values[1] = port; - keywords[2] = "user"; - values[2] = user; - keywords[3] = "password"; - values[3] = password; - keywords[4] = "dbname"; - values[4] = dbname; - keywords[5] = "fallback_application_name"; - values[5] = pset.progname; - keywords[6] = "client_encoding"; - values[6] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; - keywords[7] = NULL; - values[7] = NULL; + keywords[0] = "dbname"; + values[0] = dbname; + + if (!has_connection_string) + { + keywords[++paramnum] = "host"; + values[paramnum] = host; + keywords[++paramnum] = "port"; + values[paramnum] = port; + keywords[++paramnum] = "user"; + values[paramnum] = user; + } + keywords[++paramnum] = "password"; + values[paramnum] = password; + keywords[++paramnum] = "fallback_application_name"; + values[paramnum] = pset.progname; + keywords[++paramnum] = "client_encoding"; + values[paramnum] = (pset.notty || getenv("PGCLIENTENCODING")) ? NULL : "auto"; + keywords[++paramnum] = NULL; + values[paramnum] = NULL; n_conn = PQconnectdbParams(keywords, values, true); diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 275bdcc..e036332 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -1771,6 +1771,42 @@ session_username(void) return PQuser(pset.db); } +/* + * Checks if connection string starts with either of the valid URI prefix + * designators. + * + * Returns the URI prefix length, 0 if the string doesn't contain a URI prefix. + * + * XXX copied from libpq/fe-connect.c + */ +static int +uri_prefix_length(const char *connstr) +{ + const char uri_designator[] = "postgresql://"; + const char short_uri_designator[] = "postgres://"; + if (strncmp(connstr, uri_designator, + sizeof(uri_designator) - 1) == 0) + return sizeof(uri_designator) - 1; + + if (strncmp(connstr, short_uri_designator, + sizeof(short_uri_designator) - 1) == 0) + return sizeof(short_uri_designator) - 1; + + return 0; +} + +/* + * Recognized connection string either starts with a valid URI prefix or + * contains a "=" in it. + * + * XXX copied from libpq/fe-connect.c + */ +bool +recognized_connection_string(const char *connstr) +{ + return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL; +} + /* expand_tilde * diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h index eb14d1c..079da43 100644 --- a/src/bin/psql/common.h +++ b/src/bin/psql/common.h @@ -49,4 +49,6 @@ extern const char *session_username(void); extern void expand_tilde(char **filename); +extern bool recognized_connection_string(const char *connstr); + #endif /* COMMON_H */ diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index e39a07c..1c57924 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -69,6 +69,10 @@ extern char *filename_completion_function(); /* word break characters */ #define WORD_BREAKS "\t\n@$><=;|&{() " +/* XXX Until we can tab complete for URIs and services, each of which + * should populate their own lists */ +static const char *const empty_list[] = {NULL}; + /* * This struct is used to define "schema queries", which are custom-built * to obtain possibly-schema-qualified names of database objects. There is @@ -3706,10 +3710,22 @@ psql_completion(const char *text, int start, int end) COMPLETE_WITH_LIST_CS(my_list); } + else if (strcmp(prev_wd, "\\connect") == 0 || strcmp(prev_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_databases); + { + /* URI/service completion. Nothing for now */ + if (recognized_connection_string(text)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_databases); + } else if (strcmp(prev2_wd, "\\connect") == 0 || strcmp(prev2_wd, "\\c") == 0) - COMPLETE_WITH_QUERY(Query_for_list_of_roles); + { + if (recognized_connection_string(prev_wd)) + COMPLETE_WITH_LIST(empty_list); + else + COMPLETE_WITH_QUERY(Query_for_list_of_roles); + } else if (strncmp(prev_wd, "\\da", strlen("\\da")) == 0) COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_aggregates, NULL); -- 2.1.0
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers