Skip to main content

Keeping the online-DDL matrix current

SQLens classifies a MySQL schema change without touching a server. It can do that because it ships a table of facts — which algorithm InnoDB runs an operation under, whether that rewrites the table, whether other sessions can keep writing, and the lock it takes. That table lives in resources/data/online-ddl-matrix.json.

A facts file is only worth what its maintenance is worth. This page is the recipe for extending it: how a new entry is sourced, what the version range means, when the schema version has to move, and what a reviewer checks. The rules below are enforced by tests, not by trust — each section names the guard that backs it.

The one thing that matters most

Facts are taken. Wording is not. Every entry restates a documented fact in our own structure and our own sentences, with a link to the page it was read from. No table is transcribed, no sentence is pasted, no phrasing is borrowed.

This is not a stylistic preference. Facts carry no license; a manual's prose does. Keeping the line clean is what lets the package ship its facts under a permissive license at all, and it is why the checks below are strict about sources and about the text of notes.

Adding or changing an entry

1. Read the versioned manual page

Open the reference manual for the version the file targets — the reference_server field at the top of the matrix says which. Read the operation's row in the online-DDL operations table, then read the matching Syntax and Usage Notes section below it, which is where the conditions live.

Two traps are worth naming, because both produce entries that look right:

  • The same manual is served under more than one host, and one of those copies is frozen at an older release. A host is not enough to identify a source.
  • The right manual at the wrong version is the cheapest way for a fact to be quietly false. An operation's behavior changes between releases; a page that does not carry the version in its path may not be the version you think.

The gate therefore holds every source against a URL prefix that pins host, manual, and version in one string — not merely against a hostname.

2. Write the entry

Each entry carries the four axes (algorithm, rebuilds_table, permits_concurrent_dml, lock), a version range, its conditions, its sources, and its notes.

One entry per operation. The resolver refuses two entries that claim the same operation at the same version — that is a data error, not something it picks between. When an operation has a cheap path and an expensive fallback, the entry states the cheap one and the fallback becomes a condition: the resolver then answers undetermined rather than guessing which path a given table takes.

Conditions are facts a reader of a migration file cannot see — the table's row format, an existing index, a session variable, whether a storage size changed. They are the reason an honest answer is sometimes "unknown". Two limits apply:

  • A condition must name one of the known predicates. An unknown name is a data error, never a note that silently fails to gate anything.
  • A condition that no real migration will ever trip does not belong here. Every condition makes the operation undetermined more often, and a tool that answers "unknown" to everything is not a cautious tool, it is a useless one. State such edges in notes instead.

notes is your own explanation, long enough to be a sentence rather than a label. What a paste out of a manual produces is a table cell; the floor exists to tell the two apart.

3. Choose the version range

min_version is required, max_version may be null (open above). The range is what lets the next MySQL release add to the file instead of overwriting it: when an operation changes behavior, the old entry gets a max_version and the new one starts where it left off. Both then live side by side, and a run pinned to an older server still gets the answer that was true for it.

Ranges for one operation must nest or be disjoint. Two overlapping ranges with no clear innermost is a data error.

4. Choose a stability

stable asserts the entry is settled. preview says it is our best reading and has not been confirmed — use it when the manual is ambiguous, contradicts itself, or states a case only in prose without a table row. A preview entry is honest about its own standing; a stable one that nobody checked is not.

5. Normalize, then review the diff

Run:

composer matrix:normalize

This rewrites the file into its canonical form — fixed key order, sorted entries and sources, one fact per line. The point is the diff: adding an entry should touch only its own lines. A gate compares the shipped file against its own re-serialization and fails if they differ, so a hand edit that drifts from canonical form is caught before review rather than during it.

When the schema version moves

schema_version describes the shape of the file, not its contents. Adding entries never moves it. It moves when the shape itself changes — a new field, a field that becomes required, a field whose meaning changes.

A reader refuses a schema version it does not implement, by name. That refusal is the feature: an older SQLens meeting a newer file says so instead of silently ignoring the parts it does not understand.

When you move it, three things change together: the JSON schema in resources/data/schemas/, the loader that implements it, and the file itself.

What the checks enforce

You do not have to remember this list — the suite runs it — but knowing what is checked makes a rejection easy to read:

CheckWhat fails
SourceA URL that is not under the pinned manual's versioned prefix
AnchorA fragment id that is not really on the cited page; an anchor written with a leading #; a fragment on the URL, where the anchor field already carries it
Retrieval dateNot a real calendar day, earlier than the documented release, or in the future
Prosenotes or a condition text too short to be an explanation
ProseA phrasing distinctive of the manual's own voice appearing in ours
StructureAn entry with no source; an unknown field; a condition naming an unknown predicate; a duplicate id
StructureA matrix with no entries at all
FormA file that differs from its canonical serialization

None of these reaches the network. The whole set runs offline, against the file as it sits in the repository.

If the phrasing check fires

It means a sentence in your entry reads like the manual's rather than like yours. It does not mean the fact is wrong or that the entry has to go.

Reword the sentence. Say the same thing in your own words and it passes.

Do not remove the phrase from the list, and do not lower a threshold to reach green. The list is deliberately small and holds no free facts — the vocabulary the matrix needs (rebuilding a table, running in place, concurrent writes, the algorithm names) is yours to use, and only the manual's voice is flagged. A hit is worth a second look at where the sentence came from.

When the manual will not answer

Sometimes the documentation is silent, incomplete, or inconsistent with itself. That is a result, not a failure.

Leave the operation unclassified. An operation with no entry resolves to a named undetermined, which tells a user exactly what SQLens does not know. An entry with a guessed axis tells them something false in a field that claims to state facts — and if the guess understates the cost, it does so in the direction that ships broken deploys.

Record what you found and move on. The gap is visible, which is the whole point.

Changelog

An entry that changes what a user sees — a new operation classified, a corrected axis, a preview entry promoted to stable — belongs in CHANGELOG.md under [Unreleased]. Filling in facts that no shipped rule reads yet does not.

Review checklist

  • Every source is under the pinned manual's versioned prefix, with a real anchor and a retrieval date.
  • The facts match the operation's row in that manual — all four axes, not just the algorithm.
  • notes and every condition are written from scratch. Nothing is transcribed.
  • Conditions name known predicates and describe facts a migration file cannot show. Edges that no real migration hits are in notes, not in conditions.
  • One entry per operation; version ranges nest or are disjoint.
  • stable only where the manual is unambiguous; otherwise preview, with the reason in notes.
  • composer matrix:normalize was run and the diff touches only the new lines.
  • schema_version moved only if the file's shape changed — and then the schema, the loader, and the file moved together.