Skip to content
CREED The Handbook

Chapter 5

Databases

9 sections · 25 provisions · MUST 4 · SHOULD 1 · MAY 1

Decided

Key words The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY in this chapter are to be interpreted as described in RFC 2119.

§5.1 What the stack supports

The policy lists the MySQL versions in policy/policy.json, under mysql: 5.6, 5.7, 8.0, 8.4 and 9.7, each with its active_until and security_until dates. The shared server runs 5.6, which is past security support; its security_until is 2021-02-28. Laravel 13 documents MySQL 5.7 or later. The conventions below keep an app working on the shared server until it is upgraded. They are a bridge, not the fix: the fix is the upgrade. Code that follows them runs unchanged on 8.4, so the move can be made With Arms Wide Open.

§5.2 Which MySQL lines the policy lists

The policy lists the LTS lines, 8.4 and 9.7, for current use. It keeps the older lines, 5.6, 5.7 and 8.0, only to say that they are end of life. In MySQL 8.4 and later the default authentication plugin is caching_sha2_password: mysql_native_password is disabled by default in 8.4 and removed in 9.0. An app that MUST reach a server on the older plugin MUST set the server's plugin explicitly in its database config.

§5.3 Grace period

An app MAY stay on a database version for 90 days after it leaves support, the policy's eol_grace_days; the version is Eol through that window and EolGraceExpired the day after, when the gate fails with no override. The window is a bridge for the upgrade, not a second support window: the fix is still the upgrade.

§5.4 Names, comments and connections

  • Table and column names are descriptive. Add table and column comments (Laravel's ->comment()) where the database supports them, to say what an element is for (SHOULD) (WSU developer standards, 2024).
  • The connection between the app and the database MUST be encrypted. The database account MUST have only the privileges the app needs, never an administrator account (WSU developer standards, 2024).

§5.5 Conventions that keep 5.6 working

  • Set Schema::defaultStringLength(191) in AppServiceProvider::boot(), before any migration runs. 5.6 caps an indexed utf8mb4 column at 767 bytes, which is 191 characters. PROCTOR check: mysql56.
  • Keep utf8mb4 and utf8mb4_unicode_ci, the defaults. Never use an 8.0-only collation such as utf8mb4_0900_ai_ci (not yet confirmed on a 5.6 server).
  • Store JSON as text or longText, with Eloquent array or json casts, and filter in PHP. 5.6 has no JSON column type. PROCTOR check: mysql56.
  • Leave use_upsert_alias unset, so upsert() uses VALUES(). The row alias that setting switches to needs MySQL 8.0.19.
  • Rename a column with raw DB::statement('ALTER TABLE ... CHANGE ...'). Avoid renameColumn and renameIndex, which emit 5.7 syntax. PROCTOR check: mysql56.
  • Use Schema::hasTable only. Column introspection is not reliable on 5.6 (not yet confirmed on a 5.6 server).
  • Skip db:show and db:monitor, and run one database-queue worker (or another driver if the platform offers one). 5.6 has no SKIP LOCKED, so a single worker avoids double-processing.
  • Use timestamp, not date, for useCurrent(). A date column cannot take a current-timestamp default on 5.6.
  • Run migrations in CI against a real 5.6 container. SQLite-only tests pass code that 5.6 rejects.

§5.6 Avoid until the server is upgraded

§5.6.1 Schema

  • JSON and jsonb columns
  • Generated columns
  • renameIndex and renameColumn
  • Column introspection
  • invisible, instant and vector columns
  • InnoDB spatialIndex (needs 5.7.5, not yet confirmed on a 5.6 server)
  • DESC indexes and CHECK constraints

§5.6.2 Queries

  • whereJson* and JSON-path updates
  • Window functions and CTEs
  • SKIP LOCKED and NOWAIT in raw SQL
  • joinLateral
  • Joined deletes with an order or a limit

§5.6.3 Commands

  • db:show
  • db:monitor

§5.7 Moving to 8.4

Code that follows the conventions and avoids the patterns above runs unchanged on 8.4. The server-side large-prefix settings (innodb_large_prefix=ON, innodb_file_format=Barracuda, DYNAMIC rows) are a question for the database owners, not something an app sets (not yet confirmed on a 5.6 server). Passing ROW_FORMAT=DYNAMIC through the engine config is likewise not yet confirmed on a 5.6 server.

Oracle moved 5.6 to Sustaining Support on 2021-02-01, so it gets no new fixes. Builds older than 5.6.46 also speak only TLS 1.0, which current hosts may refuse (not yet confirmed on a 5.6 server). Both are reasons to plan the move rather than wait for it.

§5.8 How the stack checks it

PROCTOR's mysql56 check flags the patterns in the avoid list above that fail on 5.6: JSON columns, generated columns, renameColumn, long indexed strings and a missing Schema::defaultStringLength(191). The rest of the list is not checked yet.

§5.9 Sources

In policy.json

components.*.versions.*.active_until
set per version
rules.eol_grace_days
90
components.*.versions.*.security_until
set per version

Set from handbook/databases.md on GitHub, impression 10e8473. Found a mistake? Fix the Markdown; this page is rebuilt from it.