Swap MySQL for PostgreSQL 18 #9

Merged
geusebio merged 1 commit from claude/postgres-migration-pr4-2988a9 into main 2026-08-03 07:51:53 +00:00
Owner

Closes #4 by making it moot: instead of bumping the mysql image from 8.4 to
26.7, this moves the example app to PostgreSQL 18.

Rebased onto #7 (Store names in a Doctrine entity, seed via migration), which
landed while this was open — the original raw-PDO version of this change is
gone, and the swap now happens in the DBAL config and the seed migration.

Changes

  • compose.yamlmysql:8.4postgres:18. MYSQL_* env vars become
    POSTGRES_* (no separate root password needed), the healthcheck becomes
    pg_isready -U app -d app, and the data volume moves to
    /var/lib/postgresql — the postgres:18 image places PGDATA at
    /var/lib/postgresql/18/docker, so that's the mount point that persists.
  • src/Kernel.php — the DBAL connection uses driver: pdo_pgsql, and
    charset goes from utf8mb4 to utf8.
  • migrations/Version20260723085751.php — the seed migration's CREATE TABLE was MySQL-only. INT AUTO_INCREMENT becomes INT GENERATED BY DEFAULT AS IDENTITY (what DBAL 4 emits for a #[ORM\GeneratedValue]
    integer id on PostgreSQL) and the DEFAULT CHARACTER SET utf8mb4 clause is
    dropped. The 26 seed INSERTs and down() are unchanged.
  • composer.json / composer.lockext-pdo_mysqlext-pdo_pgsql. The
    matthewbaggett/php:8.5 base image already ships pdo_pgsql, so the
    Dockerfile is untouched.
  • DEPLOYING.md — the one line naming the db image.

src/Entity/Name.php and src/Controller/NameController.php needed no changes
— the ORM abstracts the difference.

Two things worth a reviewer's attention

This rewrites an applied migration in place. That is normally the wrong
move. It's justified here because swapping the database engine means every
deployment starts from an empty PostgreSQL volume regardless, and a follow-up
migration could not repair the first one's MySQL-only DDL. Say the word if you
would rather have a fresh migration and this one left as history.

It also bumps symfony/console v8.1.1 → v8.1.2, which has nothing to do with
PostgreSQL.
main is currently broken: framework-bundle v8.1.2 declares
conflict: symfony/console <8.1.2, but the lock pins console at v8.1.1, so
composer install — and therefore docker build — fails on a clean checkout
of main today. Nothing could be verified without fixing it. Happy to split it
into its own PR if you'd prefer that landed separately.

Verification

Clean docker compose down -v && docker compose up --build:

  • db healthy on PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1)
  • the sv-fixer.d init hook runs the migration before nginx starts:
    1 migrations executed, 27 sql queries
  • doctrine:schema:validate → mapping correct, database in sync with the
    mapping files
  • the identity column issues ids (a manual INSERT returned id = 27 after the
    26 seeded rows)
  • doctrine:migrations:migrate first rolls back cleanly, and migrating up
    again re-seeds
  • repeated GET / returns varying names (Julia, Charlie, Rachel, Quentin,
    Bob, Fiona…), no PHP errors

🤖 Generated with Claude Code

Closes #4 by making it moot: instead of bumping the `mysql` image from 8.4 to 26.7, this moves the example app to PostgreSQL 18. Rebased onto #7 (`Store names in a Doctrine entity, seed via migration`), which landed while this was open — the original raw-PDO version of this change is gone, and the swap now happens in the DBAL config and the seed migration. ### Changes - **compose.yaml** — `mysql:8.4` → `postgres:18`. `MYSQL_*` env vars become `POSTGRES_*` (no separate root password needed), the healthcheck becomes `pg_isready -U app -d app`, and the data volume moves to `/var/lib/postgresql` — the `postgres:18` image places `PGDATA` at `/var/lib/postgresql/18/docker`, so that's the mount point that persists. - **src/Kernel.php** — the DBAL connection uses `driver: pdo_pgsql`, and `charset` goes from `utf8mb4` to `utf8`. - **migrations/Version20260723085751.php** — the seed migration's `CREATE TABLE` was MySQL-only. `INT AUTO_INCREMENT` becomes `INT GENERATED BY DEFAULT AS IDENTITY` (what DBAL 4 emits for a `#[ORM\GeneratedValue]` integer id on PostgreSQL) and the `DEFAULT CHARACTER SET utf8mb4` clause is dropped. The 26 seed INSERTs and `down()` are unchanged. - **composer.json / composer.lock** — `ext-pdo_mysql` → `ext-pdo_pgsql`. The `matthewbaggett/php:8.5` base image already ships `pdo_pgsql`, so the Dockerfile is untouched. - **DEPLOYING.md** — the one line naming the `db` image. `src/Entity/Name.php` and `src/Controller/NameController.php` needed no changes — the ORM abstracts the difference. ### Two things worth a reviewer's attention **This rewrites an applied migration in place.** That is normally the wrong move. It's justified here because swapping the database engine means every deployment starts from an empty PostgreSQL volume regardless, and a follow-up migration could not repair the first one's MySQL-only DDL. Say the word if you would rather have a fresh migration and this one left as history. **It also bumps `symfony/console` v8.1.1 → v8.1.2, which has nothing to do with PostgreSQL.** `main` is currently broken: `framework-bundle` v8.1.2 declares `conflict: symfony/console <8.1.2`, but the lock pins console at v8.1.1, so `composer install` — and therefore `docker build` — fails on a clean checkout of `main` today. Nothing could be verified without fixing it. Happy to split it into its own PR if you'd prefer that landed separately. ### Verification Clean `docker compose down -v && docker compose up --build`: - `db` healthy on `PostgreSQL 18.4 (Debian 18.4-1.pgdg13+1)` - the `sv-fixer.d` init hook runs the migration before nginx starts: `1 migrations executed, 27 sql queries` - `doctrine:schema:validate` → mapping correct, database in sync with the mapping files - the identity column issues ids (a manual INSERT returned `id = 27` after the 26 seeded rows) - `doctrine:migrations:migrate first` rolls back cleanly, and migrating up again re-seeds - repeated `GET /` returns varying names (Julia, Charlie, Rachel, Quentin, Bob, Fiona…), no PHP errors 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Renovate PR #4 proposed bumping the `mysql` image from 8.4 to 26.7 — a
major jump onto Oracle's new version scheme. Rather than take that
upgrade, move the example app to PostgreSQL.

- compose: `mysql:8.4` -> `postgres:18`, with POSTGRES_* env vars, a
  `pg_isready` healthcheck, and the data volume at /var/lib/postgresql
  (postgres:18 puts PGDATA under /var/lib/postgresql/18/docker).
- seed SQL moves to docker/postgres/init.sql; AUTO_INCREMENT becomes
  GENERATED ALWAYS AS IDENTITY.
- controller: `pgsql:` DSN (no charset parameter) and ORDER BY RANDOM().
- composer: require ext-pdo_pgsql instead of ext-pdo_mysql. The
  matthewbaggett/php:8.5 base image already ships pdo_pgsql.

Verified with `docker compose up`: db healthy on PostgreSQL 18.4, the
firstnames table seeded with 26 rows, and repeated requests to / return
varying names.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
geusebio force-pushed claude/postgres-migration-pr4-2988a9 from 335a55997d to b28fb65cfe 2026-08-03 07:50:31 +00:00 Compare
geusebio referenced this pull request from a commit 2026-08-03 07:51:55 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Grey.ooo/someones.computer_example_app!9
No description provided.