Skip to content

Commit

Permalink
Brought back non host object API
Browse files Browse the repository at this point in the history
  • Loading branch information
ospfranco committed Aug 12, 2024
1 parent 7269256 commit 1dbc828
Show file tree
Hide file tree
Showing 18 changed files with 499 additions and 500 deletions.
196 changes: 95 additions & 101 deletions cpp/DBHostObject.cpp

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions cpp/PreparedStatementHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ jsi::Value PreparedStatementHostObject::get(jsi::Runtime &rt,
auto name = propNameID.utf8(rt);

if (name == "bind") {
return HOSTFN("bind", 1) {
return HOSTFN("bind") {
if (_stmt == nullptr) {
throw std::runtime_error("statement has been freed");
}
Expand All @@ -41,7 +41,7 @@ jsi::Value PreparedStatementHostObject::get(jsi::Runtime &rt,
}

if (name == "execute") {
return HOSTFN("execute", 1) {
return HOSTFN("execute") {
if (_stmt == nullptr) {
throw std::runtime_error("statement has been freed");
}
Expand Down
6 changes: 3 additions & 3 deletions cpp/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> invoker,
_crsqlite_path = std::string(crsqlite_path);
_invoker = invoker;

auto open = HOSTFN("open", 1) {
auto open = HOSTFN("open") {
jsi::Object options = args[0].asObject(rt);
std::string name = options.getProperty(rt, "name").asString(rt).utf8(rt);
std::string path = std::string(_base_path);
Expand Down Expand Up @@ -88,15 +88,15 @@ void install(jsi::Runtime &rt, std::shared_ptr<react::CallInvoker> invoker,
return jsi::Object::createFromHostObject(rt, db);
});

auto is_sqlcipher = HOSTFN("isSQLCipher", 0) {
auto is_sqlcipher = HOSTFN("isSQLCipher") {
#ifdef OP_SQLITE_USE_SQLCIPHER
return true;
#else
return false;
#endif
});

auto is_libsql = HOSTFN("isLibsql", 0) {
auto is_libsql = HOSTFN("isLibsql") {
#ifdef OP_SQLITE_USE_LIBSQL
return true;
#else
Expand Down
168 changes: 152 additions & 16 deletions cpp/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,7 @@ BridgeResult opsqlite_attach(std::string const &mainDBName,
std::string dbPath = opsqlite_get_db_path(databaseToAttach, docPath);
std::string statement = "ATTACH DATABASE '" + dbPath + "' AS " + alias;

BridgeResult result =
opsqlite_execute(mainDBName, statement, nullptr, nullptr, nullptr);
BridgeResult result = opsqlite_execute(mainDBName, statement, nullptr);

if (result.type == SQLiteError) {
return {
Expand All @@ -141,8 +140,7 @@ BridgeResult opsqlite_attach(std::string const &mainDBName,
BridgeResult opsqlite_detach(std::string const &mainDBName,
std::string const &alias) {
std::string statement = "DETACH DATABASE " + alias;
BridgeResult result =
opsqlite_execute(mainDBName, statement, nullptr, nullptr, nullptr);
BridgeResult result = opsqlite_execute(mainDBName, statement, nullptr);
if (result.type == SQLiteError) {
return BridgeResult{
.type = SQLiteError,
Expand Down Expand Up @@ -368,12 +366,152 @@ sqlite3_stmt *opsqlite_prepare_statement(std::string const &dbName,
return statement;
}

BridgeResult opsqlite_execute(std::string const &name, std::string const &query,
const std::vector<JSVariant> *params) {
check_db_open(name);

sqlite3 *db = dbMap[name];

sqlite3_stmt *statement;
const char *errorMessage;
const char *remainingStatement = nullptr;

bool isFailed = false;
int result, i, count, column_type;
std::string column_name, column_declared_type;
std::vector<std::string> column_names;
std::vector<std::vector<JSVariant>> rows;
std::vector<JSVariant> row;

do {
const char *queryStr =
remainingStatement == nullptr ? query.c_str() : remainingStatement;

int statementStatus =
sqlite3_prepare_v2(db, queryStr, -1, &statement, &remainingStatement);

if (statementStatus != SQLITE_OK) {
errorMessage = sqlite3_errmsg(db);
return {.type = SQLiteError,
.message =
"[op-sqlite] SQL prepare error: " + std::string(errorMessage),
.affectedRows = 0};
}

if (params != nullptr && params->size() > 0) {
opsqlite_bind_statement(statement, params);
}

bool isConsuming = true;

while (isConsuming) {
result = sqlite3_step(statement);

switch (result) {
case SQLITE_ROW:
i = 0;
row = std::vector<JSVariant>();
count = sqlite3_column_count(statement);

while (i < count) {
column_type = sqlite3_column_type(statement, i);
column_name = sqlite3_column_name(statement, i);
column_names.push_back(column_name);

switch (column_type) {

case SQLITE_INTEGER: {
/**
* It's not possible to send a int64_t in a jsi::Value because JS
* cannot represent the whole number range. Instead, we're sending a
* double, which can represent all integers up to 53 bits long,
* which is more than what was there before (a 32-bit int).
*
* See
* https://github.com/ospfranco/react-native-quick-sqlite/issues/16
* for more context.
*/
double column_value = sqlite3_column_double(statement, i);
row.push_back(JSVariant(column_value));
break;
}

case SQLITE_FLOAT: {
double column_value = sqlite3_column_double(statement, i);
row.push_back(JSVariant(column_value));
break;
}

case SQLITE_TEXT: {
const char *column_value = reinterpret_cast<const char *>(
sqlite3_column_text(statement, i));
int byteLen = sqlite3_column_bytes(statement, i);
// Specify length too; in case string contains NULL in the middle
// (which SQLite supports!)
row.push_back(JSVariant(std::string(column_value, byteLen)));
break;
}

case SQLITE_BLOB: {
int blob_size = sqlite3_column_bytes(statement, i);
const void *blob = sqlite3_column_blob(statement, i);
uint8_t *data = new uint8_t[blob_size];
memcpy(data, blob, blob_size);
row.push_back(
JSVariant(ArrayBuffer{.data = std::shared_ptr<uint8_t>{data},
.size = static_cast<size_t>(blob_size)}));
break;
}

case SQLITE_NULL:
// Intentionally left blank to switch to default case
default:
row.push_back(JSVariant(nullptr));
break;
}
i++;
}

rows.push_back(row);
break;

case SQLITE_DONE:
isConsuming = false;
break;

default:
isFailed = true;
isConsuming = false;
}
}

sqlite3_finalize(statement);
} while (remainingStatement != NULL && strcmp(remainingStatement, "") != 0 &&
!isFailed);

if (isFailed) {
const char *message = sqlite3_errmsg(db);
return {.type = SQLiteError,
.message =
"[op-sqlite] SQL execution error: " + std::string(message),
.affectedRows = 0,
.insertId = 0};
}

int changedRowCount = sqlite3_changes(db);
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
return {.type = SQLiteOk,
.affectedRows = changedRowCount,
.insertId = static_cast<double>(latestInsertRowId),
.rows = std::move(rows),
.column_names = std::move(column_names)};
}

/// Base execution function, returns HostObjects to the JS environment
BridgeResult
opsqlite_execute(std::string const &dbName, std::string const &query,
const std::vector<JSVariant> *params,
std::vector<DumbHostObject> *results,
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {
BridgeResult opsqlite_execute_host_objects(
std::string const &dbName, std::string const &query,
const std::vector<JSVariant> *params, std::vector<DumbHostObject> *results,
std::shared_ptr<std::vector<SmartHostObject>> metadatas) {

check_db_open(dbName);

Expand Down Expand Up @@ -879,16 +1017,14 @@ BatchResult opsqlite_execute_batch(std::string dbName,

try {
int affectedRows = 0;
opsqlite_execute(dbName, "BEGIN EXCLUSIVE TRANSACTION", nullptr, nullptr,
nullptr);
opsqlite_execute(dbName, "BEGIN EXCLUSIVE TRANSACTION", nullptr);
for (int i = 0; i < commandCount; i++) {
auto command = commands->at(i);
// We do not provide a datastructure to receive query data because we
// don't need/want to handle this results in a batch execution
auto result = opsqlite_execute(dbName, command.sql, command.params.get(),
nullptr, nullptr);
auto result = opsqlite_execute(dbName, command.sql, command.params.get());
if (result.type == SQLiteError) {
opsqlite_execute(dbName, "ROLLBACK", nullptr, nullptr, nullptr);
opsqlite_execute(dbName, "ROLLBACK", nullptr);
return BatchResult{
.type = SQLiteError,
.message = result.message,
Expand All @@ -897,14 +1033,14 @@ BatchResult opsqlite_execute_batch(std::string dbName,
affectedRows += result.affectedRows;
}
}
opsqlite_execute(dbName, "COMMIT", nullptr, nullptr, nullptr);
opsqlite_execute(dbName, "COMMIT", nullptr);
return BatchResult{
.type = SQLiteOk,
.affectedRows = affectedRows,
.commands = static_cast<int>(commandCount),
};
} catch (std::exception &exc) {
opsqlite_execute(dbName, "ROLLBACK", nullptr, nullptr, nullptr);
opsqlite_execute(dbName, "ROLLBACK", nullptr);
return BatchResult{
.type = SQLiteError,
.message = exc.what(),
Expand Down
5 changes: 4 additions & 1 deletion cpp/bridge.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,11 @@ BridgeResult opsqlite_attach(std::string const &mainDBName,
BridgeResult opsqlite_detach(std::string const &mainDBName,
std::string const &alias);

BridgeResult opsqlite_execute(std::string const &name, std::string const &query,
const std::vector<JSVariant> *params);

BridgeResult
opsqlite_execute(std::string const &dbName, std::string const &query,
opsqlite_execute_host_objects(std::string const &dbName, std::string const &query,
const std::vector<JSVariant> *params,
std::vector<DumbHostObject> *results,
std::shared_ptr<std::vector<SmartHostObject>> metadatas);
Expand Down
4 changes: 2 additions & 2 deletions cpp/macros.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#ifndef macros_h
#define macros_h

#define HOSTFN(name, basecount) \
#define HOSTFN(name) \
jsi::Function::createFromHostFunction( \
rt, \
jsi::PropNameID::forAscii(rt, name), \
basecount, \
0, \
[=](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value

#endif /* macros_h */
19 changes: 11 additions & 8 deletions cpp/types.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@
#include <memory>
#include <string>
#include <variant>
#include <vector>

struct ArrayBuffer {
std::shared_ptr<uint8_t> data;
size_t size;
};

using JSVariant = std::variant<nullptr_t, bool, int, double, long, long long,
std::string, ArrayBuffer>;

enum ResultType { SQLiteOk, SQLiteError };

Expand All @@ -12,6 +21,8 @@ struct BridgeResult {
std::string message;
int affectedRows;
double insertId;
std::vector<std::vector<JSVariant>> rows;
std::vector<std::string> column_names;
};

struct BatchResult {
Expand All @@ -21,14 +32,6 @@ struct BatchResult {
int commands;
};

struct ArrayBuffer {
std::shared_ptr<uint8_t> data;
size_t size;
};

using JSVariant = std::variant<nullptr_t, bool, int, double, long, long long,
std::string, ArrayBuffer>;

struct BatchArguments {
std::string sql;
std::shared_ptr<std::vector<JSVariant>> params;
Expand Down
Loading

0 comments on commit 1dbc828

Please sign in to comment.