Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ jobs:
run: |
TAG_NAME=${GITHUB_REF#refs/tags/}
jq --arg version "$TAG_NAME" '.version = $version' package.json > tmp.$$.json && mv tmp.$$.json package.json
- run: npm publish
- name: Publish to npm
run: |
if echo "$GITHUB_REF_NAME" | grep -q -- '-beta\|-alpha\|-rc'; then
npm publish --tag beta
else
npm publish
fi
- name: Create Github Release
uses: elgohr/Github-Release-Action@v5
env:
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
![benchmark](benchmark2.jpg)
![benchmark](benchmark3.jpg)

Created by [@ospfranco](https://twitter.com/ospfranco). **Please consider sponsoring!**.

Expand Down
Binary file removed benchmark2.jpg
Binary file not shown.
Binary file added benchmark3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion cpp/DBHostObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ void DBHostObject::create_jsi_functions(jsi::Runtime &rt) {
std::string query = args[0].asString(rt).utf8(rt);
std::vector<JSVariant> params;

if (count == 2) {
if (count == 2 && !args[1].isNull() && !args[1].isUndefined()) {
params = to_variant_vec(rt, args[1]);
}
#ifdef OP_SQLITE_USE_LIBSQL
Expand Down
36 changes: 22 additions & 14 deletions cpp/bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "utils.hpp"
#include <filesystem>
#include <iostream>
#include <sqlite3.h>
#include <sstream>
#include <stdexcept>
#include <unordered_map>
Expand Down Expand Up @@ -332,7 +333,7 @@
if (isFailed) {
throw std::runtime_error(
"[op-sqlite] SQLite code: " + std::to_string(result) +
" execution error: " + std::string(errorMessage));

Check warning on line 336 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 336 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 336 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-sqlcipher

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 336 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]
}

int changedRowCount = sqlite3_changes(db);
Expand Down Expand Up @@ -376,8 +377,9 @@
std::string column_name, column_declared_type;
std::vector<std::string> column_names;
std::vector<std::vector<JSVariant>> rows;
rows.reserve(20);
std::vector<JSVariant> row;
int changedRowCount = 0;
long long latestInsertRowId = 0;

do {
const char *query_str =
Expand All @@ -402,22 +404,33 @@
opsqlite_bind_statement(statement, params);
}

// sqlite3_column_count is the correct signal: it's non-zero for any
// statement that can return rows (SELECT, and write statements with
// RETURNING), regardless of sqlite3_stmt_readonly.
column_names.clear();
column_count = sqlite3_column_count(statement);
column_names.reserve(column_count);
if (column_count > 0) {
column_names.reserve(column_count);
for (int i = 0; i < column_count; i++) {
column_name = sqlite3_column_name(statement, i);
column_names.emplace_back(column_name);
}
}

bool is_consuming_rows = true;
double double_value;
const char *string_value;

// Do a first pass to get the column names
for (int i = 0; i < column_count; i++) {
column_name = sqlite3_column_name(statement, i);
column_names.emplace_back(column_name);
}

while (is_consuming_rows) {
status = sqlite3_step(statement);

switch (status) {
case SQLITE_DONE:
changedRowCount = sqlite3_changes(db);
latestInsertRowId = sqlite3_last_insert_rowid(db);
is_consuming_rows = false;
break;

case SQLITE_ROW:
current_column = 0;
row = std::vector<JSVariant>();
Expand Down Expand Up @@ -470,17 +483,14 @@
rows.emplace_back(std::move(row));
break;

case SQLITE_DONE:
is_consuming_rows = false;
break;

default:
has_failed = true;
is_consuming_rows = false;
}
}

sqlite3_finalize(statement);

} while (remainingStatement != nullptr &&
strcmp(remainingStatement, "") != 0 && !has_failed);

Expand All @@ -490,8 +500,6 @@
std::string(message));
}

int changedRowCount = sqlite3_changes(db);
long long latestInsertRowId = sqlite3_last_insert_rowid(db);
return {.affectedRows = changedRowCount,
.insertId = static_cast<double>(latestInsertRowId),
.rows = std::move(rows),
Expand Down Expand Up @@ -646,7 +654,7 @@
if (isFailed) {
throw std::runtime_error(
"[op-sqlite] SQLite error code: " + std::to_string(result) +
", description: " + std::string(errorMessage));

Check warning on line 657 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 657 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 657 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-sqlcipher

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]
}

int changedRowCount = sqlite3_changes(db);
Expand Down Expand Up @@ -785,7 +793,7 @@
if (isFailed) {
throw std::runtime_error(
"[op-sqlite] SQLite error code: " + std::to_string(step) +
", description: " + std::string(errorMessage));

Check warning on line 796 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 796 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-embedded

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]

