From 33a424faa63adb0e6112064532d808eb33e04458 Mon Sep 17 00:00:00 2001 From: Daymel Bonne <7658145+dbonne@users.noreply.github.com> Date: Thu, 18 Dec 2025 15:20:09 -0500 Subject: [PATCH v1] Allow connection string parameters as positional arguments Previously, psql only accepted a database name and username as positional arguments. Any argument containing '=' was interpreted literally as the database name, which was confusing when users tried to use connection string parameters like "service=myservice" as positional arguments. This commit modifies psql to recognize positional arguments containing '=' as connection string parameters (e.g., "host=localhost", "service=mydb", "port=5433"). These parameters are collected into a connection string that serves as the base for the connection. Explicit options like -d, -h, -p, and -U override the corresponding values in the connection string. This enables convenient usage patterns like: psql service=myservice -d postgres -c "SELECT version()" which connects using the service definition but overrides the database name with 'postgres'. The implementation: - Adds a 'connstring' field to store key=value positional arguments - Modifies parse_psql_options() to detect and collect key=value arguments - Updates the connection logic to merge connstring with explicit options - Maintains full backward compatibility with existing positional args Regular positional arguments (without '=') continue to work as before, being interpreted as database name and username respectively. --- doc/src/sgml/ref/psql-ref.sgml | 31 +++++++++++++++- src/bin/psql/meson.build | 1 + src/bin/psql/startup.c | 66 ++++++++++++++++++++++++++++++++-- 3 files changed, 95 insertions(+), 3 deletions(-) diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index f56c70263e0..41248e017d4 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -25,6 +25,7 @@ PostgreSQL documentation psql option + connparam dbname username @@ -164,7 +165,10 @@ EOF argument on the command line. The dbname can be a connection string. If so, connection string parameters will override any conflicting - command line options. + command line options. This option also overrides any + dbname specified in connection parameters passed + as positional arguments (e.g., psql service=myservice -d postgres + uses postgres as the database name). @@ -667,6 +671,31 @@ EOF administrator should have informed you about your access rights. + + Additionally, any non-option argument containing an equals sign + (=) is treated as a connection string parameter + rather than a database name or user name. This allows specifying + connection parameters such as service, + host, or sslmode directly on the + command line. For example: + +$ psql service=myservice +$ psql host=localhost port=5433 sslmode=require mydb + + Multiple connection parameters can be specified this way, and they are + combined into a connection string. Explicit options such as + , , , and + override any corresponding values specified as + connection parameters. This is particularly useful when using + connection services defined in pg_service.conf, + allowing you to use a service but override specific parameters: + +$ psql service=myservice -d postgres + + This connects using the service definition but overrides the database + name with postgres. + + When the defaults aren't quite right, you can save yourself some typing by setting the environment variables diff --git a/src/bin/psql/meson.build b/src/bin/psql/meson.build index d344053c23b..eb11c4e0c07 100644 --- a/src/bin/psql/meson.build +++ b/src/bin/psql/meson.build @@ -75,6 +75,7 @@ tests += { 'env': {'with_readline': readline.found() ? 'yes' : 'no'}, 'tests': [ 't/001_basic.pl', + 't/002_connstring.pl', 't/010_tab_completion.pl', 't/020_cancel.pl', 't/030_pager.pl', diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 249b6aa5169..d699e5799d2 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -70,6 +70,7 @@ struct adhoc_opts char *port; char *username; char *logfilename; + char *connstring; /* connection string from positional args */ bool no_readline; bool no_psqlrc; bool single_txn; @@ -248,6 +249,48 @@ main(int argc, char *argv[]) password = simple_prompt("Password: ", false); } + /* + * Build the effective dbname for connection. If we have a connection + * string from positional args (e.g., "service=myservice"), use it as + * the base. Explicit -d/--dbname overrides the dbname within it. + */ + { + char *effective_dbname = NULL; + + if (options.connstring != NULL) + { + if (options.dbname != NULL) + { + /* Combine connstring with explicit dbname override */ + effective_dbname = psprintf("%s dbname=%s", + options.connstring, options.dbname); + } + else if (options.list_dbs) + { + /* For -l, default to postgres database */ + effective_dbname = psprintf("%s dbname=postgres", + options.connstring); + } + else + { + /* Just use the connection string as-is */ + effective_dbname = pg_strdup(options.connstring); + } + } + else + { + /* No connection string, use explicit dbname or default */ + if (options.list_dbs && options.dbname == NULL) + effective_dbname = pg_strdup("postgres"); + else if (options.dbname != NULL) + effective_dbname = pg_strdup(options.dbname); + /* else effective_dbname stays NULL */ + } + + /* Store for use in connection loop */ + options.dbname = effective_dbname; + } + /* loop until we have a password if requested by backend */ do { @@ -728,11 +771,30 @@ parse_psql_options(int argc, char *argv[], struct adhoc_opts *options) } /* - * if we still have arguments, use it as the database name and username + * if we still have arguments, use it as the database name and username. + * Arguments containing '=' are treated as connection string parameters + * (e.g., "service=myservice" or "host=localhost") and collected into + * a connection string that will be used as the base for the connection. */ while (argc - optind >= 1) { - if (!options->dbname) + /* + * If argument contains '=', treat it as a connection string parameter. + * This allows syntax like: psql service=myservice -d postgres + */ + if (strchr(argv[optind], '=') != NULL) + { + if (options->connstring == NULL) + options->connstring = pg_strdup(argv[optind]); + else + { + /* Append to existing connection string with space separator */ + char *newstr = psprintf("%s %s", options->connstring, argv[optind]); + pg_free(options->connstring); + options->connstring = newstr; + } + } + else if (!options->dbname) options->dbname = argv[optind]; else if (!options->username) options->username = argv[optind]; base-commit: d49936f3028b0bb6ac8ff83e07e421ca2a4f5c3f -- 2.50.1 (Apple Git-155)