From d4492e0810dea6bfdd0b8a28b3e56205dd36385d Mon Sep 17 00:00:00 2001 From: Jay Hemnani <193022578+jayhemnani9910@users.noreply.github.com> Date: Sat, 14 Nov 2026 01:58:49 +0530 Subject: [PATCH] End use and source at the newline in batch input The mysql client ends use, source, \u and \. at the newline, so scripts often have `use db` without a semicolon. Batch input kept reading until the next ';', so the following statement was sent as part of the database name and the run stopped with "Unknown database". --- changelog.md | 1 + mycli/packages/utils/batch_utils.py | 8 ++++++++ mycli_test/pytests/test_batch_utils.py | 20 ++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/changelog.md b/changelog.md index bcfcd895b..c7f0fdf9c 100644 --- a/changelog.md +++ b/changelog.md @@ -8,6 +8,7 @@ Bug Fixes * Bracket IPv6 hosts in the DSN shown and saved by `\dsn`, so a saved alias for `::1` can be used to connect again. * Support `DELIMITER` in batch input (`mycli < file.sql`, `--batch`), so dumps with triggers or routines restore. * Read `--batch` files and read and write `--checkpoint` files as UTF-8, fixing `--resume` and non-ASCII batch files on Windows. +* Terminate `use`, `source`, `\u` and `\.` at newline in batch input, as the vendor client does. v2.30.4 (2026/11/12) diff --git a/mycli/packages/utils/batch_utils.py b/mycli/packages/utils/batch_utils.py index 36803c3a6..3e30d98d4 100644 --- a/mycli/packages/utils/batch_utils.py +++ b/mycli/packages/utils/batch_utils.py @@ -7,6 +7,8 @@ MAX_MULTILINE_BATCH_STATEMENT = 5000 DELIMITER_RE = re.compile(r'^\s*delimiter\s+(\S+)\s*$', re.IGNORECASE) +# Client commands the vendor client ends at the newline, without a delimiter +LINE_COMMAND_RE = re.compile(r'^\s*(/?use|/?source|[/\\]u|[/\\]\.)\s+\S', re.IGNORECASE) def _has_tokens(text: str) -> bool: @@ -32,6 +34,12 @@ def statements_from_filehandle(file_h: IO) -> Generator[tuple[str, int], None, N statements = '' line_counter = 0 continue + if LINE_COMMAND_RE.match(batch_text) and not _has_tokens(statements) and not batch_text.rstrip().endswith(delimiter): + yield (batch_text.strip(), batch_counter) + batch_counter += 1 + statements = '' + line_counter = 0 + continue statements += batch_text if delimiter != ';': statement = statements.rstrip() diff --git a/mycli_test/pytests/test_batch_utils.py b/mycli_test/pytests/test_batch_utils.py index 9766ef61e..3af2984f6 100644 --- a/mycli_test/pytests/test_batch_utils.py +++ b/mycli_test/pytests/test_batch_utils.py @@ -108,3 +108,23 @@ def test_statements_from_filehandle_handles_delimiter_blocks() -> None: ('CREATE PROCEDURE p1()\nBEGIN\n SELECT 1;\nEND', 0), ('CALL p1();', 1), ] + + +def test_statements_from_filehandle_ends_use_at_newline() -> None: + statements = collect_statements('select 1;\nuse mysql\nselect database();\n') + + assert statements == [ + ('select 1;', 0), + ('use mysql', 1), + ('select database();', 2), + ] + + +def test_statements_from_filehandle_ends_slash_use_at_newline() -> None: + statements = collect_statements('select 1;\n/use mysql\nselect database();\n') + + assert statements == [ + ('select 1;', 0), + ('/use mysql', 1), + ('select database();', 2), + ]