Skip to content

Commit

Permalink
feat: refreshSchema (#56)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
Chriztiaan authored Nov 4, 2024
1 parent eda627e commit 23bcb1d
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .changeset/angry-carrots-lay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@journeyapps/react-native-quick-sqlite": minor
---

Added `refreshSchema()` to bindings. Will cause all connections to be aware of a schema change.
16 changes: 16 additions & 0 deletions cpp/ConnectionPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,22 @@ void ConnectionPool::closeAll() {
}
}

std::future<void> ConnectionPool::refreshSchema() {
std::vector<std::future<void>> futures;

futures.push_back(writeConnection.refreshSchema());

for (unsigned int i = 0; i < maxReads; i++) {
futures.push_back(readConnections[i]->refreshSchema());
}

return std::async(std::launch::async, [futures = std::move(futures)]() mutable {
for (auto& future : futures) {
future.get();
}
});
}

SQLiteOPResult ConnectionPool::attachDatabase(std::string const dbFileName,
std::string const docPath,
std::string const alias) {
Expand Down
6 changes: 6 additions & 0 deletions cpp/ConnectionPool.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "sqlite3.h"
#include <string>
#include <vector>
#include <future>

#ifndef ConnectionPool_h
#define ConnectionPool_h
Expand Down Expand Up @@ -126,6 +127,11 @@ class ConnectionPool {
*/
void closeAll();

/**
* Refreshes the schema for all connections.
*/
std::future<void> refreshSchema();

/**
* Attaches another database to all connections
*/
Expand Down
19 changes: 19 additions & 0 deletions cpp/ConnectionState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,25 @@ bool ConnectionState::matchesLock(const ConnectionLockId &lockId) {

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

std::future<void> ConnectionState::refreshSchema() {
auto promise = std::make_shared<std::promise<void>>();
auto future = promise->get_future();

queueWork([promise](sqlite3* db) {
try {
int rc = sqlite3_exec(db, "PRAGMA table_info('sqlite_master')", nullptr, nullptr, nullptr);
if (rc != SQLITE_OK) {
throw std::runtime_error("Failed to refresh schema");
}
promise->set_value();
} catch (...) {
promise->set_exception(std::current_exception());
}
});

return future;
}

void ConnectionState::close() {
waitFinished();
// So that the thread can stop (if not already)
Expand Down
2 changes: 2 additions & 0 deletions cpp/ConnectionState.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <string>
#include <thread>
#include <vector>
#include <future>

#ifndef ConnectionState_h
#define ConnectionState_h
Expand Down Expand Up @@ -41,6 +42,7 @@ class ConnectionState {
bool matchesLock(const ConnectionLockId &lockId);
bool isEmptyLock();

std::future<void> refreshSchema();
void close();
void queueWork(std::function<void(sqlite3 *)> task);

Expand Down
46 changes: 46 additions & 0 deletions cpp/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,51 @@ void osp::install(jsi::Runtime &rt,
return {};
});

auto refreshSchema = HOSTFN("refreshSchema", 1) {
if (count == 0) {
throw jsi::JSError(rt, "[react-native-quick-sqlite][refreshSchema] database name is required");
}

if (!args[0].isString()) {
throw jsi::JSError(rt, "[react-native-quick-sqlite][refreshSchema] database name must be a string");
}

std::string dbName = args[0].asString(rt).utf8(rt);

auto promiseCtr = rt.global().getPropertyAsFunction(rt, "Promise");
auto jsPromise = promiseCtr.callAsConstructor(rt, HOSTFN("executor", 2) {
auto resolve = std::make_shared<jsi::Value>(rt, args[0]);
auto reject = std::make_shared<jsi::Value>(rt, args[1]);

try {
auto future = sqliteRefreshSchema(dbName);

// Waiting for the future to complete in a separate thread
std::thread([future = std::move(future), &rt, resolve, reject]() mutable {
try {
future.get();
invoker->invokeAsync([&rt, resolve] {
resolve->asObject(rt).asFunction(rt).call(rt);
});
} catch (const std::exception& exc) {
invoker->invokeAsync([&rt, reject, exc] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(rt, jsi::String::createFromUtf8(rt, exc.what()));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
}
}).detach();

} catch (const std::exception& exc) {
invoker->invokeAsync([&rt, &exc] { jsi::JSError(rt, exc.what()); });
}

return {};
}));

return jsPromise;
});

auto executeInContext = HOSTFN("executeInContext", 3) {
if (count < 4) {
throw jsi::JSError(rt,
Expand Down Expand Up @@ -500,6 +545,7 @@ void osp::install(jsi::Runtime &rt,
module.setProperty(rt, "releaseLock", move(releaseLock));
module.setProperty(rt, "executeInContext", move(executeInContext));
module.setProperty(rt, "close", move(close));
module.setProperty(rt, "refreshSchema", move(refreshSchema));

module.setProperty(rt, "attach", move(attach));
module.setProperty(rt, "detach", move(detach));
Expand Down
11 changes: 11 additions & 0 deletions cpp/sqliteBridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,17 @@ sqliteOpenDb(string const dbName, string const docPath,
};
}

std::future<void> sqliteRefreshSchema(const std::string& dbName) {
if (dbMap.count(dbName) == 0) {
std::promise<void> promise;
promise.set_value();
return promise.get_future();
}

ConnectionPool* connection = dbMap[dbName];
return connection->refreshSchema();
}

SQLiteOPResult sqliteCloseDb(string const dbName) {
if (dbMap.count(dbName) == 0) {
return generateNotOpenResult(dbName);
Expand Down
3 changes: 3 additions & 0 deletions cpp/sqliteBridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "JSIHelper.h"
#include "sqlite3.h"
#include <vector>
#include <future>

#ifndef SQLiteBridge_h
#define SQLiteBridge_h
Expand All @@ -33,6 +34,8 @@ sqliteOpenDb(std::string const dbName, std::string const docPath,
const TransactionCallbackPayload *event),
uint32_t numReadConnections);

std::future<void> sqliteRefreshSchema(const std::string& dbName);

SQLiteOPResult sqliteCloseDb(string const dbName);

void sqliteCloseAll();
Expand Down
1 change: 1 addition & 0 deletions src/setup-open.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ export function setupOpen(QuickSQLite: ISQLite) {
// Return the concurrent connection object
return {
close: () => QuickSQLite.close(dbName),
refreshSchema: () => QuickSQLite.refreshSchema(dbName),
execute: (sql: string, args?: any[]) => writeLock((context) => context.execute(sql, args)),
readLock,
readTransaction: async <T>(callback: (context: TransactionContext) => Promise<T>, options?: LockOptions) =>
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ export interface ISQLite {
open: Open;
close: (dbName: string) => void;
delete: (dbName: string, location?: string) => void;
refreshSchema: (dbName: string) => Promise<void>;

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

export type QuickSQLiteConnection = {
close: () => void;
refreshSchema: () => Promise<void>;
execute: (sql: string, args?: any[]) => Promise<QueryResult>;
readLock: <T>(callback: (context: LockContext) => Promise<T>, options?: LockOptions) => Promise<T>;
readTransaction: <T>(callback: (context: TransactionContext) => Promise<T>, options?: LockOptions) => Promise<T>;
Expand Down

0 comments on commit 23bcb1d

Please sign in to comment.