Skip to content

Commit 3ea2584

Browse files
author
Oscar Franco
authored
Merge pull request #64 from ospfranco/async-transaction
Add an async version of transactions
2 parents 1590c9d + 9df66b7 commit 3ea2584

2 files changed

Lines changed: 100 additions & 5 deletions

File tree

README.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,20 @@ QuickSQLite.transaction('myDatabase', (tx) => {
195195
});
196196
```
197197

198+
Async transactions are also possible, but the API is based on promises and/or a boolean response:
199+
200+
```ts
201+
QuickSQLite.asyncTransaction('myDatabase', async (tx) => {
202+
// If the function throws (rejects) the transaction will be rolled back
203+
const res = tx.promiseExecuteSql(
204+
'UPDATE sometable SET somecolumn = ? where somekey = ?',
205+
[0, 1]
206+
);
207+
// You must also return true to signal a correct transaction
208+
return true;
209+
});
210+
```
211+
198212
### Batch operation
199213

200214
Batch execution allows transactional execution of a set of commands

src/index.ts

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
/* eslint-disable no-shadow */
2-
/* eslint-disable no-undef */
31
import { NativeModules } from 'react-native';
42

53
declare global {
@@ -129,6 +127,19 @@ export interface Transaction {
129127
executeSql: (query: string, params?: any[]) => QueryResult;
130128
}
131129

130+
export interface AsyncTransaction {
131+
executeSql: (query: string, params?: any[]) => QueryResult;
132+
asyncExecuteSql: (
133+
query: string,
134+
params: any[] | undefined,
135+
cb: (res: QueryResult) => void
136+
) => void;
137+
promiseExecuteSql: (
138+
query: string,
139+
params: any[] | undefined
140+
) => Promise<QueryResult>;
141+
}
142+
132143
export interface PendingTransaction {
133144
start: () => void;
134145
}
@@ -141,9 +152,7 @@ interface ISQLite {
141152
status: 0 | 1;
142153
message?: string;
143154
};
144-
close: (
145-
dbName: string
146-
) => {
155+
close: (dbName: string) => {
147156
status: 0 | 1;
148157
message?: string;
149158
};
@@ -156,6 +165,10 @@ interface ISQLite {
156165
) => StatementResult;
157166
detach: (mainDbName: string, alias: string) => StatementResult;
158167
transaction: (dbName: string, fn: (tx: Transaction) => boolean) => void;
168+
asyncTransaction: (
169+
dbName: string,
170+
fn: (tx: AsyncTransaction) => Promise<boolean>
171+
) => void;
159172
executeSql: (
160173
dbName: string,
161174
query: string,
@@ -298,6 +311,70 @@ QuickSQLite.transaction = (
298311
startNextTransaction(dbName);
299312
};
300313

314+
QuickSQLite.asyncTransaction = (
315+
dbName: string,
316+
callback: (tx: AsyncTransaction) => Promise<boolean>
317+
) => {
318+
if (!locks[dbName]) {
319+
throw Error(`Quick SQLite Error: No lock found on db: ${dbName}`);
320+
}
321+
322+
// Local transaction context object implementation
323+
const executeSql = (query: string, params?: any[]): QueryResult => {
324+
return QuickSQLite.executeSql(dbName, query, params);
325+
};
326+
327+
const asyncExecuteSql = (
328+
query: string,
329+
params: any[] | undefined,
330+
cb: (res: QueryResult) => void
331+
) => {
332+
return QuickSQLite.asyncExecuteSql(dbName, query, params, cb);
333+
};
334+
335+
const promiseExecuteSql = (
336+
query: string,
337+
params: any[] | undefined
338+
): Promise<QueryResult> => {
339+
return new Promise((resolve, reject) => {
340+
QuickSQLite.asyncExecuteSql(dbName, query, params, (res) => {
341+
if (res.status) {
342+
reject(res);
343+
} else {
344+
resolve(res);
345+
}
346+
});
347+
});
348+
};
349+
350+
const tx: PendingTransaction = {
351+
start: async () => {
352+
try {
353+
QuickSQLite.executeSql(dbName, 'BEGIN TRANSACTION', null);
354+
const result = await callback({
355+
executeSql,
356+
asyncExecuteSql,
357+
promiseExecuteSql,
358+
});
359+
if (result === true) {
360+
QuickSQLite.executeSql(dbName, 'COMMIT', null);
361+
} else {
362+
QuickSQLite.executeSql(dbName, 'ROLLBACK', null);
363+
}
364+
} catch (e: any) {
365+
QuickSQLite.executeSql(dbName, 'ROLLBACK', null);
366+
throw e;
367+
} finally {
368+
locks[dbName].inProgress = false;
369+
startNextTransaction(dbName);
370+
}
371+
},
372+
};
373+
374+
locks[dbName].queue.push(tx);
375+
startNextTransaction(dbName);
376+
};
377+
301378
const startNextTransaction = (dbName: string) => {
302379
if (locks[dbName].inProgress) {
303380
// Transaction is already in process bail out
@@ -358,6 +435,7 @@ interface IDBConnection {
358435
) => void;
359436
detach: (alias: string, callback: (result: StatementResult) => void) => void;
360437
transaction: (fn: (tx: Transaction) => boolean) => void;
438+
asyncTransaction: (fn: (tx: AsyncTransaction) => Promise<boolean>) => void
361439
loadSqlFile: (
362440
location: string,
363441
callback: (result: FileLoadResult) => void
@@ -430,6 +508,9 @@ export const openDatabase = (
430508
transaction: (fn: (tx: Transaction) => boolean): void => {
431509
QuickSQLite.transaction(options.name, fn);
432510
},
511+
asyncTransaction: (fn: (tx: AsyncTransaction) => Promise<boolean>): void => {
512+
return QuickSQLite.asyncTransaction(options.name, fn);
513+
},
433514
close: (ok: any, fail: any) => {
434515
try {
435516
QuickSQLite.close(options.name);

0 commit comments

Comments
 (0)