expo-sqlite 58.0.7 → 58.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/cpp/NativeDatabaseBinding.cpp +0 -22
  4. package/android/src/main/cpp/NativeDatabaseBinding.h +0 -1
  5. package/android/src/main/cpp/NativeStatementBinding.cpp +3 -1
  6. package/android/src/main/cpp/NativeStatementBinding.h +1 -1
  7. package/android/src/main/java/expo/modules/sqlite/NativeDatabase.kt +5 -0
  8. package/android/src/main/java/expo/modules/sqlite/NativeDatabaseBinding.kt +0 -1
  9. package/android/src/main/java/expo/modules/sqlite/NativeStatement.kt +9 -1
  10. package/android/src/main/java/expo/modules/sqlite/SQLExceptions.kt +7 -1
  11. package/android/src/main/java/expo/modules/sqlite/SQLiteModule.kt +78 -48
  12. package/dev-plugin-dist/+not-found.html +1 -1
  13. package/dev-plugin-dist/_expo/static/js/web/{__common-725582fc6bba7ccfc743bb9aba24f9bc.js → __common-9b94dd2fbdbb825a662f42cf8d2077ee.js} +36 -36
  14. package/dev-plugin-dist/_expo/static/js/web/{_layout-171413bc9bf8452e491f0d7713017a36.js → _layout-b2894030c1dfbd95b717a85901b6c271.js} +22 -22
  15. package/dev-plugin-dist/_expo/static/js/web/{entry-46e748b71acc826f94c91796cdf4bc28.js → entry-04fdb7d857a65c0ed638ef2d774a4b6e.js} +547 -551
  16. package/dev-plugin-dist/_expo/static/js/web/{index-42fa8946372d43a04e2a3d5f7c907da9.js → index-4bf2da199f699eb47a42074d5e183c07.js} +1740 -1740
  17. package/dev-plugin-dist/_expo/static/js/web/{worker-b07a1b17f78db461acd77ea8aa0d302b.js → worker-d51aa6be89e271e571111274ab8e7f5c.js} +17 -17
  18. package/dev-plugin-dist/_sitemap.html +1 -1
  19. package/dev-plugin-dist/index.html +1 -1
  20. package/ios/NativeDatabase.swift +14 -5
  21. package/ios/NativeStatement.swift +23 -15
  22. package/ios/SQLiteModule.swift +46 -45
  23. package/ios/Tests/test_StatementCleanup.swift +84 -0
  24. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9-sources.jar +0 -0
  25. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9-sources.jar.md5 +1 -0
  26. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9-sources.jar.sha1 +1 -0
  27. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9-sources.jar.sha256 +1 -0
  28. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9-sources.jar.sha512 +1 -0
  29. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.aar +0 -0
  30. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.aar.md5 +1 -0
  31. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.aar.sha1 +1 -0
  32. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.aar.sha256 +1 -0
  33. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.aar.sha512 +1 -0
  34. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.7/expo.modules.sqlite-58.0.7.module → 58.0.9/expo.modules.sqlite-58.0.9.module} +22 -22
  35. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.module.md5 +1 -0
  36. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.module.sha1 +1 -0
  37. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.module.sha256 +1 -0
  38. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.module.sha512 +1 -0
  39. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/{58.0.7/expo.modules.sqlite-58.0.7.pom → 58.0.9/expo.modules.sqlite-58.0.9.pom} +1 -1
  40. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.pom.md5 +1 -0
  41. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.pom.sha1 +1 -0
  42. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.pom.sha256 +1 -0
  43. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.9/expo.modules.sqlite-58.0.9.pom.sha512 +1 -0
  44. package/package.json +5 -5
  45. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7-sources.jar +0 -0
  46. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7-sources.jar.md5 +0 -1
  47. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7-sources.jar.sha1 +0 -1
  48. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7-sources.jar.sha256 +0 -1
  49. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7-sources.jar.sha512 +0 -1
  50. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.aar +0 -0
  51. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.aar.md5 +0 -1
  52. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.aar.sha1 +0 -1
  53. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.aar.sha256 +0 -1
  54. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.aar.sha512 +0 -1
  55. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.module.md5 +0 -1
  56. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.module.sha1 +0 -1
  57. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.module.sha256 +0 -1
  58. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.module.sha512 +0 -1
  59. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.pom.md5 +0 -1
  60. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.pom.sha1 +0 -1
  61. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.pom.sha256 +0 -1
  62. package/local-maven-repo/host/exp/exponent/expo.modules.sqlite/58.0.7/expo.modules.sqlite-58.0.7.pom.sha512 +0 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 58.0.9
