step Exec to completion for SQLITE_ROW

The exec path treated SQLITE_ROW identically to SQLITE_DONE, calling newResult after a single step.

For DML with RETURNING clauses that affect multiple rows, this meant the statement was never stepped to completion, causing sqlite3_changes() to return stale counts.

Continue stepping until SQLITE_DONE before reading the result.

This could manifest as a performance regression: we're doing strictly more work by draining all the rows instead of abandoning them. But as Russ Cox says, I can make anything fast if it doesn't need to be correct.

This brings this Exec implementation in line with C sqlite3_exec in several ways:

  • Row draining: C sqlite3_exec loops sqlite3_step until rc != SQLITE_ROW, then finalizes.

  • Result after completion: C sqlite3_exec does finalize-after-loop.

  • Unconditional drain: C sqlite3_exec with a NULL callback still steps through every row.

  • Partial commit on interruption: C returns SQLITE_ABORT if the callback returns non-zero mid-drain, but already-stepped rows stay committed. (Use an explicit transaction for atomicity.)

  • Multi-statement iteration: C's outer loop prepares and fully steps each sub-statement.

Merge request reports

Loading