MY.L8.NAMING_SNAKE_CASE — An identifier whose meaning depends on the server it lands on
- Category: convention
- Level: 8
- Confidence: deterministic
- Downtime class: none
- Stability: stable
- Suites: lint, audit
- Applies to: MySQL 8.4
- Not transferable to MariaDB: this page describes MySQL 8.4 behavior. MariaDB answers the same driver and does not share these semantics, so SQLens refuses it outright rather than reasoning about it — see drivers/unsupported.
This is a portability rule, not a taste rule
Level 8 is where conventions live, and most of what belongs there is preference. This one is not.
What MySQL does with a mixed-case table name is decided by lower_case_table_names, and that is a
server setting rather than a property of the schema:
| Value | Where it is the default | What happens to Orders |
|---|---|---|
0 | Linux | stored and compared as written — Orders and orders are two tables |
1 | Windows | folded to lower case on the way in |
2 | macOS | stored as written, compared case-insensitively |
So the same migration produces one table on a developer's Mac and a different one on the Linux server it deploys to. The failure does not surface at deploy time; it surfaces later as table doesn't exist, far enough from the cause that the search starts in the wrong place.
The setting cannot be changed after initialization, so this is not something a project fixes on the server once it has noticed.
Flagged
Schema::create('Orders', function (Blueprint $table) {
$table->id();
$table->timestamp('createdAt');
});
Preferred
Schema::create('orders', function (Blueprint $table) {
$table->id();
$table->timestamp('created_at');
});
What it reports and what it does not
It judges the name of a table, column, index, constraint, view or sequence. It does not judge roles, grants, settings or extensions: those are not names the project chose.
Column names are worth a note of their own here. MySQL compares column names case-insensitively whatever the setting, so a mixed-case column is not a portability failure the way a table is — it is reported for the same reason a table is, because a schema whose names follow one shape is one a reader can predict, and because the same project is very often deployed on PostgreSQL too, where the column rule bites hard.
It also does not filter catalog noise itself. Extension objects, partitions and the Laravel table prefix are handled once, centrally, by the catalog reader.
Both before and after
The rule runs in lint against the migration that introduces the name, and in audit against the
catalog — lint while it is still a diff, audit for the name that arrived some other way.
A migration that introduces several such names reports them together in one finding. A finding is located at a statement, so a second finding for the same statement would be deduplicated away by rule id and location, and the extra names would vanish silently.
Sources
- MySQL 8.4 reference: identifier case sensitivity —
documents that table and database name comparison follows
lower_case_table_names, that its default differs by platform, and that it must not be changed after the data directory is initialized. - MySQL 8.4 reference: schema object names — which characters an unquoted identifier may hold, and what quoting changes about that.