4
+
5
+ ### Patch Changes
6
+
7
+ - [Android][iOS] Fixed database closing failure with FTS ([#38168](https://github.com/expo/expo/issues/38168)). ([#50121](https://github.com/expo/expo/pull/50121) by [@savv](https://github.com/savv))
8
+
9
+ ## 58.0.8
10
+
11
+ ### Patch Changes
12
+
13
+ - Bump precompiled artifacts due to ABI breakage in expo-modules-jsi@58.0.6. ([#50881](https://github.com/expo/expo/pull/50881) by [@kitten](https://github.com/kitten))
14
+
3
15
  ## 58.0.7
4
16
 
5
17
  ### Patch Changes
@@ -47,13 +47,13 @@ def reactNativeArchitectures() {
47
47
  }
48
48
 
49
49
  group = 'host.exp.exponent'
50
- version = '58.0.7'
50
+ version = '58.0.9'
51
51
 
52
52
  android {
53
53
  namespace "expo.modules.sqlite"
54
54
  defaultConfig {
55
55
  versionCode 18
56
- versionName "58.0.7"
56
+ versionName "58.0.9"
57
57
  buildConfigField "boolean", "WITH_SQLITE_VEC", project.ext.WITH_SQLITE_VEC.toString()
58
58
 
59
59
  externalNativeBuild {
@@ -8,20 +8,12 @@ namespace jni = facebook::jni;
8
8
 
9
9
  namespace expo {
10
10
 
11
- namespace {
12
-
13
- constexpr char TAG[] = "expo-sqlite";
14
-
15
- } // namespace
16
-
17
11
  // static
18
12
  void NativeDatabaseBinding::registerNatives() {
19
13
  registerHybrid({
20
14
  makeNativeMethod("initHybrid", NativeDatabaseBinding::initHybrid),
21
15
  makeNativeMethod("sqlite3_changes",
22
16
  NativeDatabaseBinding::sqlite3_changes),
23
- makeNativeMethod("sqlite3_finalize_all_statement",
24
- NativeDatabaseBinding::sqlite3_finalize_all_statement),
25
17
  makeNativeMethod("sqlite3_close", NativeDatabaseBinding::sqlite3_close),
26
18
  makeNativeMethod("sqlite3_interrupt", NativeDatabaseBinding::sqlite3_interrupt),
27
19
  makeNativeMethod("sqlite3_db_filename",
@@ -52,20 +44,6 @@ void NativeDatabaseBinding::registerNatives() {
52
44
 
53
45
  int NativeDatabaseBinding::sqlite3_changes() { return ::exsqlite3_changes(db); }
54
46
 
55
- void NativeDatabaseBinding::sqlite3_finalize_all_statement() {
56
- ::exsqlite3_stmt *stmt = ::exsqlite3_next_stmt(db, nullptr);
57
- while (stmt) {
58
- ::exsqlite3_stmt *nextStmt = ::exsqlite3_next_stmt(db, stmt);
59
- int ret = ::exsqlite3_finalize(stmt);
60
- if (ret != SQLITE_OK) {
61
- std::string error = convertSqlLiteErrorToSTLString();
62
- __android_log_print(ANDROID_LOG_WARN, TAG,
63
- "exsqlite3_finalize failed: %s", error.c_str());
64
- }
65
- stmt = nextStmt;
66
- }
67
- }
68
-
69
47
  void NativeDatabaseBinding::sqlite3_interrupt() { ::exsqlite3_interrupt(db); }
70
48
 
71
49
  int NativeDatabaseBinding::sqlite3_close() {
@@ -21,7 +21,6 @@ public:
21
21
 
22
22
  // sqlite3 bindings
23
23
  int sqlite3_changes();
24
- void sqlite3_finalize_all_statement();
25
24
  int sqlite3_close();
26
25
  void sqlite3_interrupt();
27
26
  std::string sqlite3_db_filename(const std::string &databaseName);
@@ -59,7 +59,9 @@ std::string NativeStatementBinding::sqlite3_column_name(int index) {
59
59
  }
60
60
 
61
61
  int NativeStatementBinding::sqlite3_finalize() {
62
- return ::exsqlite3_finalize(stmt);
62
+ int ret = ::exsqlite3_finalize(stmt);
63
+ stmt = nullptr;
64
+ return ret;
63
65
  }
64
66
 
65
67
  int NativeStatementBinding::sqlite3_reset() { return ::exsqlite3_reset(stmt); }
@@ -47,7 +47,7 @@ private:
47
47
  friend HybridBase;
48
48
  friend NativeDatabaseBinding;
49
49
 
50
- exsqlite3_stmt *stmt;
50
+ exsqlite3_stmt *stmt = nullptr;
51
51
  };
52
52
 
53
53
  } // namespace expo
@@ -9,6 +9,11 @@ import java.util.concurrent.locks.ReentrantLock
9
9
  internal class NativeDatabase(val databasePath: String, val openOptions: OpenDatabaseOptions) : SharedRef<NativeDatabaseBinding>(NativeDatabaseBinding()) {
10
10
  var isClosed = false
11
11
  val closeLock = ReentrantLock()
12
+
13
+ // Protect statement registration and cleanup through the final close attempt.
14
+ // Running statements use their own locks, so interruption need not wait for them.
15
+ val statementLifecycleLock = Any()
16
+ val statements = mutableListOf<NativeStatement>()
12
17
  private val refCount = AtomicInteger(1)
13
18
 
14
19
  internal fun addRef(): Int {
@@ -44,7 +44,6 @@ internal class NativeDatabaseBinding : Closeable {
44
44
  // region sqlite3 bindings
45
45
 
46
46
  external fun sqlite3_changes(): Int
47
- external fun sqlite3_finalize_all_statement()
48
47
  external fun sqlite3_close(): Int
49
48
  external fun sqlite3_interrupt()
50
49
  external fun sqlite3_db_filename(databaseName: String): String
@@ -8,10 +8,18 @@ import java.nio.ByteBuffer
8
8
 
9
9
  internal class NativeStatement : SharedRef<NativeStatementBinding>(NativeStatementBinding()) {
10
10
  var isFinalized = false
11
+ var isPrepared = false
12
+ var releasedByJavaScript = false
11
13
 
12
14
  override fun sharedObjectDidRelease() {
13
15
  super.sharedObjectDidRelease()
14
- this.ref.close()
16
+ synchronized(this) {
17
+ if (isFinalized || !isPrepared) {
18
+ ref.close()
19
+ } else {
20
+ releasedByJavaScript = true
21
+ }
22
+ }
15
23
  }
16
24
 
17
25
  override fun equals(other: Any?): Boolean {
@@ -27,7 +27,13 @@ internal class InvalidConvertibleException(message: String) :
27
27
 
28
28
  @DoNotStrip
29
29
  internal class UnsupportedOperationException(message: String) :
30
- CodedException(if (message.isNotEmpty()) "Unsupported operations: $message" else "Unsupported operations")
30
+ CodedException(
31
+ if (message.isNotEmpty()) {
32
+ "Unsupported operations: $message"
33
+ } else {
34
+ "Unsupported operations"
35
+ }
36
+ )
31
37
 
32
38
  internal class AccessClosedResourceException :
33
39
  CodedException("Access to closed resource")
@@ -141,11 +141,7 @@ class SQLiteModule : Module() {
141
141
  }
142
142
 
143
143
  AsyncFunction("closeAsync") { database: NativeDatabase ->
144
- maybeThrowForClosedDatabase(database)
145
- val db = removeCachedDatabase(database)
146
- if (db != null) {
147
- closeDatabase(db)
148
- }
144
+ closeDatabaseIfNeeded(database)
149
145
  }.runOnQueue(moduleCoroutineScope)
150
146
  // Interrupt must reach SQLite immediately, without waiting for the running query's queue.
151
147
  Function("interruptSync") { database: NativeDatabase ->
@@ -161,11 +157,7 @@ class SQLiteModule : Module() {
161
157
  }
162
158
  }
163
159
  Function("closeSync") { database: NativeDatabase ->
164
- maybeThrowForClosedDatabase(database)
165
- val db = removeCachedDatabase(database)
166
- if (db != null) {
167
- closeDatabase(db)
168
- }
160
+ closeDatabaseIfNeeded(database)
169
161
  }
170
162
 
171
163
  AsyncFunction("execAsync") { database: NativeDatabase, source: String ->
@@ -242,12 +234,16 @@ class SQLiteModule : Module() {
242
234
  }
243
235
 
244
236
  AsyncFunction("getColumnNamesAsync") { statement: NativeStatement ->
245
- maybeThrowForFinalizedStatement(statement)
246
- return@AsyncFunction statement.ref.getColumnNames()
237
+ synchronized(statement) {
238
+ maybeThrowForFinalizedStatement(statement)
239
+ return@AsyncFunction statement.ref.getColumnNames()
240
+ }
247
241
  }.runOnQueue(moduleCoroutineScope)
248
242
  Function("getColumnNamesSync") { statement: NativeStatement ->
249
- maybeThrowForFinalizedStatement(statement)
250
- return@Function statement.ref.getColumnNames()
243
+ synchronized(statement) {
244
+ maybeThrowForFinalizedStatement(statement)
245
+ return@Function statement.ref.getColumnNames()
246
+ }
251
247
  }
252
248
 
253
249
  AsyncFunction("finalizeAsync") { statement: NativeStatement, database: NativeDatabase ->
@@ -371,21 +367,27 @@ class SQLiteModule : Module() {
371
367
 
372
368
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
373
369
  private fun prepareStatement(database: NativeDatabase, statement: NativeStatement, source: String) {
374
- maybeThrowForClosedDatabase(database)
375
- maybeThrowForFinalizedStatement(statement)
376
- if (database.ref.sqlite3_prepare_v2(source, statement.ref) != NativeDatabaseBinding.SQLITE_OK) {
377
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
370
+ synchronized(database.statementLifecycleLock) {
371
+ synchronized(statement) {
372
+ maybeThrowForFinalizedStatement(statement)
373
+ maybeThrowForClosedDatabase(database)
374
+ if (database.ref.sqlite3_prepare_v2(source, statement.ref) != NativeDatabaseBinding.SQLITE_OK) {
375
+ throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
376
+ }
377
+ statement.isPrepared = true
378
+ database.statements.add(statement)
379
+ }
378
380
  }
379
381
  }
380
382
 
381
383
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
382
384
  private fun run(statement: NativeStatement, database: NativeDatabase, bindParams: Map<String, Any?>, bindBlobParams: Map<String, ArrayBuffer>, shouldPassAsArray: Boolean): Map<String, Any> {
383
- maybeThrowForClosedDatabase(database)
384
- maybeThrowForFinalizedStatement(statement)
385
-
386
385
  // The statement with parameter bindings is stateful,
387
386
  // we have to guard with a critical section for thread safety.
388
387
  synchronized(statement) {
388
+ maybeThrowForFinalizedStatement(statement)
389
+ maybeThrowForClosedDatabase(database)
390
+
389
391
  statement.ref.sqlite3_reset()
390
392
  statement.ref.sqlite3_clear_bindings()
391
393
  for ((key, param) in bindParams) {
@@ -429,11 +431,11 @@ class SQLiteModule : Module() {
429
431
 
430
432
  @Throws(AccessClosedResourceException::class, InvalidConvertibleException::class, SQLiteErrorException::class)
431
433
  private fun step(statement: NativeStatement, database: NativeDatabase): SQLiteColumnValues? {
432
- maybeThrowForClosedDatabase(database)
433
- maybeThrowForFinalizedStatement(statement)
434
-
435
434
  // Guard the stateful statement, see `run` above.
436
435
  synchronized(statement) {
436
+ maybeThrowForFinalizedStatement(statement)
437
+ maybeThrowForClosedDatabase(database)
438
+
437
439
  val ret = statement.ref.sqlite3_step()
438
440
  if (ret == NativeDatabaseBinding.SQLITE_ROW) {
439
441
  return statement.getTransformedColumnValues()
@@ -447,11 +449,11 @@ class SQLiteModule : Module() {
447
449
 
448
450
  @Throws(AccessClosedResourceException::class, InvalidConvertibleException::class, SQLiteErrorException::class)
449
451
  private fun getAll(statement: NativeStatement, database: NativeDatabase): List<SQLiteColumnValues> {
450
- maybeThrowForClosedDatabase(database)
451
- maybeThrowForFinalizedStatement(statement)
452
-
453
452
  // Guard the stateful statement, see `run` above.
454
453
  synchronized(statement) {
454
+ maybeThrowForFinalizedStatement(statement)
455
+ maybeThrowForClosedDatabase(database)
456
+
455
457
  val columnValuesList = mutableListOf<SQLiteColumnValues>()
456
458
  while (true) {
457
459
  val ret = statement.ref.sqlite3_step()
@@ -469,11 +471,11 @@ class SQLiteModule : Module() {
469
471
 
470
472
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
471
473
  private fun reset(statement: NativeStatement, database: NativeDatabase) {
472
- maybeThrowForClosedDatabase(database)
473
- maybeThrowForFinalizedStatement(statement)
474
-
475
474
  // Guard the stateful statement, see `run` above.
476
475
  synchronized(statement) {
476
+ maybeThrowForFinalizedStatement(statement)
477
+ maybeThrowForClosedDatabase(database)
478
+
477
479
  if (statement.ref.sqlite3_reset() != NativeDatabaseBinding.SQLITE_OK) {
478
480
  throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
479
481
  }
@@ -482,15 +484,23 @@ class SQLiteModule : Module() {
482
484
 
483
485
  @Throws(AccessClosedResourceException::class, SQLiteErrorException::class)
484
486
  private fun finalize(statement: NativeStatement, database: NativeDatabase) {
485
- maybeThrowForClosedDatabase(database)
486
- maybeThrowForFinalizedStatement(statement)
487
+ synchronized(database.statementLifecycleLock) {
488
+ // Guard the stateful statement, see `run` above.
489
+ synchronized(statement) {
490
+ maybeThrowForFinalizedStatement(statement)
491
+ maybeThrowForClosedDatabase(database)
487
492
 
488
- // Guard the stateful statement, see `run` above.
489
- synchronized(statement) {
490
- if (statement.ref.sqlite3_finalize() != NativeDatabaseBinding.SQLITE_OK) {
491
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
493
+ val ret = statement.ref.sqlite3_finalize()
494
+ // SQLite destroys the statement even when returning an earlier execution error.
495
+ statement.isFinalized = true
496
+ database.statements.removeAll { it === statement }
497
+ if (statement.releasedByJavaScript) {
498
+ statement.ref.close()
499
+ }
500
+ if (ret != NativeDatabaseBinding.SQLITE_OK) {
501
+ throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
502
+ }
492
503
  }
493
- statement.isFinalized = true
494
504
  }
495
505
  }
496
506
 
@@ -527,13 +537,15 @@ class SQLiteModule : Module() {
527
537
  private fun closeDatabase(database: NativeDatabase) {
528
538
  database.closeLock.lock()
529
539
  try {
530
- maybeThrowForClosedDatabase(database)
531
- maybeFinalizeAllStatements(database)
532
- val ret = database.ref.sqlite3_close()
533
- if (ret != NativeDatabaseBinding.SQLITE_OK) {
534
- throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
540
+ synchronized(database.statementLifecycleLock) {
541
+ maybeThrowForClosedDatabase(database)
542
+ maybeFinalizeAllStatements(database)
543
+ val ret = database.ref.sqlite3_close()
544
+ if (ret != NativeDatabaseBinding.SQLITE_OK) {
545
+ throw SQLiteErrorException(database.ref.convertSqlLiteErrorToString())
546
+ }
547
+ database.isClosed = true
535
548
  }
536
- database.isClosed = true
537
549
  } finally {
538
550
  database.closeLock.unlock()
539
551
  }
@@ -587,16 +599,22 @@ class SQLiteModule : Module() {
587
599
  }
588
600
 
589
601
  @Synchronized
590
- private fun removeCachedDatabase(database: NativeDatabase): NativeDatabase? {
602
+ private fun closeDatabaseIfNeeded(database: NativeDatabase) {
603
+ maybeThrowForClosedDatabase(database)
591
604
  val index = cachedDatabases.indexOf(database)
592
605
  if (index >= 0) {
593
606
  val db = cachedDatabases[index]
594
607
  if (db.release() == 0) {
608
+ try {
609
+ closeDatabase(db)
610
+ } catch (error: Exception) {
611
+ // Keep the connection cached and owned so callers can clean up and retry.
612
+ db.addRef()
613
+ throw error
614
+ }
595
615
  cachedDatabases.removeAt(index)
596
- return db
597
616
  }
598
617
  }
599
- return null
600
618
  }
601
619
 
602
620
  @Synchronized
@@ -615,12 +633,24 @@ class SQLiteModule : Module() {
615
633
 
616
634
  // region statements managements
617
635
 
618
- @Synchronized
619
636
  private fun maybeFinalizeAllStatements(database: NativeDatabase) {
620
637
  if (!database.openOptions.finalizeUnusedStatementsBeforeClosing) {
621
638
  return
622
639
  }
623
- database.ref.sqlite3_finalize_all_statement()
640
+ // Finalize through the wrappers so even a failed close leaves them invalidated.
641
+ // Do not destroy SQLite-internal statements owned by concurrent exec/backup operations.
642
+ var firstError: Exception? = null
643
+ for (statement in database.statements.toList()) {
644
+ try {
645
+ finalize(statement, database)
646
+ } catch (error: SQLiteErrorException) {
647
+ android.util.Log.w("expo-sqlite", "Finalizing a statement during close failed", error)
648
+ } catch (error: Exception) {
649
+ // A broken wrapper must not prevent cleanup of the remaining statements.
650
+ firstError = firstError ?: error
651
+ }
652
+ }
653
+ firstError?.let { throw it }
624
654
  }
625
655
 
626
656
  // endregion
@@ -314,5 +314,5 @@ input::-webkit-search-cancel-button,input::-webkit-search-decoration,input::-web
314
314
  @keyframes r-imtty0{0%{opacity:0;}100%{opacity:1;}}
315
315
  @keyframes r-q67da2{0%{transform:translateX(-100%);}100%{transform:translateX(400%);}}
316
316
  @keyframes r-t2lo5v{0%{opacity:1;}100%{opacity:0;}}</style><script type="module">globalThis.__EXPO_ROUTER_HYDRATE__=true;</script><link rel="preload" href="/_expo/plugins/expo-sqlite/_expo/static/css/global-b1bdf830cdbd0ec4238cc5f230f99460.css" as="style">
317
- <link rel="stylesheet" href="/_expo/plugins/expo-sqlite/_expo/static/css/global-b1bdf830cdbd0ec4238cc5f230f99460.css"></head><body><div id="root"><div class="css-g5y9jx r-13awgt0"></div></div><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/__expo-metro-runtime-8c088df87a31e4e6accd81c554ec290d.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/_layout-171413bc9bf8452e491f0d7713017a36.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/__common-725582fc6bba7ccfc743bb9aba24f9bc.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/entry-46e748b71acc826f94c91796cdf4bc28.js" defer></script>
317
+ <link rel="stylesheet" href="/_expo/plugins/expo-sqlite/_expo/static/css/global-b1bdf830cdbd0ec4238cc5f230f99460.css"></head><body><div id="root"><div class="css-g5y9jx r-13awgt0"></div></div><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/__expo-metro-runtime-8c088df87a31e4e6accd81c554ec290d.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/_layout-b2894030c1dfbd95b717a85901b6c271.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/__common-9b94dd2fbdbb825a662f42cf8d2077ee.js" defer></script><script src="/_expo/plugins/expo-sqlite/_expo/static/js/web/entry-04fdb7d857a65c0ed638ef2d774a4b6e.js" defer></script>
318
318
  </body></html>