From 91dca336fa7ab4f328ab9b9aacfafe24e1a87ff9 Mon Sep 17 00:00:00 2001 From: Wolfy-J Date: Sat, 8 Aug 2026 18:18:23 -0400 Subject: [PATCH] feat(migrations): the database encoding is a day-one contract A Postgres database that is not UTF8 rejects every non-ASCII byte at write time, poisoning commit batches long after install. The first migration now asserts the contract and refuses a misprovisioned database with the reason spelled out. --- src/migrations/00_encoding_contract.lua | 26 +++++++++++++++++++++++++ src/migrations/_index.yaml | 15 ++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/migrations/00_encoding_contract.lua diff --git a/src/migrations/00_encoding_contract.lua b/src/migrations/00_encoding_contract.lua new file mode 100644 index 0000000..8d02023 --- /dev/null +++ b/src/migrations/00_encoding_contract.lua @@ -0,0 +1,26 @@ +-- The engine stores text; a Postgres database that is not UTF8 rejects every +-- non-ASCII byte at write time, poisoning whole commit batches long after +-- install. The contract is asserted first and loudly: a misprovisioned +-- database refuses to migrate. SQLite stores byte sequences verbatim. + +return require("migration").define(function() + migration("database encoding is UTF8", function() + database("postgres", function() + up(function(db) + local rows = db:query( + "SELECT pg_encoding_to_char(encoding) AS enc FROM pg_database WHERE datname = current_database()") + local enc = type(rows) == "table" and rows[1] and tostring(rows[1].enc) or "unknown" + if enc ~= "UTF8" then + error("database encoding is " .. enc .. "; dataflow requires UTF8 — " .. + "recreate the database with ENCODING 'UTF8' (TEMPLATE template0) before installing") + end + end) + down(function(_db) end) + end) + + database("sqlite", function() + up(function(_db) end) + down(function(_db) end) + end) + end) +end) diff --git a/src/migrations/_index.yaml b/src/migrations/_index.yaml index 4bc19d4..1fc6aed 100644 --- a/src/migrations/_index.yaml +++ b/src/migrations/_index.yaml @@ -2,6 +2,21 @@ version: "1.0" namespace: userspace.dataflow.migrations entries: + # userspace.dataflow.migrations:00_encoding_contract + - name: 00_encoding_contract + kind: function.lua + meta: + type: migration + tags: [dataflows, encoding, contract] + description: The database encoding contract — Postgres must be UTF8 + depends_on: [ns:wippy.migration] + target_db: app:db + timestamp: "2025-01-01T00:00:00Z" + source: file://00_encoding_contract.lua + imports: + migration: wippy.migration:migration + method: migrate + # userspace.dataflow.migrations:01_create_dataflows_table - name: 01_create_dataflows_table kind: function.lua