Skip to content

fix: commit autocommit batches, allow >84 parameters, bind large integers, retry RDBNAM on close - #35

Merged
gurungabit merged 5 commits into
mainfrom
claude/batch-commit-params-bigint
Oct 5, 2026
Merged

gurungabit merged 5 commits into
mainfrom
claude/batch-commit-params-bigint

Conversation

@gurungabit

Copy link
Copy Markdown
Owner

Fixes the three driver bugs found while benchmarking 1.0.26, plus an intermittent connect failure on Db2 12.1 that showed up during validation.

1. executeBatch never committed under autocommit

execute_batch_with_params sent no RDBCMM. Rows from an autocommit executeBatch stayed uncommitted, and invisible to other connections, until something else committed on that connection. A DSS capture confirms that every other autocommit path chains RDBCMM and the batch path did not. On a failing row it also returned before reading the remaining row replies.

  • Replies: Db2 executes every row in the chain and answers each one (verified with a duplicate key in row 2 of 5), so all replies are read before the first row error is reported.
  • Autocommit: the batch commits once after every row succeeds. If any row fails, it rolls back and the first error is thrown, so a failed batch keeps no partial rows.
  • Transactions: a batch inside a transaction is unchanged and commits or rolls back with the transaction.
  • Docs: the API reference and README describe the behavior.

2. Statements were limited to 84 parameters

All parameter descriptors went into one FD:OCA group triplet, and its one-byte length holds at most 84 three-byte entries. An 85th parameter failed with too many parameters for SQLDTA descriptor header. Descriptors past the first 84 now continue in CPT triplets (0x7F 0x00), the same layout Db2 uses in its own QRYDSC. This was captured from a 100-column result: ff 76 d0 …84 entries… 33 7f 00 …16 entries…. Tested up to 500 parameters, including an externalized 40 KB CLOB as the 90th parameter.

3. Large whole JS numbers could not be bound to BIGINT

napi's JSON conversion keeps only u32-sized integers integral, so 606227179000 arrived as Double and BIGINT rejected it (expected integer-compatible parameter). 2**31 happened to work. Whole numbers within Number.MAX_SAFE_INTEGER now bind as integers, which is the behavior docs/data-types already described.

Value main this PR
606227179000 → BIGINT error 606227179000
±Number.MAX_SAFE_INTEGER → BIGINT error exact
1.5 → BIGINT error error (unchanged)
2**60 → BIGINT (unsafe integer) error error (pass a bigint)
DOUBLE / DECIMAL targets unchanged unchanged

4. Intermittent "RDB not accessed or database not found" on connect

The first ACCSEC sends a trimmed RDBNAM. Db2 12.1 rejects it with RDBNFNRM, then the driver retries with the padded LUW form. Db2 sometimes closes the socket before that reply is read. In that case the driver reported RDB not accessed or database not found without retrying. A closed socket during the trimmed attempt now takes the same retry, and a failed retry still reports the same message. connection_test on Db2 12.1 failed in 3 of 3 alternating runs on main and passed 3 of 3 on this branch.

Tests

  • New live Rust tests (prepared_stmt_test.rs):
    • A 600-row autocommit batch is visible to a second connection.
    • A 600-row batch with a duplicate key in its second pipeline chunk keeps no rows, and the connection stays in sync.
    • A transaction batch stays invisible until commit.
    • 84, 85, 169 and 300 parameters work through direct and prepared execution, plus a 100-column insert with a CLOB after the 84th descriptor.
  • New live Node tests (batch-params-regressions.test.ts):
    • Safe integers to BIGINT, DOUBLE and DECIMAL through query and executeBatch.
    • Batch commit and rollback, as seen from a second client.
    • 85, 169 and 500 parameters, a wide-table executeBatch and a 300-value IN list.
  • Unit tests:
    • Exact GDA/CPT triplet headers for 0, 1, 84, 85, 168, 169 and 300 parameters.
    • The RDBNAM retry predicate.
  • Fail on main: all three Node tests and the batch and wide-parameter Rust tests fail there, and pass here.

Validation

  • cargo fmt --check, cargo clippy --workspace --all-targets -D warnings, workspace unit tests and db2-napi JS tests all pass.
  • Db2 12.1: cargo test --workspace --test '*' passes. Node npm test has 140 passed, 0 failed, 7 skipped, including the TLS suite.
  • Db2 11.5: cargo test --workspace --test '*' passes. Node has the same 14 pre-existing SQL0204 failures as main (missing seed fixtures).
  • Each commit compiles on its own.

🤖 Generated with Claude Code

The first ACCSEC sends a trimmed RDBNAM, which Db2 12.1 rejects with
RDBNFNRM before the driver retries with the padded LUW form. Db2 can
close the socket before that reply is read; the driver then reported
"RDB not accessed or database not found" without retrying, so parallel
connects to 12.1 failed intermittently. A closed socket during the
trimmed attempt now takes the same retry.
execute_batch_with_params never sent RDBCMM, so rows from an autocommit
executeBatch stayed uncommitted, and invisible to other connections,
until something else committed on the connection. It also returned on
the first failing row before reading the remaining row replies.

All replies are now read before reporting the first row error. Under
autocommit the batch commits once every row succeeds and rolls back if
any row fails; inside a transaction it is left to the transaction.
All parameter descriptors went into one FD:OCA group triplet, whose
one-byte length holds at most 84 entries, so an 85th parameter failed
with "too many parameters for SQLDTA descriptor header". Longer
descriptor lists now continue in CPT triplets (0x7F 0x00), the layout
Db2 itself uses in QRYDSC for wide result sets.
napi's JSON conversion keeps only u32-sized integers integral, so a JS
number such as 606227179000 arrived as DOUBLE and was rejected for
BIGINT targets. Whole numbers within Number.MAX_SAFE_INTEGER now bind as
integers, matching the documented BIGINT parameter behavior.
@gurungabit
gurungabit merged commit 1697774 into main Oct 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant