Skip to content

Commit 45f44f7

Browse files
committed
✅ add tests for escaping and quoting of MySQL identifiers in table creation
1 parent bb7e767 commit 45f44f7

File tree

1 file changed

+98
-0
lines changed

1 file changed

+98
-0
lines changed
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
from unittest.mock import MagicMock, patch
2+
3+
from mysql_to_sqlite3.transporter import MySQLtoSQLite
4+
5+
6+
def _make_base_instance():
7+
with patch.object(MySQLtoSQLite, "__init__", return_value=None):
8+
inst = MySQLtoSQLite() # type: ignore[call-arg]
9+
inst._mysql_cur_dict = MagicMock()
10+
inst._mysql_database = "db"
11+
inst._sqlite_json1_extension_enabled = False
12+
inst._collation = "BINARY"
13+
inst._prefix_indices = False
14+
inst._without_tables = False
15+
inst._without_foreign_keys = True
16+
inst._logger = MagicMock()
17+
inst._sqlite_strict = False
18+
# Track index names for uniqueness
19+
inst._seen_sqlite_index_names = set()
20+
inst._sqlite_index_name_counters = {}
21+
return inst
22+
23+
24+
def test_show_columns_backticks_are_escaped_in_mysql_query() -> None:
25+
inst = _make_base_instance()
26+
27+
# Capture executed SQL
28+
executed_sql = []
29+
30+
def capture_execute(sql: str, *args, **kwargs):
31+
executed_sql.append(sql)
32+
33+
inst._mysql_cur_dict.execute.side_effect = capture_execute
34+
35+
# SHOW COLUMNS -> then STATISTICS query
36+
inst._mysql_cur_dict.fetchall.side_effect = [
37+
[
38+
{
39+
"Field": "id",
40+
"Type": "INT",
41+
"Null": "NO",
42+
"Default": None,
43+
"Key": "PRI",
44+
"Extra": "",
45+
}
46+
],
47+
[],
48+
]
49+
# TABLE collision check -> 0
50+
inst._mysql_cur_dict.fetchone.return_value = {"count": 0}
51+
52+
sql = inst._build_create_table_sql("we`ird")
53+
assert sql.startswith('CREATE TABLE IF NOT EXISTS "we`ird" (')
54+
55+
# First executed SQL should be SHOW COLUMNS with backticks escaped
56+
assert executed_sql
57+
assert executed_sql[0] == "SHOW COLUMNS FROM `we``ird`"
58+
59+
60+
def test_identifiers_with_double_quotes_are_safely_quoted_in_create_and_index() -> None:
61+
inst = _make_base_instance()
62+
inst._prefix_indices = True # ensure an index is emitted with a deterministic name prefix
63+
64+
# SHOW COLUMNS first call, then STATISTICS rows
65+
inst._mysql_cur_dict.fetchall.side_effect = [
66+
[
67+
{
68+
"Field": 'na"me',
69+
"Type": "VARCHAR(10)",
70+
"Null": "YES",
71+
"Default": None,
72+
"Key": "",
73+
"Extra": "",
74+
},
75+
],
76+
[
77+
{
78+
"name": "idx",
79+
"primary": 0,
80+
"unique": 0,
81+
"auto_increment": 0,
82+
"columns": 'na"me',
83+
"types": "VARCHAR(10)",
84+
}
85+
],
86+
]
87+
inst._mysql_cur_dict.fetchone.return_value = {"count": 0}
88+
89+
sql = inst._build_create_table_sql('ta"ble')
90+
91+
# Column should be quoted with doubled quotes inside
92+
assert '"na""me" VARCHAR(10)' in sql or '"na""me" TEXT' in sql
93+
94+
# Index should quote table and column names with doubled quotes
95+
assert (
96+
'CREATE INDEX IF NOT EXISTS "ta""ble_idx" ON "ta""ble" ("na""me");' in sql
97+
or 'CREATE INDEX IF NOT EXISTS "ta""ble_idx" ON "ta""ble" ("na""me")' in sql
98+
)

0 commit comments

Comments
 (0)