Skip to content

feat(model): add advisory locks and SELECT FOR UPDATE#2103

Merged
bpamiri merged 2 commits intodevelopfrom
peter/advisory-locks
Apr 15, 2026
Merged

feat(model): add advisory locks and SELECT FOR UPDATE#2103
bpamiri merged 2 commits intodevelopfrom
peter/advisory-locks

Conversation

@bpamiri
Copy link
Copy Markdown
Collaborator

@bpamiri bpamiri commented Apr 15, 2026

Summary

  • Adds withAdvisoryLock(name, timeout, callback) class-level method on models for database advisory locking with automatic release via try/finally
  • Adds forUpdate() chainable method on QueryBuilder for pessimistic row locking (SELECT ... FOR UPDATE)
  • Implements per-database adapter support: PostgreSQL (pg_advisory_lock), MySQL (GET_LOCK), SQL Server (sp_getapplock), SQLite (no-op), CockroachDB (throws, use forUpdate), H2/Oracle (throws with clear messages)
  • Adds $forUpdate argument to findAll() for direct use without QueryBuilder

Test plan

  • All 9 new BDD tests pass on SQLite (withAdvisoryLock callback execution, exception safety, timeout, forUpdate QueryBuilder flag, chain composition, findAll execution, adapter clause)
  • No regressions: 2759 pass, same pre-existing failures as develop
  • CI matrix validates compilation on all engines

🤖 Generated with Claude Code

bpamiri and others added 2 commits April 14, 2026 22:54
Adds database-level advisory locking and pessimistic row locking to close
a gap vs Rails/Laravel/Django. Advisory locks coordinate exclusive access
to shared resources without locking rows/tables. FOR UPDATE enables
pessimistic row locking within transactions via the QueryBuilder.

New files:
- vendor/wheels/model/locking.cfc: withAdvisoryLock() mixin
- vendor/wheels/tests/specs/model/lockingSpec.cfc: BDD tests

Modified:
- QueryBuilder.cfc: forUpdate() chain method
- read.cfc: $forUpdate argument for findAll()
- Base.cfc: stub methods with sensible defaults
- All 7 database adapters: per-DB implementations

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
PostgreSQL, MySQL, and SQL Server adapters were interpolating the
lock name directly into SQL strings, creating an injection vector
when the name comes from a non-trusted source. Switch to queryExecute
with bind parameters. Adds regression test using a name with single
quotes ("O'Brien's lock") that would have broken the old SQL.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bpamiri bpamiri force-pushed the peter/advisory-locks branch from f21a2b0 to 5e38c26 Compare April 15, 2026 05:57
@bpamiri bpamiri merged commit 0ae6e8c into develop Apr 15, 2026
3 checks passed
@bpamiri bpamiri deleted the peter/advisory-locks branch April 15, 2026 05:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant