Skip to content

✨ MySQL Unix socket connection support #137

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jul 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Options:
--mysql-password TEXT MySQL password
-h, --mysql-host TEXT MySQL host. Defaults to localhost.
-P, --mysql-port INTEGER MySQL port. Defaults to 3306.
-k, --mysql-socket PATH Path to MySQL unix socket file.
-S, --skip-ssl Disable MySQL connection encryption.
-i, --mysql-insert-method [DEFAULT|IGNORE|UPDATE]
MySQL insert method. DEFAULT will throw
Expand Down
1 change: 1 addition & 0 deletions docs/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Connection Options
- ``-h, --mysql-host TEXT``: MySQL host. Defaults to localhost.
- ``-P, --mysql-port INTEGER``: MySQL port. Defaults to 3306.
- ``-S, --skip-ssl``: Disable MySQL connection encryption.
- ``--mysql-socket TEXT``: Path to MySQL unix socket file.

Transfer Options
""""""""""""""""
Expand Down
12 changes: 12 additions & 0 deletions src/sqlite3_to_mysql/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,13 @@
@click.option("--mysql-password", default=None, help="MySQL password")
@click.option("-h", "--mysql-host", default="localhost", help="MySQL host. Defaults to localhost.")
@click.option("-P", "--mysql-port", type=int, default=3306, help="MySQL port. Defaults to 3306.")
@click.option(
"-k",
"--mysql-socket",
type=click.Path(exists=True),
default=None,
help="Path to MySQL unix socket file.",
)
@click.option("-S", "--skip-ssl", is_flag=True, help="Disable MySQL connection encryption.")
@click.option(
"-i",
Expand Down Expand Up @@ -137,6 +144,7 @@
mysql_database: str,
mysql_host: str,
mysql_port: int,
mysql_socket: t.Optional[str],
skip_ssl: bool,
mysql_insert_method: str,
mysql_truncate_tables: bool,
Expand All @@ -157,6 +165,9 @@
"""Transfer SQLite to MySQL using the provided CLI options."""
click.echo(_copyright_header)
try:
if mysql_port and mysql_socket:
raise click.ClickException("Error: Can only specify either -P/--mysql-port or -k/--mysql-socket, not both.")

Check warning on line 169 in src/sqlite3_to_mysql/cli.py

View check run for this annotation

Codecov / codecov/patch

src/sqlite3_to_mysql/cli.py#L169

Added line #L169 was not covered by tests

if mysql_collation:
charset_collations: t.Tuple[str, ...] = tuple(
cs.collation for cs in mysql_supported_character_sets(mysql_charset.lower())
Expand All @@ -183,6 +194,7 @@
mysql_database=mysql_database,
mysql_host=mysql_host,
mysql_port=mysql_port,
mysql_socket=mysql_socket,
mysql_ssl_disabled=skip_ssl,
mysql_insert_method=mysql_insert_method,
mysql_truncate_tables=mysql_truncate_tables,
Expand Down
10 changes: 10 additions & 0 deletions src/sqlite3_to_mysql/transporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@

self._mysql_port = kwargs.get("mysql_port", 3306) or 3306

if kwargs.get("mysql_socket") is not None:
if not os.path.exists(str(kwargs.get("mysql_socket"))):
raise FileNotFoundError("MySQL socket does not exist")

Check warning on line 80 in src/sqlite3_to_mysql/transporter.py

View check run for this annotation

Codecov / codecov/patch

src/sqlite3_to_mysql/transporter.py#L79-L80

Added lines #L79 - L80 were not covered by tests
else:
self._mysql_socket = realpath(str(kwargs.get("mysql_socket")))
self._mysql_port = None

Check warning on line 83 in src/sqlite3_to_mysql/transporter.py

View check run for this annotation

Codecov / codecov/patch

src/sqlite3_to_mysql/transporter.py#L82-L83

Added lines #L82 - L83 were not covered by tests
else:
self._mysql_socket = None

self._sqlite_tables = kwargs.get("sqlite_tables") or tuple()

self._without_foreign_keys = bool(self._sqlite_tables) or bool(kwargs.get("without_foreign_keys", False))
Expand Down Expand Up @@ -144,6 +153,7 @@
password=self._mysql_password,
host=self._mysql_host,
port=self._mysql_port,
unix_socket=self._mysql_socket,
ssl_disabled=self._mysql_ssl_disabled,
use_pure=True,
charset=self._mysql_charset,
Expand Down
4 changes: 3 additions & 1 deletion src/sqlite3_to_mysql/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class SQLite3toMySQLParams(tx.TypedDict):
mysql_password: t.Optional[t.Union[str, bool]]
mysql_host: t.Optional[str]
mysql_port: t.Optional[int]
mysql_socket: t.Optional[t.Union[str, "os.PathLike[t.Any]"]]
mysql_ssl_disabled: t.Optional[bool]
chunk: t.Optional[int]
quiet: t.Optional[bool]
Expand Down Expand Up @@ -48,7 +49,8 @@ class SQLite3toMySQLAttributes:
_mysql_user: str
_mysql_password: t.Optional[str]
_mysql_host: str
_mysql_port: int
_mysql_port: t.Optional[int]
_mysql_socket: t.Optional[t.Union[str, "os.PathLike[t.Any]"]]
_mysql_ssl_disabled: bool
_chunk_size: t.Optional[int]
_quiet: bool
Expand Down
4 changes: 4 additions & 0 deletions tests/unit/types_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def test_sqlite3_to_mysql_params_typing(self) -> None:
"mysql_password": "password",
"mysql_host": "localhost",
"mysql_port": 3306,
"mysql_socket": "/var/run/mysqld/mysqld.sock",
"mysql_ssl_disabled": True,
"chunk": 1000,
"quiet": False,
Expand Down Expand Up @@ -50,6 +51,7 @@ def test_sqlite3_to_mysql_params_typing(self) -> None:
assert params["mysql_password"] == "password"
assert params["mysql_host"] == "localhost"
assert params["mysql_port"] == 3306
assert params["mysql_socket"] == "/var/run/mysqld/mysqld.sock"
assert params["mysql_ssl_disabled"] is True
assert params["chunk"] == 1000
assert params["quiet"] is False
Expand Down Expand Up @@ -90,6 +92,7 @@ def __init__(self) -> None:
self._mysql_password = "password"
self._mysql_host = "localhost"
self._mysql_port = 3306
self._mysql_socket = "/var/run/mysqld/mysqld.sock"
self._mysql_ssl_disabled = True
self._chunk_size = 1000
self._quiet = False
Expand Down Expand Up @@ -129,6 +132,7 @@ def __init__(self) -> None:
assert instance._mysql_password == "password"
assert instance._mysql_host == "localhost"
assert instance._mysql_port == 3306
assert instance._mysql_socket == "/var/run/mysqld/mysqld.sock"
assert instance._mysql_ssl_disabled is True
assert instance._chunk_size == 1000
assert instance._quiet is False
Expand Down