Open vhakulinen opened 1 month ago
Thanks for all the input! I'm going to try improve the codebase and implement most of this in the next weeks! 🚀
We might plan on doing a greater API redesign anyway, so some of these points might already be covered by other changes
💯
Oh, another quite thing: testing support!
Running tests on another runtime (i.e. not hermes) is PITA, mainly because the current API diverges from the "normal" so much. With better API testing would be easier (because you can implement better mocks / adapters for other runtimes).
At the moment I'm using the following mock, but only does the bare minimum. This mock uses better-sqlite3
node package, but I suppose the same could be adapted to other runtimes too. Because quick-sqlite doesn't support named parameters, and better-sqlite3 doesnt support the ?NNNN
syntax, I can't use all our queries in our tests at the moment.
import BetterDB from 'better-sqlite3';
import { QuickSQLiteConnection } from 'react-native-quick-sqlite';
/**
* Partially adapts better-sqlite3 to the quick-sqlite interface.
*/
export const adaptBetterSqlite = (conn: BetterDB.Database): QuickSQLiteConnection => {
const execute: QuickSQLiteConnection['execute'] = (query, params) => {
const stmt = conn.prepare(query);
if (stmt.reader) {
const rows = stmt.all(params || []);
return {
rowsAffected: rows.length,
rows: {
length: rows.length,
_array: rows,
item: (i: number) => rows[i],
}
};
} else {
const rows = stmt.run(params || []);
return {
rowsAffected: rows.changes,
rows: {
length: 0,
_array: [],
item: (i: number) => [][i],
}
};
}
};
const executeAsync: QuickSQLiteConnection['executeAsync'] = (query: string, params?: unknown[]) => {
return Promise.resolve(execute(query, params));
};
const executeBatch: QuickSQLiteConnection['executeBatch'] = tuples => {
let n = 0;
for (const tuple of tuples) {
const stmt = conn.prepare(tuple[0]);
const res = stmt.run(tuple[1] || []);
n += res.changes;
}
return { rowsAffected: n };
};
const executeBatchAsync: QuickSQLiteConnection['executeBatchAsync'] = tuples => {
return Promise.resolve(executeBatch(tuples));
};
const transaction: QuickSQLiteConnection['transaction'] = async fn => {
const commit = () => {
return execute('COMMIT');
};
const rollback = () => {
return execute('ROLLBACK');
};
try {
execute('BEGIN TRANSACTION');
await fn({
commit,
execute,
executeAsync,
rollback,
});
commit();
} catch (err) {
rollback();
throw err;
}
};
const notImplemented = () => {
throw new Error('not implemented');
};
return {
execute,
executeAsync,
executeBatch,
executeBatchAsync,
transaction,
close: notImplemented,
delete: notImplemented,
detach: notImplemented,
attach: notImplemented,
loadFile: notImplemented,
loadFileAsync: notImplemented,
};
};
Another performance related issue: cancellation.
Say you have a view that loads data for ~0.5s from the database, but the user can press a button to change the query parameters (for example, time frame) faster that the query executes. This will currently cause the queries to queue up and delay the UI quite a bit.
Responding to https://github.com/margelo/react-native-quick-sqlite/pull/55#issuecomment-2391285662, since bloating that PR with extra hopes and dreams wouldn't be productive.
W.r.t. Concurrency. It would be nice to be able to have a read only connection pool and the one connection (or another connection pool) for writing. This could be a huge performance win for more complex application that has heavy database usage.
Having studied this code base (and having had my own, long abandoned venture to alternative, sqlite-jsi), I have some idea of the effort required to implement these things. While I dont have the time / resources to work on this myself (nor am I expecting anyone to do the work either!), if someone is up for the task I am more than happy to chat about the ideas / design / implementation details.
As the sqlite-jsi repo might suggest, I have interest in having a solid sqlite library for react-native - I just lack the time to work on it.