MY.L4.IDENTIFIER_LENGTH — An identifier over MySQL's limit
- Category: safety
- Level: 4
- Confidence: deterministic
- Downtime class:
online - Stability: stable
- Suites: lint
- Applies to: MySQL 8.4 and newer
What it checks
Any identifier a migration statement names — table, column, index, constraint — longer than 64 characters.
Reported:
Schema::create('subscription_billing_adjustments', function (Blueprint $table) {
$table->string('customer_reference_number');
$table->timestamp('effective_from_timestamp');
$table->index(['customer_reference_number', 'effective_from_timestamp']);
});
Not reported:
Schema::create('subscription_billing_adjustments', function (Blueprint $table) {
$table->string('customer_reference_number');
$table->timestamp('effective_from_timestamp');
$table->index(
['customer_reference_number', 'effective_from_timestamp'],
'sba_customer_effective_idx',
);
});
Nothing in that example is long
That is the point, and it is why the rule earns its place.
The table is 32 characters. The columns are 25 and 24. Every name a person typed is comfortably inside the limit — and the index name Laravel derives from all three is 89:
subscription_billing_adjustments_customer_reference_number_effective_from_timestamp_index
Laravel builds an index name from the table plus every column plus a suffix whenever you do not pass one. So the overflow arrives without anyone writing an over-long name, which is exactly the class of defect that survives review: there is nothing on the page to notice.
The unit is CHARACTERS, not bytes
MySQL's limit is 64 characters for databases, tables, columns, indexes and constraints. The same 32 two-byte characters PostgreSQL truncates are created here without complaint — so this rule counts characters and its PostgreSQL sibling counts bytes, and a shared measurement would be wrong on one engine.
The statement is REFUSED, and that is the good failure mode
Measured against 8.4.10:
ERROR 1059 (42000): Identifier name 'bbbb…' is too long
The deploy stops, and the message names the identifier. Loud, immediate, in front of somebody who can act — which is why the rule is still worth having: it moves that failure from the deploy to the diff, where it costs a rename instead of a rollback.
Its PostgreSQL sibling PG.L4.IDENTIFIER_LENGTH reports the same shape for the opposite reason:
there the server truncates the name and the statement succeeds.
The fix is an argument
Pass a name. Laravel derives one only when none is given, so a second argument takes the length out of the schema builder's hands entirely — and a name you chose can say what the index is for, which the derived one never did.
There is nothing to reconcile afterwards. MySQL refused the statement, so no database holds a partially applied version of it — the PostgreSQL sibling's advice about an already-created object does not apply here.
What the rule cannot see
A name assembled at runtime by code the migration calls. The rule reads the statement the schema builder compiled, so anything built after that point is outside its reach. That is a limit of where the rule stands, not a case it decided to skip.
Why lint only, with no audit half
An over-long identifier never reaches the catalog. MySQL refused the statement outright — so by the time an audit reads the schema, there is nothing over the limit left to find.
An audit half here would be a check that cannot fail, and this package treats that as worse than an absent one: it reports a clean result over a question it never asked. The migration is the only place the written name still exists.
Suppressing it
The ordinary routes apply: a baseline entry, an ignore rule in config/sqlens.php, or lowering the
level below 4. There is nothing to configure — the limit is the engine's, not a preference.
Sources
- MySQL 8.4 — Identifier length limits — 64 characters for databases, tables, columns, indexes and constraints
- Laravel 13 — Migrations — the schema builder derives an index name from the table and its columns when none is passed
Not transferable to MariaDB. Its identifier limits and its information_schema shapes are its own,
and this package targets MySQL 8.4 semantics — advice derived from them and applied elsewhere would
be confident, specific and about another product.
The fix material this rule carries
A finding from this rule carries machine-readable fix material, using this sequence:
explicit_identifier— Give the object a name of your own, short enough for the engine — the fix for an identifier that goes over the limit.
The payload is material for you or an agent to apply. SQLens writes no migration and runs no DDL. See the remediation payload for every field, the placeholder semantics, and the version rules a consumer has to follow.