@@ -67,6 +67,65 @@ def test_cli_sqlite_views_flag_propagates(
6767 assert transporter_ctor .call_args .kwargs ["sqlite_views_as_tables" ] is True
6868
6969
70+ def test_cli_mysql_table_prefix_passed_to_transporter (
71+ cli_runner : CliRunner ,
72+ sqlite_database : str ,
73+ mysql_credentials : MySQLCredentials ,
74+ mocker : MockerFixture ,
75+ ) -> None :
76+ transporter_ctor = mocker .patch ("sqlite3_to_mysql.cli.SQLite3toMySQL" , autospec = True )
77+ transporter_instance = transporter_ctor .return_value
78+ transporter_instance .transfer .return_value = None
79+
80+ common_args = [
81+ "-f" ,
82+ sqlite_database ,
83+ "-d" ,
84+ mysql_credentials .database ,
85+ "-u" ,
86+ mysql_credentials .user ,
87+ "--mysql-password" ,
88+ mysql_credentials .password ,
89+ "-h" ,
90+ mysql_credentials .host ,
91+ "-P" ,
92+ str (mysql_credentials .port ),
93+ ]
94+
95+ result : Result = cli_runner .invoke (sqlite3mysql , common_args + ["--mysql-table-prefix" , "stage_" ])
96+ assert result .exit_code == 0
97+ assert transporter_ctor .call_args .kwargs ["mysql_table_prefix" ] == "stage_"
98+
99+
100+ def test_cli_mysql_table_prefix_validation (
101+ cli_runner : CliRunner ,
102+ sqlite_database : str ,
103+ mysql_credentials : MySQLCredentials ,
104+ mocker : MockerFixture ,
105+ ) -> None :
106+ transporter_ctor = mocker .patch ("sqlite3_to_mysql.cli.SQLite3toMySQL" , autospec = True )
107+
108+ common_args = [
109+ "-f" ,
110+ sqlite_database ,
111+ "-d" ,
112+ mysql_credentials .database ,
113+ "-u" ,
114+ mysql_credentials .user ,
115+ "--mysql-password" ,
116+ mysql_credentials .password ,
117+ "-h" ,
118+ mysql_credentials .host ,
119+ "-P" ,
120+ str (mysql_credentials .port ),
121+ ]
122+
123+ result : Result = cli_runner .invoke (sqlite3mysql , common_args + ["--mysql-table-prefix" , "123bad" ])
124+ assert result .exit_code != 0
125+ assert "Table prefix" in result .output
126+ transporter_ctor .assert_not_called ()
127+
128+
70129def test_cli_collation_validation (
71130 cli_runner : CliRunner ,
72131 sqlite_database : str ,
@@ -401,6 +460,7 @@ def _make_transfer_stub(mocker: MockFixture) -> SQLite3toMySQL:
401460 instance ._translate_sqlite_view_definition = mocker .MagicMock (return_value = "CREATE VIEW translated AS SELECT 1" )
402461 instance ._sqlite_cur .fetchall .return_value = []
403462 instance ._sqlite_cur .execute .return_value = None
463+ instance ._mysql_table_prefix = ""
404464 instance ._get_table_info = mocker .MagicMock (
405465 return_value = [
406466 {"name" : "c1" , "type" : "TEXT" , "hidden" : 0 },
@@ -483,6 +543,27 @@ def execute_side_effect(sql, *params):
483543 assert "INSERT" in sql_arg
484544
485545
546+ def test_transfer_applies_mysql_table_prefix (mocker : MockFixture ) -> None :
547+ instance = _make_transfer_stub (mocker )
548+ instance ._mysql_table_prefix = "pre_"
549+ instance ._mysql_transfer_data = True
550+ instance ._sqlite_cur .fetchone .return_value = {"total_records" : 1 }
551+ instance ._sqlite_cur .fetchall .return_value = [(1 ,)]
552+
553+ def execute_side_effect (sql , * params ):
554+ if sql .startswith ("SELECT " ) and "FROM" in sql and "COUNT" not in sql .upper ():
555+ instance ._sqlite_cur .description = [("c1" ,)]
556+ return None
557+
558+ instance ._sqlite_cur .execute .side_effect = execute_side_effect
559+ instance ._fetch_sqlite_master_rows = mocker .MagicMock (side_effect = [[{"name" : "tbl" , "type" : "table" }], []])
560+
561+ instance .transfer ()
562+
563+ sql_arg = instance ._transfer_table_data .call_args .kwargs ["sql" ]
564+ assert "INTO `pre_tbl`" in sql_arg
565+
566+
486567def test_transfer_escapes_sqlite_identifiers (mocker : MockFixture ) -> None :
487568 instance = _make_transfer_stub (mocker )
488569 instance ._mysql_transfer_data = True
@@ -1717,6 +1798,34 @@ def test_add_foreign_keys_shorthand_references_primary_key(
17171798 assert "REFERENCES `parent`(`id`)" in executed_sql
17181799 proc ._mysql .commit .assert_called_once ()
17191800
1801+ def test_add_foreign_keys_apply_mysql_table_prefix (self , mocker : MockFixture ) -> None :
1802+ proc = SQLite3toMySQL .__new__ (SQLite3toMySQL )
1803+ sqlite_cursor = mocker .MagicMock ()
1804+ sqlite_cursor .fetchall .return_value = [
1805+ {
1806+ "id" : 0 ,
1807+ "seq" : 0 ,
1808+ "table" : "parent" ,
1809+ "from" : "parent_id" ,
1810+ "to" : "id" ,
1811+ "on_delete" : "NO ACTION" ,
1812+ "on_update" : "NO ACTION" ,
1813+ }
1814+ ]
1815+ proc ._sqlite_cur = sqlite_cursor
1816+ proc ._sqlite_table_xinfo_support = False
1817+ proc ._mysql_cur = mocker .MagicMock ()
1818+ proc ._mysql = mocker .MagicMock ()
1819+ proc ._logger = mocker .MagicMock ()
1820+ proc ._mysql_table_prefix = "pre_"
1821+
1822+ proc ._add_foreign_keys ("child" )
1823+
1824+ executed_sql = proc ._mysql_cur .execute .call_args [0 ][0 ]
1825+ assert "ALTER TABLE `pre_child`" in executed_sql
1826+ assert "REFERENCES `pre_parent`" in executed_sql
1827+ proc ._mysql .commit .assert_called_once ()
1828+
17201829 def test_add_foreign_keys_shorthand_pk_mismatch_is_skipped (
17211830 self ,
17221831 sqlite_database : str ,
0 commit comments