Skip to content

Commit 7eef51d

Browse files
authored
Merge pull request #1812 from dbcli/RW/rename-parseutils-sql-utils
Move SQL utilities to `sql_utils.py`
2 parents 2329c03 + d708afc commit 7eef51d

14 files changed

Lines changed: 199 additions & 127 deletions

File tree

AGENTS.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,12 @@ A command line client for MySQL with auto-completion and syntax highlighting.
2727
├── mycli/packages/ # application packages
2828
├── mycli/packages/batch_utils.py # utilities for `--batch` mode
2929
├── mycli/packages/checkup.py # implementation of `--checkup` mode
30+
├── mycli/packages/cli_utils.py # utilities for parsing CLI arguments
3031
├── mycli/packages/completion_engine.py # implementation of completion suggestions
3132
├── mycli/packages/filepaths.py # utilities for files, including completion suggestions
3233
├── mycli/packages/hybrid_redirection.py # implementation of shell-style redirects
3334
├── mycli/packages/paramiko_stub/ # stub in case the Paramiko library is not installed
34-
├── mycli/packages/parseutils.py # utilities for parsing SQL statements
35+
├── mycli/packages/sql_utils.py # utilities for parsing SQL statements
3536
├── mycli/packages/prompt_utils.py # utilities for confirming on destructive statements
3637
├── mycli/packages/ptoolkit/ # extends prompt_toolkit
3738
├── mycli/packages/shortcuts.py # utilities for keyboard shortcuts

changelog.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ Internal
4141
* Move `--list-ssh-config` logic to the new `main_modes` with `--batch`.
4242
* Sort coverage report in tox suite.
4343
* Skip more tests when a database connection is not present.
44+
* Move SQL utilities to a new `sql_utils.py`.
45+
* Move CLI utilities to a new `cli_utils.py`.
4446

4547

4648
1.67.1 (2026/03/28)

mycli/main.py

Lines changed: 8 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,21 @@
9090
from mycli.main_modes.list_dsn import main_list_dsn
9191
from mycli.main_modes.list_ssh_config import main_list_ssh_config
9292
from mycli.packages import special
93+
from mycli.packages.cli_utils import is_valid_connection_scheme
9394
from mycli.packages.filepaths import dir_path_exists, guess_socket_location
9495
from mycli.packages.hybrid_redirection import get_redirect_components, is_redirect_command
95-
from mycli.packages.parseutils import is_dropping_database, is_valid_connection_scheme
9696
from mycli.packages.prompt_utils import confirm, confirm_destructive_query
9797
from mycli.packages.ptoolkit.history import FileHistoryWithTimestamp
9898
from mycli.packages.special.favoritequeries import FavoriteQueries
9999
from mycli.packages.special.main import ArgType
100100
from mycli.packages.special.utils import format_uptime, get_ssl_version, get_uptime, get_warning_count
101+
from mycli.packages.sql_utils import (
102+
is_dropping_database,
103+
is_mutating,
104+
is_select,
105+
need_completion_refresh,
106+
need_completion_reset,
107+
)
101108
from mycli.packages.sqlresult import SQLResult
102109
from mycli.packages.ssh_utils import read_ssh_config
103110
from mycli.packages.string_utils import sanitize_terminal_title
@@ -2650,53 +2657,6 @@ def get_password_from_file(password_file: str | None) -> str | None:
26502657
mycli.close()
26512658

26522659

2653-
def need_completion_refresh(queries: str) -> bool:
2654-
"""Determines if the completion needs a refresh by checking if the sql
2655-
statement is an alter, create, drop or change db."""
2656-
for query in sqlparse.split(queries):
2657-
try:
2658-
first_token = query.split()[0]
2659-
if first_token.lower() in ("alter", "create", "use", "\\r", "\\u", "connect", "drop", "rename"):
2660-
return True
2661-
except Exception:
2662-
continue
2663-
return False
2664-
2665-
2666-
def need_completion_reset(queries: str) -> bool:
2667-
"""Determines if the statement is a database switch such as 'use' or '\\u'.
2668-
When a database is changed the existing completions must be reset before we
2669-
start the completion refresh for the new database.
2670-
"""
2671-
for query in sqlparse.split(queries):
2672-
try:
2673-
tokens = query.split()
2674-
first_token = tokens[0]
2675-
if first_token.lower() in ("use", "\\u"):
2676-
return True
2677-
if first_token.lower() in ("\\r", "connect") and len(tokens) > 1:
2678-
return True
2679-
except Exception:
2680-
continue
2681-
return False
2682-
2683-
2684-
def is_mutating(status_plain: str | None) -> bool:
2685-
"""Determines if the statement is mutating based on the status."""
2686-
if not status_plain:
2687-
return False
2688-
2689-
mutating = {"insert", "update", "delete", "alter", "create", "drop", "replace", "truncate", "load", "rename"}
2690-
return status_plain.split(None, 1)[0].lower() in mutating
2691-
2692-
2693-
def is_select(status_plain: str | None) -> bool:
2694-
"""Returns true if the first word in status is 'select'."""
2695-
if not status_plain:
2696-
return False
2697-
return status_plain.split(None, 1)[0].lower() == "select"
2698-
2699-
27002660
def thanks_picker() -> str:
27012661
import mycli
27022662

