Skip to content

Commit

Permalink
explicitly catch runtime_error to fix broken RN error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
ospfranco committed Dec 15, 2024
1 parent c229cd5 commit 34cb771
Show file tree
Hide file tree
Showing 6 changed files with 106 additions and 69 deletions.
22 changes: 11 additions & 11 deletions android/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ project(OPSQLite)
cmake_minimum_required(VERSION 3.9.0)

set (PACKAGE_NAME "op-sqlite")
set (CMAKE_VERBOSE_MAKEFILE ON)
set (CMAKE_CXX_STANDARD 20)
set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)
#set (CMAKE_CXX_STANDARD_REQUIRED ON)
#set (CMAKE_CXX_EXTENSIONS OFF)
#set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -fexceptions -frtti -stdlib=libc++")
#set (BUILD_DIR ${CMAKE_SOURCE_DIR}/build)

include_directories(
../cpp
../cpp/sqlcipher
../cpp/libsql
# ../example/c_sources
)

add_definitions(
Expand Down Expand Up @@ -61,13 +61,13 @@ if (USE_SQLITE_VEC)
-DOP_SQLITE_USE_SQLITE_VEC=1
)
endif()

set_target_properties(
${PACKAGE_NAME} PROPERTIES
CXX_STANDARD 20
CXX_EXTENSIONS OFF
POSITION_INDEPENDENT_CODE ON
)
#
#set_target_properties(
# ${PACKAGE_NAME} PROPERTIES
# CXX_STANDARD 20
# CXX_EXTENSIONS OFF
# POSITION_INDEPENDENT_CODE ON
#)

