expo-sqlite 11.1.1 → 11.3.0

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.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 11.3.0 — 2023-06-21
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed Android build warnings for Gradle version 8. ([#22537](https://github.com/expo/expo/pull/22537), [#22609](https://github.com/expo/expo/pull/22609) by [@kudo](https://github.com/kudo))
18
+
19
+ ## 11.2.0 — 2023-05-08
20
+
21
+ ### 🎉 New features
22
+
23
+ - Migrated to Expo Modules API. ([#21721](https://github.com/expo/expo/pull/21721) by [@alanjhughes](https://github.com/alanjhughes))
24
+
13
25
  ## 11.1.1 — 2023-02-09
14
26
 
15
27
  _This version does not introduce any user-facing changes._
package/README.md CHANGED
@@ -1,4 +1,11 @@
1
- # expo-sqlite
1
+ <p>
2
+ <a href="https://docs.expo.dev/versions/latest/sdk/sqlite/">
3
+ <img
4
+ src="../../.github/resources/expo-sqlite.svg"
5
+ alt="expo-sqlite"
6
+ height="64" />
7
+ </a>
8
+ </p>
2
9
 
3
10
  Provides access to a database that can be queried through a WebSQL-like API (https://www.w3.org/TR/webdatabase/). The database is persisted across restarts of your app.
4
11
 
@@ -9,7 +16,7 @@ Provides access to a database that can be queried through a WebSQL-like API (htt
9
16
 
10
17
  # Installation in managed Expo projects
11
18
 
12
- For [managed](https://docs.expo.dev/versions/latest/introduction/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/sqlite/).
19
+ For [managed](https://docs.expo.dev/archive/managed-vs-bare/) Expo projects, please follow the installation instructions in the [API documentation for the latest stable release](https://docs.expo.dev/versions/latest/sdk/sqlite/).
13
20
 
14
21
  # Installation in bare React Native projects
15
22
 
@@ -18,7 +25,7 @@ For bare React Native projects, you must ensure that you have [installed and con
18
25
  ### Add the package to your npm dependencies
19
26
 
20
27
  ```
21
- expo install expo-sqlite
28
+ npx expo install expo-sqlite
22
29
  ```
23
30
 
24
31
  ### Configure for iOS
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '11.1.1'
6
+ version = '11.3.0'
7
7
 
8
8
  buildscript {
9
9
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -35,19 +35,11 @@ buildscript {
35
35
  }
36
36
  }
37
37
 
38
- // Creating sources with comments
39
- task androidSourcesJar(type: Jar) {
40
- classifier = 'sources'
41
- from android.sourceSets.main.java.srcDirs
42
- }
43
-
44
38
  afterEvaluate {
45
39
  publishing {
46
40
  publications {
47
41
  release(MavenPublication) {
48
42
  from components.release
49
- // Add additional sourcesJar to artifacts
50
- artifact(androidSourcesJar)
51
43
  }
52
44
  }
53
45
  repositories {
@@ -70,15 +62,21 @@ android {
70
62
  jvmTarget = JavaVersion.VERSION_11.majorVersion
71
63
  }
72
64
 
65
+ namespace "expo.modules.sqlite"
73
66
  defaultConfig {
74
67
  minSdkVersion safeExtGet("minSdkVersion", 21)
75
68
  targetSdkVersion safeExtGet("targetSdkVersion", 33)
76
69
  versionCode 18
77
- versionName "11.1.1"
70
+ versionName "11.3.0"
78
71
  }
79
72
  lintOptions {
80
73
  abortOnError false
81
74
  }
75
+ publishing {
76
+ singleVariant("release") {
77
+ withSourcesJar()
78
+ }
79
+ }
82
80
  }
83
81
 
84
82
  dependencies {
@@ -1,5 +1,3 @@
1
-
2
- <manifest package="expo.modules.sqlite">
1
+ <manifest>
3
2
 
4
3
  </manifest>
5
-
@@ -19,7 +19,7 @@ private val DATABASES: MutableMap<String, SQLiteDatabase?> = HashMap()
19
19
 
20
20
  class SQLiteModule(private val mContext: Context) : ExportedModule(mContext) {
21
21
  override fun getName(): String {
22
- return "ExponentSQLite"
22
+ return "ExpoSQLite"
23
23
  }
24
24
 
25
25
  @ExpoMethod
package/build/SQLite.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import './polyfillNextTick';
2
2
  import customOpenDatabase from '@expo/websql/custom';
3
- import { NativeModulesProxy } from 'expo-modules-core';
3
+ import { requireNativeModule } from 'expo-modules-core';
4
4
  import { Platform } from 'react-native';
5
- const { ExponentSQLite } = NativeModulesProxy;
5
+ const ExpoSQLite = requireNativeModule('ExpoSQLite');
6
6
  function zipObject(keys, values) {
7
7
  const result = {};
8
8
  for (let i = 0; i < keys.length; i++) {
@@ -20,7 +20,7 @@ class SQLiteDatabase {
20
20
  if (this._closed) {
21
21
  throw new Error(`The SQLite database is closed`);
22
22
  }
23
- ExponentSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then((nativeResultSets) => {
23
+ ExpoSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then((nativeResultSets) => {
24
24
  callback(null, nativeResultSets.map(_deserializeResultSet));
25
25
  }, (error) => {
26
26
  // TODO: make the native API consistently reject with an error, not a string or other type
@@ -29,13 +29,13 @@ class SQLiteDatabase {
29
29
  }
30
30
  close() {
31
31
  this._closed = true;
32
- return ExponentSQLite.close(this._name);
32
+ return ExpoSQLite.close(this._name);
33
33
  }
34
34
  deleteAsync() {
35
35
  if (!this._closed) {
36
36
  throw new Error(`Unable to delete '${this._name}' database that is currently open. Close it prior to deletion.`);
37
37
  }
38
- return ExponentSQLite.deleteAsync(this._name);
38
+ return ExpoSQLite.deleteAsync(this._name);
39
39
  }
40
40
  }
41
41
  function _serializeQuery(query) {
@@ -1 +1 @@
1
- {"version":3,"file":"SQLite.js","sourceRoot":"","sources":["../src/SQLite.ts"],"names":[],"mappings":"AAAA,OAAO,oBAAoB,CAAC;AAE5B,OAAO,kBAAkB,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAIxC,MAAM,EAAE,cAAc,EAAE,GAAG,kBAAkB,CAAC;AAE9C,SAAS,SAAS,CAAC,IAAc,EAAE,MAAa;IAC9C,MAAM,MAAM,GAAG,EAAE,CAAC;IAClB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;QACpC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;KAC7B;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,cAAc;IAClB,KAAK,CAAS;IACd,OAAO,GAAY,KAAK,CAAC;IAEzB,YAAY,IAAY;QACtB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,IAAI,CAAC,OAAgB,EAAE,QAAiB,EAAE,QAAwB;QAChE,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;SAClD;QAED,cAAc,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAC1E,CAAC,gBAAgB,EAAE,EAAE;YACnB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAC;QAC9D,CAAC,EACD,CAAC,KAAK,EAAE,EAAE;YACR,0FAA0F;YAC1F,QAAQ,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9D,CAAC,CACF,CAAC;IACJ,CAAC;IAED,KAAK;QACH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACpB,OAAO,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED,WAAW;QACT,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE;YACjB,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,CAAC,KAAK,gEAAgE,CAChG,CAAC;SACH;QAED,OAAO,cAAc,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC;CACF;AAED,SAAS,eAAe,CAAC,KAAY;IACnC,OAAO,CAAC,KAAK,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,KAAK,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC3F,CAAC;AAED,SAAS,qBAAqB,CAAC,YAAY;IACzC,MAAM,CAAC,YAAY,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,CAAC,GAAG,YAAY,CAAC;IAC3E,iGAAiG;IACjG,wBAAwB;IACxB,IAAI,YAAY,KAAK,IAAI,EAAE;QACzB,OAAO,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,YAAY,CAAC,EAAoB,CAAC;KAC7D;IAED,OAAO;QACL,QAAQ;QACR,YAAY;QACZ,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;KACjD,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAI,IAAO;IAC7B,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE;QAC5B,qCAAqC;QACrC,OAAO,IAAI;aACR,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC;aAClC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAC;aAClC,OAAO,CAAC,SAAS,EAAE,cAAc,CAAQ,CAAC;QAC7C,oCAAoC;KACrC;SAAM;QACL,OAAO,IAAI,CAAC;KACb;AACH,CAAC;AAED,MAAM,uBAAuB,GAAG,kBAAkB,CAAC,cAAc,CAAC,CAAC;AAEnE,2BAA2B;AAC3B;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,UAAkB,KAAK,EACvB,cAAsB,IAAI,EAC1B,OAAe,CAAC,EAChB,QAAuC;IAEvC,IAAI,IAAI,KAAK,SAAS,EAAE;QACtB,MAAM,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;KAChE;IACD,MAAM,EAAE,GAAG,uBAAuB,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC/E,EAAE,CAAC,IAAI,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACnC,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IAC1C,EAAE,CAAC,WAAW,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACjD,OAAO,EAAE,CAAC;AACZ,CAAC","sourcesContent":["import './polyfillNextTick';\n\nimport customOpenDatabase from '@expo/websql/custom';\nimport { NativeModulesProxy } from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nimport { Query, ResultSet, ResultSetError, SQLiteCallback, WebSQLDatabase } from './SQLite.types';\n\nconst { ExponentSQLite } = NativeModulesProxy;\n\nfunction zipObject(keys: string[], values: any[]) {\n const result = {};\n for (let i = 0; i < keys.length; i++) {\n result[keys[i]] = values[i];\n }\n return result;\n}\n\nclass SQLiteDatabase {\n _name: string;\n _closed: boolean = false;\n\n constructor(name: string) {\n this._name = name;\n }\n\n exec(queries: Query[], readOnly: boolean, callback: SQLiteCallback): void {\n if (this._closed) {\n throw new Error(`The SQLite database is closed`);\n }\n\n ExponentSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then(\n (nativeResultSets) => {\n callback(null, nativeResultSets.map(_deserializeResultSet));\n },\n (error) => {\n // TODO: make the native API consistently reject with an error, not a string or other type\n callback(error instanceof Error ? error : new Error(error));\n }\n );\n }\n\n close() {\n this._closed = true;\n return ExponentSQLite.close(this._name);\n }\n\n deleteAsync(): Promise<void> {\n if (!this._closed) {\n throw new Error(\n `Unable to delete '${this._name}' database that is currently open. Close it prior to deletion.`\n );\n }\n\n return ExponentSQLite.deleteAsync(this._name);\n }\n}\n\nfunction _serializeQuery(query: Query): [string, unknown[]] {\n return [query.sql, Platform.OS === 'android' ? query.args.map(_escapeBlob) : query.args];\n}\n\nfunction _deserializeResultSet(nativeResult): ResultSet | ResultSetError {\n const [errorMessage, insertId, rowsAffected, columns, rows] = nativeResult;\n // TODO: send more structured error information from the native module so we can better construct\n // a SQLException object\n if (errorMessage !== null) {\n return { error: new Error(errorMessage) } as ResultSetError;\n }\n\n return {\n insertId,\n rowsAffected,\n rows: rows.map((row) => zipObject(columns, row)),\n };\n}\n\nfunction _escapeBlob<T>(data: T): T {\n if (typeof data === 'string') {\n /* eslint-disable no-control-regex */\n return data\n .replace(/\\u0002/g, '\\u0002\\u0002')\n .replace(/\\u0001/g, '\\u0001\\u0002')\n .replace(/\\u0000/g, '\\u0001\\u0001') as any;\n /* eslint-enable no-control-regex */\n } else {\n return data;\n }\n}\n\nconst _openExpoSQLiteDatabase = customOpenDatabase(SQLiteDatabase);\n\n// @needsAudit @docsMissing\n/**\n * Open a database, creating it if it doesn't exist, and return a `Database` object. On disk,\n * the database will be created under the app's [documents directory](./filesystem), i.e.\n * `${FileSystem.documentDirectory}/SQLite/${name}`.\n * > The `version`, `description` and `size` arguments are ignored, but are accepted by the function\n * for compatibility with the WebSQL specification.\n * @param name Name of the database file to open.\n * @param version\n * @param description\n * @param size\n * @param callback\n * @return\n */\nexport function openDatabase(\n name: string,\n version: string = '1.0',\n description: string = name,\n size: number = 1,\n callback?: (db: WebSQLDatabase) => void\n): WebSQLDatabase {\n if (name === undefined) {\n throw new TypeError(`The database name must not be undefined`);\n }\n const db = _openExpoSQLiteDatabase(name, version, description, size, callback);\n db.exec = db._db.exec.bind(db._db);\n db.closeAsync = db._db.close.bind(db._db);\n db.deleteAsync = db._db.deleteAsync.bind(db._db);\n return db;\n}\n"]}
1
+ {"version":3,"file":"SQLite.js","sourceRoot":"","sources":["../src/SQLite.ts"],"names":[],"mappings":"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","sourcesContent":["import './polyfillNextTick';\n\nimport customOpenDatabase from '@expo/websql/custom';\nimport { requireNativeModule } from 'expo-modules-core';\nimport { Platform } from 'react-native';\n\nimport { Query, ResultSet, ResultSetError, SQLiteCallback, WebSQLDatabase } from './SQLite.types';\n\nconst ExpoSQLite = requireNativeModule('ExpoSQLite');\n\nfunction zipObject(keys: string[], values: any[]) {\n const result = {};\n for (let i = 0; i < keys.length; i++) {\n result[keys[i]] = values[i];\n }\n return result;\n}\n\nclass SQLiteDatabase {\n _name: string;\n _closed: boolean = false;\n\n constructor(name: string) {\n this._name = name;\n }\n\n exec(queries: Query[], readOnly: boolean, callback: SQLiteCallback): void {\n if (this._closed) {\n throw new Error(`The SQLite database is closed`);\n }\n\n ExpoSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then(\n (nativeResultSets) => {\n callback(null, nativeResultSets.map(_deserializeResultSet));\n },\n (error) => {\n // TODO: make the native API consistently reject with an error, not a string or other type\n callback(error instanceof Error ? error : new Error(error));\n }\n );\n }\n\n close() {\n this._closed = true;\n return ExpoSQLite.close(this._name);\n }\n\n deleteAsync(): Promise<void> {\n if (!this._closed) {\n throw new Error(\n `Unable to delete '${this._name}' database that is currently open. Close it prior to deletion.`\n );\n }\n\n return ExpoSQLite.deleteAsync(this._name);\n }\n}\n\nfunction _serializeQuery(query: Query): [string, unknown[]] {\n return [query.sql, Platform.OS === 'android' ? query.args.map(_escapeBlob) : query.args];\n}\n\nfunction _deserializeResultSet(nativeResult): ResultSet | ResultSetError {\n const [errorMessage, insertId, rowsAffected, columns, rows] = nativeResult;\n // TODO: send more structured error information from the native module so we can better construct\n // a SQLException object\n if (errorMessage !== null) {\n return { error: new Error(errorMessage) } as ResultSetError;\n }\n\n return {\n insertId,\n rowsAffected,\n rows: rows.map((row) => zipObject(columns, row)),\n };\n}\n\nfunction _escapeBlob<T>(data: T): T {\n if (typeof data === 'string') {\n /* eslint-disable no-control-regex */\n return data\n .replace(/\\u0002/g, '\\u0002\\u0002')\n .replace(/\\u0001/g, '\\u0001\\u0002')\n .replace(/\\u0000/g, '\\u0001\\u0001') as any;\n /* eslint-enable no-control-regex */\n } else {\n return data;\n }\n}\n\nconst _openExpoSQLiteDatabase = customOpenDatabase(SQLiteDatabase);\n\n// @needsAudit @docsMissing\n/**\n * Open a database, creating it if it doesn't exist, and return a `Database` object. On disk,\n * the database will be created under the app's [documents directory](./filesystem), i.e.\n * `${FileSystem.documentDirectory}/SQLite/${name}`.\n * > The `version`, `description` and `size` arguments are ignored, but are accepted by the function\n * for compatibility with the WebSQL specification.\n * @param name Name of the database file to open.\n * @param version\n * @param description\n * @param size\n * @param callback\n * @return\n */\nexport function openDatabase(\n name: string,\n version: string = '1.0',\n description: string = name,\n size: number = 1,\n callback?: (db: WebSQLDatabase) => void\n): WebSQLDatabase {\n if (name === undefined) {\n throw new TypeError(`The database name must not be undefined`);\n }\n const db = _openExpoSQLiteDatabase(name, version, description, size, callback);\n db.exec = db._db.exec.bind(db._db);\n db.closeAsync = db._db.close.bind(db._db);\n db.deleteAsync = db._db.deleteAsync.bind(db._db);\n return db;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "expo-sqlite",
3
+ "platforms": ["ios", "android"],
4
+ "ios": {
5
+ "modules": ["SQLiteModule"]
6
+ }
7
+ }
@@ -0,0 +1,53 @@
1
+ import ExpoModulesCore
2
+
3
+ internal class DatabaseException: Exception {
4
+ override var code: String {
5
+ "E_SQLITE_OPEN_DATABASE"
6
+ }
7
+
8
+ override var reason: String {
9
+ "Could not open database"
10
+ }
11
+ }
12
+
13
+ internal class DeleteDatabaseException: GenericException<String> {
14
+ override var code: String {
15
+ "E_SQLITE_DELETE_DATABASE"
16
+ }
17
+
18
+ override var reason: String {
19
+ "Unable to delete database \(param) that is currently open. Close it prior to deletion"
20
+ }
21
+ }
22
+
23
+ internal class DatabaseNotFoundException: GenericException<String> {
24
+ override var code: String {
25
+ "E_SQLITE_DELETE_DATABASE"
26
+ }
27
+
28
+ override var reason: String {
29
+ "Database \(param) not found"
30
+ }
31
+ }
32
+
33
+ internal class DeleteDatabaseFileException: GenericException<String> {
34
+ override var code: String {
35
+ "E_SQLITE_DELETE_DATABASE"
36
+ }
37
+
38
+ override var reason: String {
39
+ "Unable to delete the database file for \(param) database"
40
+ }
41
+ }
42
+
43
+ internal class InvalidSqlException: Exception {
44
+ override var reason: String {
45
+ "sql argument must be a string"
46
+ }
47
+ }
48
+
49
+ internal class InvalidArgumentsException: Exception {
50
+ override var reason: String {
51
+ "args must be an array"
52
+ }
53
+ }
@@ -3,7 +3,7 @@ require 'json'
3
3
  package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
4
 
5
5
  Pod::Spec.new do |s|
6
- s.name = 'EXSQLite'
6
+ s.name = 'ExpoSQLite'
7
7
  s.version = package['version']
8
8
  s.summary = package['description']
9
9
  s.description = package['description']
@@ -17,9 +17,9 @@ Pod::Spec.new do |s|
17
17
  s.dependency 'ExpoModulesCore'
18
18
 
19
19
  if !$ExpoUseSources&.include?(package['name']) && ENV['EXPO_USE_SOURCE'].to_i == 0 && File.exist?("#{s.name}.xcframework") && Gem::Version.new(Pod::VERSION) >= Gem::Version.new('1.10.0')
20
- s.source_files = "#{s.name}/**/*.h"
20
+ s.source_files = "**/*.h"
21
21
  s.vendored_frameworks = "#{s.name}.xcframework"
22
22
  else
23
- s.source_files = "#{s.name}/**/*.{h,m}"
23
+ s.source_files = "**/*.{h,m,swift}"
24
24
  end
25
25
  end
@@ -0,0 +1,211 @@
1
+ import ExpoModulesCore
2
+ import SQLite3
3
+
4
+ public final class SQLiteModule: Module {
5
+ private var cachedDatabases = [String: OpaquePointer]()
6
+
7
+ public func definition() -> ModuleDefinition {
8
+ Name("ExpoSQLite")
9
+
10
+ AsyncFunction("exec") { (dbName: String, queries: [[Any]], readOnly: Bool) -> [Any?] in
11
+ guard let db = openDatabase(dbName: dbName) else {
12
+ throw DatabaseException()
13
+ }
14
+
15
+ let results = try queries.map { query in
16
+ guard let sql = query[0] as? String else {
17
+ throw InvalidSqlException()
18
+ }
19
+
20
+ guard let args = query[1] as? [Any] else {
21
+ throw InvalidArgumentsException()
22
+ }
23
+
24
+ return executeSql(sql: sql, with: args, for: db, readOnly: readOnly)
25
+ }
26
+
27
+ return results
28
+ }
29
+
30
+ AsyncFunction("close") { (dbName: String) in
31
+ cachedDatabases.removeValue(forKey: dbName)
32
+ }
33
+
34
+ AsyncFunction("deleteAsync") { (dbName: String) in
35
+ if cachedDatabases[dbName] != nil {
36
+ throw DeleteDatabaseException(dbName)
37
+ }
38
+
39
+ guard let path = self.pathForDatabaseName(name: dbName) else {
40
+ throw Exceptions.FileSystemModuleNotFound()
41
+ }
42
+
43
+ if !FileManager.default.fileExists(atPath: path.absoluteString) {
44
+ throw DatabaseNotFoundException(dbName)
45
+ }
46
+
47
+ do {
48
+ try FileManager.default.removeItem(atPath: path.absoluteString)
49
+ } catch {
50
+ throw DeleteDatabaseFileException(dbName)
51
+ }
52
+ }
53
+
54
+ OnDestroy {
55
+ cachedDatabases.values.forEach {
56
+ sqlite3_close($0)
57
+ }
58
+ }
59
+ }
60
+
61
+ private func pathForDatabaseName(name: String) -> URL? {
62
+ guard let fileSystem = appContext?.fileSystem else {
63
+ return nil
64
+ }
65
+
66
+ var directory = URL(string: fileSystem.documentDirectory)?.appendingPathComponent("SQLite")
67
+ fileSystem.ensureDirExists(withPath: directory?.absoluteString)
68
+
69
+ return directory?.appendingPathComponent(name)
70
+ }
71
+
72
+ private func openDatabase(dbName: String) -> OpaquePointer? {
73
+ var db: OpaquePointer?
74
+ guard let path = try pathForDatabaseName(name: dbName) else {
75
+ return nil
76
+ }
77
+
78
+ let fileExists = FileManager.default.fileExists(atPath: path.absoluteString)
79
+
80
+ if fileExists {
81
+ db = cachedDatabases[dbName]
82
+ }
83
+
84
+ if db == nil {
85
+ cachedDatabases.removeValue(forKey: dbName)
86
+ if sqlite3_open(path.absoluteString, &db) != SQLITE_OK {
87
+ return nil
88
+ }
89
+
90
+ cachedDatabases[dbName] = db
91
+ }
92
+ return db
93
+ }
94
+
95
+ private func executeSql(sql: String, with args: [Any], for db: OpaquePointer, readOnly: Bool) -> [Any?] {
96
+ var resultRows = [Any]()
97
+ var statement: OpaquePointer?
98
+ var rowsAffected: Int32 = 0
99
+ var insertId: Int64 = 0
100
+ var error: String?
101
+
102
+ if sqlite3_prepare_v2(db, sql, -1, &statement, nil) != SQLITE_OK {
103
+ return [convertSqlLiteErrorToString(db: db)]
104
+ }
105
+
106
+ let queryIsReadOnly = sqlite3_stmt_readonly(statement) > 0
107
+
108
+ if readOnly && !queryIsReadOnly {
109
+ return ["could not prepare \(sql)"]
110
+ }
111
+
112
+ for (index, arg) in args.enumerated() {
113
+ guard let obj = arg as? NSObject else { continue }
114
+ bindStatement(statement: statement, with: obj, at: Int32(index + 1))
115
+ }
116
+
117
+ var columnCount: Int32 = 0
118
+ var columnNames = [String]()
119
+ var columnType: Int32
120
+ var fetchedColumns = false
121
+ var value: Any?
122
+ var hasMore = true
123
+
124
+ while hasMore {
125
+ let result = sqlite3_step(statement)
126
+
127
+ switch result {
128
+ case SQLITE_ROW:
129
+ if !fetchedColumns {
130
+ columnCount = sqlite3_column_count(statement)
131
+
132
+ for i in 0..<Int(columnCount) {
133
+ let columnName = NSString(format: "%s", sqlite3_column_name(statement, Int32(i))) as String
134
+ columnNames.append(columnName)
135
+ }
136
+ fetchedColumns = true
137
+ }
138
+
139
+ var entry = [Any]()
140
+
141
+ for i in 0..<Int(columnCount) {
142
+ columnType = sqlite3_column_type(statement, Int32(i))
143
+ value = getSqlValue(for: columnType, with: statement, index: Int32(i))
144
+ entry.append(value)
145
+ }
146
+
147
+ resultRows.append(entry)
148
+ case SQLITE_DONE:
149
+ hasMore = false
150
+ default:
151
+ error = convertSqlLiteErrorToString(db: db)
152
+ hasMore = false
153
+ }
154
+ }
155
+
156
+ if !queryIsReadOnly {
157
+ rowsAffected = sqlite3_changes(db)
158
+ if rowsAffected > 0 {
159
+ insertId = sqlite3_last_insert_rowid(db)
160
+ }
161
+ }
162
+
163
+ sqlite3_finalize(statement)
164
+
165
+ if error != nil {
166
+ return [error]
167
+ }
168
+
169
+ return [nil, insertId, rowsAffected, columnNames, resultRows]
170
+ }
171
+
172
+ private func bindStatement(statement: OpaquePointer?, with arg: NSObject, at index: Int32) {
173
+ if arg == NSNull() {
174
+ sqlite3_bind_null(statement, index)
175
+ } else if arg is Double {
176
+ sqlite3_bind_double(statement, index, arg as? Double ?? 0.0)
177
+ } else {
178
+ var stringArg: NSString
179
+
180
+ if arg is NSString {
181
+ stringArg = NSString(format: "%@", arg)
182
+ } else {
183
+ stringArg = arg.description as NSString
184
+ }
185
+
186
+ let SQLITE_TRANSIENT = unsafeBitCast(OpaquePointer(bitPattern: -1), to: sqlite3_destructor_type.self)
187
+
188
+ let data = stringArg.data(using: NSUTF8StringEncoding)
189
+ sqlite3_bind_text(statement, index, stringArg.utf8String, Int32(data?.count ?? 0), SQLITE_TRANSIENT)
190
+ }
191
+ }
192
+
193
+ private func getSqlValue(for columnType: Int32, with statement: OpaquePointer?, index: Int32) -> Any? {
194
+ switch columnType {
195
+ case SQLITE_INTEGER:
196
+ return sqlite3_column_int64(statement, index)
197
+ case SQLITE_FLOAT:
198
+ return sqlite3_column_double(statement, index)
199
+ case SQLITE_BLOB, SQLITE_TEXT:
200
+ return NSString(bytes: sqlite3_column_text(statement, index), length: Int(sqlite3_column_bytes(statement, index)), encoding: NSUTF8StringEncoding)
201
+ default:
202
+ return nil
203
+ }
204
+ }
205
+
206
+ private func convertSqlLiteErrorToString(db: OpaquePointer?) -> String {
207
+ let code = sqlite3_errcode(db)
208
+ let message = NSString(utf8String: sqlite3_errmsg(db)) ?? ""
209
+ return NSString(format: "Error code %i: %@", code, message) as String
210
+ }
211
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-sqlite",
3
- "version": "11.1.1",
3
+ "version": "11.3.0",
4
4
  "description": "Provides access to a database that can be queried through a WebSQL-like API (https://www.w3.org/TR/webdatabase/). The database is persisted across restarts of your app.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -44,5 +44,5 @@
44
44
  "peerDependencies": {
45
45
  "expo": "*"
46
46
  },
47
- "gitHead": "1f8a6a09570fd451378565ca34933018ce48454e"
47
+ "gitHead": "fa5ecca8251986b9f197cc14074eec0ab6dfb6db"
48
48
  }
package/src/SQLite.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import './polyfillNextTick';
2
2
 
3
3
  import customOpenDatabase from '@expo/websql/custom';
4
- import { NativeModulesProxy } from 'expo-modules-core';
4
+ import { requireNativeModule } from 'expo-modules-core';
5
5
  import { Platform } from 'react-native';
6
6
 
7
7
  import { Query, ResultSet, ResultSetError, SQLiteCallback, WebSQLDatabase } from './SQLite.types';
8
8
 
9
- const { ExponentSQLite } = NativeModulesProxy;
9
+ const ExpoSQLite = requireNativeModule('ExpoSQLite');
10
10
 
11
11
  function zipObject(keys: string[], values: any[]) {
12
12
  const result = {};
@@ -29,7 +29,7 @@ class SQLiteDatabase {
29
29
  throw new Error(`The SQLite database is closed`);
30
30
  }
31
31
 
32
- ExponentSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then(
32
+ ExpoSQLite.exec(this._name, queries.map(_serializeQuery), readOnly).then(
33
33
  (nativeResultSets) => {
34
34
  callback(null, nativeResultSets.map(_deserializeResultSet));
35
35
  },
@@ -42,7 +42,7 @@ class SQLiteDatabase {
42
42
 
43
43
  close() {
44
44
  this._closed = true;
45
- return ExponentSQLite.close(this._name);
45
+ return ExpoSQLite.close(this._name);
46
46
  }
47
47
 
48
48
  deleteAsync(): Promise<void> {
@@ -52,7 +52,7 @@ class SQLiteDatabase {
52
52
  );
53
53
  }
54
54
 
55
- return ExponentSQLite.deleteAsync(this._name);
55
+ return ExpoSQLite.deleteAsync(this._name);
56
56
  }
57
57
  }
58
58
 
@@ -1,8 +0,0 @@
1
- // Copyright 2015-present 650 Industries. All rights reserved.
2
-
3
- #import <ExpoModulesCore/EXExportedModule.h>
4
- #import <ExpoModulesCore/EXModuleRegistryConsumer.h>
5
-
6
- @interface EXSQLite : EXExportedModule <EXModuleRegistryConsumer>
7
-
8
- @end
@@ -1,270 +0,0 @@
1
- // Copyright 2015-present 650 Industries. All rights reserved.
2
-
3
- #import <EXSQLite/EXSQLite.h>
4
-
5
- #import <ExpoModulesCore/EXFileSystemInterface.h>
6
-
7
- #import <sqlite3.h>
8
-
9
- @interface EXSQLite ()
10
-
11
- @property (nonatomic, copy) NSMutableDictionary *cachedDatabases;
12
- @property (nonatomic, weak) EXModuleRegistry *moduleRegistry;
13
-
14
- @end
15
-
16
- @implementation EXSQLite
17
-
18
- @synthesize cachedDatabases;
19
-
20
- - (dispatch_queue_t)methodQueue
21
- {
22
- return dispatch_get_main_queue();
23
- }
24
-
25
- EX_EXPORT_MODULE(ExponentSQLite);
26
-
27
- - (void)setModuleRegistry:(EXModuleRegistry *)moduleRegistry
28
- {
29
- _moduleRegistry = moduleRegistry;
30
- cachedDatabases = [NSMutableDictionary dictionary];
31
- }
32
-
33
- - (NSString *)pathForDatabaseName:(NSString *)name
34
- {
35
- id<EXFileSystemInterface> fileSystem = [_moduleRegistry getModuleImplementingProtocol:@protocol(EXFileSystemInterface)];
36
- if (!fileSystem) {
37
- EXLogError(@"No FileSystem module.");
38
- return nil;
39
- }
40
- NSString *directory = [fileSystem.documentDirectory stringByAppendingPathComponent:@"SQLite"];
41
- [fileSystem ensureDirExistsWithPath:directory];
42
- return [directory stringByAppendingPathComponent:name];
43
- }
44
-
45
- - (NSValue *)openDatabase:(NSString *)dbName
46
- {
47
- NSValue *cachedDB = nil;
48
- NSString *path = [self pathForDatabaseName:dbName];
49
- if (!path) {
50
- return nil;
51
- }
52
- if ([[NSFileManager defaultManager] fileExistsAtPath:path]) {
53
- cachedDB = [cachedDatabases objectForKey:dbName];
54
- }
55
- if (cachedDB == nil) {
56
- [cachedDatabases removeObjectForKey:dbName];
57
- sqlite3 *db;
58
- if (sqlite3_open([path UTF8String], &db) != SQLITE_OK) {
59
- return nil;
60
- };
61
- cachedDB = [NSValue valueWithPointer:db];
62
- [cachedDatabases setObject:cachedDB forKey:dbName];
63
- }
64
- return cachedDB;
65
- }
66
-
67
- EX_EXPORT_METHOD_AS(exec,
68
- exec:(NSString *)dbName
69
- queries:(NSArray *)sqlQueries
70
- readOnly:(BOOL)readOnly
71
- resolver:(EXPromiseResolveBlock)resolve
72
- rejecter:(EXPromiseRejectBlock)reject)
73
- {
74
- @synchronized(self) {
75
- NSValue *databasePointer = [self openDatabase:dbName];
76
- if (!databasePointer) {
77
- reject(@"E_SQLITE_OPEN_DATABASE", @"Could not open database.", nil);
78
- return;
79
- }
80
-
81
- sqlite3 *db = [databasePointer pointerValue];
82
- NSMutableArray *sqlResults = [NSMutableArray arrayWithCapacity:sqlQueries.count];
83
- for (NSArray *sqlQueryObject in sqlQueries) {
84
- NSString *sql = [sqlQueryObject objectAtIndex:0];
85
- NSArray *sqlArgs = [sqlQueryObject objectAtIndex:1];
86
- [sqlResults addObject:[self executeSql:sql withSqlArgs:sqlArgs withDb:db withReadOnly:readOnly]];
87
- }
88
- resolve(sqlResults);
89
- }
90
- }
91
-
92
- EX_EXPORT_METHOD_AS(close,
93
- close:(NSString *)dbName
94
- resolver:(EXPromiseResolveBlock)resolve
95
- rejecter:(EXPromiseRejectBlock)reject)
96
- {
97
- @synchronized(self) {
98
- [cachedDatabases removeObjectForKey:dbName];
99
- resolve(nil);
100
- }
101
- }
102
-
103
- EX_EXPORT_METHOD_AS(deleteAsync,
104
- deleteDbName:(NSString *)dbName
105
- resolver:(EXPromiseResolveBlock)resolve
106
- rejecter:(EXPromiseRejectBlock)reject)
107
- {
108
- NSString *errorCode = @"E_SQLITE_DELETE_DATABASE";
109
-
110
- @synchronized(self) {
111
- if ([cachedDatabases objectForKey:dbName]) {
112
- reject(errorCode, [NSString stringWithFormat:@"Unable to delete database '%@' that is currently open. Close it prior to deletion.", dbName], nil);
113
- return;
114
- }
115
- }
116
-
117
- NSString *path = [self pathForDatabaseName:dbName];
118
- if (!path) {
119
- reject(errorCode, @"No FileSystem module.", nil);
120
- return;
121
- }
122
- if (![[NSFileManager defaultManager] fileExistsAtPath:path]) {
123
- reject(errorCode, [NSString stringWithFormat:@"Database '%@' not found", dbName], nil);
124
- return;
125
- }
126
- NSError *error;
127
- if (![[NSFileManager defaultManager] removeItemAtPath:path error:&error]) {
128
- reject(errorCode, [NSString stringWithFormat:@"Unable to delete the database file for '%@' database", dbName], error);
129
- return;
130
- }
131
- resolve(nil);
132
- }
133
-
134
- - (id)getSqlValueForColumnType:(int)columnType withStatement:(sqlite3_stmt*)statement withIndex:(int)i
135
- {
136
- switch (columnType) {
137
- case SQLITE_INTEGER:
138
- return @(sqlite3_column_int64(statement, i));
139
- case SQLITE_FLOAT:
140
- return @(sqlite3_column_double(statement, i));
141
- case SQLITE_BLOB:
142
- case SQLITE_TEXT:
143
- return [[NSString alloc] initWithBytes:(char *)sqlite3_column_text(statement, i)
144
- length:sqlite3_column_bytes(statement, i)
145
- encoding:NSUTF8StringEncoding];
146
- }
147
- return [NSNull null];
148
- }
149
-
150
- - (NSArray *)executeSql:(NSString*)sql withSqlArgs:(NSArray*)sqlArgs
151
- withDb:(sqlite3*)db withReadOnly:(BOOL)readOnly
152
- {
153
- NSString *error = nil;
154
- sqlite3_stmt *statement;
155
- NSMutableArray *resultRows = [NSMutableArray arrayWithCapacity:0];
156
- NSMutableArray *entry;
157
- long long insertId = 0;
158
- int rowsAffected = 0;
159
- int i;
160
-
161
- // compile the statement, throw an error if necessary
162
- if (sqlite3_prepare_v2(db, [sql UTF8String], -1, &statement, NULL) != SQLITE_OK) {
163
- error = [EXSQLite convertSQLiteErrorToString:db];
164
- return @[error];
165
- }
166
-
167
- bool queryIsReadOnly = sqlite3_stmt_readonly(statement);
168
- if (readOnly && !queryIsReadOnly) {
169
- error = [NSString stringWithFormat:@"could not prepare %@", sql];
170
- return @[error];
171
- }
172
-
173
- // bind any arguments
174
- if (sqlArgs != nil) {
175
- for (i = 0; i < sqlArgs.count; i++) {
176
- [self bindStatement:statement withArg:[sqlArgs objectAtIndex:i] atIndex:(i + 1)];
177
- }
178
- }
179
-
180
- // iterate through sql results
181
- int columnCount = 0;
182
- NSMutableArray *columnNames = [NSMutableArray arrayWithCapacity:0];
183
- NSString *columnName;
184
- int columnType;
185
- BOOL fetchedColumns = NO;
186
- int result;
187
- NSObject *columnValue;
188
- BOOL hasMore = YES;
189
- while (hasMore) {
190
- result = sqlite3_step (statement);
191
- switch (result) {
192
- case SQLITE_ROW:
193
- if (!fetchedColumns) {
194
- // get all column names once at the beginning
195
- columnCount = sqlite3_column_count(statement);
196
-
197
- for (i = 0; i < columnCount; i++) {
198
- columnName = [NSString stringWithFormat:@"%s", sqlite3_column_name(statement, i)];
199
- [columnNames addObject:columnName];
200
- }
201
- fetchedColumns = YES;
202
- }
203
- entry = [NSMutableArray arrayWithCapacity:columnCount];
204
- for (i = 0; i < columnCount; i++) {
205
- columnType = sqlite3_column_type(statement, i);
206
- columnValue = [self getSqlValueForColumnType:columnType withStatement:statement withIndex: i];
207
- [entry addObject:columnValue];
208
- }
209
- [resultRows addObject:entry];
210
- break;
211
- case SQLITE_DONE:
212
- hasMore = NO;
213
- break;
214
- default:
215
- error = [EXSQLite convertSQLiteErrorToString:db];
216
- hasMore = NO;
217
- break;
218
- }
219
- }
220
-
221
- if (!queryIsReadOnly) {
222
- rowsAffected = sqlite3_changes(db);
223
- if (rowsAffected > 0) {
224
- insertId = sqlite3_last_insert_rowid(db);
225
- }
226
- }
227
-
228
- sqlite3_finalize(statement);
229
-
230
- if (error) {
231
- return @[error];
232
- }
233
- return @[[NSNull null], @(insertId), @(rowsAffected), columnNames, resultRows];
234
- }
235
-
236
- - (void)bindStatement:(sqlite3_stmt *)statement withArg:(NSObject *)arg atIndex:(int)argIndex
237
- {
238
- if ([arg isEqual:[NSNull null]]) {
239
- sqlite3_bind_null(statement, argIndex);
240
- } else if ([arg isKindOfClass:[NSNumber class]]) {
241
- sqlite3_bind_double(statement, argIndex, [((NSNumber *) arg) doubleValue]);
242
- } else { // NSString
243
- NSString *stringArg;
244
-
245
- if ([arg isKindOfClass:[NSString class]]) {
246
- stringArg = (NSString *)arg;
247
- } else {
248
- stringArg = [arg description]; // convert to text
249
- }
250
-
251
- NSData *data = [stringArg dataUsingEncoding:NSUTF8StringEncoding];
252
- sqlite3_bind_text(statement, argIndex, data.bytes, (int)data.length, SQLITE_TRANSIENT);
253
- }
254
- }
255
-
256
- - (void)dealloc
257
- {
258
- for (NSString *key in cachedDatabases) {
259
- sqlite3_close([[cachedDatabases objectForKey:key] pointerValue]);
260
- }
261
- }
262
-
263
- + (NSString *)convertSQLiteErrorToString:(struct sqlite3 *)db
264
- {
265
- int code = sqlite3_errcode(db);
266
- NSString *message = [NSString stringWithUTF8String:sqlite3_errmsg(db)];
267
- return [NSString stringWithFormat:@"Error code %i: %@", code, message];
268
- }
269
-
270
- @end
package/unimodule.json DELETED
@@ -1,4 +0,0 @@
1
- {
2
- "name": "expo-sqlite",
3
- "platforms": ["ios", "android"]
4
- }