From d11caa61638ba5b41e727d4e77554de58132115e Mon Sep 17 00:00:00 2001 From: Lakhan Samani Date: Fri, 30 Oct 2026 10:23:57 +0100 Subject: [PATCH] chore(release): 2.4.4 --- CHANGELOG.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 02478672..3fa3f1a7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,34 @@ All notable changes to Authorizer will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [2.4.4] - 2026-10-30 + +Four of the SQL backends in the supported list could not create their own schema, +and PostgreSQL was rewriting four tables on every restart. **Upgrading is +recommended for every SQL deployment.** The first boot after this release emits a +small, one-time set of `ALTER COLUMN` statements and nothing on every boot after; +see the upgrade notes at the end. + +### Fixed + +- **`char(36)` columns were retyped on every single boot, and that was fatal on CockroachDB** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). GORM's postgres driver reads these columns back from `information_schema` as `bpchar`, which never prefix-matches the declared `char(36)`, so AutoMigrate re-emitted the same no-op type change at every startup: `ALTER TABLE "authorizer_sessions" ALTER COLUMN "user_id" TYPE char(36) USING "user_id"::char(36)`. On PostgreSQL that is a silent table rewrite under `ACCESS EXCLUSIVE` on four tables at every restart. On CockroachDB it is fatal — `ALTER COLUMN TYPE` sits behind an experimental flag (SQLSTATE XCEXF) — so the server started once and never started again. Five non-PK columns now declare `varchar(36)`, which is what the catalog reports; values are 36-character uuids either way. Measured on PostgreSQL 18 with `log_statement=all`: 5 alters on the upgrade boot, 0 on every boot after. +- **CockroachDB aborted at startup dropping a constraint it never had** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). CockroachDB lists unique *indexes* in `information_schema.table_constraints`, and MySQL reports `COLUMN_KEY='UNI'` for them, so GORM saw every `gorm:"uniqueIndex"` field as a constraint the model no longer wants and issued `DROP CONSTRAINT "uni__"` — a name it only ever creates from a `unique` tag. Startup died on `constraint "uni_authorizer_oauth_states_state_key" ... does not exist (SQLSTATE 42704)`, and on MySQL on `Error 1091: Can't DROP`. Those indexes are declared by the models and are meant to be there, so the drop is skipped; the create direction is delegated untouched. +- **The pre-2.3.0 uniqueness cleanup silently did nothing on CockroachDB** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). `ALTER TABLE ... DROP CONSTRAINT` on a UNIQUE is rejected outright there (`unimplemented: ... use DROP INDEX CASCADE instead`, SQLSTATE 0A000), and the failure was logged at debug level and swallowed. Releases before 2.3.0 declared `authorizer_users.email` and `authorizer_otps.phone_number` as UNIQUE; v2 keeps plain indexes and enforces uniqueness in the application layer, writing the empty string into `phone_number` for every email OTP — so on an upgraded CockroachDB database the **second email OTP was rejected** and email OTP login was broken. The drop now goes through `DROP INDEX ... CASCADE`. +- **MySQL and MariaDB had never worked at all, on three separate aborts** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). Nothing had ever run the storage suite against them. (1) GORM's mysql driver widens an unsized string to `varchar(191)` when the field carries `index` or `unique`, but it checks only those two tags — a field tagged `uniqueIndex`, named or composite, is missed and becomes `longtext`, which MySQL refuses to index: `Error 1170: BLOB/TEXT column 'client_id' used in key specification without a key length`, so the schema could not be created. (2) Then the phantom `DROP CONSTRAINT` above, as `Error 1091`. (3) Then `DeleteSessionTokensByUserID` used a backslash as its `LIKE` escape character, which MySQL rejects as `Error 1064`; the escape character is now `!` and the escaping is portable across engines, pinned by a new metacharacter test case. +- **SQL Server could not create its schema since 2.3.0-rc.10** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). `gorm.io/driver/sqlserver` has the identical two-tag gap as the mysql driver, so ten unsized `uniqueIndex` strings became `nvarchar(MAX)`: `mssql: Column 'client_id' in table 'authorizer_clients' is of a type that is invalid for use as a key column in an index.` Fixed at the driver's own indexed-string width. Two upsert tests remain red on an unrelated driver bug — `MergeCreate` builds its `MERGE` predicate from the primary key and ignores `clause.OnConflict.Columns`, so an upsert keyed on a composite unique index inserts a second row instead of replacing one. `make test-sqlserver` therefore exists but is deliberately outside `make test-all-db`. +- **`TestDeleteUserCascadesAllUserOwnedRows` could not be run twice against a persistent database** ([#826](https://github.com/authorizerdev/authorizer/pull/826)). Fixed fixture ids and no cleanup, so a second run collided on rows the first left behind. Pre-existing — it failed identically on PostgreSQL — and it masked real failures behind a false one on every re-run. + +### Changed + +- **`make test-all-db` now covers 12 databases**, with new `make test-cockroachdb`, `test-mysql`, `test-mariadb` and `test-sqlserver` targets ([#826](https://github.com/authorizerdev/authorizer/pull/826)). CI runs SQLite only, so a backend with no target had most likely never been executed — which is how four of them shipped unable to boot. Readiness is gated by a new `scripts/wait-for-sql.sh`, because `nc -z` connects to Docker Desktop's published-port proxy the instant `docker run -p` returns, and MySQL binds its port, runs its init scripts and then *restarts* — `mysqladmin ping` answers during that window, so the probe now runs a real query against the test database instead. + +### Upgrade notes + +- **One-time DDL on the first boot.** Five columns move from `char(36)` to `varchar(36)`: `audit_logs.actor_id`, `audit_logs.resource_id`, `authenticators.user_id`, `sessions.user_id` and `webhook_logs.webhook_id`. Stored values are unchanged — a 36-character uuid is identical in both types — and nothing is narrowed. On PostgreSQL this is five rewrites on the upgrade boot and zero thereafter; take the usual care if `authorizer_audit_logs` is large. +- **MySQL/MariaDB and SQL Server column widths.** An unsized string column under a `uniqueIndex` is now created as `varchar(191)` / `nvarchar(256)` instead of `longtext` / `nvarchar(MAX)`. Every affected column lives in a table introduced in 2.3.0-rc.10 (`clients`, `organizations`, `org_memberships`, `federated_identities`, `scim_endpoints`, `trusted_issuers`, `webauthn_credentials`), so no existing data is narrowed. +- **A SQL Server database left behind by a failed boot must be dropped.** The unsized `uniqueIndex` columns and the composite index over `authenticators.user_id` both arrived in 2.3.0-rc.10, so the two never coexist in a database that completed AutoMigrate. A database touched by a 2.3.0-rc.10 or later SQL Server boot has `user_id` as `char(36)` *inside* that index, and SQL Server will not retype an indexed column. No boot there ever finished, so such a database holds no data. Databases from 2.0.0 through 2.3.0 did boot, carry no index over any widened column, and upgrade in one boot with rows intact. +- **PlanetScale and Yugabyte were not executed.** PlanetScale is hosted MySQL-wire and shares the MySQL code path verbatim; Yugabyte keeps the unchanged `postgres.Open` path and the same `bpchar` catalog, so the `varchar(36)` change covers it. + ## [2.4.3] - 2026-10-29 ### Fixed