find_package(ReactAndroid REQUIRED CONFIG)
find_package(fbjni REQUIRED CONFIG)
Expand Down
3 changes: 2 additions & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ android {

buildFeatures {
prefab true
prefabPublishing true
}

defaultConfig {
Expand Down Expand Up @@ -177,7 +178,7 @@ android {
tokenizersHeaderPath = "../c_sources/tokenizers.h"
}

cppFlags "-O2", "-fexceptions", "-DONANDROID"
cppFlags "-O2 -frtti -fexceptions -Wall -fstack-protector-all"
abiFilters 'x86', 'x86_64', 'armeabi-v7a', 'arm64-v8a'
arguments "-DANDROID_STL=c++_shared",
"-DSQLITE_FLAGS='$sqliteFlags'",
Expand Down
50 changes: 45 additions & 5 deletions cpp/DBHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,14 @@ void DBHostObject::create_jsi_functions() {
auto jsiResult = create_raw_result(rt, status, &results);
resolve->asObject(rt).asFunction(rt).call(rt, std::move(jsiResult));
});

} catch (std::runtime_error &e) {
auto what = e.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
} catch (std::exception &exc) {
auto what = exc.what();
invoker->invokeAsync([&rt, what = std::move(what), reject] {
Expand Down Expand Up @@ -381,7 +388,17 @@ void DBHostObject::create_jsi_functions() {
auto jsiResult = create_js_rows(rt, status);
resolve->asObject(rt).asFunction(rt).call(rt, std::move(jsiResult));
});

// On Android RN is broken and does not correctly match runtime_error
// to the generic exception We have to explicitly catch it
// https://github.com/facebook/react-native/issues/48027
} catch (std::runtime_error &e) {
auto what = e.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
} catch (std::exception &exc) {
auto what = exc.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
Expand Down Expand Up @@ -442,7 +459,14 @@ void DBHostObject::create_jsi_functions() {
resolve->asObject(rt).asFunction(rt).call(rt,
std::move(jsiResult));
});

} catch (std::runtime_error &e) {
auto what = e.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
} catch (std::exception &exc) {
auto what = exc.what();
invoker->invokeAsync([&rt, what = std::move(what), reject] {
Expand All @@ -463,7 +487,7 @@ void DBHostObject::create_jsi_functions() {
});

auto execute_batch = HOSTFN("executeBatch") {
if (sizeof(args) < 1) {
if (count < 1) {
throw std::runtime_error(
"[op-sqlite][executeAsyncBatch] Incorrect parameter count");
return {};
Expand Down Expand Up @@ -511,6 +535,14 @@ void DBHostObject::create_jsi_functions() {
jsi::Value(batchResult.affectedRows));
resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
});
} catch (std::runtime_error &e) {
auto what = e.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
} catch (std::exception &exc) {
auto what = exc.what();
invoker->invokeAsync([&rt, what = std::move(what), reject] {
Expand Down Expand Up @@ -565,9 +597,17 @@ void DBHostObject::create_jsi_functions() {
res.setProperty(rt, "commands", jsi::Value(result.commands));
resolve->asObject(rt).asFunction(rt).call(rt, std::move(res));
});
} catch (std::runtime_error &e) {
auto what = e.what();
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
reject->asObject(rt).asFunction(rt).call(rt, error);
});
} catch (std::exception &exc) {
auto what = exc.what();
invoker->invokeAsync([&rt, what = std::move(what), reject] {
invoker->invokeAsync([&rt, what = std::string(what), reject] {
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
auto error = errorCtr.callAsConstructor(
rt, jsi::String::createFromAscii(rt, what));
Expand Down
93 changes: 45 additions & 48 deletions cpp/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "utils.h"
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
#include <variant>

Expand All @@ -30,27 +31,29 @@ inline void opsqlite_bind_statement(sqlite3_stmt *statement,
int stmt_index = ii + 1;
JSVariant value = values->at(ii);

if (std::holds_alternative<bool>(value)) {
sqlite3_bind_int(statement, stmt_index,
static_cast<int>(std::get<bool>(value)));
} else if (std::holds_alternative<int>(value)) {
sqlite3_bind_int(statement, stmt_index, std::get<int>(value));
} else if (std::holds_alternative<long long>(value)) {
sqlite3_bind_double(statement, stmt_index,
static_cast<double>(std::get<long long>(value)));
} else if (std::holds_alternative<double>(value)) {
sqlite3_bind_double(statement, stmt_index, std::get<double>(value));
} else if (std::holds_alternative<std::string>(value)) {
std::string str = std::get<std::string>(value);
sqlite3_bind_text(statement, stmt_index, str.c_str(),
static_cast<int>(str.length()), SQLITE_TRANSIENT);
} else if (std::holds_alternative<ArrayBuffer>(value)) {
ArrayBuffer buffer = std::get<ArrayBuffer>(value);
sqlite3_bind_blob(statement, stmt_index, buffer.data.get(),
static_cast<int>(buffer.size), SQLITE_TRANSIENT);
} else {
sqlite3_bind_null(statement, stmt_index);
}
std::visit(
[&](auto &&v) {
using T = std::decay_t<decltype(v)>;

if constexpr (std::is_same_v<T, bool>) {
sqlite3_bind_int(statement, stmt_index, static_cast<int>(v));
} else if constexpr (std::is_same_v<T, int>) {
sqlite3_bind_int(statement, stmt_index, v);
} else if constexpr (std::is_same_v<T, long long>) {
sqlite3_bind_double(statement, stmt_index, static_cast<double>(v));
} else if constexpr (std::is_same_v<T, double>) {
sqlite3_bind_double(statement, stmt_index, v);
} else if constexpr (std::is_same_v<T, std::string>) {
sqlite3_bind_text(statement, stmt_index, v.c_str(),
static_cast<int>(v.length()), SQLITE_TRANSIENT);
} else if constexpr (std::is_same_v<T, ArrayBuffer>) {
sqlite3_bind_blob(statement, stmt_index, v.data.get(),
static_cast<int>(v.size), SQLITE_TRANSIENT);
} else {
sqlite3_bind_null(statement, stmt_index);
}
},
value);
}
}

Expand All @@ -68,24 +71,23 @@ std::string opsqlite_get_db_path(std::string const &db_name,
}

#ifdef OP_SQLITE_USE_SQLCIPHER
BridgeResult opsqlite_open(std::string const &name,
std::string const &last_path,
BridgeResult opsqlite_open(std::string const &name, std::string const &path,
std::string const &crsqlite_path,
std::string const &sqlite_vec_path,
std::string const &encryption_key) {
#else
sqlite3 *opsqlite_open(std::string const &name, std::string const &last_path,
sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
[[maybe_unused]] std::string const &crsqlite_path,
std::string const &sqlite_vec_path) {
[[maybe_unused]] std::string const &sqlite_vec_path) {
#endif
std::string final_path = opsqlite_get_db_path(name, last_path);
std::string final_path = opsqlite_get_db_path(name, path);
char *errMsg;
sqlite3 *db;

int sqlOpenFlags =
int flags =
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;

sqlite3 *db;

int status = sqlite3_open_v2(final_path.c_str(), &db, sqlOpenFlags, nullptr);
int status = sqlite3_open_v2(final_path.c_str(), &db, flags, nullptr);

if (status != SQLITE_OK) {
throw std::runtime_error(sqlite3_errmsg(db));
Expand All @@ -97,8 +99,6 @@ sqlite3 *opsqlite_open(std::string const &name, std::string const &last_path,

sqlite3_enable_load_extension(db, 1);

char *errMsg;

#ifdef OP_SQLITE_USE_CRSQLITE
const char *crsqliteEntryPoint = "sqlite3_crsqlite_init";

Expand Down Expand Up @@ -138,10 +138,8 @@ void opsqlite_attach(sqlite3 *db, std::string const &main_db_name,
std::string const &doc_path,
std::string const &secondary_db_name,
std::string const &alias) {
std::string secondary_db_path =
opsqlite_get_db_path(secondary_db_name, doc_path);
std::string statement =
"ATTACH DATABASE '" + secondary_db_path + "' AS " + alias;
auto secondary_db_path = opsqlite_get_db_path(secondary_db_name, doc_path);
auto statement = "ATTACH DATABASE '" + secondary_db_path + "' AS " + alias;

opsqlite_execute(db, statement, nullptr);
}
Expand Down Expand Up @@ -426,7 +424,7 @@ BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,

if (has_failed) {
const char *message = sqlite3_errmsg(db);
throw std::runtime_error("[op-sqlite] SQL execution error: " +
throw std::runtime_error("[op-sqlite] statement execution error: " +
std::string(message));
}

Expand Down Expand Up @@ -619,9 +617,9 @@ opsqlite_execute_raw(sqlite3 *db, std::string const &query,

if (statementStatus != SQLITE_OK) {
const char *message = sqlite3_errmsg(db);
throw std::runtime_error("[op-sqlite] SQL statement error:" +
std::to_string(statementStatus) +
" description:" + std::string(message));
throw std::runtime_error(
"[op-sqlite] SQL statement error:" + std::to_string(statementStatus) +
" description:" + std::string(message));
}

// The statement did not fail to parse but there is nothing to do, just
Expand Down Expand Up @@ -714,15 +712,15 @@ opsqlite_execute_raw(sqlite3 *db, std::string const &query,
strcmp(remainingStatement, "") != 0 && !isFailed);

if (isFailed) {
throw std::runtime_error("[op-sqlite] SQLite error code: " + std::to_string(step) +
", description: " + std::string(errorMessage));
throw std::runtime_error(
"[op-sqlite] SQLite error code: " + std::to_string(step) +
", description: " + std::string(errorMessage));
}

int changedRowCount = sqlite3_changes(db);
long long latestInsertRowId = sqlite3_last_insert_rowid(db);

return {
.affectedRows = changedRowCount,
return {.affectedRows = changedRowCount,
.insertId = static_cast<double>(latestInsertRowId)};
}

Expand All @@ -738,7 +736,7 @@ std::string operation_to_string(int operation_type) {
return "UPDATE";

default:
throw std::invalid_argument("Unknown SQLite operation on hook");
throw std::runtime_error("Unknown SQLite operation on hook");
}
}

Expand Down Expand Up @@ -890,10 +888,9 @@ BatchResult opsqlite_execute_batch(sqlite3 *db,
std::vector<BatchArguments> *commands) {
size_t commandCount = commands->size();
if (commandCount <= 0) {
throw std::invalid_argument("No SQL commands provided");
throw std::runtime_error("No SQL commands provided");
}


int affectedRows = 0;
opsqlite_execute(db, "BEGIN EXCLUSIVE TRANSACTION", nullptr);
for (int i = 0; i < commandCount; i++) {
Expand All @@ -903,7 +900,7 @@ BatchResult opsqlite_execute_batch(sqlite3 *db,
try {
auto result = opsqlite_execute(db, command.sql, command.params.get());
affectedRows += result.affectedRows;
} catch(std::exception &exc) {
} catch (std::exception &exc) {
opsqlite_execute(db, "ROLLBACK", nullptr);
throw exc;
}
Expand Down
2 changes: 1 addition & 1 deletion cpp/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ std::string opsqlite_get_db_path(std::string const &db_name,
std::string const &location);

#ifdef OP_SQLITE_USE_SQLCIPHER
BridgeResult opsqlite_open(std::string const &dbName, std::string const &dbPath,
BridgeResult opsqlite_open(std::string const &dbName, std::string const &path,
std::string const &crsqlite_path,
std::string const &sqlite_vec_path,
std::string const &encryption_key);
Expand Down
5 changes: 2 additions & 3 deletions cpp/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ JSVariant toVariant(jsi::Runtime &rt, const jsi::Value &value) {
auto obj = value.asObject(rt);

if (!obj.isArrayBuffer(rt)) {
throw std::invalid_argument(
throw std::runtime_error(
"Object is not an ArrayBuffer, cannot bind to SQLite");
}

Expand All @@ -74,8 +74,7 @@ JSVariant toVariant(jsi::Runtime &rt, const jsi::Value &value) {
.size = buffer.size(rt)});

} else {
throw std::invalid_argument(
"Cannot convert JSI value to C++ Variant value");
throw std::runtime_error("Cannot convert JSI value to C++ Variant value");
}
}

Expand Down

0 comments on commit 34cb771

Please sign in to comment.