@capacitor-community/sqlite 5.0.6 → 5.0.7-1

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/README.md CHANGED
@@ -171,8 +171,11 @@ npm install --save better-sqlite3-multiple-ciphers
171
171
  npm install --save electron-json-storage
172
172
  npm install --save jszip
173
173
  npm install --save node-fetch@2.6.7
174
+ npm install --save crypto
175
+ npm install --save crypto-js
174
176
  npm install --save-dev @types/better-sqlite3
175
177
  npm install --save-dev @types/electron-json-storage
178
+ npm install --save-dev @types/crypto-js
176
179
  ```
177
180
  - **Important**: `node-fetch` version must be `<=2.6.7`; otherwise [you'll get an error](https://github.com/capacitor-community/sqlite/issues/349 "you'll get an error ERR_REQUIRE_ESM") running the app.
178
181
 
@@ -0,0 +1,506 @@
1
+ //
2
+ // UtilsDelete.swift
3
+ // CapacitorCommunitySqlite
4
+ //
5
+ // Created by Quéau Jean Pierre on 30/07/2023.
6
+ //
7
+
8
+ import Foundation
9
+ enum UtilsDeleteError: Error {
10
+ case findReferencesAndUpdate(message: String)
11
+ case getReferences(message: String)
12
+ case getRefs(message: String)
13
+ case getUpdDelReturnedValues(message: String)
14
+ case getFirstPK(message: String)
15
+ case extractColumnNames(message: String)
16
+ case executeUpdateForDelete(message: String)
17
+ case extractForeignKeyInfo(message: String)
18
+ case searchForRelatedItems(message: String)
19
+ case upDateWhereForRestrict(message: String)
20
+ case upDateWhereForCascade(message: String)
21
+ }
22
+
23
+ // swiftlint:disable type_body_length
24
+ class UtilsDelete {
25
+
26
+ // MARK: - findReferencesAndUpdate
27
+
28
+ // swiftlint:disable function_body_length
29
+ // swiftlint:disable cyclomatic_complexity
30
+ class func findReferencesAndUpdate(mDB: Database, tableName: String,
31
+ whereStmt: String,
32
+ initColNames: [String],
33
+ values: [Any]) throws -> Bool {
34
+ do {
35
+ var retBool: Bool = true
36
+ let result = try getReferences(mDB: mDB,
37
+ tableName: tableName)
38
+ let references = result.retRefs
39
+ let tableNameWithRefs = result.tableWithRefs
40
+ if references.count <= 0 {
41
+ return retBool
42
+ }
43
+ if tableName == tableNameWithRefs {
44
+ return retBool
45
+ }
46
+ // Loop through references
47
+ for ref in references {
48
+
49
+ // Extract the FOREIGN KEY constraint info
50
+ // from the ref statement
51
+ let foreignKeyInfo = try UtilsSQLStatement
52
+ .extractForeignKeyInfo(from: ref)
53
+ // get the tableName of the references
54
+ guard let refTable = foreignKeyInfo["tableName"]
55
+ as? String else {
56
+ let msg = "findReferencesAndUpdate: no foreignKeyInfo " +
57
+ "tableName"
58
+ throw UtilsDeleteError
59
+ .findReferencesAndUpdate(message: msg)
60
+ }
61
+ if refTable.isEmpty || refTable != tableName {
62
+ continue
63
+ }
64
+ // get the with ref columnName
65
+ guard let withRefsNames = foreignKeyInfo["forKeys"]
66
+ as? [String] else {
67
+
68
+ let msg = "findReferencesAndUpdate: no foreignKeyInfo " +
69
+ "forKeys"
70
+ throw UtilsDeleteError
71
+ .findReferencesAndUpdate(message: msg)
72
+ }
73
+ guard let colNames = foreignKeyInfo["refKeys"]
74
+ as? [String] else {
75
+ let msg = "findReferencesAndUpdate: no foreignKeyInfo " +
76
+ "refKeys"
77
+ throw UtilsDeleteError
78
+ .findReferencesAndUpdate(message: msg)
79
+ }
80
+ if colNames.count != withRefsNames.count {
81
+ let msg = "findReferencesAndUpdate: no foreignKeyInfo " +
82
+ "colNames"
83
+ throw UtilsDeleteError
84
+ .findReferencesAndUpdate(message: msg)
85
+
86
+ }
87
+ guard let action = foreignKeyInfo["action"]
88
+ as? String else {
89
+ let msg = "findReferencesAndUpdate: no action"
90
+ throw UtilsDeleteError
91
+ .findReferencesAndUpdate(message: msg)
92
+ }
93
+ if action == "NO_ACTION" {
94
+ continue
95
+ }
96
+
97
+ var updTableName: String = tableNameWithRefs
98
+ var updColNames: [String] = withRefsNames
99
+ var results: (setStmt: String, uWhereStmt: String)
100
+ results.uWhereStmt = ""
101
+ results.setStmt = ""
102
+ if !checkValuesMatch(withRefsNames,
103
+ against: initColNames) {
104
+ // Search for related items in tableName
105
+ let result: (String,[[String: Any]]) = try UtilsDelete
106
+ .searchForRelatedItems(mDB: mDB,
107
+ updTableName: updTableName,
108
+ tableName: tableName,
109
+ whStmt: whereStmt,
110
+ withRefsNames: withRefsNames,
111
+ colNames: colNames,
112
+ values: values);
113
+ let key: String = result.0
114
+ let relatedItems: [Any] = result.1
115
+ if relatedItems.count == 0 && key.count <= 0 {
116
+ continue
117
+ }
118
+
119
+ // case no match
120
+ if updTableName != tableName {
121
+ switch action {
122
+ case "CASCADE":
123
+ // updTableName
124
+ // update all related element
125
+ // set sql_deleted = 1 and last_modified
126
+ // tableName
127
+ //update all by sending return true
128
+ results = try upDateWhereForCascade(
129
+ results: result)
130
+
131
+ case "RESTRICT":
132
+ // find for elements related in updTableName
133
+ // if some elements
134
+ // send a message
135
+ // do not update tableName
136
+ // return false
137
+ // If no related elements in updTableName
138
+ // return true to update tableName
139
+ results = try upDateWhereForRestrict(
140
+ results: result)
141
+
142
+ default:
143
+ // updTableName
144
+ // update the result_id result_slug to Null
145
+ // update the last_modified
146
+ // keep sql_deleted to 0
147
+ // return true to update tableName
148
+ results = try upDateWhereForDefault(
149
+ withRefsNames: withRefsNames,
150
+ results: result)
151
+ }
152
+ }
153
+
154
+ } else {
155
+ let msg = "Not implemented. Please transfer your " +
156
+ "example to the maintener"
157
+ throw UtilsDeleteError
158
+ .findReferencesAndUpdate(message: msg)
159
+ }
160
+ if results.setStmt.count > 0 &&
161
+ results.uWhereStmt.count > 0 {
162
+
163
+ try executeUpdateForDelete(
164
+ mDB: mDB,
165
+ tableName: updTableName,
166
+ whereStmt: results.uWhereStmt,
167
+ setStmt: results.setStmt,
168
+ colNames: updColNames,
169
+ values: values)
170
+ }
171
+ }
172
+ return retBool
173
+ } catch UtilsDeleteError.upDateWhereForRestrict(let message) {
174
+ throw UtilsDeleteError
175
+ .findReferencesAndUpdate(message: message)
176
+ } catch UtilsSQLStatementError
177
+ .extractForeignKeyInfo(let message) {
178
+ throw UtilsDeleteError
179
+ .findReferencesAndUpdate(message: message)
180
+ } catch UtilsDeleteError.executeUpdateForDelete(let message) {
181
+ throw UtilsDeleteError
182
+ .findReferencesAndUpdate(message: message)
183
+ } catch UtilsSQLCipherError.prepareSQL(let message) {
184
+ throw UtilsDeleteError
185
+ .findReferencesAndUpdate(message: message)
186
+ } catch UtilsSQLCipherError.querySQL(let message) {
187
+ throw UtilsDeleteError
188
+ .findReferencesAndUpdate(message: message)
189
+ }
190
+ }
191
+ // swiftlint:enable cyclomatic_complexity
192
+ // swiftlint:enable function_body_length
193
+
194
+ // MARK: - upDateWhereForDefault
195
+
196
+ class func upDateWhereForDefault(withRefsNames: [String],
197
+ results: (String,[[String:Any]]))
198
+ throws -> ((setStmt: String, uWhereStmt: String)) {
199
+
200
+ var setStmt = ""
201
+ var uWhereStmt = ""
202
+ let key: String = results.0
203
+ let relatedItems = results.1
204
+
205
+ var cols: [Any] = []
206
+ for relItem in relatedItems {
207
+ if let mVal = relItem[key] {
208
+ cols.append(mVal)
209
+ }
210
+ }
211
+ // create the set statement
212
+ for name in withRefsNames {
213
+ setStmt += "\(name) = NULL, "
214
+ }
215
+ setStmt += "sql_deleted = 0"
216
+
217
+ // create the where statement
218
+ uWhereStmt = "WHERE \(key) IN ("
219
+ for col in cols {
220
+ uWhereStmt += "\(col),"
221
+ }
222
+ if uWhereStmt.hasSuffix(",") {
223
+ uWhereStmt = String(uWhereStmt.dropLast())
224
+ }
225
+
226
+ uWhereStmt += ");"
227
+
228
+ return(setStmt, uWhereStmt)
229
+ }
230
+
231
+ // MARK: - upDateWhereForRestrict
232
+
233
+ class func upDateWhereForRestrict(results: (String,[[String: Any]]))
234
+ throws -> ((setStmt: String, uWhereStmt: String)) {
235
+
236
+ // Search for related items in tableName
237
+ let setStmt = ""
238
+ let uWhereStmt = ""
239
+ let relatedItems = results.1
240
+
241
+ if !relatedItems.isEmpty {
242
+ let msg = "Restrict mode related items exist" +
243
+ " please delete them first"
244
+ throw UtilsDeleteError
245
+ .upDateWhereForRestrict(message: msg)
246
+ }
247
+ return(setStmt, uWhereStmt)
248
+ }
249
+
250
+ // MARK: - upDateWhereForCascade
251
+
252
+ class func upDateWhereForCascade(results: (String,[[String:Any]]))
253
+ throws -> ((setStmt: String, uWhereStmt: String)) {
254
+
255
+ // Search for related items in tableName
256
+ var setStmt = ""
257
+ var uWhereStmt = ""
258
+ let key: String = results.0
259
+ let relatedItems = results.1
260
+ var cols: [Any] = []
261
+ for relItem in relatedItems {
262
+ if let mVal = relItem[key] {
263
+ cols.append(mVal)
264
+ }
265
+ }
266
+ setStmt += "sql_deleted = 1"
267
+ // create the where statement
268
+ uWhereStmt = "WHERE \(key) IN ("
269
+ for col in cols {
270
+ uWhereStmt += "\(col),"
271
+ }
272
+ if uWhereStmt.hasSuffix(",") {
273
+ uWhereStmt = String(uWhereStmt.dropLast())
274
+ }
275
+
276
+ uWhereStmt += ");"
277
+ return (setStmt, uWhereStmt)
278
+ }
279
+
280
+ // MARK: - searchForRelatedItems
281
+
282
+ // swiftlint:disable function_parameter_count
283
+ class func searchForRelatedItems(mDB: Database,
284
+ updTableName: String,
285
+ tableName: String, whStmt: String,
286
+ withRefsNames: [String],
287
+ colNames: [String], values: [Any])
288
+ throws -> (String,[[String: Any]]) {
289
+ var relatedItems: [[String: Any]] = []
290
+ var key: String = ""
291
+ let t1Names = withRefsNames.map { "t1.\($0)" }
292
+ let t2Names = colNames.map { "t2.\($0)" }
293
+
294
+ do {
295
+ var whereClause = try UtilsSQLStatement
296
+ .addPrefixToWhereClause(whStmt, from: colNames,
297
+ to: withRefsNames,
298
+ prefix: "t2.")
299
+ if whereClause.hasSuffix(";") {
300
+ whereClause = String(whereClause.dropLast())
301
+ }
302
+ let resultString = zip(t1Names, t2Names)
303
+ .map {"\($0) = \($1)" }
304
+ .joined(separator: " AND ")
305
+
306
+ let sql = "SELECT t1.rowid FROM \(updTableName) t1 " +
307
+ "JOIN \(tableName) t2 ON \(resultString) " +
308
+ "WHERE \(whereClause) AND t1.sql_deleted = 0;"
309
+ var vals = try UtilsSQLCipher.querySQL(mDB: mDB, sql: sql,
310
+ values: values)
311
+ if vals.count > 1 {
312
+ if let mVals = vals[0]["ios_columns"] as? [String] {
313
+ key = mVals[0]
314
+ let keyToRemove = "ios_columns"
315
+ vals.removeAll { dict in
316
+ return dict.keys.contains(keyToRemove)
317
+ }
318
+ for val in vals {
319
+ relatedItems.append(val)
320
+ }
321
+ }
322
+ }
323
+ return (key, relatedItems)
324
+ } catch UtilsSQLStatementError.addPrefixToWhereClause(let message) {
325
+ throw UtilsDeleteError
326
+ .searchForRelatedItems(message: message)
327
+ } catch UtilsSQLCipherError.querySQL(let message) {
328
+ throw UtilsDeleteError
329
+ .searchForRelatedItems(message: message)
330
+ }
331
+ }
332
+ // swiftlint:enable function_parameter_count
333
+
334
+ // MARK: - executeUpdateForDelete
335
+
336
+ // swiftlint:disable function_parameter_count
337
+ class func executeUpdateForDelete(mDB: Database, tableName: String,
338
+ whereStmt: String, setStmt: String,
339
+ colNames: [String], values: [Any])
340
+ throws {
341
+ var lastId: Int64 = -1
342
+ //update sql_deleted for this references
343
+ let stmt = "UPDATE \(tableName) SET \(setStmt) \(whereStmt)"
344
+ var selValues: [Any] = []
345
+ if !values.isEmpty {
346
+ var arrVal: [String] = whereStmt
347
+ .components(separatedBy: "?")
348
+ if arrVal[arrVal.count - 1] == ";" {
349
+ arrVal.removeLast()
350
+ }
351
+ for (jdx, val) in arrVal.enumerated() {
352
+ for updVal in colNames {
353
+ let indices: [Int] = val.indicesOf(string: updVal)
354
+ if indices.count > 0 {
355
+ selValues.append(values[jdx])
356
+ }
357
+ }
358
+ }
359
+ }
360
+
361
+ let resp = try UtilsSQLCipher.prepareSQL(mDB: mDB, sql: stmt,
362
+ values: selValues,
363
+ fromJson: false, returnMode: "no")
364
+ lastId = resp.0
365
+ if lastId == -1 {
366
+ let msg = "UPDATE sql_deleted failed for " +
367
+ "table: \(tableName) "
368
+ throw UtilsDeleteError.executeUpdateForDelete(message: msg)
369
+ }
370
+
371
+ }
372
+
373
+ // MARK: - getCurrentTimeAsInteger
374
+
375
+ class func getCurrentTimeAsInteger() -> Int {
376
+ let currentTime = Date().timeIntervalSince1970
377
+ print(">>>> in getCurrentTimeAsInteger currentTime: \(currentTime)")
378
+ return Int(currentTime)
379
+ }
380
+
381
+ // MARK: - checkValuesMatch
382
+
383
+ class func checkValuesMatch(_ array1: [String],
384
+ against array2: [String]) -> Bool {
385
+ for value in array1 {
386
+ if !array2.contains(value) {
387
+ return false
388
+ }
389
+ }
390
+ return true
391
+ }
392
+
393
+ // MARK: - getReferences
394
+
395
+ class func getReferences(mDB: Database, tableName: String)
396
+ throws -> (tableWithRefs: String, retRefs: [String]) {
397
+ // find the REFERENCES
398
+ var sqlStmt = "SELECT sql FROM sqlite_master "
399
+ sqlStmt += "WHERE sql LIKE('%FOREIGN KEY%') AND "
400
+ sqlStmt += "sql LIKE('%REFERENCES%') AND "
401
+ sqlStmt += "sql LIKE('%\(tableName)%') AND "
402
+ sqlStmt += "sql LIKE('%ON DELETE%');"
403
+ do {
404
+ var references: [[String: Any]] = try UtilsSQLCipher
405
+ .querySQL(mDB: mDB, sql: sqlStmt,values: [])
406
+ var retRefs = [String]()
407
+ var tableWithRefs: String = ""
408
+ if references.count > 1 {
409
+ references.removeFirst()
410
+ if let refValue = references[0]["sql"] as? String {
411
+ let result = try getRefs(sqlStatement: refValue)
412
+ retRefs = result.foreignKeys
413
+ tableWithRefs = result.tableName
414
+ }
415
+ }
416
+ return (tableWithRefs, retRefs)
417
+ } catch UtilsDeleteError.getRefs(let message) {
418
+ throw UtilsDeleteError
419
+ .getReferences(message: message)
420
+ } catch UtilsSQLCipherError.querySQL(let message) {
421
+ throw UtilsDeleteError
422
+ .getReferences(message: message)
423
+ }
424
+ }
425
+
426
+ // MARK: - getRefs
427
+
428
+ class func getRefs(sqlStatement: String)
429
+ throws -> (tableName: String, foreignKeys: [String]) {
430
+ var tableName = ""
431
+ var foreignKeys = [String]()
432
+ let statement = UtilsSQLStatement
433
+ .flattenMultilineString(sqlStatement)
434
+
435
+ do {
436
+ // Regular expression pattern to match the table name
437
+ let tableNamePattern = #"CREATE\s+TABLE\s+(\w+)\s+\("#
438
+ let tableNameRegex = try NSRegularExpression(
439
+ pattern: tableNamePattern, options: [])
440
+ if let tableNameMatch = tableNameRegex
441
+ .firstMatch(in: statement, options: [],
442
+ range: NSRange(location: 0,
443
+ length: statement.utf16.count)) {
444
+ let tableNameRange = Range(tableNameMatch.range(
445
+ at: 1), in: statement)!
446
+ tableName = String(statement[tableNameRange])
447
+ }
448
+
449
+ // Regular expression pattern to match the FOREIGN KEY
450
+ // constraints
451
+ // swiftlint:disable line_length
452
+ let foreignKeyPattern = #"FOREIGN\s+KEY\s+\([^)]+\)\s+REFERENCES\s+(\w+)\s*\([^)]+\)\s+ON\s+DELETE\s+(CASCADE|RESTRICT|SET\s+DEFAULT|SET\s+NULL|NO\s+ACTION)"#
453
+ // swiftlint:enable line_length
454
+
455
+ let foreignKeyRegex = try NSRegularExpression(
456
+ pattern: foreignKeyPattern, options: [])
457
+ let foreignKeyMatches = foreignKeyRegex
458
+ .matches(in: statement, options: [],
459
+ range: NSRange(location: 0,
460
+ length: statement.utf16.count))
461
+ for foreignKeyMatch in foreignKeyMatches {
462
+ let foreignKeyRange = Range(
463
+ foreignKeyMatch.range(at: 0), in: statement)!
464
+ let foreignKey = String(statement[foreignKeyRange])
465
+ foreignKeys.append(foreignKey)
466
+ }
467
+ } catch {
468
+ let msg = "getRefs: Error creating regular expression: " +
469
+ "\(error)"
470
+ throw UtilsDeleteError.getRefs(message: msg)
471
+ }
472
+ return (tableName, foreignKeys)
473
+ }
474
+
475
+ // MARK: - getUpdDelReturnedValues
476
+
477
+ class func getUpdDelReturnedValues(mDB: Database,
478
+ sqlStmt: String,
479
+ names: String )
480
+ throws -> [[String: Any]] {
481
+ var result: [[String: Any]] = []
482
+ let tableName = UtilsSQLStatement
483
+ .extractTableName(from: sqlStmt)
484
+ let whereClause = UtilsSQLStatement
485
+ .extractWhereClause(from: sqlStmt)
486
+ if let tblName = tableName {
487
+ if var wClause = whereClause {
488
+ if wClause.suffix(1) == ";" {
489
+ wClause = String(wClause.dropLast())
490
+ }
491
+ do {
492
+ var query: String = "SELECT \(names) FROM " +
493
+ "\(tblName) WHERE "
494
+ query += "\(wClause);"
495
+ result = try UtilsSQLCipher
496
+ .querySQL(mDB: mDB, sql: query, values: [])
497
+ } catch UtilsSQLCipherError.querySQL(let message) {
498
+ throw UtilsDeleteError
499
+ .getUpdDelReturnedValues(message: message)
500
+ }
501
+ }
502
+ }
503
+ return result
504
+ }
505
+ }
506
+ // swiftlint:enable type_body_length