Check warning on line 796 in cpp/bridge.cpp

View workflow job for this annotation

GitHub Actions / ios-sqlcipher

variable 'errorMessage' may be uninitialized when used here [-Wconditional-uninitialized]
}

int changedRowCount = sqlite3_changes(db);
Expand Down
28 changes: 18 additions & 10 deletions cpp/utils.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

#include "utils.hpp"
#include "SmartHostObject.h"
#include "types.hpp"
Expand All @@ -15,7 +16,7 @@ namespace opsqlite {
namespace jsi = facebook::jsi;
namespace react = facebook::react;

inline jsi::Value to_jsi(jsi::Runtime &rt, const JSVariant &value) {
jsi::Value to_jsi(jsi::Runtime &rt, const JSVariant &value) {
if (std::holds_alternative<bool>(value)) {
return std::get<bool>(value);
} else if (std::holds_alternative<int>(value)) {
Expand Down Expand Up @@ -76,7 +77,7 @@ inline jsi::Value to_jsi(jsi::Runtime &rt, const JSVariant &value) {
// value);
}

inline JSVariant to_variant(jsi::Runtime &rt, const jsi::Value &value) {
JSVariant to_variant(jsi::Runtime &rt, const jsi::Value &value) {
if (value.isNull() || value.isUndefined()) {
return JSVariant(nullptr);
} else if (value.isBool()) {
Expand All @@ -94,7 +95,7 @@ inline JSVariant to_variant(jsi::Runtime &rt, const jsi::Value &value) {
}
} else if (value.isString()) {
std::string strVal = value.asString(rt).utf8(rt);
return JSVariant(strVal);
return JSVariant(std::move(strVal));
} else if (value.isObject()) {
auto obj = value.asObject(rt);
size_t byteOffset = 0;
Expand Down Expand Up @@ -142,7 +143,7 @@ inline JSVariant to_variant(jsi::Runtime &rt, const jsi::Value &value) {
}

std::vector<std::string> to_string_vec(jsi::Runtime &rt, jsi::Value const &xs) {
jsi::Array values = xs.asObject(rt).asArray(rt);
jsi::Array const values = xs.asObject(rt).asArray(rt);
std::vector<std::string> res;
for (int ii = 0; ii < values.length(rt); ii++) {
std::string value = values.getValueAtIndex(rt, ii).asString(rt).utf8(rt);
Expand All @@ -152,7 +153,7 @@ std::vector<std::string> to_string_vec(jsi::Runtime &rt, jsi::Value const &xs) {
}

std::vector<int> to_int_vec(jsi::Runtime &rt, jsi::Value const &xs) {
jsi::Array values = xs.asObject(rt).asArray(rt);
jsi::Array const values = xs.asObject(rt).asArray(rt);
std::vector<int> res;
for (int ii = 0; ii < values.length(rt); ii++) {
int value = static_cast<int>(values.getValueAtIndex(rt, ii).asNumber());
Expand All @@ -162,13 +163,15 @@ std::vector<int> to_int_vec(jsi::Runtime &rt, jsi::Value const &xs) {
}

std::vector<JSVariant> to_variant_vec(jsi::Runtime &rt, jsi::Value const &xs) {
jsi::Array const values = xs.asObject(rt).asArray(rt);
size_t arg_length = values.length(rt);

std::vector<JSVariant> res;
jsi::Array values = xs.asObject(rt).asArray(rt);
res.reserve(arg_length);

for (int ii = 0; ii < values.length(rt); ii++) {
jsi::Value value = values.getValueAtIndex(rt, ii);
res.emplace_back(to_variant(rt, value));
}
for (size_t ii = 0; ii < arg_length; ii++) {
res.emplace_back(to_variant(rt, values.getValueAtIndex(rt, ii)));
}

return res;
}
Expand All @@ -184,6 +187,11 @@ jsi::Value create_js_rows(jsi::Runtime &rt, const BridgeResult &status) {
size_t row_count = status.rows.size();
size_t column_count = status.column_names.size();

if (row_count == 0) {
res.setProperty(rt, "rows", jsi::Array(rt, 0));
return res;
}

std::vector<jsi::PropNameID> column_prop_ids;
column_prop_ids.reserve(column_count);
for (size_t i = 0; i < column_count; i++) {
Expand Down
21 changes: 11 additions & 10 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
} from "@op-engineering/op-test";
import { useEffect, useState } from "react";
import "./tests"; // import all tests to register them
import {performanceTest} from './performance_test';
import {performanceTest, insertTest} from './performance_test';
import { StyleSheet, Text, View } from "react-native";
import { SafeAreaProvider, SafeAreaView } from "react-native-safe-area-context";
// import {open} from '@op-engineering/op-sqlite';
Expand Down Expand Up @@ -36,15 +36,16 @@ export default function App() {
console.log("OPSQLITE_TEST_RESULT:FAIL");
}

setTimeout(() => {
try {
global?.gc?.();
let perfRes = performanceTest();
setPerfResult(perfRes);
} catch (e) {
// intentionally left blank
}
}, 4000);
// setTimeout(() => {
// try {
// global?.gc?.();
// // let perfRes = performanceTest();
// let perfRes = insertTest();
// setPerfResult(perfRes);
// } catch (e) {
// // intentionally left blank
// }
// }, 4000);
};

work();
Expand Down
27 changes: 27 additions & 0 deletions example/src/performance_test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import {open} from '@op-engineering/op-sqlite';

const ITERATIONS = 1000;

export function performanceTest() {
const db = open({
name: 'perfTest.sqlite',
Expand Down Expand Up @@ -39,3 +41,28 @@ export function performanceTest() {
// await db.close();
return end - start;
}

export function insertTest() {
const db = open({
name: 'insertTest.sqlite'
});


db.executeSync("DROP TABLE IF EXISTS bench");
db.executeSync(
"CREATE TABLE bench (id INTEGER PRIMARY KEY, name TEXT, value REAL)",
);

// sync inserts
for (let i = 0; i < ITERATIONS; i++) {
db.executeSync("INSERT INTO bench VALUES (?,?,?)", [i, `n${i}`, i * 1.5]);
}

// select all
let t = performance.now();
for (let i = 0; i < ITERATIONS; i++) {
db.executeSync("SELECT * FROM bench");
}

return performance.now() - t;
}
50 changes: 48 additions & 2 deletions example/src/tests/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,7 @@ describe("Queries tests", () => {

expect(res2.rowsAffected).toEqual(1);
expect(res2.insertId).toEqual(1);
// expect(res2.rows).toBe([]);
expect(res2.rows?.length).toEqual(0);
expect(res2.rows.length).toEqual(0);
});

it("Insert", async () => {
Expand Down Expand Up @@ -891,6 +890,53 @@ describe("Queries tests", () => {
expect(res.rows).toDeepEqual([{ user_version: 0 }]);
});

it("INSERT RETURNING yields rows with correct columns", async () => {
if (isLibsql() || isTurso()) {
return;
}
const res = await db.execute(
"INSERT INTO User (id, name, age, networth) VALUES (?, ?, ?, ?) RETURNING id, name",
[42, "Alice", 30, 1234.56],
);
expect(res.rows.length).toBe(1);
expect(res.rows[0]!.id).toBe(42);
expect(res.rows[0]!.name).toBe("Alice");
});

it("UPDATE RETURNING yields updated rows", async () => {
if (isLibsql() || isTurso()) {
return;
}
await db.execute(
"INSERT INTO User (id, name, age, networth) VALUES (?, ?, ?, ?)",
[1, "Bob", 25, 500.0],
);
const res = await db.execute(
"UPDATE User SET name = ? WHERE id = ? RETURNING id, name",
["Robert", 1],
);
expect(res.rows.length).toBe(1);
expect(res.rows[0]!.id).toBe(1);
expect(res.rows[0]!.name).toBe("Robert");
});

it("DELETE RETURNING yields deleted rows", async () => {
if (isLibsql() || isTurso()) {
return;
}
await db.execute(
"INSERT INTO User (id, name, age, networth) VALUES (?, ?, ?, ?)",
[99, "Eve", 40, 999.0],
);
const res = await db.execute(
"DELETE FROM User WHERE id = ? RETURNING id, name",
[99],
);
expect(res.rows.length).toBe(1);
expect(res.rows[0]!.id).toBe(99);
expect(res.rows[0]!.name).toBe("Eve");
});

// const sqliteVecEnabled = pkg?.['op-sqlite']?.sqliteVec === true;
// if (sqliteVecEnabled) {
// it('sqlite-vec extension: vector similarity search', async () => {
Expand Down
2 changes: 1 addition & 1 deletion example/src/tests/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe("Storage", () => {
expect(res).toEqual("bark");
});

it("can remove item sync", async () => {
it("Can remove item sync", async () => {
storage.setItemSync("foo", "bar");
storage.removeItemSync("foo");
const res = storage.getItemSync("foo");
Expand Down
Loading
Loading