Skip to content

Commit c45a35f

Browse files
committed
#8 optional transfer of implicit column rowid using --with-rowid
1 parent e8d469b commit c45a35f

File tree

4 files changed

+51
-6
lines changed

4 files changed

+51
-6
lines changed

.gitignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -110,4 +110,5 @@ venv.bak/
110110
.DS_Store
111111

112112
# Potential leftovers
113-
tests/db_credentials.json
113+
tests/db_credentials.json
114+
test.db

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Options:
4040
separated table names). Implies --without-
4141
foreign-keys which inhibits the transfer of
4242
foreign keys.
43+
4344
-X, --without-foreign-keys Do not transfer foreign keys.
4445
-d, --mysql-database TEXT MySQL database name [required]
4546
-u, --mysql-user TEXT MySQL user [required]
@@ -48,8 +49,11 @@ Options:
4849
-P, --mysql-port INTEGER MySQL port. Defaults to 3306.
4950
--mysql-integer-type TEXT MySQL default integer field type. Defaults to
5051
INT(11).
52+
5153
--mysql-string-type TEXT MySQL default string field type. Defaults to
5254
VARCHAR(255).
55+
56+
--with-rowid Transfer rowid columns.
5357
-c, --chunk INTEGER Chunk reading/writing SQL records
5458
-l, --log-file PATH Log file
5559
--help Show this message and exit.

sqlite3_to_mysql/cli.py

+3
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
default="VARCHAR(255)",
4848
help="MySQL default string field type. Defaults to VARCHAR(255).",
4949
)
50+
@click.option("--with-rowid", is_flag=True, help="Transfer rowid columns.")
5051
@click.option(
5152
"-c", "--chunk", type=int, default=None, help="Chunk reading/writing SQL records"
5253
)
@@ -62,6 +63,7 @@ def cli( # noqa: ignore=C0330 # pylint: disable=C0330,R0913
6263
mysql_port,
6364
mysql_integer_type,
6465
mysql_string_type,
66+
with_rowid,
6567
chunk,
6668
log_file,
6769
):
@@ -79,6 +81,7 @@ def cli( # noqa: ignore=C0330 # pylint: disable=C0330,R0913
7981
mysql_port=mysql_port,
8082
mysql_integer_type=mysql_integer_type,
8183
mysql_string_type=mysql_string_type,
84+
with_rowid=with_rowid,
8285
chunk=chunk,
8386
log_file=log_file,
8487
)

sqlite3_to_mysql/transporter.py

+42-5
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ def __init__(self, **kwargs): # noqa: ignore=C901 pylint: disable=R0912
8484
kwargs.get("mysql_string_type") or "VARCHAR(255)"
8585
).upper()
8686

87+
self._with_rowid = kwargs.get("with_rowid") or False
88+
8789
sqlite3.register_adapter(Decimal, adapt_decimal)
8890
sqlite3.register_converter("DECIMAL", convert_decimal)
8991
sqlite3.register_adapter(timedelta, adapt_timedelta)
@@ -196,6 +198,14 @@ def _check_mysql_fulltext_support(version_string):
196198
return True
197199
return False
198200

201+
def _sqlite_table_has_rowid(self, table):
202+
try:
203+
self._sqlite_cur.execute("""SELECT rowid FROM "{}" LIMIT 1""".format(table))
204+
self._sqlite_cur.fetchall()
205+
return True
206+
except sqlite3.OperationalError:
207+
return False
208+
199209
def _create_database(self):
200210
try:
201211
self._mysql_cur.execute(
@@ -266,11 +276,15 @@ def _column_type_length(cls, column_type, default=None):
266276
return "({})".format(default)
267277
return ""
268278

269-
def _create_table(self, table_name):
279+
def _create_table(self, table_name, transfer_rowid=False):
270280
primary_key = ""
281+
primary_key_length = ""
271282

272283
sql = "CREATE TABLE IF NOT EXISTS `{}` ( ".format(table_name)
273284

285+
if transfer_rowid:
286+
sql += " `rowid` BIGINT NOT NULL, "
287+
274288
self._sqlite_cur.execute('PRAGMA table_info("{}")'.format(table_name))
275289

276290
for row in self._sqlite_cur.fetchall():
@@ -287,10 +301,20 @@ def _create_table(self, table_name):
287301
)
288302
if column["pk"]:
289303
primary_key = column["name"]
304+
# In case we have a non-numeric primary key
305+
column_type = self._translate_type_from_sqlite_to_mysql(column["type"])
306+
if column_type in {"TEXT", "BLOB"} or column_type.startswith(
307+
("CHAR", "VARCHAR")
308+
):
309+
primary_key_length = self._column_type_length(column_type, 255)
290310

291311
sql = sql.rstrip(", ")
292312
if primary_key:
293-
sql += ", PRIMARY KEY (`{}`)".format(primary_key)
313+
sql += ", PRIMARY KEY (`{column}`{length})".format(
314+
column=primary_key, length=primary_key_length,
315+
)
316+
if transfer_rowid:
317+
sql += ", CONSTRAINT `{}_rowid` UNIQUE (`rowid`)".format(table_name)
294318
sql += " ) ENGINE = InnoDB CHARACTER SET utf8mb4"
295319

296320
try:
@@ -343,6 +367,7 @@ def _add_indices(self, table_name): # noqa: ignore=C901 pylint: disable=R0914
343367
column_list = []
344368
for index_info in index_infos:
345369
index_length = ""
370+
# Limit the max BLOB field index length to 255
346371
if table_columns[index_info["name"]].upper() == "BLOB":
347372
index_length = "({})".format(255)
348373
else:
@@ -364,7 +389,7 @@ def _add_indices(self, table_name): # noqa: ignore=C901 pylint: disable=R0914
364389
""".format(
365390
table=table_name,
366391
index_type=index_type,
367-
name=index["name"],
392+
name=index["name"][:64], # Limit the index name to 64 chars.
368393
columns=index_columns,
369394
)
370395

@@ -472,8 +497,13 @@ def transfer(self):
472497
for row in self._sqlite_cur.fetchall():
473498
table = dict(row)
474499

500+
# check if we're transferring rowid
501+
transfer_rowid = self._with_rowid and self._sqlite_table_has_rowid(
502+
table["name"]
503+
)
504+
475505
# create the table
476-
self._create_table(table["name"])
506+
self._create_table(table["name"], transfer_rowid=transfer_rowid)
477507

478508
# get the size of the data
479509
self._sqlite_cur.execute(
@@ -485,7 +515,14 @@ def transfer(self):
485515
if total_records > 0:
486516
# populate it
487517
self._logger.info("Transferring table %s", table["name"])
488-
self._sqlite_cur.execute('SELECT * FROM "{}"'.format(table["name"]))
518+
self._sqlite_cur.execute(
519+
"""
520+
SELECT {rowid} * FROM "{table_name}"
521+
""".format(
522+
rowid='rowid as "rowid",' if transfer_rowid else "",
523+
table_name=table["name"],
524+
)
525+
)
489526
columns = [column[0] for column in self._sqlite_cur.description]
490527
sql = "INSERT IGNORE INTO `{table}` ({fields}) VALUES ({placeholders})".format( # noqa: ignore=E501 pylint: disable=C0301
491528
table=table["name"],

0 commit comments

Comments
 (0)