diff --git a/changelog.d/4095-migrator-unsupported-column-type.changed.md b/changelog.d/4095-migrator-unsupported-column-type.changed.md index 5ac1ec0b55..71129640d0 100644 --- a/changelog.d/4095-migrator-unsupported-column-type.changed.md +++ b/changelog.d/4095-migrator-unsupported-column-type.changed.md @@ -1 +1 @@ -- A migration that uses a column type the database adapter doesn't map (for example `t.column(columnName="payload", columnType="jsonb")`) now fails at migrate time with `Wheels.Migrator.UnsupportedColumnType`, naming the type and the adapter and listing the supported types, instead of emitting a column with no type; that typeless column was a DDL syntax error on most databases, a raw missing-key error on Oracle, and on SQLite a column whose model failed to load (#4095). +- A migration that uses a column type the database adapter doesn't map (for example `t.column(columnName="payload", columnType="jsonb")`) now fails at migrate time with `Wheels.Migrator.UnsupportedColumnType`, naming the type and the adapter and listing the supported types, instead of emitting a column with no type; that typeless column was a DDL syntax error on most databases, a raw missing-key error on Oracle, and on SQLite a column whose model failed to load (#4095) diff --git a/changelog.d/4134-migration-lock.changed.md b/changelog.d/4134-migration-lock.changed.md index 9c12c0c8e3..09c5834c29 100644 --- a/changelog.d/4134-migration-lock.changed.md +++ b/changelog.d/4134-migration-lock.changed.md @@ -1 +1 @@ -- Instances that share a database now run its migrations one at a time: `migrateTo()`, `migrateToLatest()`, `migrateIndividual()` and `redoMigration()` hold a cross-process lock, a lease row in the new `wheels_migrator_locks` table, on every supported database. An instance that waits re-reads which migrations are pending once it has the lock, so it doesn't run them again. It gives up after `migrationLockTimeout` (default 300 seconds) with `Wheels.MigrationLockTimeout`, naming the host holding the lock. The lease (`migrationLockLease`, default 3600 seconds) is renewed before each migration step, and a holder that died frees the lock when its lease runs out; a single migration step that runs longer than the lease can lose the lock to another instance (`Wheels.MigrationLockLost`). To clear a stuck lock at once, run `wheels migrate unlock --force` or delete its row from `wheels_migrator_locks` (#4134). +- Instances that share a database now run its migrations one at a time: `migrateTo()`, `migrateToLatest()`, `migrateIndividual()` and `redoMigration()` hold a cross-process lock, a lease row in the new `wheels_migrator_locks` table, on every supported database. An instance that waits re-reads which migrations are pending once it has the lock, so it doesn't run them again. It gives up after `migrationLockTimeout` (default 300 seconds) with `Wheels.MigrationLockTimeout`, naming the host holding the lock. The lease (`migrationLockLease`, default 3600 seconds) is renewed before each migration step, and a holder that died frees the lock when its lease runs out; a single migration step that runs longer than the lease can lose the lock to another instance (`Wheels.MigrationLockLost`). To clear a stuck lock at once, run `wheels migrate unlock --force` or delete its row from `wheels_migrator_locks` (#4134) diff --git a/changelog.d/4149-controller-construction.performance.md b/changelog.d/4149-controller-construction.performance.md index 4b25c82d49..fb6268d1b1 100644 --- a/changelog.d/4149-controller-construction.performance.md +++ b/changelog.d/4149-controller-construction.performance.md @@ -1 +1 @@ -- Creating a controller instance, which happens on every request, no longer mixes the framework's controller and view methods in one at a time. Each controller class works out once which methods to add and which `super` aliases to create, and every later instance applies that result with two `StructAppend` calls. Measured per instance in the same process: 4.8 ms → 1.4 ms on RustCFML (the CI-pinned v0.693.0), 2.3 ms → 0.9 ms on Adobe ColdFusion 2023, 1.7 ms → 0.8 ms on Adobe ColdFusion 2025, 0.58 ms → 0.33 ms on Lucee 7 and 4.7 ms → 3.6 ms on BoxLang. Development keeps the old per-method path, so controller methods edited there still take effect without a reload; the new `cacheControllerIntegration` setting controls it (#4149). +- Creating a controller instance, which happens on every request, no longer mixes the framework's controller and view methods in one at a time. Each controller class works out once which methods to add and which `super` aliases to create, and every later instance applies that result with two `StructAppend` calls. Measured per instance in the same process: 4.8 ms → 1.4 ms on RustCFML (the CI-pinned v0.693.0), 2.3 ms → 0.9 ms on Adobe ColdFusion 2023, 1.7 ms → 0.8 ms on Adobe ColdFusion 2025, 0.58 ms → 0.33 ms on Lucee 7 and 4.7 ms → 3.6 ms on BoxLang. Development keeps the old per-method path, so controller methods edited there still take effect without a reload; the new `cacheControllerIntegration` setting controls it (#4149) diff --git a/changelog.d/4172-wide-decimal-exact-bind.fixed.md b/changelog.d/4172-wide-decimal-exact-bind.fixed.md index fa21a02067..04c09e7f0d 100644 --- a/changelog.d/4172-wide-decimal-exact-bind.fixed.md +++ b/changelog.d/4172-wide-decimal-exact-bind.fixed.md @@ -1 +1 @@ -- A `DECIMAL` / `NUMERIC` value with more than 15 significant digits is now stored and matched in a `where` exactly on PostgreSQL, SQL Server, MySQL and CockroachDB under Lucee and BoxLang, which bound it through a double and so wrote or compared a different number; such a value now binds in each database's exact form, and values with up to 15 significant digits keep their existing bind (#4172). +- A `DECIMAL` / `NUMERIC` value with more than 15 significant digits is now stored and matched in a `where` exactly on PostgreSQL, SQL Server, MySQL and CockroachDB under Lucee and BoxLang, which bound it through a double and so wrote or compared a different number; such a value now binds in each database's exact form, and values with up to 15 significant digits keep their existing bind (#4172) diff --git a/changelog.d/4174-promote-globals-metadata.performance.md b/changelog.d/4174-promote-globals-metadata.performance.md index b0bde2bfda..d8e903ad04 100644 --- a/changelog.d/4174-promote-globals-metadata.performance.md +++ b/changelog.d/4174-promote-globals-metadata.performance.md @@ -1 +1 @@ -- Creating any model object or controller no longer calls `GetMetadata(this)` while the object is being constructed. The memo of include-injected functions to promote to `this` always ended up under one entry (`wheels.Global`) on every supported engine, so it now uses that key directly. On RustCFML the call cost about 225 µs per object; model `new()` went from 1.30 ms to 1.13 ms there, and from 0.31 ms to 0.25 ms on Lucee 7 (#4174). +- Creating any model object or controller no longer calls `GetMetadata(this)` while the object is being constructed. The memo of include-injected functions to promote to `this` always ended up under one entry (`wheels.Global`) on every supported engine, so it now uses that key directly. On RustCFML the call cost about 225 µs per object; model `new()` went from 1.30 ms to 1.13 ms there, and from 0.31 ms to 0.25 ms on Lucee 7 (#4174) diff --git a/changelog.d/4187-singularize-prefixed-analyses.changed.md b/changelog.d/4187-singularize-prefixed-analyses.changed.md index c2b7969274..96292da539 100644 --- a/changelog.d/4187-singularize-prefixed-analyses.changed.md +++ b/changelog.d/4187-singularize-prefixed-analyses.changed.md @@ -1 +1 @@ -- `singularize()` returns `psychoanalysis` for `psychoanalyses` instead of the misspelled `psychoanalyasis`; other `-ses` words such as `bases`, `theses` and `analyses` are unchanged (#4187). +- `singularize()` returns `psychoanalysis` for `psychoanalyses` instead of the misspelled `psychoanalyasis`; other `-ses` words such as `bases`, `theses` and `analyses` are unchanged (#4187) diff --git a/changelog.d/4189-singularize-databases.changed.md b/changelog.d/4189-singularize-databases.changed.md index 7efa130adc..e13d50a3a4 100644 --- a/changelog.d/4189-singularize-databases.changed.md +++ b/changelog.d/4189-singularize-databases.changed.md @@ -1 +1 @@ -- `singularize("databases")` returns `database` instead of `databasis`, so a name such as `Databases` or `userDatabases` now singularizes to `Database` / `userDatabase`. An app that relied on the old `databasis` output, for example a model class or table named after it, needs to rename it. That's unlikely, but check before upgrading. Other `-ases` words (`bases`, `cases`, `phases`, `purchases`, `vases`) are unchanged (#4189). +- `singularize("databases")` returns `database` instead of `databasis`, so a name such as `Databases` or `userDatabases` now singularizes to `Database` / `userDatabase`. An app that relied on the old `databasis` output, for example a model class or table named after it, needs to rename it. That's unlikely, but check before upgrading. Other `-ases` words (`bases`, `cases`, `phases`, `purchases`, `vases`) are unchanged (#4189) diff --git a/changelog.d/oracle-beyond-64bit.fixed.md b/changelog.d/oracle-beyond-64bit.fixed.md index a1e82f6707..90af7f1c36 100644 --- a/changelog.d/oracle-beyond-64bit.fixed.md +++ b/changelog.d/oracle-beyond-64bit.fixed.md @@ -1 +1 @@ -- On Oracle, a whole number beyond the signed 64-bit range (more than 9,223,372,036,854,775,807, or less than -9,223,372,036,854,775,808) written to or compared against a `NUMBER` column is now bound as text, which Oracle converts exactly. It was clamped (Lucee), wrapped (BoxLang) or rounded through a double, so it could be stored or matched as a different number (#4162). +- On Oracle, a whole number beyond the signed 64-bit range (more than 9,223,372,036,854,775,807, or less than -9,223,372,036,854,775,808) written to or compared against a `NUMBER` column is now bound as text, which Oracle converts exactly. It was clamped (Lucee), wrapped (BoxLang) or rounded through a double, so it could be stored or matched as a different number (#4162) diff --git a/changelog.d/tx-advisory-locks.added.md b/changelog.d/tx-advisory-locks.added.md index ac51799f2c..3d65eeae91 100644 --- a/changelog.d/tx-advisory-locks.added.md +++ b/changelog.d/tx-advisory-locks.added.md @@ -1 +1 @@ -- `withAdvisoryLock()` now accepts an opt-in `transaction = true` argument that pins the lock acquire, the callback, and the release to one connection inside a transaction, so the lock genuinely covers the callback's own queries. On PostgreSQL the lock is transaction-scoped and auto-releases when the transaction ends; on MySQL and SQL Server it is a session lock released before the transaction closes. Supported on PostgreSQL, MySQL, and SQL Server; other databases throw rather than silently running session-scoped. Recommended for short critical sections — a long-running callback holds its row locks and a pooled connection (and blocks PostgreSQL VACUUM) for the whole call. Defaults to `false` (the existing behaviour). ([#4198](https://github.com/wheels-dev/wheels/issues/4198)). +- `withAdvisoryLock()` now accepts an opt-in `transaction = true` argument that pins the lock acquire, the callback, and the release to one connection inside a transaction, so the lock genuinely covers the callback's own queries. On PostgreSQL the lock is transaction-scoped and auto-releases when the transaction ends; on MySQL and SQL Server it is a session lock released before the transaction closes. Supported on PostgreSQL, MySQL, and SQL Server; other databases throw rather than silently running session-scoped. Recommended for short critical sections — a long-running callback holds its row locks and a pooled connection (and blocks PostgreSQL VACUUM) for the whole call. Defaults to `false` (the existing behaviour). ([#4198](https://github.com/wheels-dev/wheels/issues/4198))