Repository navigation
fix(oracle): run bulk inserts as a PL/SQL block so BoxLang asks no keys - #3718
Conversation
On BoxLang + Oracle every insertAll() spec errored with "Error closing connection: Connection is closed". Outside a transaction the real error shows: ORA-17009 (Closed statement). BoxLang requests generated keys on every INSERT, and Oracle's driver cannot return them for INSERT ... SELECT, the shape $bulkInsertSQL emits (#3302). INSERT ... VALUES, UPDATE and SELECT are unaffected; a leading comment or a WITH clause does not help. Wrapping the statement as BEGIN INSERT ... SELECT ...; END; makes it not an INSERT to the engine, so no key request is made. Bind parameters work inside the block, and nothing on this path reads a generated key. OracleMigrator already runs BEGIN ... END; blocks the same way on every engine. The fix exposed one more masked error: bulkOperationsSpec measured Len() of a raw oracle.sql.TIMESTAMP. It now asserts the column normalizes to a date through $normalizeDbTimestamp(). Fixes #3715 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Peter Amiri <peter@alurium.com>
|
Review: independent reviewer (rev1.r1), commit
Root cause and fix. BoxLang requests generated keys on every INSERT, and the Oracle driver can't return them for Checked for side effects:
|
|
Independent review of 77ce2cb: source review clean; Oracle matrix evidence pending. Independently ran The timestamp assertion now verifies a normalized date instead of merely nonempty text; it retains the requirement that timestamps were populated. Final clearance awaits actual Oracle results from dispatch 36344707369, whose head matches this PR. The change applies to all Oracle engines, so the BoxLang reproduction fix also needs Lucee/Adobe execution evidence. A soft-fail job colour alone will not suffice. |
|
APPROVED at 77ce2cb. Independently downloaded the actual Oracle JSON artifacts from exact-head dispatch 36344707369. All insertAll and upsertAll specs passed on BoxLang, Lucee 6/7, and Adobe 2023. Lucee 6/7 and Adobe 2023 each report 5,792 pass / 0 fail / 0 error / 24 skip. BoxLang reports 5,791 pass / 0 fail / 1 error / 24 skip; its sole error is the separate TimestampRoundTripSpec diagnostic issue #3714, addressed by #3716. Adobe 2025 remains pending and is not claimed as verified here. The Oracle anonymous block keeps bound parameters, batching and transaction ownership intact; it does not commit or suppress errors. The caller already computes inserted count from the batch and does not consume generated keys. Earlier independent Lucee 7+SQLite model suite: 1,051 passed, zero failures/errors. No blocking finding in #3715's scope. |
|
Follow-up from rev1.r1, for the record: the gate is met. I read the result JSON of dispatch 36344707369 (
|
Fixes #3715. Based on develop at b313a44, which includes #3710, so the BoxLang + Oracle leg gets past fixture population.
Root cause
The failure reproduces locally on BoxLang + Oracle (isolated compose project) with the same 4
insertAllerrors as CI. Probes then isolated the cause with plainqueryExecutecalls, no Wheels code involved:INSERT ... SELECT ... UNION ALLORA-17009: Closed statementError closing connection: Connection is closedINSERT ... SELECT(single row)ORA-17009Connection is closedINSERT ... VALUES,UPDATE,SELECT/* */comment,WITHclauseORA-17009BEGIN INSERT ... SELECT ...; END;(with bind params)BoxLang requests generated keys on every INSERT, whether or not a
resultis captured. Oracle's driver cannot return keys forINSERT ... SELECT, which is the shape$bulkInsertSQLmust emit (#3302 per-row identity). In a transaction, the resulting error is masked by the connection-close failure. This is the same class of bug as #2745 and #3653.Change
OracleModel.$bulkInsertSQLwraps the statement:BEGIN INSERT ... SELECT ...; END;. It is still one statement with one source row per record, and all values are still bound.OracleMigratoralready runsBEGIN ... END;blocks on every engine.oracleBulkInsertSqlSpecpins the block shape. Existing assertions are unchanged.bulkOperationsSpecmeasuredLen()of a raworacle.sql.TIMESTAMP(Cannot determine length of object). It now asserts that the column normalizes to a date via$normalizeDbTimestamp(), a stronger check.Verification
wheels.tests.specs.model: before, 1047 pass / 4 error; after, 1052 pass / 0 fail / 0 error / 14 skip.tools/test-local.sh): 1051 passed.🤖 Generated with Claude Code