8.1 KiB
MySQL schema fidelity
The MySQL reader/writer preserves the table-schema features below. It generates
DDL for new tables in existing databases, not migrations or data copies.
MySQL 8.4 / InnoDB is exercised by the live suite. The reader uses MySQL 8.0.23+
metadata (including functional indexes, checks, spatial metadata, and invisible
columns); older MySQL versions and the mariadb alias are not certified by this
suite and may return explicit metadata-query errors.
Conversion diagnostics
MySQL output warns on stderr when it encounters known unsupported features.
Add --strict-conversion to reject these conversions before opening an output
file or connecting to an output database:
relspec convert --from mysql --from-conn "$MYSQL_DSN" \
--to mysql --to-path schema.sql --strict-conversion
relspec convert --from json --from-path schema.json \
--to mysql --to-path schema.sql --strict-conversion --dry-run
The flag also applies to MySQL output from batch, split, and schema jobs.
Jobs can set options.strict_conversion: true. Library callers use
writers.WriterOptions.StrictConversion; Warning func(string) collects
non-strict diagnostics instead of printing them. mysql.Writer.Statements
performs the same preflight without opening a destination and returns structured
SQL statements suitable for execution without splitting on semicolons.
Strict conversion currently covers MySQL output. It is not a general losslessness check for other writers or a validator of arbitrary SQL fragments. Warnings identify the object and omitted or changed feature. Inspect them before using non-strict output; unsupported index types are omitted, while a partial index's predicate and INCLUDE columns cannot be represented in MySQL.
MySQL capability matrix
| Feature | Reader → model | Model → MySQL DDL |
|---|---|---|
| Ordered columns, nullability, primary/composite keys | Preserved | Preserved |
| Unique/composite keys | Preserved, including backing indexes | Preserved once; no duplicate unique index |
| Foreign/composite keys and DELETE/UPDATE actions | Preserved | Added after all tables, allowing forward references/cycles |
| CHECK and NOT ENFORCED CHECK | Preserved | Preserved |
| BTREE, FULLTEXT, SPATIAL indexes | Preserved | Emitted; engine restrictions still apply |
| Expression/prefix/descending/invisible index parts | Preserved in Index.KeyParts and metadata |
Preserved |
| HASH indexes | Preserved | Engine-dependent; incompatible engine produces a diagnostic |
| Integer unsignedness; mediumint; enum/set; binary/blob/text variants | Canonical type plus native declaration | Native declaration preserved |
| Length, numeric precision/scale, fractional seconds | Typed dimensions plus native declaration | Preserved; canonical dimensions also supported |
| Defaults, including SQL expressions, bit/binary literals and multiline text | Values plus native SQL spelling from SHOW CREATE | Preserved |
| Auto-increment and current next value | Preserved | Preserved |
| Stored/virtual generated columns, ON UPDATE, invisible columns | Preserved | Preserved |
| Engine, table/column collation, charset and comments | Preserved | Preserved |
| Views, triggers, routines, events, partitioned tables | Names recorded in mysql_unsupported |
Diagnostic; bodies/partition definitions are not emitted |
| Nonempty table CREATE_OPTIONS; native parser/storage/engine attributes; spatial SRID | Recorded as unsupported attributes | Diagnostic; not emitted |
| Partial/INCLUDE indexes, unsupported index methods | Retained when supplied in the model | Diagnostic |
| Deferrable constraints, identity semantics, timestamptz, jsonb storage | Retained when supplied in the model | Diagnostic for unsupported/different semantics |
| Standalone sequences/enums/domains, ownership/permissions, scripts and dialect directives | Retained when supplied in the model | Diagnostic; not emitted |
| Data, grants, users, server/plugin configuration | Outside the reader's schema contract | Outside the writer's contract |
Diagnostics depend on the objects visible to the connection account. They cover known modeled/discovered features, not every engine/plugin extension. The server still validates SQL expressions, engine rules, and references at execution time. For example, source and target engines must support the constraints requested.
Preservation across intermediate formats
| Path | Scope |
|---|---|
| MySQL → model → MySQL | Live-tested for the features above on MySQL 8.4 / InnoDB |
| MySQL → JSON → model → MySQL | Live-tested; preserves typed index parts and MySQL metadata |
| MySQL → YAML → model → MySQL | YAML serializes the same fields/metadata; covered by the live round-trip test |
| Other source/target formats | Their own mappings apply; MySQL-specific metadata and semantics are not certified to survive |
Native declarations and defaults are stored in column metadata as mysql_type
and mysql_default_sql. Reader-generated signatures (mysql_type_signature,
mysql_default_value) prevent these spellings from silently overriding edited
types/dimensions/defaults. Editing canonical dimensions invalidates the native
type signature, produces a diagnostic, and uses the canonical mapping instead.
For a binary default, the logical default is its SQL hex/literal spelling so it
survives serialization without invalid UTF-8.
Other metadata includes mysql_charset, mysql_generation_storage,
mysql_default_expression, mysql_on_update, mysql_invisible, table
mysql_engine / mysql_collation / mysql_auto_increment, index
mysql_visible, and check mysql_enforced. Unknown mysql_* metadata produces
a diagnostic. For a manually authored expression default, set
mysql_default_expression: true; otherwise strings are literal values.
Index.KeyParts is authoritative when present; Index.Columns remains the
compatibility representation for simple indexes.
Raw native type declarations, default SQL, generation/check/index expressions,
and ON UPDATE expressions are executable SQL input. Lowercasing changes names
and structured index parts but leaves expressions untouched; update references
inside raw SQL yourself when using --lowercase.
Applying the output
Create the destination database/schema first. Qualified names are preserved,
including foreign-key target schemas. Adjust them explicitly when restoring to
a different database. Output assumes standard MySQL backslash escaping;
NO_BACKSLASH_ESCAPES or ANSI_QUOTES on the source produces a conversion
diagnostic, and live execution refuses these target modes before DDL. File consumers must use the same
supported mode. Other SQL-mode incompatibilities surface as server errors.
CREATE TABLE IF NOT EXISTS does not reconcile existing definitions. Foreign
keys use ALTER TABLE ADD CONSTRAINT and therefore repeated application can
fail on existing constraints. This is creation DDL, not a rerunnable migration.
Live execution stops on the first SQL error by default. Explicit
ContinueOnError attempts later statements and still returns an error if any
fail. MySQL DDL implicitly commits; AtomicExecution is rejected. PostgreSQL's
shared execution-report support is not yet implemented for this writer.
Verification
./tests/dbtest/dbtest.sh test mysql
# Or use your own disposable database:
RELSPEC_TEST_MYSQL_CONN="$MYSQL_DSN" go test -mod=vendor ./pkg/readers/mysql ./pkg/writers/mysql
The live tests compare MySQL's SHOW CREATE TABLE definitions, normalizing only
temporary target-name suffixes and nonsemantic ordinary-index display order.
They re-read the target, exercise constraint enforcement/default values, and
verify unsupported objects produce warnings or strict failures without touching
an existing output file. Test fixtures are removed after each run.
Metadata references: COLUMNS, STATISTICS, TABLE_CONSTRAINTS, and REFERENTIAL_CONSTRAINTS.