Skip to content
Open
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
10 changes: 9 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ jobs:
if: steps.select.outputs.cpp_tests == 'true'
run: bash scripts/test-cpp.sh
- name: Test SQLite thread-safety pod configuration
if: steps.select.outputs.ios == 'true'
# Paused because Nitro V1 doesn't generate the CocoaPods files that the podspec loads.
if: false && steps.select.outputs.ios == 'true'
run: ruby scripts/test-podspec-threadsafe.rb
- name: Check documentation
if: steps.select.outputs.docs == 'true'
Expand All @@ -149,7 +150,9 @@ jobs:
lockfiles:
name: Native lockfiles
needs: checks
# Paused because Nitro V1 doesn't support CocoaPods. Remove `false &&` to resume it.
if: >-
false &&
github.event_name != 'push' && !github.event.pull_request.draft &&
(needs.checks.outputs.ios_pods == 'true' || needs.checks.outputs.macos_pods == 'true')
uses: ./.github/workflows/update-lockfiles.yml
Expand All @@ -173,7 +176,10 @@ jobs:
ios:
name: iOS
needs: [checks, lockfiles]
# Paused because Nitro V1 builds iOS with Swift Package Manager, which this workflow doesn't set up yet.
# Remove `false &&` to resume it.
if: >-
false &&
!cancelled() && needs.checks.result == 'success' &&
(needs.lockfiles.result == 'success' || needs.lockfiles.result == 'skipped') &&
github.event_name != 'push' && !github.event.pull_request.draft &&
Expand All @@ -185,7 +191,9 @@ jobs:
macos:
name: macOS
needs: [checks, lockfiles]
# Paused until Nitro V1 supports React Native macOS. Remove `false &&` to resume it.
if: >-
false &&
!cancelled() && needs.checks.result == 'success' &&
(needs.lockfiles.result == 'success' || needs.lockfiles.result == 'skipped') &&
github.event_name != 'push' && !github.event.pull_request.draft &&
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,18 @@ jobs:
bun run sync:lockfile
bun run check:lockfile
bun install --frozen-lockfile
# The CocoaPods steps are paused because Nitro V1 doesn't support CocoaPods.
- uses: ruby/setup-ruby@v1
if: false
with:
ruby-version: 3.3.0
bundler-cache: true
working-directory: example
- name: Select Xcode
if: false
run: sudo xcode-select -s "/Applications/Xcode_26.5.app/Contents/Developer"
- name: Update iOS and macOS lockfiles
if: false
run: |
bun example pods
bun example pods:macos
Expand Down
89 changes: 43 additions & 46 deletions bun.lock

Large diffs are not rendered by default.

6 changes: 0 additions & 6 deletions docs/scripts/check-api.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,6 @@ const requiredMembers = {
'executeAsync',
'executeBatch',
'executeBatchAsync',
'close',
'drop',
'attach',
'detach',
'loadFile',
'loadFileAsync',
],
NitroSQLiteConnection: [
'close',
Expand Down
5 changes: 4 additions & 1 deletion docs/typedoc-fumadocs.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,11 @@ class NitroMemberRouter extends MemberRouter {
}

function isHybridObject(reflection) {
// Nitro V1 exports the HybridObject base class through a NativeHybridObject constant.
return reflection.extendedTypes?.some(
(type) => type.type === 'reference' && type.name === 'HybridObject',
(type) =>
type.type === 'reference' &&
['HybridObject', 'NativeHybridObject'].includes(type.name),
)
}

Expand Down
2 changes: 1 addition & 1 deletion example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
"expo-status-bar": "^1.12.1",
"react": "19.3.1",
"react-native": "0.88.0",
"react-native-nitro-modules": "*",
"react-native-nitro-modules": "1.0.0-alpha.nightly.86.g15e19db42041",
"react-native-nitro-sqlite": "10.2.0",
"react-native-nitro-sqlite-vec": "10.2.0",
"react-native-safe-area-context": "^5.10.1",
Expand Down
2 changes: 1 addition & 1 deletion example/tests/unit/specs/operations/execute.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { createArrayBufferTestDb, testDb } from '@tests/db'
import { open } from 'react-native-nitro-sqlite'
import { buildJSQueryResult } from '@nitro-sqlite/operations/execute'
import type { ColumnType } from 'react-native-nitro-sqlite'
import type { NitroSQLiteQueryResult } from '@nitro-sqlite/specs/NitroSQLiteQueryResult.nitro'
import type { NitroSQLiteQueryResult } from '@nitro-sqlite/specs/NitroSQLiteQueryResult'

const QUERY_RESULT_SIZES = [60, 1_000, 10_000]
// Asynchronous reads hand rows to JavaScript in batches of 256; cover the batch edges.
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"patch-package": "^8.0.0",
"react": "19.3.1",
"react-native": "0.88.0",
"react-native-nitro-modules": "0.37.1"
"react-native-nitro-modules": "1.0.0-alpha.nightly.86.g15e19db42041"
},
"devDependencies": {
"@babel/core": "^7.28.5",
Expand All @@ -67,7 +67,7 @@
"eslint-plugin-prettier": "^5.5.4",
"jest": "^30.2.0",
"jsonc-parser": "^3.3.1",
"nitrogen": "0.37.1",
"nitrogen": "1.0.0-alpha.nightly.86.g15e19db42041",
"prettier": "^3.6.2",
"typeorm": "1.1.1",
"typescript": "^6.0.3",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#include "HybridNitroSQLite.hpp"
#include "RNNitroSQLiteOnLoad.hpp"
#include <RNNitroSQLite/private/RNNitroSQLiteOnLoad.hpp>
#include <cstdlib>
#include <fbjni/fbjni.h>
#include <jni.h>
Expand All @@ -10,7 +10,7 @@
using namespace margelo::nitro::rnnitrosqlite;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return margelo::nitro::rnnitrosqlite::initialize(vm);
return facebook::jni::initialize(vm, [] { margelo::nitro::rnnitrosqlite::registerAllNatives(); });
}