mycli/main_modes/batch.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
import pymysql
1313

1414
from mycli.packages.batch_utils import statements_from_filehandle
15-
from mycli.packages.parseutils import is_destructive
1615
from mycli.packages.prompt_utils import confirm_destructive_query
16+
from mycli.packages.sql_utils import is_destructive
1717

1818
if TYPE_CHECKING:
1919
from mycli.main import CliArgs, MyCli

mycli/packages/cli_utils.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from __future__ import annotations
2+
3+
4+
def is_valid_connection_scheme(text: str) -> tuple[bool, str | None]:
5+
# exit early if the text does not resemble a DSN URI
6+
if "://" not in text:
7+
return False, None
8+
scheme = text.split("://")[0]
9+
if scheme not in ("mysql", "mysqlx", "tcp", "socket", "ssh"):
10+
return False, scheme
11+
else:
12+
return True, None

mycli/packages/completion_engine.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
import sqlparse
77
from sqlparse.sql import Comparison, Identifier, Token, Where
88

9-
from mycli.packages.parseutils import extract_tables, find_prev_keyword, last_word
109
from mycli.packages.special.main import COMMANDS as SPECIAL_COMMANDS
1110
from mycli.packages.special.main import parse_special_command
11+
from mycli.packages.sql_utils import extract_tables, find_prev_keyword, last_word
1212

1313
sqlparse.engine.grouping.MAX_GROUPING_DEPTH = None # type: ignore[assignment]
1414
sqlparse.engine.grouping.MAX_GROUPING_TOKENS = None # type: ignore[assignment]

mycli/packages/prompt_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import click
44

5-
from mycli.packages.parseutils import is_destructive
5+
from mycli.packages.sql_utils import is_destructive
66

77

88
class ConfirmBoolParamType(click.ParamType):
Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,6 @@
2323
}
2424

2525

26-
def is_valid_connection_scheme(text: str) -> tuple[bool, str | None]:
27-
# exit early if the text does not resemble a DSN URI
28-
if "://" not in text:
29-
return False, None
30-
scheme = text.split("://")[0]
31-
if scheme not in ("mysql", "mysqlx", "tcp", "socket", "ssh"):
32-
return False, scheme
33-
else:
34-
return True, None
35-
36-
3726
def last_word(
3827
text: str,
3928
include: Literal[
@@ -433,3 +422,50 @@ def normalize_db_name(db: str) -> str:
433422
if database_token is not None and normalize_db_name(database_token.get_name()) == dbname:
434423
result = keywords[0].normalized == "DROP"
435424
return result
425+
426+
427+
def need_completion_refresh(queries: str) -> bool:
428+
"""Determines if the completion needs a refresh by checking if the sql
429+
statement is an alter, create, drop or change db."""
430+
for query in sqlparse.split(queries):
431+
try:
432+
first_token = query.split()[0]
433+
if first_token.lower() in ("alter", "create", "use", "\\r", "\\u", "connect", "drop", "rename"):
434+
return True
435+
except Exception:
436+
continue
437+
return False
438+
439+
440+
def need_completion_reset(queries: str) -> bool:
441+
"""Determines if the statement is a database switch such as 'use' or '\\u'.
442+
When a database is changed the existing completions must be reset before we
443+
start the completion refresh for the new database.
444+
"""
445+
for query in sqlparse.split(queries):
446+
try:
447+
tokens = query.split()
448+
first_token = tokens[0]
449+
if first_token.lower() in ("use", "\\u"):
450+
return True
451+
if first_token.lower() in ("\\r", "connect") and len(tokens) > 1:
452+
return True
453+
except Exception:
454+
continue
455+
return False
456+
457+
458+
def is_mutating(status_plain: str | None) -> bool:
459+
"""Determines if the statement is mutating based on the status."""
460+
if not status_plain:
461+
return False
462+
463+
mutating = {"insert", "update", "delete", "alter", "create", "drop", "replace", "truncate", "load", "rename"}
464+
return status_plain.split(None, 1)[0].lower() in mutating
465+
466+
467+
def is_select(status_plain: str | None) -> bool:
468+
"""Returns true if the first word in status is 'select'."""
469+
if not status_plain:
470+
return False
471+
return status_plain.split(None, 1)[0].lower() == "select"

mycli/packages/tabular_output/sql_format.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from cli_helpers.tabular_output import TabularOutputFormatter
88

9-
from mycli.packages.parseutils import extract_tables_from_complete_statements
9+
from mycli.packages.sql_utils import extract_tables_from_complete_statements
1010

1111
supported_formats = (
1212
"sql-insert",

mycli/sqlcompleter.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313

1414
from mycli.packages.completion_engine import is_inside_quotes, suggest_type
1515
from mycli.packages.filepaths import complete_path, parse_path, suggest_path
16-
from mycli.packages.parseutils import extract_columns_from_select, extract_tables, last_word
1716
from mycli.packages.special import llm
1817
from mycli.packages.special.favoritequeries import FavoriteQueries
1918
from mycli.packages.special.main import COMMANDS as SPECIAL_COMMANDS
19+
from mycli.packages.sql_utils import extract_columns_from_select, extract_tables, last_word
2020

2121
_logger = logging.getLogger(__name__)
2222
_CASE_CHANGE_PAT = re.compile('(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])')

0 commit comments

Comments
 (0)