From ade42bf4c782c1fddbd48d7265b99320c4bce803 Mon Sep 17 00:00:00 2001 From: Roland Walker Date: Fri, 3 Jul 2026 12:47:20 -0400 Subject: [PATCH] accept DSN schemes with a plus sign DSNs are sometimes written in the form mysql+pymysql://mycli@localhost:3306/mysql with extensions to the scheme. While we will ignore the part of the scheme to the right of the plus sign, mycli should accept the DSN and make the connection. --- changelog.md | 1 + mycli/packages/cli_utils.py | 9 ++++++--- test/pytests/test_cli_runner.py | 14 ++++++++++++++ test/pytests/test_cli_utils.py | 2 ++ 4 files changed, 23 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 520a9614..b1d548fc 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ Features * Add `/help` prompt to initial toolbar help. * Advertise forward-slash `/command` forms in `/help` output. * Override `min_completion_trigger` for the `/command` form. +* Accept DSN schemes with `+` sign. Bug Fixes diff --git a/mycli/packages/cli_utils.py b/mycli/packages/cli_utils.py index d90575db..482348ce 100644 --- a/mycli/packages/cli_utils.py +++ b/mycli/packages/cli_utils.py @@ -24,7 +24,10 @@ def is_valid_connection_scheme(text: str) -> tuple[bool, str | None]: if "://" not in text: return False, None scheme = text.split("://")[0] - if scheme not in ("mysql", "mysqlx", "tcp", "socket"): - return False, scheme - else: + if scheme.startswith('mysql+'): return True, None + if scheme.startswith('mysqlx+'): + return True, None + if scheme in ("mysql", "mysqlx", "tcp", "socket"): + return True, None + return False, scheme diff --git a/test/pytests/test_cli_runner.py b/test/pytests/test_cli_runner.py index 5c2ae740..4adba139 100644 --- a/test/pytests/test_cli_runner.py +++ b/test/pytests/test_cli_runner.py @@ -337,6 +337,20 @@ def test_run_from_cli_args_rejects_unknown_alias_dsn_scheme(monkeypatch: pytest. ] +def test_run_from_cli_args_accepts_mysql_plus_dsn_scheme(monkeypatch: pytest.MonkeyPatch) -> None: + cli_args = make_cli_args() + cli_args.dsn = 'mysql+pymysql://user:pass@host:3306/db' + client = DummyMyCli() + + run_with_client(monkeypatch, cli_args, client) + + assert client.connect_calls[-1]['user'] == 'user' + assert client.connect_calls[-1]['passwd'] == 'pass' + assert client.connect_calls[-1]['host'] == 'host' + assert client.connect_calls[-1]['port'] == 3306 + assert client.connect_calls[-1]['database'] == 'db' + + def test_run_from_cli_args_maps_dsn_ssl_parameters(monkeypatch: pytest.MonkeyPatch) -> None: cli_args = make_cli_args() cli_args.dsn = ( diff --git a/test/pytests/test_cli_utils.py b/test/pytests/test_cli_utils.py index 9a921214..01fa66ae 100644 --- a/test/pytests/test_cli_utils.py +++ b/test/pytests/test_cli_utils.py @@ -35,7 +35,9 @@ def test_filtered_sys_argv_appends_empty_password_sentinel(monkeypatch, password [ ('localhost', False, None), ('mysql://user@localhost/db', True, None), + ('mysql+pymysql://user@localhost/db', True, None), ('mysqlx://user@localhost/db', True, None), + ('mysqlx+pymysql://user@localhost/db', True, None), ('tcp://localhost:3306', True, None), ('socket:///tmp/mysql.sock', True, None), ('ssh://user@example.com', False, 'ssh'),