Skip to content

feat(mysql): table DDL and generated migrations, with a migration fuzzer (#53) - #136

Merged
Makisuo merged 7 commits into
mainfrom
feat/mysql-schema
Nov 6, 2026
Merged

Makisuo merged 7 commits into
mainfrom
feat/mysql-schema

Merge feat/mysql-dialect into feat/mysql-schema

d1d37ad
Select commit
Loading
Failed to load commit list.
Maple Review Bot / Maple / review completed Nov 6, 2026 in 4m 4s

Confidence 6/10 · 1 issue to address

🟡 Confidence 6/10 · needs attention. Held at 6 because a correctness warning is open.
1 warning −1 · tests covered · risk high −1

MySQL gets table DDL, an in-place schema diff and generate migrations, with a live fuzzer. One diff path writes an ALTER that aborts on existing NULLs; otherwise the change is sound. The open src/schema.ts errors are Schema module-loading frames in other services, untouched here.

  • MySQL.table carries DDL with column, index and uniqueIndex helpers plus problem detection
  • diffMysqlSchemas alters tables in place, gating drops and type changes on confirm_data_loss
  • generate with dialect: "mysql" writes migration.sql; migrate, verify and --from-drizzle refuse MySQL
  • A live fuzzer compares information_schema after migration with a fresh create

Before merge

  • Manual · Run the live MySQL 8.4 and MariaDB 11.4 suites on the final commit (EFFECT_ORM_MYSQL_URL, EFFECT_ORM_MARIADB_URL)

Findings

🟠 Warning · F3 · fill with no default writes MODIFY … NOT NULL that fails on existing NULLs

correctness · src/schema/mysql-ops.ts:75-81

Making an existing nullable column NOT NULL with no default leaves op.fill true while column.default is null, so only MODIFY COLUMN … NOT NULL is written. Strict mode refuses it over any NULL row (the comment above says so), and MySQL DDL is not transactional, so the statements before it in migration.sql stay applied.

In `diffMysqlSchemas`, when `fill` is needed and `column.default === null`, push an `unsupported` entry instead of the op, so `generate` refuses the change rather than writing a statement that aborts on rows.

Production impact

Open errors in the changed files (8)
Issue Service Occurrences File
ToolCallFailed maple-security-audit 838 src/schema.ts
@maple/http/errors/WarehouseAuthError maple-web 20 src/schema.ts
ContextBudgetError maple-security-audit 8 src/schema.ts
ModelProtocolError (2 issues) maple-chat 11 src/schema.ts
AgentPolicyError maple-chat 7 src/schema.ts
@maple/security-audit/AgentError maple-security-audit 4 src/schema.ts
Error maple-web 4 src/schema.ts
ContextBudgetError maple-chat 3 src/schema.ts

After this merges, Maple checks whether they stop.

What was checked
  • An index over a TEXT column cannot reach the diff: checkKey rejects it and mysqlEntitiesOf fails (mysql-define.ts:249)
  • Column renames render CHANGE COLUMN, so MariaDB's JSON json_valid check follows the name (mysql-ops.ts:69)
  • --from-drizzle and migrate refuse MySQL with explicit KitErrors rather than writing or running (generate.ts:236, source.ts:232)

d1d37ad · Reply "won't fix" to dismiss a finding, or mention @maple-review-bot to ask about one.

Annotations

Check warning on line 81 in src/schema/mysql-ops.ts

See this annotation in the file changed.

@maple-review-bot maple-review-bot / Maple / review

correctness: `fill` with no default writes `MODIFY … NOT NULL` that fails on existing NULLs

Making an existing nullable column NOT NULL with no default leaves `op.fill` true while `column.default` is null, so only `MODIFY COLUMN … NOT NULL` is written. Strict mode refuses it over any NULL row (the comment above says so), and MySQL DDL is not transactional, so the statements before it in `migration.sql` stay applied.