Skip to content

Commit 0ca17a1

Browse files
committed
Fixes
1 parent 96f9e81 commit 0ca17a1

File tree

17 files changed

+455
-382
lines changed

17 files changed

+455
-382
lines changed

android/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ if(!tokenizers.isEmpty()) {
122122
throw new GradleException("[OP-SQLITE] Error: libsql does not support tokenizers. Please disable tokenizers or do not enable libsql.")
123123
}
124124

125+
if(useTurso) {
126+
throw new GradleException("[OP-SQLITE] Error: turso backend does not support tokenizers. Please disable tokenizers or do not enable turso.")
127+
}
128+
125129
println "[OP-SQLITE] Tokenizers enabled. Detected tokenizers: " + tokenizers
126130
}
127131

cpp/OPSqlite.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ void install(jsi::Runtime &rt,
109109
#endif
110110
});
111111

112+
auto is_turso = HFN(=) {
113+
#ifdef OP_SQLITE_USE_TURSO
114+
return true;
115+
#else
116+
return false;
117+
#endif
118+
});
119+
112120
#ifdef OP_SQLITE_USE_LIBSQL
113121
auto open_remote = HFN(=) {
114122
jsi::Object options = args[0].asObject(rt);
@@ -180,6 +188,7 @@ void install(jsi::Runtime &rt,
180188
module.setProperty(rt, "open", std::move(open));
181189
module.setProperty(rt, "isSQLCipher", std::move(is_sqlcipher));
182190
module.setProperty(rt, "isLibsql", std::move(is_libsql));
191+
module.setProperty(rt, "isTurso", std::move(is_turso));
183192
module.setProperty(rt, "isIOSEmbedded", std::move(is_ios_embedded));
184193
#ifdef OP_SQLITE_USE_LIBSQL
185194
module.setProperty(rt, "openRemote", std::move(open_remote));

cpp/turso_bridge.cpp

Lines changed: 47 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,16 @@ inline TursoStmtHandle *to_turso_stmt(sqlite3_stmt *statement) {
4242
return reinterpret_cast<TursoStmtHandle *>(statement);
4343
}
4444

45+
inline turso_connection_t *require_turso_connection(TursoDbHandle *handle,
46+
const std::string &context) {
47+
if (handle == nullptr || handle->connection == nullptr) {
48+
throw std::runtime_error("[op-sqlite][turso] " + context +
49+
": invalid database connection");
50+
}
51+
52+
return handle->connection;
53+
}
54+
4555
void throw_if_turso_error(turso_status_code_t code, const char *error,
4656
const std::string &context) {
4757
if (code == TURSO_OK || code == TURSO_ROW || code == TURSO_DONE) {
@@ -157,11 +167,12 @@ std::string opsqlite_get_db_path(std::string const &db_name,
157167
sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
158168
[[maybe_unused]] std::string const &crsqlite_path,
159169
[[maybe_unused]] std::string const &sqlite_vec_path) {
160-
std::string final_path = opsqlite_get_db_path(name, path);
170+
auto *handle = new TursoDbHandle();
171+
handle->path = opsqlite_get_db_path(name, path);
161172

162173
turso_database_config_t db_config = {
163174
.async_io = 0,
164-
.path = final_path.c_str(),
175+
.path = handle->path.c_str(),
165176
.experimental_features = nullptr,
166177
.vfs = nullptr,
167178
.encryption_cipher = nullptr,
@@ -171,21 +182,33 @@ sqlite3 *opsqlite_open(std::string const &name, std::string const &path,
171182
const char *error = nullptr;
172183
const turso_database_t *database = nullptr;
173184

174-
throw_if_turso_error(
175-
turso_database_new(&db_config, &database, &error), error,
176-
"create database at " + final_path);
177-
throw_if_turso_error(turso_database_open(database, &error), error,
178-
"open database at " + final_path);
185+
try {
186+
throw_if_turso_error(
187+
turso_database_new(&db_config, &database, &error), error,
188+
"create database at " + handle->path);
189+
throw_if_turso_error(turso_database_open(database, &error), error,
190+
"open database at " + handle->path);
191+
192+
turso_connection_t *connection = nullptr;
193+
throw_if_turso_error(
194+
turso_database_connect(database, &connection, &error), error,
195+
"connect database at " + handle->path);
196+
197+
handle->database = database;
198+
handle->connection = connection;
199+
} catch (...) {
200+
if (handle->connection != nullptr) {
201+
turso_connection_deinit(handle->connection);
202+
handle->connection = nullptr;
203+
}
179204

180-
turso_connection_t *connection = nullptr;
181-
throw_if_turso_error(
182-
turso_database_connect(database, &connection, &error), error,
183-
"connect database at " + final_path);
205+
if (database != nullptr) {
206+
turso_database_deinit(database);
207+
}
184208

185-
auto *handle = new TursoDbHandle();
186-
handle->database = database;
187-
handle->connection = connection;
188-
handle->path = final_path;
209+
delete handle;
210+
throw;
211+
}
189212

190213
return reinterpret_cast<sqlite3 *>(handle);
191214
}
@@ -240,7 +263,8 @@ sqlite3_stmt *opsqlite_prepare_statement(sqlite3 *db,
240263
const char *error = nullptr;
241264

242265
throw_if_turso_error(turso_connection_prepare_single(
243-
handle->connection, query.c_str(), &statement, &error),
266+
require_turso_connection(handle, "prepare statement"),
267+
query.c_str(), &statement, &error),
244268
error, "prepare statement");
245269

246270
auto *stmt_handle = new TursoStmtHandle();
@@ -344,8 +368,8 @@ BridgeResult opsqlite_execute_prepared_statement(
344368
reset_statement(stmt->statement);
345369

346370
return {.affectedRows = changes,
347-
.insertId = static_cast<double>(
348-
turso_connection_last_insert_rowid(db_handle->connection))};
371+
.insertId = static_cast<double>(turso_connection_last_insert_rowid(
372+
require_turso_connection(db_handle, "last_insert_rowid")))};
349373
}
350374

351375
BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
@@ -361,9 +385,9 @@ BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
361385
turso_statement_t *statement = nullptr;
362386
size_t tail = 0;
363387

364-
auto code = turso_connection_prepare_first(db_handle->connection,
365-
query.c_str() + offset,
366-
&statement, &tail, &error);
388+
auto code = turso_connection_prepare_first(
389+
require_turso_connection(db_handle, "prepare statement in batch execute"),
390+
query.c_str() + offset, &statement, &tail, &error);
367391
throw_if_turso_error(code, error, "prepare statement in batch execute");
368392

369393
if (tail == 0) {
@@ -439,8 +463,8 @@ BridgeResult opsqlite_execute(sqlite3 *db, std::string const &query,
439463
}
440464

441465
return {.affectedRows = changes,
442-
.insertId = static_cast<double>(
443-
turso_connection_last_insert_rowid(db_handle->connection)),
466+
.insertId = static_cast<double>(turso_connection_last_insert_rowid(
467+
require_turso_connection(db_handle, "last_insert_rowid"))),
444468
.rows = std::move(rows),
445469
.column_names = std::move(column_names)};
446470
}

example/package.json

Lines changed: 69 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,71 @@
11
{
2-
"name": "op_sqlite_example",
3-
"version": "0.0.1",
4-
"private": true,
5-
"scripts": {
6-
"android": "react-native run-android",
7-
"ios": "react-native run-ios --scheme='debug' --simulator='iPhone 16 Pro'",
8-
"run:ios:unused": "xcodebuild -workspace ios/OPSQLiteExample.xcworkspace -scheme release -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 16 Pro' clean build",
9-
"run:ios:release": "react-native run-ios --scheme='release' --no-packager",
10-
"postinstall": "patch-package",
11-
"start": "react-native start",
12-
"pods": "cd ios && bundle exec pod install && rm -f .xcode.env.local",
13-
"pods:nuke": "cd ios && rm -rf Pods && rm -rf Podfile.lock && bundle exec pod install",
14-
"run:android:release": "cd android && ./gradlew assembleRelease && adb install -r app/build/outputs/apk/release/app-release.apk && adb shell am start -n com.op.sqlite.example/.MainActivity",
15-
"build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
16-
"build:ios": "cd ios && xcodebuild -workspace OPSQLiteExample.xcworkspace -scheme debug -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO",
17-
"web": "vite",
18-
"web:build": "vite build",
19-
"web:preview": "vite preview"
20-
},
21-
"dependencies": {
22-
"@op-engineering/op-test": "^0.2.5",
23-
"@sqlite.org/sqlite-wasm": "^3.51.2-build8",
24-
"chance": "^1.1.9",
25-
"clsx": "^2.0.0",
26-
"events": "^3.3.0",
27-
"react": "19.1.1",
28-
"react-dom": "19.1.1",
29-
"react-native": "0.82.1",
30-
"react-native-safe-area-context": "^5.6.2",
31-
"react-native-web": "^0.21.2"
32-
},
33-
"devDependencies": {
34-
"@babel/core": "^7.25.2",
35-
"@babel/preset-env": "^7.25.3",
36-
"@babel/runtime": "^7.25.0",
37-
"@react-native-community/cli": "^18.0.0",
38-
"@react-native-community/cli-platform-android": "18.0.0",
39-
"@react-native-community/cli-platform-ios": "18.0.0",
40-
"@react-native/babel-preset": "0.82.1",
41-
"@react-native/metro-config": "0.82.1",
42-
"@react-native/typescript-config": "0.81.5",
43-
"@types/chance": "^1.1.7",
44-
"@types/react": "^19.1.1",
45-
"@vitejs/plugin-react": "^5.1.0",
46-
"patch-package": "^8.0.1",
47-
"react-native-builder-bob": "^0.40.13",
48-
"react-native-monorepo-config": "^0.1.9",
49-
"react-native-restart": "^0.0.27",
50-
"tailwindcss": "3.3.2",
51-
"vite": "^7.1.9"
52-
},
53-
"engines": {
54-
"node": ">=18"
55-
},
56-
"op-sqlite": {
57-
"libsql": false,
58-
"turso": true,
59-
"sqlcipher": false,
60-
"iosSqlite": false,
61-
"fts5": true,
62-
"rtree": true,
63-
"crsqlite": false,
64-
"sqliteVec": false,
65-
"performanceMode": true,
66-
"tokenizers": [
67-
"wordtokenizer",
68-
"porter"
69-
]
70-
}
2+
"name": "op_sqlite_example",
3+
"version": "0.0.1",
4+
"private": true,
5+
"scripts": {
6+
"android": "react-native run-android",
7+
"ios": "react-native run-ios --scheme='debug' --simulator='iPhone 16 Pro'",
8+
"run:ios:unused": "xcodebuild -workspace ios/OPSQLiteExample.xcworkspace -scheme release -configuration Release -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 16 Pro' clean build",
9+
"run:ios:release": "react-native run-ios --scheme='release' --no-packager",
10+
"postinstall": "patch-package",
11+
"start": "react-native start",
12+
"pods": "cd ios && bundle exec pod install && rm -f .xcode.env.local",
13+
"pods:nuke": "cd ios && rm -rf Pods && rm -rf Podfile.lock && bundle exec pod install",
14+
"run:android:release": "cd android && ./gradlew assembleRelease && adb install -r app/build/outputs/apk/release/app-release.apk && adb shell am start -n com.op.sqlite.example/.MainActivity",
15+
"build:android": "cd android && ./gradlew assembleDebug --no-daemon --console=plain -PreactNativeArchitectures=arm64-v8a",
16+
"build:ios": "cd ios && xcodebuild -workspace OPSQLiteExample.xcworkspace -scheme debug -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO",
17+
"web": "vite",
18+
"web:build": "vite build",
19+
"web:preview": "vite preview"
20+
},
21+
"dependencies": {
22+
"@op-engineering/op-test": "^0.2.5",
23+
"@sqlite.org/sqlite-wasm": "^3.51.2-build8",
24+
"chance": "^1.1.9",
25+
"clsx": "^2.0.0",
26+
"events": "^3.3.0",
27+
"react": "19.1.1",
28+
"react-dom": "19.1.1",
29+
"react-native": "0.82.1",
30+
"react-native-safe-area-context": "^5.6.2",
31+
"react-native-web": "^0.21.2"
32+
},
33+
"devDependencies": {
34+
"@babel/core": "^7.25.2",
35+
"@babel/preset-env": "^7.25.3",
36+
"@babel/runtime": "^7.25.0",
37+
"@react-native-community/cli": "^18.0.0",
38+
"@react-native-community/cli-platform-android": "18.0.0",
39+
"@react-native-community/cli-platform-ios": "18.0.0",
40+
"@react-native/babel-preset": "0.82.1",
41+
"@react-native/metro-config": "0.82.1",
42+
"@react-native/typescript-config": "0.81.5",
43+
"@types/chance": "^1.1.7",
44+
"@types/react": "^19.1.1",
45+
"@vitejs/plugin-react": "^5.1.0",
46+
"patch-package": "^8.0.1",
47+
"react-native-builder-bob": "^0.40.13",
48+
"react-native-monorepo-config": "^0.1.9",
49+
"react-native-restart": "^0.0.27",
50+
"tailwindcss": "3.3.2",
51+
"vite": "^7.1.9"
52+
},
53+
"engines": {
54+
"node": ">=18"
55+
},
56+
"op-sqlite": {
57+
"libsql": false,
58+
"turso": false,
59+
"sqlcipher": false,
60+
"iosSqlite": false,
61+
"fts5": true,
62+
"rtree": true,
63+
"crsqlite": false,
64+
"sqliteVec": false,
65+
"performanceMode": true,
66+
"tokenizers": [
67+
"wordtokenizer",
68+
"porter"
69+
]
70+
}
7171
}

example/src/tests/dbsetup.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,26 @@ import {
55
isIOSEmbeeded,
66
isLibsql,
77
isSQLCipher,
8+
isTurso,
89
moveAssetsDatabase,
910
open,
1011
} from "@op-engineering/op-sqlite";
1112
import { describe, expect, it } from "@op-engineering/op-test";
1213
import { Platform } from "react-native";
1314

14-
const expectedVersion = isLibsql()
15-
? "3.45.1"
16-
: isSQLCipher()
17-
? "3.51.3"
18-
: "3.51.3";
19-
const flavor = isLibsql() ? "libsql" : isSQLCipher() ? "sqlcipher" : "sqlite";
15+
let expectedVersion = "3.51.3";
16+
let flavor = "sqlite";
17+
18+
if (isLibsql()) {
19+
expectedVersion = "3.45.1";
20+
flavor = "libsql";
21+
} else if (isTurso()) {
22+
expectedVersion = "3.50.4";
23+
flavor = "turso";
24+
} else if (isSQLCipher()) {
25+
expectedVersion = "3.51.3";
26+
flavor = "sqlcipher";
27+
}
2028

2129
// const expectedSqliteVecVersion = 'v0.1.2-alpha.7';
2230

@@ -226,6 +234,9 @@ describe("DB setup tests", () => {
226234
});
227235

228236
it("Can attach/dettach database", () => {
237+
if (isTurso()) {
238+
return;
239+
}
229240
const db = open({
230241
name: "attachTest.sqlite",
231242
encryptionKey: "test",

example/src/tests/hooks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type DB, isLibsql, open } from "@op-engineering/op-sqlite";
1+
import { type DB, isLibsql, isTurso, open } from "@op-engineering/op-sqlite";
22
import {
33
afterEach,
44
beforeEach,
@@ -17,7 +17,7 @@ const chance = new Chance();
1717

1818
describe("Hooks", () => {
1919
let db: DB;
20-
if (isLibsql()) {
20+
if (isLibsql() || isTurso()) {
2121
return;
2222
}
2323

0 commit comments

Comments
 (0)