@capacitor-community/sqlite 5.0.5 → 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 +3 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/CapacitorSQLite.java +9 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/Database.java +9 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/ImportExportJson/UtilsEncryption.java +111 -0
- package/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSecret.java +3 -3
- package/dist/esm/definitions.d.ts +66 -0
- package/dist/esm/definitions.js +37 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -1
- package/dist/esm/web.js +8 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +45 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +45 -0
- package/dist/plugin.js.map +1 -1
- package/electron/dist/plugin.js +138 -52
- package/electron/dist/plugin.js.map +1 -1
- package/electron/rollup.config.js +2 -0
- package/ios/Plugin/CapacitorSQLite.swift +117 -88
- package/ios/Plugin/Database.swift +17 -5
- package/ios/Plugin/ImportExportJson/ImportData.swift +434 -0
- package/ios/Plugin/ImportExportJson/ImportFromJson.swift +43 -57
- package/ios/Plugin/ImportExportJson/JsonSQLite.swift +7 -0
- package/ios/Plugin/Utils/UtilsDelete.swift +506 -0
- package/ios/Plugin/Utils/UtilsJson.swift +123 -1
- package/ios/Plugin/Utils/UtilsSQLCipher.swift +173 -477
- package/ios/Plugin/Utils/UtilsSQLStatement.swift +450 -0
- package/package.json +2 -2
- package/src/definitions.ts +104 -0
- package/src/web.ts +10 -0
|
@@ -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
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
//
|
|
7
7
|
|
|
8
8
|
import Foundation
|
|
9
|
+
import CryptoKit
|
|
10
|
+
import CommonCrypto
|
|
9
11
|
|
|
10
12
|
enum UtilsJsonError: Error {
|
|
11
13
|
case tableNotExists(message: String)
|
|
@@ -20,7 +22,12 @@ enum UtilsJsonError: Error {
|
|
|
20
22
|
case isLastModified(message: String)
|
|
21
23
|
case isSqlDeleted(message: String)
|
|
22
24
|
case checkUpdate(message: String)
|
|
23
|
-
case checkValues(message: String)
|
|
25
|
+
case checkValues(message: String)
|
|
26
|
+
case encryptDictionaryToBase64(message: String)
|
|
27
|
+
case decryptBase64ToDictionary(message: String)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
var mSalt = "jeep_capacitor_sqlite"
|
|
24
31
|
|
|
25
32
|
// swiftlint:disable file_length
|
|
26
33
|
// swiftlint:disable type_body_length
|
|
@@ -466,6 +473,121 @@ class UtilsJson {
|
|
|
466
473
|
return isViews
|
|
467
474
|
}
|
|
468
475
|
|
|
476
|
+
// MARK: encryptDictionaryToBase64
|
|
477
|
+
|
|
478
|
+
class func encryptDictionaryToBase64(_ dictionary: [String: Any], forAccount account: String) throws -> String {
|
|
479
|
+
// Encryption using Swift Crypto and Keychain-stored passphrase
|
|
480
|
+
|
|
481
|
+
let jsonData = try JSONSerialization.data(withJSONObject: dictionary)
|
|
482
|
+
|
|
483
|
+
let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
|
|
484
|
+
if !isPassPhrase {
|
|
485
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
486
|
+
message: "No passphrase stored")
|
|
487
|
+
}
|
|
488
|
+
let passphrase = UtilsSecret.getPassphrase(account: account)
|
|
489
|
+
// Generate a constant salt
|
|
490
|
+
let salt = Data(mSalt.utf8)
|
|
491
|
+
|
|
492
|
+
// Derive the encryption key using the passphrase and salt
|
|
493
|
+
guard let key = deriveSymmetricKeyFromPassphrase(passphrase, salt: salt) else {
|
|
494
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
495
|
+
message: "No Encryption key returned")
|
|
496
|
+
}
|
|
497
|
+
let symKey = SymmetricKey(data: key)
|
|
498
|
+
|
|
499
|
+
// Use Swift Crypto to perform AES encryption with GCM mode
|
|
500
|
+
let sealedBox = try AES.GCM.seal(jsonData, using: symKey, nonce: AES.GCM.Nonce())
|
|
501
|
+
|
|
502
|
+
if let combined = sealedBox.combined {
|
|
503
|
+
// combined the salt and the encrypted data
|
|
504
|
+
var saltAndEncryptedData = salt
|
|
505
|
+
saltAndEncryptedData.append(combined)
|
|
506
|
+
// Convert to base64 string
|
|
507
|
+
|
|
508
|
+
let base64String = saltAndEncryptedData.base64EncodedString()
|
|
509
|
+
return base64String
|
|
510
|
+
} else {
|
|
511
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
512
|
+
message: "Conversion to base64String failed")
|
|
513
|
+
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// MARK: decrypt DictionaryToBase64
|
|
519
|
+
|
|
520
|
+
class func decryptBase64ToDictionary(_ base64String: String,
|
|
521
|
+
forAccount account: String)
|
|
522
|
+
throws -> [String: Any] {
|
|
523
|
+
guard let data = Data(base64Encoded: base64String) else {
|
|
524
|
+
throw UtilsJsonError.decryptBase64ToDictionary(
|
|
525
|
+
message: "Conversion from Base64 to Dictionary failed")
|
|
526
|
+
|
|
527
|
+
}
|
|
528
|
+
let isPassPhrase = try UtilsSecret.isPassphrase(account: account)
|
|
529
|
+
if !isPassPhrase {
|
|
530
|
+
throw UtilsJsonError.decryptBase64ToDictionary(
|
|
531
|
+
message: "No passphrase stored")
|
|
532
|
+
}
|
|
533
|
+
let passphrase = UtilsSecret.getPassphrase(account: account)
|
|
534
|
+
// Generate a constant salt
|
|
535
|
+
let salt = Data(mSalt.utf8)
|
|
536
|
+
|
|
537
|
+
// Derive the encryption key using the passphrase and salt
|
|
538
|
+
guard let key = deriveSymmetricKeyFromPassphrase(passphrase,
|
|
539
|
+
salt: salt) else {
|
|
540
|
+
throw UtilsJsonError.encryptDictionaryToBase64(
|
|
541
|
+
message: "No Encryption key returned")
|
|
542
|
+
}
|
|
543
|
+
let symKey = SymmetricKey(data: key)
|
|
544
|
+
|
|
545
|
+
// Extract the encrypted data from the remaining bytes
|
|
546
|
+
let encryptedData = data.suffix(from: salt.count)
|
|
547
|
+
|
|
548
|
+
// Convert the base64 string to a sealed box
|
|
549
|
+
let sealedBox = try AES.GCM.SealedBox(combined: encryptedData)
|
|
550
|
+
|
|
551
|
+
// Decrypt the ciphertext using AES GCM open
|
|
552
|
+
let decryptedData = try AES.GCM.open(sealedBox, using: symKey)
|
|
553
|
+
|
|
554
|
+
// Convert decrypted data to dictionary
|
|
555
|
+
let dictionary = try JSONSerialization.jsonObject(
|
|
556
|
+
with: decryptedData) as? [String: Any] ?? [:]
|
|
557
|
+
|
|
558
|
+
return dictionary
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
// MARK: deriveSymmetricKeyFromPassphrase
|
|
562
|
+
|
|
563
|
+
class func deriveSymmetricKeyFromPassphrase(_ passphrase: String,
|
|
564
|
+
salt: Data)
|
|
565
|
+
-> Data? {
|
|
566
|
+
|
|
567
|
+
let passphraseData = Data(passphrase.utf8)
|
|
568
|
+
let keyLength = kCCKeySizeAES256
|
|
569
|
+
let iterations: UInt32 = 10000
|
|
570
|
+
var derivedKeyData = Data(count: keyLength)
|
|
571
|
+
let derivedCount = derivedKeyData.count
|
|
572
|
+
let derivationStatus = derivedKeyData.withUnsafeMutableBytes { derivedKeyUnsafeMutableRawBufferPointer in
|
|
573
|
+
passphraseData.withUnsafeBytes { passphraseUnsafeRawBufferPointer in
|
|
574
|
+
salt.withUnsafeBytes { saltUnsafeRawBufferPointer in
|
|
575
|
+
CCKeyDerivationPBKDF(
|
|
576
|
+
CCPBKDFAlgorithm(kCCPBKDF2),
|
|
577
|
+
passphraseUnsafeRawBufferPointer.baseAddress,
|
|
578
|
+
passphraseData.count,
|
|
579
|
+
saltUnsafeRawBufferPointer.baseAddress,
|
|
580
|
+
salt.count,
|
|
581
|
+
CCPseudoRandomAlgorithm(kCCPRFHmacAlgSHA256),
|
|
582
|
+
iterations,
|
|
583
|
+
derivedKeyUnsafeMutableRawBufferPointer.baseAddress,
|
|
584
|
+
derivedCount
|
|
585
|
+
)
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
return derivationStatus == kCCSuccess ? derivedKeyData : nil
|
|
590
|
+
}
|
|
469
591
|
}
|
|
470
592
|
// swiftlint:enable type_body_length
|
|
471
593
|
// swiftlint:enable file_length
|