Repository navigation
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). GORM's postgres driver reads these columns back frominformation_schemaasbpchar, which never prefix-matches the declaredchar(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 underACCESS EXCLUSIVEon four tables at every restart. On CockroachDB it is fatal —ALTER COLUMN TYPEsits behind an experimental flag (SQLSTATE XCEXF) — so the server started once and never started again. Five non-PK columns now declarevarchar(36), which is what the catalog reports; values are 36-character uuids either way. Measured on PostgreSQL 18 withlog_statement=all: 5 alters on the upgrade boot, 0 on every boot after.- CockroachDB aborted at startup dropping a constraint it never had (#826). CockroachDB lists unique indexes in
information_schema.table_constraints, and MySQL reportsCOLUMN_KEY='UNI'for them, so GORM saw everygorm:"uniqueIndex"field as a constraint the model no longer wants and issuedDROP CONSTRAINT "uni_<table>_<column>"— a name it only ever creates from auniquetag. Startup died onconstraint "uni_authorizer_oauth_states_state_key" ... does not exist (SQLSTATE 42704), and on MySQL onError 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).
ALTER TABLE ... DROP CONSTRAINTon 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 declaredauthorizer_users.emailandauthorizer_otps.phone_numberas UNIQUE; v2 keeps plain indexes and enforces uniqueness in the application layer, writing the empty string intophone_numberfor 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 throughDROP INDEX ... CASCADE. - MySQL and MariaDB had never worked at all, on three separate aborts (#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 carriesindexorunique, but it checks only those two tags — a field taggeduniqueIndex, named or composite, is missed and becomeslongtext, 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 phantomDROP CONSTRAINTabove, asError 1091. (3) ThenDeleteSessionTokensByUserIDused a backslash as itsLIKEescape character, which MySQL rejects asError 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).
gorm.io/driver/sqlserverhas the identical two-tag gap as the mysql driver, so ten unsizeduniqueIndexstrings becamenvarchar(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 —MergeCreatebuilds itsMERGEpredicate from the primary key and ignoresclause.OnConflict.Columns, so an upsert keyed on a composite unique index inserts a second row instead of replacing one.make test-sqlservertherefore exists but is deliberately outsidemake test-all-db. TestDeleteUserCascadesAllUserOwnedRowscould not be run twice against a persistent database (#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-dbnow covers 12 databases, with newmake test-cockroachdb,test-mysql,test-mariadbandtest-sqlservertargets (#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 newscripts/wait-for-sql.sh, becausenc -zconnects to Docker Desktop's published-port proxy the instantdocker run -preturns, and MySQL binds its port, runs its init scripts and then restarts —mysqladmin pinganswers 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)tovarchar(36):audit_logs.actor_id,audit_logs.resource_id,authenticators.user_id,sessions.user_idandwebhook_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 ifauthorizer_audit_logsis large. - MySQL/MariaDB and SQL Server column widths. An unsized string column under a
uniqueIndexis now created asvarchar(191)/nvarchar(256)instead oflongtext/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
uniqueIndexcolumns and the composite index overauthenticators.user_idboth 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 hasuser_idaschar(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.Openpath and the samebpcharcatalog, so thevarchar(36)change covers it.