-
-
Notifications
You must be signed in to change notification settings - Fork 65
🐛 transfer TEXT default
#152
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
Conversation
WalkthroughThe changes introduce MariaDB detection and enhanced DEFAULT value handling for SQLite-to-MySQL schema translation. New helper methods parse and format SQL DEFAULT expressions using sqlglot, with dialect-aware logic distinguishing MariaDB from MySQL behaviour. A MariaDB flag is detected from version strings and propagated through table creation workflows. Changes
Sequence DiagramsequenceDiagram
participant Transporter as SQLite3toMySQL
participant Detector as MariaDB Detector
participant TypeCheck as Type Checker
participant Parser as Expression Parser
participant Formatter as Default Formatter
Transporter->>Detector: Detect MariaDB from version
Detector-->>Transporter: _is_mariadb flag
loop For each column with DEFAULT
Transporter->>TypeCheck: Check if type supports default
alt Type supports DEFAULT
TypeCheck-->>Transporter: true
Transporter->>Parser: Parse default expression
Parser-->>Transporter: Expression or None
alt Expression parsed successfully
Transporter->>Formatter: Format with dialect awareness
note over Formatter: MariaDB: '...' literal<br/>MySQL: (EXPR) function style
Formatter-->>Transporter: Formatted DEFAULT
else Expression unparseable
Transporter->>Formatter: Wrap in parentheses
Formatter-->>Transporter: Formatted DEFAULT
end
else Type unsupported
TypeCheck-->>Transporter: false
note over Transporter: Omit DEFAULT clause
end
end
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Possibly Related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
🧹 Nitpick comments (1)
src/sqlite3_to_mysql/transporter.py (1)
356-395: expr.copy() usage is correct and tested; add inline comments explaining the MySQL 8.0.13+ DEFAULT wrapping logic.The script results confirm that
expr.copy()is used safely at line 394 and is covered by unit tests (specificallytest_format_textual_default_preserves_existing_parens). The pattern correctly wraps copies of expressions without mutating the originals, which is standard for sqlglot AST manipulation.The complex conditional logic—particularly the MariaDB vs MySQL parentheses wrapping at lines 377–395—handles MySQL 8.0.13+ expression DEFAULT requirements correctly. However, the logic would benefit from an inline comment clarifying why MySQL requires parentheses wrapping whilst MariaDB does not, to aid future maintainers.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
src/sqlite3_to_mysql/transporter.py(4 hunks)src/sqlite3_to_mysql/types.py(1 hunks)tests/unit/sqlite3_to_mysql_test.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
src/sqlite3_to_mysql/types.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Provide typed parameter/attribute structures in types.py for consumption via typing.Unpack in SQLite3toMySQL.init
Files:
src/sqlite3_to_mysql/types.py
src/sqlite3_to_mysql/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/sqlite3_to_mysql/**/*.py: Always wrap dynamic table/index/column names with safe_identifier_length(...) before emitting SQL
Use the class _logger for logging; do not print directly; respect the quiet flag for progress/INFO while always emitting errors
Files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.py
src/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/**/*.py: Format with Black (line length 120) and isort (profile=black); adhere to Flake8’s 88-column soft cap to avoid long chained expressions on one line
Preserve and add type hints for new public interfaces
Files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.py
src/sqlite3_to_mysql/transporter.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
src/sqlite3_to_mysql/transporter.py: Keep SQLite3toMySQL.transfer() flow: create DB (if missing) → create tables → optionally truncate → bulk insert (chunked or streamed) → then create indices → then add foreign keys
On table creation failures due to expression DEFAULTs, retry without DEFAULTs (two-pass _create_table with skip_default=True)
Initialize MySQL capability booleans (e.g., _mysql_fulltext_support, _allow_expr_defaults) early in init, and gate conditional behavior on them
Extend column type translation only in _translate_type_from_sqlite_to_mysql instead of scattering conversions
Handle DEFAULT normalization in _translate_default_for_mysql; return empty string to suppress invalid defaults
During index creation, handle duplicate names by appending numeric suffixes, unless _ignore_duplicate_keys is set to skip retries
Implement chunked transfer with cursor.fetchmany when --chunk is set; otherwise use fetchall with tqdm progress
Only add foreign keys when no table include/exclude filters are set and --without-foreign-keys is not in effect
Implement insert methods: IGNORE (default), UPDATE using ON DUPLICATE KEY UPDATE (with optional VALUES alias), and DEFAULT (no modifiers)
Expose new capability booleans from init of SQLite3toMySQL for downstream logic
Use prepared cursors (cursor(prepared=True)) and batch inserts via executemany for performance
For large transfers, prefer --chunk and preserve commit granularity in any new bulk path
Files:
src/sqlite3_to_mysql/transporter.py
src/sqlite3_to_mysql/{cli.py,transporter.py}
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
In debug mode (--debug), surface exceptions instead of swallowing them
Files:
src/sqlite3_to_mysql/transporter.py
tests/{unit,func}/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Place unit tests under tests/unit and functional/CLI tests under tests/func
Files:
tests/unit/sqlite3_to_mysql_test.py
tests/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Format test code with Black/isort and respect Flake8’s 88-column soft cap
Files:
tests/unit/sqlite3_to_mysql_test.py
tests/unit/**/*.py
📄 CodeRabbit inference engine (.github/copilot-instructions.md)
Add unit tests for new behavior, isolating pure functions
Files:
tests/unit/sqlite3_to_mysql_test.py
🧠 Learnings (13)
📓 Common learnings
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/{mysql_utils.py,sqlite_utils.py} : Add new MySQL/SQLite capability checks in mysql_utils.py/sqlite_utils.py and keep them as dedicated helpers
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Handle DEFAULT normalization in _translate_default_for_mysql; return empty string to suppress invalid defaults
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/{mysql_utils.py,sqlite_utils.py} : Centralize dialect/version feature checks, type adaptation, and identifier safety in mysql_utils.py and sqlite_utils.py; add new MySQL capability gates here
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : On table creation failures due to expression DEFAULTs, retry without DEFAULTs (two-pass _create_table with skip_default=True)
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Extend column type translation only in _translate_type_from_sqlite_to_mysql instead of scattering conversions
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Initialize MySQL capability booleans (e.g., _mysql_fulltext_support, _allow_expr_defaults) early in __init__, and gate conditional behavior on them
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/cli.py : Expose insert method choices IGNORE (default), UPDATE, and DEFAULT via CLI with clear help text
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/cli.py : Define new CLI flags above cli() with consistent help text and error messages; maintain mutual exclusion patterns
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/cli.py : Make --sqlite-tables and --exclude-sqlite-tables mutually exclusive; setting either implies --without-foreign-keys
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Implement insert methods: IGNORE (default), UPDATE using ON DUPLICATE KEY UPDATE (with optional VALUES alias), and DEFAULT (no modifiers)
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Expose new capability booleans from __init__ of SQLite3toMySQL for downstream logic
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Expose new capability booleans from __init__ of SQLite3toMySQL for downstream logic
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/{mysql_utils.py,sqlite_utils.py} : Add new MySQL/SQLite capability checks in mysql_utils.py/sqlite_utils.py and keep them as dedicated helpers
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/types.py : Provide typed parameter/attribute structures in types.py for consumption via typing.Unpack in SQLite3toMySQL.__init__
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Extend column type translation only in _translate_type_from_sqlite_to_mysql instead of scattering conversions
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Initialize MySQL capability booleans (e.g., _mysql_fulltext_support, _allow_expr_defaults) early in __init__, and gate conditional behavior on them
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/{mysql_utils.py,sqlite_utils.py} : Centralize dialect/version feature checks, type adaptation, and identifier safety in mysql_utils.py and sqlite_utils.py; add new MySQL capability gates here
Applied to files:
src/sqlite3_to_mysql/types.pysrc/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Handle DEFAULT normalization in _translate_default_for_mysql; return empty string to suppress invalid defaults
Applied to files:
src/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Implement insert methods: IGNORE (default), UPDATE using ON DUPLICATE KEY UPDATE (with optional VALUES alias), and DEFAULT (no modifiers)
Applied to files:
src/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : On table creation failures due to expression DEFAULTs, retry without DEFAULTs (two-pass _create_table with skip_default=True)
Applied to files:
src/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/transporter.py : Keep SQLite3toMySQL.transfer() flow: create DB (if missing) → create tables → optionally truncate → bulk insert (chunked or streamed) → then create indices → then add foreign keys
Applied to files:
src/sqlite3_to_mysql/transporter.pytests/unit/sqlite3_to_mysql_test.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/{cli.py,transporter.py} : In debug mode (--debug), surface exceptions instead of swallowing them
Applied to files:
src/sqlite3_to_mysql/transporter.py
📚 Learning: 2025-10-23T18:16:08.709Z
Learnt from: CR
Repo: techouse/sqlite3-to-mysql PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2025-10-23T18:16:08.709Z
Learning: Applies to src/sqlite3_to_mysql/**/*.py : Always wrap dynamic table/index/column names with safe_identifier_length(...) before emitting SQL
Applied to files:
tests/unit/sqlite3_to_mysql_test.py
🧬 Code graph analysis (2)
src/sqlite3_to_mysql/transporter.py (1)
src/sqlite3_to_mysql/debug_info.py (1)
_mysql_version(58-68)
tests/unit/sqlite3_to_mysql_test.py (1)
src/sqlite3_to_mysql/transporter.py (8)
_sqlite_quote_ident(296-298)_translate_type_from_sqlite_to_mysql(397-405)_create_table(839-941)_translate_default_for_mysql(535-664)_parse_sql_expression(357-366)_format_textual_default(368-395)_base_mysql_column_type(335-342)_column_type_supports_default(344-354)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
- GitHub Check: Test (python3.10, mariadb:10.0, 1, true, 3.10)
- GitHub Check: Test (python3.9, mariadb:10.0, 1, true, 3.9)
- GitHub Check: Test (python3.12, mariadb:5.5, 1, true, 3.12)
- GitHub Check: Test (python3.13, mariadb:5.5, 1, true, 3.13)
- GitHub Check: Test (python3.10, mariadb:5.5, 1, true, 3.10)
- GitHub Check: Test (python3.14, mariadb:5.5, 1, true, 3.14)
- GitHub Check: Test (python3.11, mariadb:5.5, 1, true, 3.11)
- GitHub Check: Test (python3.9, mariadb:5.5, 1, true, 3.9)
- GitHub Check: Codacy Static Code Analysis
🔇 Additional comments (8)
src/sqlite3_to_mysql/types.py (1)
90-90: LGTM! MariaDB detection flag added consistently.The new
_is_mariadbattribute follows the established pattern for capability flags and is positioned logically among other MySQL version-dependent attributes.src/sqlite3_to_mysql/transporter.py (4)
111-111: Good defensive initialization.Setting
_is_mariadb = Falsebefore establishing the MySQL connection ensures the attribute exists and has a sensible default value.
334-354: Well-designed helper methods.Both
_base_mysql_column_typeand_column_type_supports_defaulthandle edge cases appropriately:
- Empty/whitespace inputs
- Unparseable types
- Type-specific DEFAULT constraints (GEOMETRY, BLOB, TEXT with JSON)
The logic correctly implements MySQL/MariaDB DEFAULT support rules.
871-891: LGTM! Two-pass default processing is appropriate.The integration correctly:
- Uses defensive
getattrfor test scenarios- Replaces static exclusion list with dynamic
_column_type_supports_defaultcheck- Applies two-pass processing: first
_translate_default_for_mysql(SQLite → MySQL semantics), then_format_textual_defaultfor TEXT types (MariaDB-aware expression wrapping)The two-pass approach is intentional and correct – general translation first, then type-specific formatting.
Based on learnings.
235-235: MariaDB detection approach is verified and reliable.The codebase already validates this detection thoroughly through unit tests. Test cases explicitly confirm that the case-insensitive substring match for "-mariadb" works reliably across supported MariaDB versions (10.2.6, 10.2.7, 11.4.0), including verification of mixed-case variants like "-mArIaDb". The same detection pattern is consistently applied across all MySQL capability checks in
mysql_utils.pyand has been tested in multiple integration tests. No version-specific edge cases or caveats are documented, indicating the approach is well-established and robust.tests/unit/sqlite3_to_mysql_test.py (3)
1029-1116: Excellent test coverage for MariaDB vs MySQL default formatting.The three tests comprehensively validate the key behaviour differences:
- MariaDB: literal defaults without parentheses
- MySQL: expression defaults with parentheses
- MySQL functions: properly wrapped function calls
Test setup and assertions are clean and focused.
1118-1222: Thorough edge-case testing.Tests comprehensively cover:
- Dialect fallback (MySQL → SQLite)
- Unparseable expression handling
- Blank/NULL inputs
- MariaDB vs MySQL parentheses rules
- Preservation of existing wrapping
The use of mocking to control parsing behaviour is appropriate and makes tests focused.
1180-1222: Complete boundary condition testing.Helper method tests validate:
- Whitespace and empty input handling
- Type normalization and uppercasing
- Correct DEFAULT support for GEOMETRY, BLOB, TEXT, and VARCHAR
- Blank and NULL literal preservation
Edge case coverage is excellent.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #152 +/- ##
==========================================
+ Coverage 98.32% 98.41% +0.09%
==========================================
Files 8 8
Lines 1016 1075 +59
==========================================
+ Hits 999 1058 +59
Misses 17 17 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
This pull request enhances the handling of default values for text and JSON columns when migrating from SQLite3 to MySQL/MariaDB, ensuring compatibility with both database systems. It introduces new logic to detect MariaDB, refactors how column types and defaults are processed, and adds comprehensive tests for these behaviors.
Default value handling improvements:
_is_mariadbattribute. This allows for conditional formatting of default values depending on the target database. [1] [2] [3]_column_type_supports_defaultmethod for improved flexibility and correctness. [1] [2]_base_mysql_column_type,_parse_sql_expression, and_format_textual_defaulthelper methods to normalize column types and safely format default expressions, especially for text and JSON columns. These methods handle differences between MySQL and MariaDB regarding expression defaults. [1] [2]Testing and validation:
sqlite3_to_mysql_test.pyto verify the new default value handling logic, including MariaDB detection, expression parsing, and default formatting for various scenarios.Code cleanup:
MYSQL_COLUMN_TYPES_WITHOUT_DEFAULTimport, as its logic is now handled by the new helper methods.