@@ -12,10 +12,17 @@ import { invoke } from '@tauri-apps/api/core'
1212 */
1313export type SqlValue = string | number | boolean | null | Uint8Array
1414
15- export interface QueryResult {
16- /** The number of rows affected by the query. */
15+ /**
16+ * Result returned from write operations (INSERT, UPDATE, DELETE, etc.).
17+ */
18+ export interface WriteQueryResult {
19+ /** The number of rows affected by the write operation. */
1720 rowsAffected : number
18- /** The last inserted row ID (SQLite ROWID). */
21+ /**
22+ * The last inserted row ID (SQLite ROWID).
23+ * Only set for INSERT operations on tables with a ROWID.
24+ * Tables created with WITHOUT ROWID will not set this value (returns 0).
25+ */
1926 lastInsertId : number
2027}
2128
@@ -107,11 +114,9 @@ export default class Database {
107114 * **execute**
108115 *
109116 * Executes a write query against the database (INSERT, UPDATE, DELETE, etc.).
110- * This method is specifically for mutations that modify data.
117+ * This method is for mutations that modify data.
111118 *
112- * **Important:** Do NOT use this for SELECT queries. Use `fetchX()` instead.
113- * Using `execute()` for read queries will trigger an error to prevent unnecessary
114- * write mode initialization.
119+ * For SELECT queries, use `fetchAll()` or `fetchOne()` instead.
115120 *
116121 * SQLite uses `$1`, `$2`, etc. for parameter binding.
117122 *
@@ -132,7 +137,7 @@ export default class Database {
132137 * );
133138 * ```
134139 */
135- async execute ( query : string , bindValues ?: SqlValue [ ] ) : Promise < QueryResult > {
140+ async execute ( query : string , bindValues ?: SqlValue [ ] ) : Promise < WriteQueryResult > {
136141 const [ rowsAffected , lastInsertId ] = await invoke < [ number , number ] > (
137142 'plugin:sqlite|execute' ,
138143 {
@@ -147,6 +152,50 @@ export default class Database {
147152 }
148153 }
149154
155+ /**
156+ * **executeTransaction**
157+ *
158+ * Executes multiple write statements atomically within a transaction.
159+ * All statements either succeed together or fail together.
160+ *
161+ * The function automatically:
162+ * - Begins a transaction (BEGIN)
163+ * - Executes all statements in order
164+ * - Commits on success (COMMIT)
165+ * - Rolls back on any error (ROLLBACK)
166+ *
167+ * @param statements - Array of [query, values?] tuples to execute
168+ * @returns Promise that resolves with results for each statement when all complete successfully
169+ * @throws SqliteError if any statement fails (after rollback)
170+ *
171+ * @example
172+ * ```ts
173+ * // Execute multiple inserts atomically
174+ * const results = await db.executeTransaction([
175+ * ['INSERT INTO users (name, email) VALUES ($1, $2)', ['Alice', 'alice@example.com']],
176+ * ['INSERT INTO audit_log (action, user) VALUES ($1, $2)', ['user_created', 'Alice']]
177+ * ]);
178+ * console.log(`User ID: ${results[0].lastInsertId}`);
179+ * console.log(`Log rows affected: ${results[1].rowsAffected}`);
180+ *
181+ * // Mixed operations
182+ * const results = await db.executeTransaction([
183+ * ['UPDATE accounts SET balance = balance - $1 WHERE id = $2', [100, 1]],
184+ * ['UPDATE accounts SET balance = balance + $1 WHERE id = $2', [100, 2]],
185+ * ['INSERT INTO transfers (from_id, to_id, amount) VALUES ($1, $2, $3)', [1, 2, 100]]
186+ * ]);
187+ * ```
188+ */
189+ async executeTransaction ( statements : Array < [ string , SqlValue [ ] ?] > ) : Promise < WriteQueryResult [ ] > {
190+ return await invoke < WriteQueryResult [ ] > ( 'plugin:sqlite|execute_transaction' , {
191+ db : this . path ,
192+ statements : statements . map ( ( [ query , values ] ) => ( {
193+ query,
194+ values : values ?? [ ]
195+ } ) )
196+ } )
197+ }
198+
150199 /**
151200 * **fetchAll**
152201 *
@@ -211,78 +260,6 @@ export default class Database {
211260 return result
212261 }
213262
214- /**
215- * **beginTransaction**
216- *
217- * Begins a new database transaction. All subsequent operations will be
218- * part of this transaction until `commitTransaction()` or `rollbackTransaction()`
219- * is called.
220- *
221- * Transactions provide atomicity - either all operations succeed or all are rolled back.
222- *
223- * @example
224- * ```ts
225- * await db.beginTransaction();
226- * try {
227- * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']);
228- * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']);
229- * await db.commitTransaction();
230- * } catch (error) {
231- * await db.rollbackTransaction();
232- * throw error;
233- * }
234- * ```
235- */
236- async beginTransaction ( ) : Promise < void > {
237- await invoke ( 'plugin:sqlite|begin_transaction' , {
238- db : this . path
239- } )
240- }
241-
242- /**
243- * **commitTransaction**
244- *
245- * Commits the current transaction, making all changes permanent.
246- *
247- * @example
248- * ```ts
249- * await db.beginTransaction();
250- * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']);
251- * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']);
252- * await db.commitTransaction();
253- * ```
254- */
255- async commitTransaction ( ) : Promise < void > {
256- await invoke ( 'plugin:sqlite|commit_transaction' , {
257- db : this . path
258- } )
259- }
260-
261- /**
262- * **rollbackTransaction**
263- *
264- * Rolls back the current transaction, discarding all changes made since
265- * `beginTransaction()` was called.
266- *
267- * @example
268- * ```ts
269- * await db.beginTransaction();
270- * try {
271- * await db.execute('INSERT INTO users (name) VALUES ($1)', ['Alice']);
272- * await db.execute('INSERT INTO logs (action) VALUES ($1)', ['user_created']);
273- * await db.commitTransaction();
274- * } catch (error) {
275- * await db.rollbackTransaction();
276- * throw error;
277- * }
278- * ```
279- */
280- async rollbackTransaction ( ) : Promise < void > {
281- await invoke ( 'plugin:sqlite|rollback_transaction' , {
282- db : this . path
283- } )
284- }
285-
286263 /**
287264 * **close**
288265 *
0 commit comments