Split out of the identity-retrieval-race fix (finding "Top 10 #10" / DA3 / DA8 / DA9 of the internal multi-agent framework review, 2026-06-09). That PR makes both adapters prefer the driver-supplied generated key, which fixes the race/trigger hazard on Lucee and Adobe CF. Three pieces of deliberately deferred scope remain:
1. SQL Server: same-batch retrieval for the no-generated-key fallback
vendor/wheels/databaseAdapters/MicrosoftSQLServer/MicrosoftSQLServerModel.cfc — when the engine surfaces no result.generatedKey (currently BoxLang), the adapter falls back to a standalone SELECT @@IDENTITY, which is session-scoped and can return a trigger-generated identity from another table. A standalone SELECT SCOPE_IDENTITY() is not a fix: it executes in its own batch (a batch is a scope per MS docs) and returns NULL over JDBC. Proper fix options:
- run the retrieval in the insert's own batch (e.g.
INSERT ...; SELECT SCOPE_IDENTITY() AS lastId as one statement), or
- use
OUTPUT INSERTED.<pk>, or
- drop the fallback entirely once BoxLang surfaces JDBC generated keys in its query result struct.
2. Oracle: replace the MAX(ROWID) last-resort with CURRVAL
vendor/wheels/databaseAdapters/Oracle/OracleModel.cfc — the same no-generated-key fallback still resolves the new PK via WHERE ROWID = (SELECT MAX(ROWID) FROM <table>). ROWID encodes physical storage location, not insertion order, so this races under concurrent inserts and can assign another session's key. Replace with CURRVAL on the backing identity sequence (session-scoped, race-free), or remove once BoxLang surfaces generated keys.
3. DA3: dedupe the six divergent $identitySelect implementations
$identitySelect is copy-pasted across six adapters with divergent engine workarounds:
vendor/wheels/databaseAdapters/Base.cfc
MicrosoftSQLServer/MicrosoftSQLServerModel.cfc
PostgreSQL/PostgreSQLModel.cfc
Oracle/OracleModel.cfc
SQLite/SQLiteModel.cfc
CockroachDB/CockroachDBModel.cfc
The BoxLang ReplaceList column-parse workaround and the missing-closing-paren guard each exist in only a subset (the race-fix PR copied the ReplaceList workaround into MSSQL/Oracle, widening the duplication). Extract a Base $parseInsertColumnList template method plus a small per-engine $lastIdLookup hook so future engine fixes land once.
Split out of the identity-retrieval-race fix (finding "Top 10 #10" / DA3 / DA8 / DA9 of the internal multi-agent framework review, 2026-06-09). That PR makes both adapters prefer the driver-supplied generated key, which fixes the race/trigger hazard on Lucee and Adobe CF. Three pieces of deliberately deferred scope remain:
1. SQL Server: same-batch retrieval for the no-generated-key fallback
vendor/wheels/databaseAdapters/MicrosoftSQLServer/MicrosoftSQLServerModel.cfc— when the engine surfaces noresult.generatedKey(currently BoxLang), the adapter falls back to a standaloneSELECT @@IDENTITY, which is session-scoped and can return a trigger-generated identity from another table. A standaloneSELECT SCOPE_IDENTITY()is not a fix: it executes in its own batch (a batch is a scope per MS docs) and returns NULL over JDBC. Proper fix options:INSERT ...; SELECT SCOPE_IDENTITY() AS lastIdas one statement), orOUTPUT INSERTED.<pk>, or2. Oracle: replace the MAX(ROWID) last-resort with CURRVAL
vendor/wheels/databaseAdapters/Oracle/OracleModel.cfc— the same no-generated-key fallback still resolves the new PK viaWHERE ROWID = (SELECT MAX(ROWID) FROM <table>). ROWID encodes physical storage location, not insertion order, so this races under concurrent inserts and can assign another session's key. Replace withCURRVALon the backing identity sequence (session-scoped, race-free), or remove once BoxLang surfaces generated keys.3. DA3: dedupe the six divergent
$identitySelectimplementations$identitySelectis copy-pasted across six adapters with divergent engine workarounds:vendor/wheels/databaseAdapters/Base.cfcMicrosoftSQLServer/MicrosoftSQLServerModel.cfcPostgreSQL/PostgreSQLModel.cfcOracle/OracleModel.cfcSQLite/SQLiteModel.cfcCockroachDB/CockroachDBModel.cfcThe BoxLang
ReplaceListcolumn-parse workaround and the missing-closing-paren guard each exist in only a subset (the race-fix PR copied theReplaceListworkaround into MSSQL/Oracle, widening the duplication). Extract a Base$parseInsertColumnListtemplate method plus a small per-engine$lastIdLookuphook so future engine fixes land once.