Skip to content

Commit d64fafb

Browse files
better closing
1 parent 7438ef8 commit d64fafb

File tree

13 files changed

+439
-318
lines changed

13 files changed

+439
-318
lines changed

cpp/ConnectionPool.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,14 @@ ConnectionPool::queueInContext(ConnectionLockId contextId,
9494
};
9595
}
9696

97-
state->queueWork(task);
97+
try {
98+
state->queueWork(task);
99+
} catch (const std::exception &e) {
100+
return SQLiteOPResult{
101+
.errorMessage = e.what(),
102+
.type = SQLiteError,
103+
};
104+
}
98105

99106
return SQLiteOPResult{
100107
.type = SQLiteOk,

cpp/ConnectionState.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ SQLiteOPResult genericSqliteOpenDb(string const dbName, string const docPath,
1010
ConnectionState::ConnectionState(const std::string dbName,
1111
const std::string docPath, int SQLFlags) {
1212
auto result = genericSqliteOpenDb(dbName, docPath, &connection, SQLFlags);
13+
isClosed = false;
1314

1415
this->clearLock();
1516
threadDone = false;
@@ -64,13 +65,18 @@ std::future<void> ConnectionState::refreshSchema() {
6465
}
6566

6667
void ConnectionState::close() {
68+
isClosed = true;
6769
waitFinished();
6870
// So that the thread can stop (if not already)
6971
threadDone = true;
7072
sqlite3_close_v2(connection);
7173
}
7274

7375
void ConnectionState::queueWork(std::function<void(sqlite3 *)> task) {
76+
if (isClosed) {
77+
throw std::runtime_error("Connection is not open. Connection has been closed before queueing work.");
78+
}
79+
7480
// Grab the mutex
7581
std::lock_guard<std::mutex> g(workQueueMutex);
7682

@@ -104,9 +110,9 @@ void ConnectionState::doWork() {
104110
workQueue.pop();
105111
}
106112

107-
++threadBusy;
113+
threadBusy = true;
108114
task(connection);
109-
--threadBusy;
115+
threadBusy = false;
110116
// Need to notify in order for waitFinished to be updated when
111117
// the queue is empty and not busy
112118
{
@@ -122,7 +128,7 @@ void ConnectionState::waitFinished() {
122128
return;
123129
}
124130
workQueueConditionVariable.wait(
125-
g, [&] { return workQueue.empty() && (threadBusy == 0); });
131+
g, [&] { return workQueue.empty() && (threadBusy == false); });
126132
}
127133

128134
SQLiteOPResult genericSqliteOpenDb(string const dbName, string const docPath,

cpp/ConnectionState.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,12 @@ class ConnectionState {
2929
// This condition variable is used for the threads to wait until there is work
3030
// to do
3131
std::condition_variable_any workQueueConditionVariable;
32-
unsigned int threadBusy;
32+
bool threadBusy;
3333
bool threadDone;
3434

3535
public:
36+
bool isClosed;
37+
3638
ConnectionState(const std::string dbName, const std::string docPath,
3739
int SQLFlags);
3840
~ConnectionState();

cpp/bindings.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,14 @@ void osp::install(jsi::Runtime &rt,
384384
}
385385
};
386386

387-
sqliteQueueInContext(dbName, contextLockId, task);
387+
auto response = sqliteQueueInContext(dbName, contextLockId, task);
388+
if (response.type == SQLiteError) {
389+
auto errorCtr = rt.global().getPropertyAsFunction(rt, "Error");
390+
auto error = errorCtr.callAsConstructor(
391+
rt, jsi::String::createFromUtf8(
392+
rt, response.errorMessage));
393+
reject->asObject(rt).asFunction(rt).call(rt, error);
394+
}
388395
return {};
389396
}));
390397

tests/android/build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
// Top-level build file where you can add configuration options common to all sub-projects/modules.
22

3+
task printEnv() {
4+
System.out.println( System.getenv().toString())
5+
}
6+
37
buildscript {
48
ext {
59
buildToolsVersion = findProperty('android.buildToolsVersion') ?: '35.0.0'
@@ -39,3 +43,4 @@ allprojects {
3943
maven { url 'https://www.jitpack.io' }
4044
}
4145
}
46+

tests/babel.config.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module.exports = function (api) {
33
return {
44
presets: ['babel-preset-expo'],
55
plugins: [
6+
'@babel/plugin-transform-class-static-block',
67
[
78
'module-resolver',
89
{

tests/ios/Podfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ install! 'cocoapods',
1212
:deterministic_uuids => false
1313

1414
prepare_react_native_project!
15+
use_modular_headers!
1516

1617
target 'tests' do
1718
use_expo_modules!

tests/ios/Podfile.lock

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,19 @@
11
PODS:
22
- boost (1.84.0)
33
- DoubleConversion (1.1.6)
4-
- EXConstants (17.0.3):
4+
- EXConstants (17.0.4):
55
- ExpoModulesCore
6-
- Expo (52.0.7):
6+
- Expo (52.0.27):
77
- ExpoModulesCore
8-
- ExpoAsset (11.0.1):
8+
- ExpoAsset (11.0.2):
99
- ExpoModulesCore
10-
- ExpoFileSystem (18.0.3):
10+
- ExpoFileSystem (18.0.7):
1111
- ExpoModulesCore
12-
- ExpoFont (13.0.1):
12+
- ExpoFont (13.0.3):
1313
- ExpoModulesCore
14-
- ExpoKeepAwake (14.0.1):
14+
- ExpoKeepAwake (14.0.2):
1515
- ExpoModulesCore
16-
- ExpoModulesCore (2.0.3):
16+
- ExpoModulesCore (2.1.4):
1717
- DoubleConversion
1818
- glog
1919
- hermes-engine
@@ -1280,7 +1280,7 @@ PODS:
12801280
- ReactCommon/turbomodule/bridging
12811281
- ReactCommon/turbomodule/core
12821282
- Yoga
1283-
- react-native-quick-sqlite (2.2.1):
1283+
- react-native-quick-sqlite (2.3.0):
12841284
- DoubleConversion
12851285
- glog
12861286
- hermes-engine
@@ -1809,18 +1809,18 @@ EXTERNAL SOURCES:
18091809

18101810
SPEC CHECKSUMS:
18111811
boost: 1dca942403ed9342f98334bf4c3621f011aa7946
1812-
DoubleConversion: f16ae600a246532c4020132d54af21d0ddb2a385
1813-
EXConstants: dd2fe64c6cdb1383b694c309a63028a8e9f2be6d
1814-
Expo: 46cbe74ce0d0f4a4d7b726e90693eb8dfcec6de0
1815-
ExpoAsset: 8138f2a9ec55ae1ad7c3871448379f7d97692d15
1816-
ExpoFileSystem: cc31b7a48031ab565f9eb5c2b61aa08d774a271a
1817-
ExpoFont: 7522d869d84ee2ee8093ee997fef5b86f85d856b
1818-
ExpoKeepAwake: 783e68647b969b210a786047c3daa7b753dcac1f
1819-
ExpoModulesCore: 2d1df04dc27f91d8b383c0ec7f1d121e3d9b7f68
1812+
DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54
1813+
EXConstants: cc788e18fb962b77999c16f6b90e551542b1bcf4
1814+
Expo: 7021c4880d0bbd02781e88e213b6786c1200ebdf
1815+
ExpoAsset: 6cc988c426b5fc997859cc0f8f6728d1376bf53f
1816+
ExpoFileSystem: a0a5c44d3d3e8a2aa54fe7c70c7690df03cf4dd8
1817+
ExpoFont: 8b14db1afebf62e9774613a082e402985f4e747c
1818+
ExpoKeepAwake: 1f74cdf53ab3b3aeb7110f18eb053c92e7c3e710
1819+
ExpoModulesCore: 52b2f1c87f218a8a6ad2b0cb9d3d238bce3529fb
18201820
ExpoSplashScreen: cb78ad96510786e97b35c1d9014b705cb196b8a3
18211821
FBLazyVector: bc70dcb22ad30ce734a7cce7210791dc737e230f
18221822
fmt: 10c6e61f4be25dc963c36bd73fc7b1705fe975be
1823-
glog: 08b301085f15bcbb6ff8632a8ebaf239aae04e6a
1823+
glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b
18241824
hermes-engine: 3852e37f6158a2fcfad23e31215ed495da3a6a40
18251825
powersync-sqlite-core: 3b1cc184e277776aaf22e221fd0336575c7173c4
18261826
RCT-Folly: bf5c0376ffe4dd2cf438dcf86db385df9fdce648
@@ -1852,7 +1852,7 @@ SPEC CHECKSUMS:
18521852
React-logger: 81d58ca6f1d93fca9a770bda6cc1c4fbfcc99c9c
18531853
React-Mapbuffer: 726951e68f4bb1c2513d322f2548798b2a3d628d
18541854
React-microtasksnativemodule: bc55596cbf40957f5099bc495f1a06f459d0be88
1855-
react-native-quick-sqlite: de5eef2b2092b045d5c546018afd5b6b93843a1b
1855+
react-native-quick-sqlite: 3fda3ac881a9cc52d32c4553f6c894c0efda4110
18561856
react-native-safe-area-context: 4532f1a0c5d34a46b9324ccaaedcb5582a302b7d
18571857
React-nativeconfig: 470fce6d871c02dc5eff250a362d56391b7f52d6
18581858
React-NativeModulesApple: 6297fc3136c1fd42884795c51d7207de6312b606
@@ -1882,8 +1882,8 @@ SPEC CHECKSUMS:
18821882
ReactCodegen: 93b271af49774429f34d7fd561197020d86436e2
18831883
ReactCommon: 208cb02e3c0bb8a727b3e1a1782202bcfa5d9631
18841884
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
1885-
Yoga: 96872ee462cfc43866ad013c8160d4ff6b85709b
1885+
Yoga: c5b0e913b5b3b4cde588227c1402e747797061f3
18861886

1887-
PODFILE CHECKSUM: d6c8a5c5ff2d3984a5826e0d3631fd656816f915
1887+
PODFILE CHECKSUM: d4fee3078705a7168879502ec45d504d647dadf3
18881888

18891889
COCOAPODS: 1.16.2

tests/ios/tests.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@
261261
"${PODS_CONFIGURATION_BUILD_DIR}/React-Core/React-Core_privacy.bundle",
262262
"${PODS_CONFIGURATION_BUILD_DIR}/React-cxxreact/React-cxxreact_privacy.bundle",
263263
"${PODS_CONFIGURATION_BUILD_DIR}/boost/boost_privacy.bundle",
264-
"${PODS_CONFIGURATION_BUILD_DIR}/glog/glog_privacy.bundle",
265264
);
266265
name = "[CP] Copy Pods Resources";
267266
outputPaths = (
@@ -272,7 +271,6 @@
272271
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-Core_privacy.bundle",
273272
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/React-cxxreact_privacy.bundle",
274273
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/boost_privacy.bundle",
275-
"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/glog_privacy.bundle",
276274
);
277275
runOnlyForDeploymentPostprocessing = 0;
278276
shellPath = /bin/sh;
@@ -461,7 +459,10 @@
461459
ONLY_ACTIVE_ARCH = YES;
462460
OTHER_CFLAGS = "$(inherited)";
463461
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
464-
OTHER_LDFLAGS = "$(inherited) ";
462+
OTHER_LDFLAGS = (
463+
"$(inherited)",
464+
" ",
465+
);
465466
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
466467
SDKROOT = iphoneos;
467468
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
@@ -520,7 +521,10 @@
520521
MTL_ENABLE_DEBUG_INFO = NO;
521522
OTHER_CFLAGS = "$(inherited)";
522523
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
523-
OTHER_LDFLAGS = "$(inherited) ";
524+
OTHER_LDFLAGS = (
525+
"$(inherited)",
526+
" ",
527+
);
524528
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
525529
SDKROOT = iphoneos;
526530
SWIFT_VERSION = 5.0;

tests/package.json

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,21 @@
1313
"@craftzdog/react-native-buffer": "^6.0.5",
1414
"base-64": "^1.0.0",
1515
"base64-arraybuffer": "^1.0.2",
16-
"chai": "^4.3.7",
16+
"chai": "^5.1.2",
1717
"chance": "^1.1.9",
1818
"events": "^3.3.0",
19-
"expo": "^52.0.0",
20-
"expo-build-properties": "~0.13.1",
21-
"expo-splash-screen": "~0.29.10",
22-
"expo-status-bar": "~2.0.0",
19+
"expo": "~52.0.26",
20+
"expo-build-properties": "~0.13.2",
21+
"expo-splash-screen": "~0.29.21",
22+
"expo-status-bar": "~2.0.1",
2323
"lodash": "^4.17.21",
2424
"mocha": "^10.1.0",
2525
"nativewind": "^2.0.11",
26+
"p-defer": "^4.0.1",
2627
"react": "18.3.1",
27-
"react-native": "0.76.2",
28+
"react-native": "0.76.6",
2829
"react-native-quick-sqlite": "link:..",
29-
"react-native-safe-area-context": "4.14.0",
30+
"react-native-safe-area-context": "4.12.0",
3031
"reflect-metadata": "^0.1.13",
3132
"stream-browserify": "^3.0.0",
3233
"tailwindcss": "^3.2.4",
@@ -35,15 +36,18 @@
3536
},
3637
"devDependencies": {
3738
"@babel/core": "^7.20.0",
39+
"@babel/plugin-transform-class-static-block": "^7.26.0",
3840
"@react-native-community/cli": "^15.1.2",
39-
"@types/chai": "^4.3.4",
41+
"@types/chai": "^5.0.1",
42+
"@types/chai-as-promised": "^8.0.1",
4043
"@types/chance": "^1.1.3",
4144
"@types/express": "^5.0.0",
4245
"@types/lodash": "^4.17.1",
4346
"@types/mocha": "^10.0.1",
4447
"@types/react": "~18.3.12",
4548
"babel-plugin-module-resolver": "^4.1.0",
4649
"body-parser": "^1.20.2",
50+
"chai-as-promised": "^8.0.1",
4751
"chalk": "4.1.2",
4852
"commander": "^11.1.0",
4953
"express": "^4.21.1",

tests/tests/sqlite/rawQueries.spec.ts

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { expect, use } from 'chai';
2+
import chaiAsPromised from 'chai-as-promised';
13
import Chance from 'chance';
24
import {
35
BatchedUpdateNotification,
@@ -10,11 +12,10 @@ import {
1012
UpdateNotification
1113
} from 'react-native-quick-sqlite';
1214
import { beforeEach, describe, it } from '../mocha/MochaRNAdapter';
13-
import chai from 'chai';
14-
import { randomIntFromInterval, numberName } from './utils';
15+
import { numberName, randomIntFromInterval } from './utils';
1516

16-
const { expect } = chai;
1717
const chance = new Chance();
18+
use(chaiAsPromised);
1819

1920
// Need to store the db on the global state since this variable will be cleared on hot reload,
2021
// Attempting to open an already open DB results in an error.
@@ -649,5 +650,32 @@ export function registerBaseTests() {
649650
}
650651
}
651652
});
653+
654+
it('Should handle multiple closes', async () => {
655+
// populate test data
656+
const dbName = 'test-close';
657+
const db = open(dbName);
658+
await db.execute('CREATE TABLE IF NOT EXISTS t1(id INTEGER PRIMARY KEY, c TEXT)');
659+
await db.execute('DELETE FROM t1');
660+
// Bulk insert 50000 rows without using a transaction
661+
const bulkInsertCommands: SQLBatchTuple[] = [];
662+
for (let i = 0; i < 50000; i++) {
663+
bulkInsertCommands.push(['INSERT INTO t1(id, c) VALUES(?, ?)', [i + 1, `value${i + 1}`]]);
664+
}
665+
await db.executeBatch(bulkInsertCommands);
666+
db.close();
667+
668+
for (let i = 1; i < 1_000; i++) {
669+
const db = open(dbName, {
670+
numReadConnections: NUM_READ_CONNECTIONS
671+
});
672+
673+
// don't await this on purpose
674+
const pExecute = db.execute(`SELECT * FROM t1} `);
675+
db.close();
676+
677+
await expect(pExecute).to.eventually.rejectedWith('is not open');
678+
}
679+
});
652680
});
653681
}

tests/tsconfig.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
2-
"compilerOptions": {},
2+
"compilerOptions": {
3+
"typeRoots": ["node_modules/@types"]
4+
},
35
"extends": "expo/tsconfig.base"
46
}

0 commit comments

Comments
 (0)