Repository navigation
feat: opt-in EXECUTE_ARRAY prepared batch path for executemany() #839
Copy link
Copy link
Closed
Labels
area: protocolCAS wire protocolCAS wire protocolenhancementNew feature or requestNew feature or requestperformancePerformance improvementPerformance improvementpriority: highShould be fixed soonShould be fixed soonsize: LLarge changeLarge changestatus: needs triageNot yet looked at by a maintainerNot yet looked at by a maintainer
Description
Problem and evidence
#836 measured a prototype EXECUTE_ARRAY (function code 21) path: one PREPARE, one request with every row's bound values, one CLOSE_REQ. At 10,000 rows, commit included, it takes 0.33-0.55x of the shipped executemany() for INSERT, UPDATE and DELETE and 0.15-0.16x for MERGE on CUBRID 11.0-11.4 (0.51-0.61x on 10.2). It is also 0.41-0.54x of the prepare-once loop. Experiment and raw data: cubrid-lab/cubrid-benchmark#23 (experiments/pycubrid-prepared-batch).
On 10.2, 11.0, 11.2 and 11.4 the server keeps the shipped batch's semantics:
- every row is executed and only the failing row is undone;
- autocommit commits each successful row;
- per-row counts and the
rowcountsum are identical.
The path differs in three ways:
- it accepts only the prepared type set (feat: widen the prepared parameter types (BIGINT, DOUBLE, NUMERIC, temporal, binary) #840 widens it);
- values are coerced as host variables, not literals;
- with autocommit on, a LOB handle fails after the first row's commit (-1016).
So the decision in #836 is an opt-in path. The default stays the same.
Scope
- Add
ExecuteArrayPacket(request: query handle, query timeout for protocol V4+, autocommit byte, one type/value pair per marker per row; reply: per-row count + OID or error code + message, shard id for V5+), with offline packet tests. - Add an opt-in to
executemany()on the sync and async cursors, for example a keyword-onlyprepared=True. Agree on the API in the design review first. For INSERT/UPDATE/DELETE/MERGE it runsPREPARE+EXECUTE_ARRAY, and it keeps the existing session-generation fence, the deferred-close handling (perf: release unclosed cursor handles in autocommit mode with a deferred close queue #488) and the reconnect rules. - Keep the batch semantics: raise the first returned error, as today, with the same exception mapping, and set
rowcountto the sum of the per-row counts. - Reject values outside the prepared type set before anything is sent. Reject LOB handles when autocommit is on.
- Add integration tests on 10.2-11.4 for duplicate-key and conversion errors in the middle of a batch (autocommit on and off),
rowcount, NULLs and SET values, matching the chore(performance): evaluate a prepared-statement batch path for executemany #836 probes. UpdatePERFORMANCE.md,API_REFERENCE.md,PARAMETER_BINDING.md, the Korean copies,CHANGELOG.mdandRELEASE_POLICY.md(additive API).
Non-goals
- Changing the default
executemany()path. - Server-side protocol changes.
Links
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
area: protocolCAS wire protocolCAS wire protocolenhancementNew feature or requestNew feature or requestperformancePerformance improvementPerformance improvementpriority: highShould be fixed soonShould be fixed soonsize: LLarge changeLarge changestatus: needs triageNot yet looked at by a maintainerNot yet looked at by a maintainer
Activity
yeongseon commentedon Oct 11, 2026
Deferred until after the current stabilization release (2026-10-12). #815 admits only fixes for demonstrated regressions in the supported contract, plus the release-candidate work (#833). It does not admit "additional APIs … or opportunistic feature additions". This issue is a new opt-in feature or optional optimization, not a regression fix, so it waits for the next cycle. The evidence from #836 and #822 is already recorded (cubrid-benchmark#22, cubrid-benchmark#23, and the
executemany()sections ofdocs/PERFORMANCE.mdfrom #838 and #843). Nothing is lost by waiting.feat(cursor): opt-in EXECUTE_ARRAY prepared batch for executemany() (#…