Skip to content

Commit c1afb1b

Browse files
authored
fix(DataStore): FatalError accessing SQLite connection (#1671)
1 parent 9c0427b commit c1afb1b

File tree

5 files changed

+102
-20
lines changed

5 files changed

+102
-20
lines changed

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Migration/MutationSyncMetadataMigrationDelegate+SQLite.swift

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -47,17 +47,17 @@ final class SQLiteMutationSyncMetadataMigrationDelegate: MutationSyncMetadataMig
4747

4848
@discardableResult func emptyMutationSyncMetadataStore() throws -> String {
4949
guard let storageAdapter = storageAdapter else {
50-
log.debug("Missing SQLiteStorageEngineAdapter")
51-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
50+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
51+
throw DataStoreError.nilStorageAdapter()
5252
}
5353

5454
return try storageAdapter.emptyStore(for: MutationSyncMetadata.schema)
5555
}
5656

5757
@discardableResult func emptyModelSyncMetadataStore() throws -> String {
5858
guard let storageAdapter = storageAdapter else {
59-
log.debug("Missing SQLiteStorageEngineAdapter")
60-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
59+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
60+
throw DataStoreError.nilStorageAdapter()
6161
}
6262

6363
return try storageAdapter.emptyStore(for: ModelSyncMetadata.schema)
@@ -67,23 +67,32 @@ final class SQLiteMutationSyncMetadataMigrationDelegate: MutationSyncMetadataMig
6767

6868
@discardableResult func removeMutationSyncMetadataCopyStore() throws -> String {
6969
guard let storageAdapter = storageAdapter else {
70-
log.debug("Missing SQLiteStorageEngineAdapter")
71-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
70+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
71+
throw DataStoreError.nilStorageAdapter()
7272
}
7373

7474
return try storageAdapter.removeStore(for: MutationSyncMetadataMigration.MutationSyncMetadataCopy.schema)
7575
}
7676

7777
@discardableResult func createMutationSyncMetadataCopyStore() throws -> String {
7878
guard let storageAdapter = storageAdapter else {
79-
log.debug("Missing SQLiteStorageEngineAdapter")
80-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
79+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
80+
throw DataStoreError.nilStorageAdapter()
8181
}
8282

8383
return try storageAdapter.createStore(for: MutationSyncMetadataMigration.MutationSyncMetadataCopy.schema)
8484
}
8585

8686
@discardableResult func backfillMutationSyncMetadata() throws -> String {
87+
guard let storageAdapter = storageAdapter else {
88+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
89+
throw DataStoreError.nilStorageAdapter()
90+
}
91+
92+
guard let connection = storageAdapter.connection else {
93+
throw DataStoreError.nilSQLiteConnection()
94+
}
95+
8796
var sql = ""
8897
for modelSchema in modelSchemas {
8998
let modelName = modelSchema.name
@@ -96,23 +105,23 @@ final class SQLiteMutationSyncMetadataMigrationDelegate: MutationSyncMetadataMig
96105
sql = "INSERT INTO \(MutationSyncMetadataMigration.MutationSyncMetadataCopy.modelName) (id,deleted,lastChangedAt,version) " +
97106
"select models.tableName || '|' || mm.id, mm.deleted, mm.lastChangedAt, mm.version " +
98107
"from MutationSyncMetadata mm INNER JOIN (" + sql + ") as models on mm.id=models.id"
99-
try storageAdapter?.connection.execute(sql)
108+
try connection.execute(sql)
100109
return sql
101110
}
102111

103112
@discardableResult func removeMutationSyncMetadataStore() throws -> String {
104113
guard let storageAdapter = storageAdapter else {
105-
log.debug("Missing SQLiteStorageEngineAdapter")
106-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
114+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
115+
throw DataStoreError.nilStorageAdapter()
107116
}
108117

109118
return try storageAdapter.removeStore(for: MutationSyncMetadata.schema)
110119
}
111120

112121
@discardableResult func renameMutationSyncMetadataCopy() throws -> String {
113122
guard let storageAdapter = storageAdapter else {
114-
log.debug("Missing SQLiteStorageEngineAdapter")
115-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
123+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
124+
throw DataStoreError.nilStorageAdapter()
116125
}
117126

118127
return try storageAdapter.renameStore(from: MutationSyncMetadataMigration.MutationSyncMetadataCopy.schema,

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Migration/MutationSyncMetadataMigrationDelegate+SQLiteValidation.swift

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,18 @@ extension SQLiteMutationSyncMetadataMigrationDelegate {
6565
/// 2. the total number of records that have the `id` match `<modelName>|<modelId>`
6666
func selectMutationSyncMetadataRecords() throws -> (metadataCount: Int64, metadataIdMatchNewKeyCount: Int64) {
6767
guard let storageAdapter = storageAdapter else {
68-
log.debug("Missing SQLiteStorageEngineAdapter")
69-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
68+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
69+
throw DataStoreError.nilStorageAdapter()
70+
}
71+
guard let connection = storageAdapter.connection else {
72+
throw DataStoreError.nilSQLiteConnection()
7073
}
7174
let sql = """
7275
select (select count(1) as count from MutationSyncMetadata) as allRecords,
7376
(select count(1) as count from MutationSyncMetadata where id like '%|%') as newKeys
7477
"""
7578
log.debug("Checking MutationSyncMetadata records, SQL: \(sql)")
76-
let rows = try storageAdapter.connection.run(sql)
79+
let rows = try connection.run(sql)
7780
let iter = rows.makeIterator()
7881
while let row = try iter.failableNext() {
7982
if let metadataCount = row[0] as? Int64, let metadataIdMatchNewKeyCount = row[1] as? Int64 {
@@ -91,12 +94,15 @@ extension SQLiteMutationSyncMetadataMigrationDelegate {
9194

9295
func containsDuplicateIdsAcrossModels() throws -> Bool {
9396
guard let storageAdapter = storageAdapter else {
94-
log.debug("Missing SQLiteStorageEngineAdapter")
95-
throw DataStoreError.unknown("Missing storage adapter for model migration", "", nil)
97+
log.debug("Missing SQLiteStorageEngineAdapter for model migration")
98+
throw DataStoreError.nilStorageAdapter()
99+
}
100+
guard let connection = storageAdapter.connection else {
101+
throw DataStoreError.nilSQLiteConnection()
96102
}
97103
let sql = selectDuplicateIdAcrossModels()
98104
log.debug("Checking for duplicate IDs, SQL: \(sql)")
99-
let rows = try storageAdapter.connection.run(sql)
105+
let rows = try connection.run(sql)
100106
let iter = rows.makeIterator()
101107
while let row = try iter.failableNext() {
102108
return !row.isEmpty

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/SQLite/StorageEngineAdapter+SQLite.swift

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import AWSPluginsCore
1414
/// an integration layer between the AppSyncLocal `StorageEngine` and SQLite for local storage.
1515
final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
1616

17-
internal var connection: Connection!
17+
internal var connection: Connection?
1818
private var dbFilePath: URL?
1919
static let dbVersionKey = "com.amazonaws.DataStore.dbVersion"
2020

@@ -89,6 +89,10 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
8989
}
9090

9191
func setUp(modelSchemas: [ModelSchema]) throws {
92+
guard let connection = connection else {
93+
throw DataStoreError.invalidOperation(causedBy: nil)
94+
}
95+
9296
log.debug("Setting up \(modelSchemas.count) models")
9397

9498
let createTableStatements = modelSchemas
@@ -130,6 +134,10 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
130134
modelSchema: ModelSchema,
131135
condition: QueryPredicate? = nil,
132136
completion: DataStoreCallback<M>) {
137+
guard let connection = connection else {
138+
completion(.failure(DataStoreError.nilSQLiteConnection()))
139+
return
140+
}
133141
do {
134142
let modelType = type(of: model)
135143
let modelExists = try exists(modelSchema, withId: model.id)
@@ -189,6 +197,10 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
189197
modelSchema: ModelSchema,
190198
predicate: QueryPredicate,
191199
completion: (DataStoreResult<[M]>) -> Void) {
200+
guard let connection = connection else {
201+
completion(.failure(DataStoreError.nilSQLiteConnection()))
202+
return
203+
}
192204
do {
193205
let statement = DeleteStatement(modelSchema: modelSchema, predicate: predicate)
194206
_ = try connection.prepare(statement.stringValue).run(statement.variables)
@@ -218,6 +230,10 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
218230
withId id: Model.Identifier,
219231
predicate: QueryPredicate? = nil,
220232
completion: DataStoreCallback<Void>) {
233+
guard let connection = connection else {
234+
completion(.failure(DataStoreError.nilSQLiteConnection()))
235+
return
236+
}
221237
do {
222238
let statement = DeleteStatement(modelSchema: modelSchema, withId: id, predicate: predicate)
223239
_ = try connection.prepare(statement.stringValue).run(statement.variables)
@@ -246,6 +262,10 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
246262
sort: [QuerySortDescriptor]? = nil,
247263
paginationInput: QueryPaginationInput? = nil,
248264
completion: DataStoreCallback<[M]>) {
265+
guard let connection = connection else {
266+
completion(.failure(DataStoreError.nilSQLiteConnection()))
267+
return
268+
}
249269
do {
250270
let statement = SelectStatement(from: modelSchema,
251271
predicate: predicate,
@@ -264,6 +284,9 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
264284
func exists(_ modelSchema: ModelSchema,
265285
withId id: Model.Identifier,
266286
predicate: QueryPredicate? = nil) throws -> Bool {
287+
guard let connection = connection else {
288+
throw DataStoreError.nilSQLiteConnection()
289+
}
267290
let primaryKey = modelSchema.primaryKey.sqlName
268291
var sql = "select count(\(primaryKey)) from \"\(modelSchema.name)\" where \(primaryKey) = ?"
269292
var variables: [Binding?] = [id]
@@ -294,6 +317,9 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
294317
}
295318

296319
func queryMutationSync(for models: [Model], modelName: String) throws -> [MutationSync<AnyModel>] {
320+
guard let connection = connection else {
321+
throw DataStoreError.nilSQLiteConnection()
322+
}
297323
let statement = SelectStatement(from: MutationSyncMetadata.schema)
298324
let primaryKey = MutationSyncMetadata.schema.primaryKey.sqlName
299325
// This is a temp workaround since we don't currently support the "in" operator
@@ -327,6 +353,9 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
327353
}
328354

329355
func queryMutationSyncMetadata(for modelIds: [Model.Identifier], modelName: String) throws -> [MutationSyncMetadata] {
356+
guard let connection = connection else {
357+
throw DataStoreError.nilSQLiteConnection()
358+
}
330359
let modelType = MutationSyncMetadata.self
331360
let modelSchema = MutationSyncMetadata.schema
332361
let fields = MutationSyncMetadata.keys
@@ -352,6 +381,9 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
352381
}
353382

354383
func queryModelSyncMetadata(for modelSchema: ModelSchema) throws -> ModelSyncMetadata? {
384+
guard let connection = connection else {
385+
throw DataStoreError.nilSQLiteConnection()
386+
}
355387
let statement = SelectStatement(from: ModelSyncMetadata.schema,
356388
predicate: field("id").eq(modelSchema.name))
357389
let rows = try connection.prepare(statement.stringValue).run(statement.variables)
@@ -362,6 +394,9 @@ final class SQLiteStorageEngineAdapter: StorageEngineAdapter {
362394
}
363395

364396
func transaction(_ transactionBlock: BasicThrowableClosure) throws {
397+
guard let connection = connection else {
398+
throw DataStoreError.nilSQLiteConnection()
399+
}
365400
try connection.transaction {
366401
try transactionBlock()
367402
}
@@ -431,4 +466,15 @@ private func getDocumentPath() -> URL? {
431466
return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
432467
}
433468

469+
extension DataStoreError {
470+
471+
static func nilSQLiteConnection() -> DataStoreError {
472+
.internalOperation("SQLite connection is `nil`",
473+
"""
474+
This is expected if DataStore.clear is called while syncing as the SQLite connection is closed.
475+
Call DataStore.start to restart the sync process.
476+
""", nil)
477+
}
478+
}
479+
434480
extension SQLiteStorageEngineAdapter: DefaultLogger { }

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/SQLite/StorageEngineAdapter+UntypedModel.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,11 @@ import SQLite
1111
extension SQLiteStorageEngineAdapter {
1212

1313
func save(untypedModel: Model, completion: DataStoreCallback<Model>) {
14+
guard let connection = connection else {
15+
completion(.failure(.nilSQLiteConnection()))
16+
return
17+
}
18+
1419
do {
1520
let modelName: ModelName
1621
if let jsonModel = untypedModel as? JSONValueHolder,
@@ -46,6 +51,10 @@ extension SQLiteStorageEngineAdapter {
4651
func query(modelSchema: ModelSchema,
4752
predicate: QueryPredicate? = nil,
4853
completion: DataStoreCallback<[Model]>) {
54+
guard let connection = connection else {
55+
completion(.failure(.nilSQLiteConnection()))
56+
return
57+
}
4958
do {
5059
let statement = SelectStatement(from: modelSchema, predicate: predicate)
5160
let rows = try connection.prepare(statement.stringValue).run(statement.variables)

AmplifyPlugins/DataStore/AWSDataStoreCategoryPlugin/Storage/SQLite/StorageEngineMigrationAdapter+SQLite.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ import SQLite
1212
extension SQLiteStorageEngineAdapter {
1313

1414
@discardableResult func createStore(for modelSchema: ModelSchema) throws -> String {
15+
guard let connection = connection else {
16+
throw DataStoreError.nilSQLiteConnection()
17+
}
1518
let createTableStatement = CreateTableStatement(modelSchema: modelSchema).stringValue
1619
let createIndexStatement = modelSchema.createIndexStatements()
1720
try connection.execute(createTableStatement)
@@ -20,18 +23,27 @@ extension SQLiteStorageEngineAdapter {
2023
}
2124

2225
@discardableResult func removeStore(for modelSchema: ModelSchema) throws -> String {
26+
guard let connection = connection else {
27+
throw DataStoreError.nilSQLiteConnection()
28+
}
2329
let dropStatement = DropTableStatement(modelSchema: modelSchema).stringValue
2430
try connection.execute(dropStatement)
2531
return dropStatement
2632
}
2733

2834
@discardableResult func emptyStore(for modelSchema: ModelSchema) throws -> String {
35+
guard let connection = connection else {
36+
throw DataStoreError.nilSQLiteConnection()
37+
}
2938
let deleteStatement = DeleteStatement(modelSchema: modelSchema).stringValue
3039
try connection.execute(deleteStatement)
3140
return deleteStatement
3241
}
3342

3443
@discardableResult func renameStore(from: ModelSchema, toModelSchema: ModelSchema) throws -> String {
44+
guard let connection = connection else {
45+
throw DataStoreError.nilSQLiteConnection()
46+
}
3547
let alterTableStatement = AlterTableStatement(from: from, toModelSchema: toModelSchema).stringValue
3648
try connection.execute(alterTableStatement)
3749
return alterTableStatement

0 commit comments

Comments
 (0)