Skip to content

Commit 23bcb1d

Browse files
authored
feat: refreshSchema (#56)
* Added refreshSchema to bindings. Will cause all connections to be aware of a schema change. * Return for binding function. * Formatting. * Changing refreshSchema to return a promise. * Fixed header defitinion. * Minor changeset change.
1 parent eda627e commit 23bcb1d

File tree

10 files changed

+111
-0
lines changed

10 files changed

+111
-0
lines changed

.changeset/angry-carrots-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@journeyapps/react-native-quick-sqlite": minor
3+
---
4+
5+
Added `refreshSchema()` to bindings. Will cause all connections to be aware of a schema change.

cpp/ConnectionPool.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,22 @@ void ConnectionPool::closeAll() {
168168
}
169169
}
170170

171+
std::future<void> ConnectionPool::refreshSchema() {
172+
std::vector<std::future<void>> futures;
173+
174+
futures.push_back(writeConnection.refreshSchema());
175+
176+
for (unsigned int i = 0; i < maxReads; i++) {
177+
futures.push_back(readConnections[i]->refreshSchema());
178+
}
179+
180+
return std::async(std::launch::async, [futures = std::move(futures)]() mutable {
181+
for (auto& future : futures) {
182+
future.get();
183+
}
184+
});
185+
}
186+
171187
SQLiteOPResult ConnectionPool::attachDatabase(std::string const dbFileName,
172188
std::string const docPath,
173189
std::string const alias) {

cpp/ConnectionPool.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include "sqlite3.h"
44
#include <string>
55
#include <vector>
6+
#include <future>
67

78
#ifndef ConnectionPool_h
89
#define ConnectionPool_h
@@ -126,6 +127,11 @@ class ConnectionPool {
126127
*/
127128
void closeAll();
128129

130+
/**
131+
* Refreshes the schema for all connections.
132+
*/
133+
std::future<void> refreshSchema();
134+
129135
/**
130136
* Attaches another database to all connections
131137
*/

cpp/ConnectionState.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,25 @@ bool ConnectionState::matchesLock(const ConnectionLockId &lockId) {
4444

4545
bool ConnectionState::isEmptyLock() { return _currentLockId == EMPTY_LOCK_ID; }
4646

47+
std::future<void> ConnectionState::refreshSchema() {
48+
auto promise = std::make_shared<std::promise<void>>();
49+
auto future = promise->get_future();
50+
51+
queueWork([promise](sqlite3* db) {
52+
try {
53+
int rc = sqlite3_exec(db, "PRAGMA table_info('sqlite_master')", nullptr, nullptr, nullptr);
54+
if (rc != SQLITE_OK) {
55+
throw std::runtime_error("Failed to refresh schema");
56+
}
57+
promise->set_value();
58+
} catch (...) {
59+
promise->set_exception(std::current_exception());
60+
}
61+
});
62+
63+
return future;
64+
}
65+
4766
void ConnectionState::close() {
4867
waitFinished();
4968
// So that the thread can stop (if not already)

cpp/ConnectionState.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <string>
77
#include <thread>
88
#include <vector>
9+
#include <future>
910

1011
#ifndef ConnectionState_h
1112
#define ConnectionState_h
@@ -41,6 +42,7 @@ class ConnectionState {
4142
bool matchesLock(const ConnectionLockId &lockId);
4243
bool isEmptyLock();
4344

45+
std::future<void> refreshSchema();
4446
void close();
4547
void queueWork(std::function<void(sqlite3 *)> task);
4648

cpp/bindings.cpp

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,51 @@ void osp::install(jsi::Runtime &rt,
286286
return {};
287287
});
288288

289+
auto refreshSchema = HOSTFN("refreshSchema", 1) {
290+
if (count == 0) {
291+
throw jsi::JSError(rt, "[react-native-quick-sqlite][refreshSchema] database name is required");
292+
}
293+
294+
if (!args[0].isString()) {
295+
throw jsi::JSError(rt, "[react-native-quick-sqlite][refreshSchema] database name must be a string");
296+
}
297+
298+
std::string dbName = args[0].asString(rt).utf8(rt);
299+
300+
auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
301+
auto jsPromise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
302+
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
303+
auto reject = std::make_shared<jsi::Value>(rt, args[1]);
304+
305+
try {
306+
auto future = sqliteRefreshSchema(dbName);
307+
308+
// Waiting for the future to complete in a separate thread
309+
std::thread([future = std::move(future), &rt, resolve, reject]() mutable {
310+
try {
311+
future.get();
312+
invoker->invokeAsync([&rt, resolve] {
313+
resolve->asObject(rt).asFunction(rt).call(rt);
314+
});
315+
} catch (const std::exception& exc) {
316+
invoker->invokeAsync([&rt, reject, exc] {
317+
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
318+
auto error = errorCtr.callAsConstructor(rt, jsi::String::createFromUtf8(rt, exc.what()));
319+
reject->asObject(rt).asFunction(rt).call(rt, error);
320+
});
321+
}
322+
}).detach();
323+
324+
} catch (const std::exception& exc) {
325+
invoker->invokeAsync([&rt, &exc] { jsi::JSError(rt, exc.what()); });
326+
}
327+
328+
return {};
329+
}));
330+
331+
return jsPromise;
332+
});
333+
289334
auto executeInContext = HOSTFN("executeInContext", 3) {
290335
if (count < 4) {
291336
throw jsi::JSError(rt,
@@ -500,6 +545,7 @@ void osp::install(jsi::Runtime &rt,
500545
module.setProperty(rt, "releaseLock", move(releaseLock));
501546
module.setProperty(rt, "executeInContext", move(executeInContext));
502547
module.setProperty(rt, "close", move(close));
548+
module.setProperty(rt, "refreshSchema", move(refreshSchema));
503549

504550
module.setProperty(rt, "attach", move(attach));
505551
module.setProperty(rt, "detach", move(detach));

cpp/sqliteBridge.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,17 @@ sqliteOpenDb(string const dbName, string const docPath,
6060
};
6161
}
6262

63+
std::future<void> sqliteRefreshSchema(const std::string& dbName) {
64+
if (dbMap.count(dbName) == 0) {
65+
std::promise<void> promise;
66+
promise.set_value();
67+
return promise.get_future();
68+
}
69+
70+
ConnectionPool* connection = dbMap[dbName];
71+
return connection->refreshSchema();
72+
}
73+
6374
SQLiteOPResult sqliteCloseDb(string const dbName) {
6475
if (dbMap.count(dbName) == 0) {
6576
return generateNotOpenResult(dbName);

cpp/sqliteBridge.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "JSIHelper.h"
1212
#include "sqlite3.h"
1313
#include <vector>
14+
#include <future>
1415

1516
#ifndef SQLiteBridge_h
1617
#define SQLiteBridge_h
@@ -33,6 +34,8 @@ sqliteOpenDb(std::string const dbName, std::string const docPath,
3334
const TransactionCallbackPayload *event),
3435
uint32_t numReadConnections);
3536

37+
std::future<void> sqliteRefreshSchema(const std::string& dbName);
38+
3639
SQLiteOPResult sqliteCloseDb(string const dbName);
3740

3841
void sqliteCloseAll();

src/setup-open.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,6 +225,7 @@ export function setupOpen(QuickSQLite: ISQLite) {
225225
// Return the concurrent connection object
226226
return {
227227
close: () => QuickSQLite.close(dbName),
228+
refreshSchema: () => QuickSQLite.refreshSchema(dbName),
228229
execute: (sql: string, args?: any[]) => writeLock((context) => context.execute(sql, args)),
229230
readLock,
230231
readTransaction: async <T>(callback: (context: TransactionContext) => Promise<T>, options?: LockOptions) =>

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export interface ISQLite {
124124
open: Open;
125125
close: (dbName: string) => void;
126126
delete: (dbName: string, location?: string) => void;
127+
refreshSchema: (dbName: string) => Promise<void>;
127128

128129
requestLock: (dbName: string, id: ContextLockID, type: ConcurrentLockType) => QueryResult;
129130
releaseLock(dbName: string, id: ContextLockID): void;
@@ -151,6 +152,7 @@ export interface TransactionContext extends LockContext {
151152

152153
export type QuickSQLiteConnection = {
153154
close: () => void;
155+
refreshSchema: () => Promise<void>;
154156
execute: (sql: string, args?: any[]) => Promise<QueryResult>;
155157
readLock: <T>(callback: (context: LockContext) => Promise<T>, options?: LockOptions) => Promise<T>;
156158
readTransaction: <T>(callback: (context: TransactionContext) => Promise<T>, options?: LockOptions) => Promise<T>;

0 commit comments

Comments
 (0)