Problem
ExecutionQuery::Execute() (src/queries.cc) brackets every single statement in its own BEGIN TRANSACTION; / COMMIT;. The batch overloads (Save(const std::vector<T>&), Update(const std::vector<T>&), and SaveAutoIncrement for vectors in include/database.h) simply call the single-record path in a loop, so each row is its own transaction.
Impact
- Performance: saving N rows performs N transactions = N commits/fsyncs. On a file-backed database this is dramatically slower than a single transaction for the whole batch.
- Robustness / atomicity: a batch is not atomic. If row k fails, rows 0..k-1 are already committed and remain in the database — there is no all-or-nothing guarantee for a batch operation.
Suggested direction
Wrap a batch operation in a single transaction (one BEGIN ... loop ... COMMIT, with ROLLBACK on failure), and ideally reuse one prepared statement bound repeatedly across the rows instead of re-preparing per row.
Problem
ExecutionQuery::Execute()(src/queries.cc) brackets every single statement in its ownBEGIN TRANSACTION;/COMMIT;. The batch overloads (Save(const std::vector<T>&),Update(const std::vector<T>&), andSaveAutoIncrementfor vectors ininclude/database.h) simply call the single-record path in a loop, so each row is its own transaction.Impact
Suggested direction
Wrap a batch operation in a single transaction (one
BEGIN... loop ...COMMIT, withROLLBACKon failure), and ideally reuse one prepared statement bound repeatedly across the rows instead of re-preparing per row.