extern "C" JNIEXPORT void JNICALL Java_com_margelo_rnnitrosqlite_DocPathSetter_setDocPathInJNI(JNIEnv* env, jclass clazz, jstring doc_path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@ import com.margelo.rnnitrosqlite.DocPathSetter
@Keep
@DoNotStrip
class HybridNitroSQLiteOnLoad : HybridNitroSQLiteOnLoadSpec() {
@Keep
@DoNotStrip
override fun init() {
NitroModules.applicationContext?.let { context ->
DocPathSetter.setDocPath(context)
companion object : HybridNitroSQLiteOnLoadSpec.Companion {
@Keep
@DoNotStrip
override fun init() {
NitroModules.applicationContext?.let { context ->
DocPathSetter.setDocPath(context)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
*/
#pragma once

#include "BatchQueryCommand.hpp"
#include "NitroSQLiteTypes.hpp"
#include <RNNitroSQLite/BatchQueryCommand.hpp>
#include <memory>

namespace margelo::nitro::rnnitrosqlite {
Expand Down
19 changes: 9 additions & 10 deletions packages/react-native-nitro-sqlite/cpp/NitroSQLiteOperations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -105,9 +105,12 @@ void bindStatement(sqlite3_stmt* statement, const SQLiteQueryParams& values) {
} else if (std::holds_alternative<std::string>(*optionalValue)) {
const auto& stringValue = std::get<std::string>(*optionalValue);
bindStatus = sqlite3_bind_text(statement, sqliteIndex, stringValue.c_str(), stringValue.length(), SQLITE_TRANSIENT);
} else if (std::holds_alternative<std::shared_ptr<ArrayBuffer>>(*optionalValue)) {
const auto& arrayBufferValue = std::get<std::shared_ptr<ArrayBuffer>>(*optionalValue);
bindStatus = sqlite3_bind_blob(statement, sqliteIndex, arrayBufferValue->data(), arrayBufferValue->size(), SQLITE_TRANSIENT);
} else if (std::holds_alternative<ArrayBuffer>(*optionalValue)) {
const auto& arrayBufferValue = std::get<ArrayBuffer>(*optionalValue);
// Empty buffers may have no data pointer, which sqlite3_bind_blob would bind as NULL.
bindStatus = arrayBufferValue.size() == 0
? sqlite3_bind_zeroblob(statement, sqliteIndex, 0)
: sqlite3_bind_blob(statement, sqliteIndex, arrayBufferValue.data(), arrayBufferValue.size(), SQLITE_TRANSIENT);
}

if (bindStatus != SQLITE_OK) {
Expand Down Expand Up @@ -306,13 +309,9 @@ namespace {
metadata->insert({columnName, std::move(columnMeta)});
}

NitroSQLiteQueryResult result;
result.rowsAffected = sqlite3_changes(db);
result.insertId = static_cast<double>(sqlite3_last_insert_rowid(db));
result.results = SQLiteQueryResults(std::move(columnNames), std::move(rows));
result.metadata = std::move(metadata);
result.rowBatchCount = static_cast<double>(rowBatchCount);
return result;
return NitroSQLiteQueryResult(sqlite3_changes(db), static_cast<double>(sqlite3_last_insert_rowid(db)),
SQLiteQueryResults(std::move(columnNames), std::move(rows)), std::move(metadata),
static_cast<double>(rowBatchCount));
}

} // namespace
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#pragma once

#include "NitroSQLiteDatabaseConnections.hpp"
#include "NitroSQLiteQueryResult.hpp"
#include "sqlite/sqlite3.h"
#include <RNNitroSQLite/NitroSQLiteQueryResult.hpp>
#include <functional>
#include <memory>
#include <mutex>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#pragma once

#include "sqlite/sqlite3.h"
#include <cstddef>
#include <sqlite3.h>
#include <string>
#include <utility>
#include <vector>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#pragma once

#include <sqlite3.h>
#include "sqlite/sqlite3.h"

namespace margelo::nitro::rnnitrosqlite {

Expand Down
7 changes: 4 additions & 3 deletions packages/react-native-nitro-sqlite/cpp/NitroSQLiteTypes.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#pragma once

#include "ColumnType.hpp"
#include "NitroSQLiteQueryColumnMetadata.hpp"
#include <NitroModules/ArrayBuffer.hpp>
#include <NitroModules/Null.hpp>
#include <RNNitroSQLite/ColumnType.hpp>
#include <RNNitroSQLite/NitroSQLiteQueryColumnMetadata.hpp>
#include <optional>
#include <string>
#include <unordered_map>
Expand All @@ -11,7 +12,7 @@

namespace margelo::nitro::rnnitrosqlite {

using SQLiteValue = std::optional<std::variant<NullType, bool, std::shared_ptr<ArrayBuffer>, std::string, double>>;
using SQLiteValue = std::optional<std::variant<bool, ArrayBuffer, NullType, double, std::string>>;
using SQLiteQueryParams = std::vector<SQLiteValue>;
using SQLiteQueryTableMetadata = std::unordered_map<std::string, NitroSQLiteQueryColumnMetadata>;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ static std::optional<SQLiteQueryParams> copyArrayBufferParamsForBackground(const
copiedParams.reserve(params->size());

for (const auto& value : *params) {
if (value && std::holds_alternative<std::shared_ptr<ArrayBuffer>>(*value)) {
const auto& buffer = std::get<std::shared_ptr<ArrayBuffer>>(*value);
if (value && std::holds_alternative<ArrayBuffer>(*value)) {
const auto& buffer = std::get<ArrayBuffer>(*value);
const auto copiedBuffer = ArrayBuffer::copy(buffer);
copiedParams.push_back(copiedBuffer);
} else {
Expand Down Expand Up @@ -61,31 +61,31 @@ static std::vector<BatchQuery> copyArrayBufferParamsForBackground(const std::vec
}

template <typename Result, typename Operation>
static std::shared_ptr<Promise<Result>> enqueueConnectionOperation(const SQLiteConnectionPtr& connection, Operation&& operation) {
auto promise = Promise<Result>::create();
static Promise<Result> enqueueConnectionOperation(const SQLiteConnectionPtr& connection, Operation&& operation) {
auto promise = Promise<Result>::pending();
try {
connection->enqueueAsync([promise, operation = std::forward<Operation>(operation)]() mutable {
std::optional<Result> result;
try {
result.emplace(operation());
} catch (...) {
promise->reject(std::current_exception());
promise.reject(std::current_exception());
return;
}
// Resolving may dispatch to JavaScript and throw after the native promise
// has settled. Do not try to reject that same promise again.
try {
promise->resolve(std::move(*result));
promise.resolve(std::move(*result));
} catch (...) {
if (promise->isPending()) {
promise->reject(std::current_exception());
if (promise.isPending()) {
promise.reject(std::current_exception());
return;
}
throw;
}
});
} catch (...) {
promise->reject(std::current_exception());
promise.reject(std::current_exception());
}
return promise;
}
Expand Down Expand Up @@ -133,7 +133,7 @@ const std::string getMigratedDocPath(DatabaseConnections& connections, const std
return selectedPath;
}

void HybridNitroSQLite::open(const std::string& dbName, const std::optional<std::string>& location, std::optional<bool> readOnly,
void HybridNitroSQLite::open(const std::string& dbName, const std::optional<std::string>& location, const std::optional<bool>& readOnly,
const std::optional<std::string>& encryptionKey) {
validateDatabaseName(dbName);
std::lock_guard lock(_connections.lifecycleMutex);
Expand All @@ -145,7 +145,7 @@ void HybridNitroSQLite::open(const std::string& dbName, const std::optional<std:
}

std::string HybridNitroSQLite::openConnection(const std::string& dbName, const std::optional<std::string>& location,
std::optional<bool> readOnly, const std::optional<std::string>& encryptionKey) {
const std::optional<bool>& readOnly, const std::optional<std::string>& encryptionKey) {
validateDatabaseName(dbName);
if (sqlite3_threadsafe() == 0) {
throw NitroSQLiteException(NitroSQLiteExceptionType::DatabaseCannotBeOpened,
Expand Down Expand Up @@ -212,9 +212,9 @@ NitroSQLiteQueryResult HybridNitroSQLite::execute(const std::string& dbName, con
return sqliteExecute(_connections.get(dbName), query, params);
};

std::shared_ptr<Promise<NitroSQLiteQueryResult>> HybridNitroSQLite::executeAsync(const std::string& dbName, const std::string& query,
const std::optional<SQLiteQueryParams>& params,
const std::optional<SQLiteRowBatchHandler>& onRows) {
Promise<NitroSQLiteQueryResult> HybridNitroSQLite::executeAsync(const std::string& dbName, const std::string& query,
const std::optional<SQLiteQueryParams>& params,
const std::optional<Callback<void(SQLiteQueryResults)>>& onRows) {
const auto copiedParams = copyArrayBufferParamsForBackground(params);
SQLiteConnectionPtr connection;
try {
Expand All @@ -223,10 +223,15 @@ std::shared_ptr<Promise<NitroSQLiteQueryResult>> HybridNitroSQLite::executeAsync
return Promise<NitroSQLiteQueryResult>::rejected(std::current_exception());
}

return enqueueConnectionOperation<NitroSQLiteQueryResult>(
connection, [connection, query, copiedParams, onRows = onRows.value_or(nullptr)]() -> NitroSQLiteQueryResult {
return sqliteExecute(connection, query, copiedParams, onRows);
});
SQLiteRowBatchHandler rowBatchHandler = nullptr;
if (onRows) {
rowBatchHandler = [onRows = *onRows](SQLiteQueryResults rows) { onRows(std::move(rows)); };
}

return enqueueConnectionOperation<NitroSQLiteQueryResult>(connection,
[connection, query, copiedParams, rowBatchHandler]() -> NitroSQLiteQueryResult {
return sqliteExecute(connection, query, copiedParams, rowBatchHandler);
});
};

std::shared_ptr<HybridNitroSQLitePreparedStatementSpec> HybridNitroSQLite::prepare(const std::string& dbName, const std::string& query) {
Expand All @@ -240,8 +245,8 @@ BatchQueryResult HybridNitroSQLite::executeBatch(const std::string& dbName, cons
return BatchQueryResult(result.rowsAffected);
};

std::shared_ptr<Promise<BatchQueryResult>> HybridNitroSQLite::executeBatchAsync(const std::string& dbName,
const std::vector<BatchQueryCommand>& batchParams) {
Promise<BatchQueryResult> HybridNitroSQLite::executeBatchAsync(const std::string& dbName,
const std::vector<BatchQueryCommand>& batchParams) {
// Convert BatchQueryCommand objects on the JS thread and copy any JS-backed
// ArrayBuffers into native buffers before going off-thread.
const auto commands = batchParamsToCommands(batchParams);
Expand All @@ -264,7 +269,7 @@ FileLoadResult HybridNitroSQLite::loadFile(const std::string& dbName, const std:
return FileLoadResult(result.commands, result.rowsAffected);
};

std::shared_ptr<Promise<FileLoadResult>> HybridNitroSQLite::loadFileAsync(const std::string& dbName, const std::string& location) {
Promise<FileLoadResult> HybridNitroSQLite::loadFileAsync(const std::string& dbName, const std::string& location) {
SQLiteConnectionPtr connection;
try {
connection = _connections.get(dbName);
Expand Down
Loading
Loading