Files
relspecgo/docs/MYSQL_FIDELITY.md
T

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.