Skip to content

Commit 042dec9

Browse files
authored
✨ add MySQL 8.4 and MariaDB 11.4 support (#122)
1 parent 21d0306 commit 042dec9

File tree

8 files changed

+143
-19
lines changed

8 files changed

+143
-19
lines changed

.github/workflows/test.yml

+96-14
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,36 @@ jobs:
306306
experimental: false
307307
py: "3.12"
308308

309+
- toxenv: "python3.8"
310+
db: "mariadb:11.4"
311+
legacy_db: 0
312+
experimental: false
313+
py: "3.8"
314+
315+
- toxenv: "python3.9"
316+
db: "mariadb:11.4"
317+
legacy_db: 0
318+
experimental: false
319+
py: "3.9"
320+
321+
- toxenv: "python3.10"
322+
db: "mariadb:11.4"
323+
legacy_db: 0
324+
experimental: false
325+
py: "3.10"
326+
327+
- toxenv: "python3.11"
328+
db: "mariadb:11.4"
329+
legacy_db: 0
330+
experimental: false
331+
py: "3.11"
332+
333+
- toxenv: "python3.12"
334+
db: "mariadb:11.4"
335+
legacy_db: 0
336+
experimental: false
337+
py: "3.12"
338+
309339
- toxenv: "python3.8"
310340
db: "mysql:5.5"
311341
legacy_db: 1
@@ -425,15 +455,46 @@ jobs:
425455
legacy_db: 0
426456
experimental: false
427457
py: "3.12"
458+
459+
- toxenv: "python3.8"
460+
db: "mysql:8.4"
461+
legacy_db: 0
462+
experimental: true
463+
py: "3.8"
464+
465+
- toxenv: "python3.9"
466+
db: "mysql:8.4"
467+
legacy_db: 0
468+
experimental: true
469+
py: "3.9"
470+
471+
- toxenv: "python3.10"
472+
db: "mysql:8.4"
473+
legacy_db: 0
474+
experimental: true
475+
py: "3.10"
476+
477+
- toxenv: "python3.11"
478+
db: "mysql:8.4"
479+
legacy_db: 0
480+
experimental: true
481+
py: "3.11"
482+
483+
- toxenv: "python3.12"
484+
db: "mysql:8.4"
485+
legacy_db: 0
486+
experimental: true
487+
py: "3.12"
428488
continue-on-error: ${{ matrix.experimental }}
429489
services:
430490
mysql:
431-
image: "${{ matrix.db }}"
491+
image: ${{ matrix.db }}
432492
ports:
433493
- 3306:3306
434494
env:
435495
MYSQL_ALLOW_EMPTY_PASSWORD: yes
436-
options: "--name=mysqld"
496+
options: >-
497+
--name=mysqld
437498
steps:
438499
- uses: actions/checkout@v4
439500
- name: Set up Python ${{ matrix.py }}
@@ -461,31 +522,52 @@ jobs:
461522
MYSQL_PORT: 3306
462523
run: |
463524
set -e
525+
464526
while :
465527
do
466528
sleep 1
467529
mysql -h127.0.0.1 -uroot -e 'select version()' && break
468530
done
531+
532+
case "$DB" in
533+
'mysql:8.0'|'mysql:8.4')
534+
mysql -h127.0.0.1 -uroot -e "SET GLOBAL local_infile=on"
535+
docker cp mysqld:/var/lib/mysql/public_key.pem "${HOME}"
536+
docker cp mysqld:/var/lib/mysql/ca.pem "${HOME}"
537+
docker cp mysqld:/var/lib/mysql/server-cert.pem "${HOME}"
538+
docker cp mysqld:/var/lib/mysql/client-key.pem "${HOME}"
539+
docker cp mysqld:/var/lib/mysql/client-cert.pem "${HOME}"
540+
;;
541+
esac
542+
543+
USER_CREATION_COMMANDS=''
544+
WITH_PLUGIN=''
545+
469546
if [ "$DB" == 'mysql:8.0' ]; then
470547
WITH_PLUGIN='with mysql_native_password'
471-
mysql -h127.0.0.1 -uroot -e "SET GLOBAL local_infile=on"
472-
docker cp mysqld:/var/lib/mysql/public_key.pem "${HOME}"
473-
docker cp mysqld:/var/lib/mysql/ca.pem "${HOME}"
474-
docker cp mysqld:/var/lib/mysql/server-cert.pem "${HOME}"
475-
docker cp mysqld:/var/lib/mysql/client-key.pem "${HOME}"
476-
docker cp mysqld:/var/lib/mysql/client-cert.pem "${HOME}"
477-
mysql -uroot -h127.0.0.1 -e '
548+
USER_CREATION_COMMANDS='
478549
CREATE USER
479550
user_sha256 IDENTIFIED WITH "sha256_password" BY "pass_sha256",
480551
nopass_sha256 IDENTIFIED WITH "sha256_password",
481552
user_caching_sha2 IDENTIFIED WITH "caching_sha2_password" BY "pass_caching_sha2",
482553
nopass_caching_sha2 IDENTIFIED WITH "caching_sha2_password"
483-
PASSWORD EXPIRE NEVER;'
484-
mysql -uroot -h127.0.0.1 -e 'GRANT RELOAD ON *.* TO user_caching_sha2;'
485-
else
486-
WITH_PLUGIN=''
554+
PASSWORD EXPIRE NEVER;
555+
GRANT RELOAD ON *.* TO user_caching_sha2;'
556+
elif [ "$DB" == 'mysql:8.4' ]; then
557+
WITH_PLUGIN='with caching_sha2_password'
558+
USER_CREATION_COMMANDS='
559+
CREATE USER
560+
user_caching_sha2 IDENTIFIED WITH "caching_sha2_password" BY "pass_caching_sha2",
561+
nopass_caching_sha2 IDENTIFIED WITH "caching_sha2_password"
562+
PASSWORD EXPIRE NEVER;
563+
GRANT RELOAD ON *.* TO user_caching_sha2;'
564+
fi
565+
566+
if [ ! -z "$USER_CREATION_COMMANDS" ]; then
567+
mysql -uroot -h127.0.0.1 -e "$USER_CREATION_COMMANDS"
487568
fi
488-
mysql -h127.0.0.1 -uroot -e "create database $MYSQL_DATABASE DEFAULT CHARACTER SET utf8mb4"
569+
570+
mysql -h127.0.0.1 -uroot -e "create database $MYSQL_DATABASE DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci"
489571
mysql -h127.0.0.1 -uroot -e "create user $MYSQL_USER identified $WITH_PLUGIN by '${MYSQL_PASSWORD}'; grant all on ${MYSQL_DATABASE}.* to ${MYSQL_USER};"
490572
mysql -h127.0.0.1 -uroot -e "create user ${MYSQL_USER}@localhost identified $WITH_PLUGIN by '${MYSQL_PASSWORD}'; grant all on ${MYSQL_DATABASE}.* to ${MYSQL_USER}@localhost;"
491573
- name: Create db_credentials.json

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
[![PyPI](https://img.shields.io/pypi/v/sqlite3-to-mysql)](https://pypi.org/project/sqlite3-to-mysql/)
22
[![PyPI - Downloads](https://img.shields.io/pypi/dm/sqlite3-to-mysql)](https://pypistats.org/packages/sqlite3-to-mysql)
33
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/sqlite3-to-mysql)](https://pypi.org/project/sqlite3-to-mysql/)
4-
[![MySQL Support](https://img.shields.io/static/v1?label=MySQL&message=5.5+|+5.6+|+5.7+|+8.0&color=2b5d80)](https://img.shields.io/static/v1?label=MySQL&message=5.6+|+5.7+|+8.0&color=2b5d80)
5-
[![MariaDB Support](https://img.shields.io/static/v1?label=MariaDB&message=5.5+|+10.0+|+10.1+|+10.2+|+10.3+|+10.4+|+10.5+|+10.6|+10.11&color=C0765A)](https://img.shields.io/static/v1?label=MariaDB&message=10.0+|+10.1+|+10.2+|+10.3+|+10.4+|+10.5&color=C0765A)
4+
[![MySQL Support](https://img.shields.io/static/v1?label=MySQL&message=5.5+|+5.6+|+5.7+|+8.0+|+8.4&color=2b5d80)](https://img.shields.io/static/v1?label=MySQL&message=5.5+|+5.6+|+5.7+|+8.0+|+8.4&color=2b5d80)
5+
[![MariaDB Support](https://img.shields.io/static/v1?label=MariaDB&message=5.5+|+10.0+|+10.1+|+10.2+|+10.3+|+10.4+|+10.5+|+10.6|+10.11+|+11.4&color=C0765A)](https://img.shields.io/static/v1?label=MariaDB&message=5.5|+10.0+|+10.1+|+10.2+|+10.3+|+10.4+|+10.5|+11.4&color=C0765A)
66
[![GitHub license](https://img.shields.io/github/license/techouse/sqlite3-to-mysql)](https://github.com/techouse/sqlite3-to-mysql/blob/master/LICENSE)
77
[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE-OF-CONDUCT.md)
88
[![PyPI - Format](https://img.shields.io/pypi/format/sqlite3-to-mysql)]((https://pypi.org/project/sqlite3-to-mysql/))

pyproject.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ classifiers = [
3939
]
4040
dependencies = [
4141
"Click>=8.1.3",
42-
"mysql-connector-python>=8.2.0",
42+
"mysql-connector-python>=9.0.0",
4343
"pytimeparse2",
4444
"python-dateutil>=2.9.0.post0",
4545
"types_python_dateutil",

requirements_dev.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Click>=8.1.3
22
docker>=6.1.3
33
factory-boy
44
Faker>=18.10.0
5-
mysql-connector-python>=8.2.0
5+
mysql-connector-python>=9.0.0
66
PyMySQL>=1.0.3
77
pytest>=7.3.1
88
pytest-cov

src/sqlite3_to_mysql/transporter.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ def __init__(self, **kwargs: tx.Unpack[SQLite3toMySQLParams]):
107107
kwargs.get("mysql_collation") or CharacterSet().get_default_collation(self._mysql_charset.lower())[0]
108108
)
109109
if not kwargs.get("mysql_collation") and self._mysql_collation == "utf8mb4_0900_ai_ci":
110-
self._mysql_collation = "utf8mb4_general_ci"
110+
self._mysql_collation = "utf8mb4_unicode_ci"
111111

112112
self._ignore_duplicate_keys = kwargs.get("ignore_duplicate_keys", False) or False
113113

@@ -144,6 +144,8 @@ def __init__(self, **kwargs: tx.Unpack[SQLite3toMySQLParams]):
144144
port=self._mysql_port,
145145
ssl_disabled=self._mysql_ssl_disabled,
146146
use_pure=True,
147+
charset=self._mysql_charset,
148+
collation=self._mysql_collation,
147149
)
148150
if isinstance(_mysql_connection, mysql.connector.MySQLConnection):
149151
self._mysql = _mysql_connection

tests/conftest.py

+2
Original file line numberDiff line numberDiff line change
@@ -293,6 +293,8 @@ def mysql_instance(mysql_credentials: MySQLCredentials, pytestconfig: Config) ->
293293
password=mysql_credentials.password,
294294
host=mysql_credentials.host,
295295
port=mysql_credentials.port,
296+
charset="utf8mb4",
297+
collation="utf8mb4_unicode_ci",
296298
)
297299
except mysql.connector.Error as err:
298300
if err.errno == errorcode.CR_SERVER_LOST:

tests/func/sqlite3_to_mysql_test.py

+2
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,8 @@ def test_transfer_transfers_all_tables_in_sqlite_file(
348348
host=mysql_credentials.host,
349349
port=mysql_credentials.port,
350350
database=mysql_credentials.database,
351+
charset="utf8mb4",
352+
collation="utf8mb4_unicode_ci",
351353
)
352354
)
353355
server_version: t.Tuple[int, ...] = mysql_connector_connection.get_server_version()

tests/func/test_cli.py

+36
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ def test_invalid_database_name(
7777
"_".join(faker.words(nb=3)),
7878
"-u",
7979
faker.first_name().lower(),
80+
"-h",
81+
mysql_credentials.host,
82+
"-P",
83+
str(mysql_credentials.port),
8084
],
8185
)
8286
assert result.exit_code > 0
@@ -99,6 +103,10 @@ def test_invalid_database_user(
99103
mysql_credentials.database,
100104
"-u",
101105
faker.first_name().lower(),
106+
"-h",
107+
mysql_credentials.host,
108+
"-P",
109+
str(mysql_credentials.port),
102110
],
103111
)
104112
assert result.exit_code > 0
@@ -123,6 +131,10 @@ def test_invalid_database_password(
123131
mysql_credentials.user,
124132
"--mysql-password",
125133
faker.password(length=16),
134+
"-h",
135+
mysql_credentials.host,
136+
"-P",
137+
str(mysql_credentials.port),
126138
],
127139
)
128140
assert result.exit_code > 0
@@ -145,6 +157,10 @@ def test_database_password_prompt(
145157
"-u",
146158
mysql_credentials.user,
147159
"-p",
160+
"-h",
161+
mysql_credentials.host,
162+
"-P",
163+
str(mysql_credentials.port),
148164
],
149165
input=mysql_credentials.password,
150166
)
@@ -168,6 +184,10 @@ def test_invalid_database_password_prompt(
168184
"-u",
169185
mysql_credentials.user,
170186
"-p",
187+
"-h",
188+
mysql_credentials.host,
189+
"-P",
190+
str(mysql_credentials.port),
171191
],
172192
input=faker.password(length=16),
173193
)
@@ -230,6 +250,10 @@ def test_mysql_skip_transfer_data(
230250
"--mysql-password",
231251
mysql_credentials.password,
232252
"--mysql-skip-transfer-data",
253+
"-h",
254+
mysql_credentials.host,
255+
"-P",
256+
str(mysql_credentials.port),
233257
],
234258
)
235259
assert result.exit_code == 0
@@ -254,6 +278,10 @@ def test_mysql_skip_create_tables(
254278
"--mysql-password",
255279
mysql_credentials.password,
256280
"--mysql-skip-transfer-data",
281+
"-h",
282+
mysql_credentials.host,
283+
"-P",
284+
str(mysql_credentials.port),
257285
],
258286
)
259287
assert result1.exit_code == 0
@@ -270,6 +298,10 @@ def test_mysql_skip_create_tables(
270298
"--mysql-password",
271299
mysql_credentials.password,
272300
"--mysql-skip-create-tables",
301+
"-h",
302+
mysql_credentials.host,
303+
"-P",
304+
str(mysql_credentials.port),
273305
],
274306
)
275307
assert result2.exit_code == 0
@@ -294,6 +326,10 @@ def test_mysql_skip_create_tables_and_transfer_data(
294326
mysql_credentials.password,
295327
"--mysql-skip-create-tables",
296328
"--mysql-skip-transfer-data",
329+
"-h",
330+
mysql_credentials.host,
331+
"-P",
332+
str(mysql_credentials.port),
297333
],
298334
)
299335
assert result.exit_code > 0

0 commit comments

Comments
 (0)