@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.
@@ -0,0 +1,450 @@
1
+ //
2
+ // UtilsSQLStatement.swift
3
+ // CapacitorCommunitySqlite
4
+ //
5
+ // Created by Quéau Jean Pierre on 30/07/2023.
6
+ //
7
+
8
+ import Foundation
9
+ enum UtilsSQLStatementError: Error {
10
+ case extractColumnNames(message:String)
11
+ case extractForeignKeyInfo(message:String)
12
+ case addPrefixToWhereClause(message:String)
13
+ }
14
+
15
+ class UtilsSQLStatement {
16
+
17
+ // MARK: - extractTableName
18
+
19
+ class func extractTableName(from statement: String) -> String? {
20
+ let pattern =
21
+ "(?:INSERT\\s+INTO|UPDATE|DELETE\\s+FROM)\\s+([^\\s]+)"
22
+ guard let regex = try? NSRegularExpression(pattern: pattern,
23
+ options: []) else {
24
+ return nil }
25
+
26
+ let range = NSRange(location: 0, length: statement.count)
27
+ if let match = regex.firstMatch(in: statement, options: [],
28
+ range: range) {
29
+ let tableNameRange = match.range(at: 1)
30
+ if let tableNameRange = Range(tableNameRange,
31
+ in: statement) {
32
+ let tableName = String(statement[tableNameRange])
33
+ return tableName
34
+ }
35
+ }
36
+
37
+ return nil
38
+ }
39
+
40
+ // MARK: - extractWhereClause
41
+
42
+ class func extractWhereClause(from statement: String) -> String? {
43
+ let pattern = "WHERE(.+?)(?:ORDER\\s+BY|LIMIT|$)"
44
+ guard let regex = try? NSRegularExpression(
45
+ pattern: pattern,
46
+ options: [.caseInsensitive,
47
+ .dotMatchesLineSeparators]) else {
48
+ return nil
49
+
50
+ }
51
+
52
+ let range = NSRange(location: 0, length: statement.count)
53
+ if let match = regex.firstMatch(in: statement, options: [],
54
+ range: range) {
55
+ let whereClauseRange = match.range(at: 1)
56
+ if let whereClauseRange = Range(whereClauseRange,
57
+ in: statement) {
58
+ let whereClause = String(statement[whereClauseRange])
59
+ return whereClause
60
+ .trimmingCharacters(in: .whitespacesAndNewlines)
61
+ }
62
+ }
63
+
64
+ return nil
65
+ }
66
+
67
+ // MARK: - addPrefixToWhereClause
68
+
69
+ class func addPrefixToWhereClause(_ whereClause: String, from: [String],
70
+ to: [String],prefix: String)
71
+ throws -> String {
72
+ var columnValuePairs: [String]
73
+ if whereClause.contains("AND") {
74
+ if #available(iOS 16.0, *) {
75
+ let subSequenceArray = whereClause
76
+ .split(separator: "AND")
77
+ columnValuePairs = subSequenceArray.map { String($0) }
78
+ } else {
79
+ columnValuePairs = whereClause
80
+ .components(separatedBy: "AND")
81
+ }
82
+ } else {
83
+ columnValuePairs = [whereClause]
84
+ }
85
+ let modifiedPairs = try columnValuePairs.map { pair -> String in
86
+ let pattern = #"(\w+)\s*(=|IN|BETWEEN|LIKE)\s*(.+)"#
87
+
88
+ if let range = pair.range(of: pattern, options: .regularExpression) {
89
+ let match = String(pair[range])
90
+ let regex = try NSRegularExpression(pattern: pattern)
91
+ let matchRange = NSRange(match.startIndex..., in: match)
92
+
93
+ if let matchResult = regex.firstMatch(in: match, range: matchRange) {
94
+ let columnRange = Range(matchResult.range(at: 1), in: match)!
95
+ let operatorRange = Range(matchResult.range(at: 2), in: match)!
96
+ let valueRange = Range(matchResult.range(at: 3), in: match)!
97
+
98
+ let column = String(match[columnRange]).trimmingCharacters(in: .whitespacesAndNewlines)
99
+ let mOperator = String(match[operatorRange]).trimmingCharacters(in: .whitespacesAndNewlines)
100
+ let value = String(match[valueRange]).trimmingCharacters(in: .whitespacesAndNewlines)
101
+
102
+ var newColumn = column
103
+ if let index = UtilsSQLStatement
104
+ .findIndexOfStringInArray(column, to), index != -1 {
105
+ guard let mNewColumn = UtilsSQLStatement
106
+ .getStringAtIndex(from, index) else {
107
+ let msg = "addPrefixToWhereClause: index " +
108
+ "mistmatch "
109
+ throw UtilsSQLStatementError
110
+ .addPrefixToWhereClause(message: msg)
111
+ }
112
+ newColumn = mNewColumn
113
+ }
114
+
115
+ let modifiedColumn = "\(prefix)\(newColumn)"
116
+ return "\(modifiedColumn) \(mOperator) \(value)"
117
+ }
118
+ }
119
+ return pair
120
+ }
121
+ return modifiedPairs.joined(separator: " AND ")
122
+
123
+ }
124
+
125
+ // MARK: - findIndexOfStringInArray
126
+
127
+ class func findIndexOfStringInArray(_ target: String, _ array: [String]) -> Int? {
128
+ return array.firstIndex(of: target)
129
+ }
130
+
131
+ // MARK: - getStringAtIndex
132
+
133
+ class func getStringAtIndex(_ array: [String], _ index: Int) -> String? {
134
+ if index >= 0 && index < array.count {
135
+ return array[index]
136
+ } else {
137
+ return nil
138
+ }
139
+ }
140
+ // MARK: - extractForeignKeyInfo
141
+
142
+ class func extractForeignKeyInfo(from sqlStatement: String)
143
+ throws ->
144
+ [String: Any] {
145
+ var foreignKeyInfo: [String: Any] = [:]
146
+ // Define the regular expression patterns for extracting the
147
+ // FOREIGN KEY clause and composite primary keys
148
+ let foreignKeyPattern = #"\bFOREIGN\s+KEY\s*\(([^)]+)\)\s+REFERENCES\s+(\w+)\s*\(([^)]+)\)\s+(ON\s+DELETE\s+(RESTRICT|CASCADE|SET\s+NULL|SET\s+DEFAULT|NO\s+ACTION))?"#
149
+
150
+ // Create a regular expression object
151
+ guard let regex = try? NSRegularExpression(
152
+ pattern: foreignKeyPattern, options: []) else {
153
+ let msg = "extractForeignKeyInfo: creating regular " +
154
+ "expression pattern"
155
+ throw UtilsSQLStatementError
156
+ .extractForeignKeyInfo(message: msg)
157
+
158
+ }
159
+
160
+ // Find the range of the FOREIGN KEY clause in the SQL
161
+ // statement
162
+ let range = NSRange(location: 0,
163
+ length: sqlStatement.utf16.count)
164
+ if let match = regex.firstMatch(in: sqlStatement, options: [],
165
+ range: range) {
166
+ // Extract the column names for the FOREIGN KEY
167
+ let foreignKeyColumnsRange = match.range(at: 1)
168
+ if let foreignKeyColumnsRangeInString =
169
+ Range(foreignKeyColumnsRange, in: sqlStatement) {
170
+ let foreignKeyColumns =
171
+ String(sqlStatement[foreignKeyColumnsRangeInString])
172
+ .components(separatedBy: ", ")
173
+
174
+ // Extract the referenced table and columns
175
+ let referencedTableRange = match.range(at: 2)
176
+ let referencedColumnsRange = match.range(at: 3)
177
+ if
178
+ let referencedTableRangeInString =
179
+ Range(referencedTableRange, in: sqlStatement),
180
+ let referencedColumnsRangeInString =
181
+ Range(referencedColumnsRange, in: sqlStatement)
182
+ {
183
+ let referencedTable = String(
184
+ sqlStatement[referencedTableRangeInString])
185
+ let referencedColumns = String(
186
+ sqlStatement[referencedColumnsRangeInString])
187
+ .components(separatedBy: ", ")
188
+
189
+ // Extract the ON DELETE action if available
190
+ let onDeleteActionRange = match.range(at: 5)
191
+ if let onDeleteActionRangeInString =
192
+ Range(onDeleteActionRange, in: sqlStatement) {
193
+ let onDeleteAction = String(
194
+ sqlStatement[onDeleteActionRangeInString])
195
+ foreignKeyInfo["forKeys"] = foreignKeyColumns
196
+ foreignKeyInfo["tableName"] = referencedTable
197
+ foreignKeyInfo["refKeys"] = referencedColumns
198
+ foreignKeyInfo["action"] = onDeleteAction
199
+ return foreignKeyInfo
200
+ } else {
201
+ foreignKeyInfo["forKeys"] = foreignKeyColumns
202
+ foreignKeyInfo["tableName"] = referencedTable
203
+ foreignKeyInfo["refKeys"] = referencedColumns
204
+ foreignKeyInfo["action"] = "NO_ACTION"
205
+ return foreignKeyInfo
206
+ }
207
+ } else {
208
+ let msg = "extractForeignKeyInfo: No match found"
209
+ throw UtilsSQLStatementError
210
+ .extractForeignKeyInfo(message: msg)
211
+ }
212
+ }
213
+ } else {
214
+ let msg = "extractForeignKeyInfo: No FOREIGN KEY found"
215
+ throw UtilsSQLStatementError
216
+ .extractForeignKeyInfo(message: msg)
217
+ }
218
+ foreignKeyInfo["forKeys"] = []
219
+ foreignKeyInfo["tableName"] = ""
220
+ foreignKeyInfo["refKeys"] = []
221
+ foreignKeyInfo["action"] = "NO_ACTION"
222
+ return foreignKeyInfo
223
+ }
224
+
225
+
226
+ // MARK: - extractColumnNames
227
+
228
+ class func extractColumnNames(from whereClause: String)
229
+ throws -> [String] {
230
+ let regexPattern = #"\b(\w+)\s*(?=(?:[=<>]|IN\s*\(VALUES\s*|\bBETWEEN\b|\bLIKE\s*'))|(?<=\()\s*(\w+),\s*(\w+)\s*(?=\))"#
231
+
232
+ do {
233
+
234
+ let regex = try NSRegularExpression(pattern: regexPattern)
235
+ let matches = regex.matches(in: whereClause, range: NSRange(whereClause.startIndex..., in: whereClause))
236
+ var columnNames: [String] = []
237
+
238
+ for match in matches {
239
+ for rangeIdx in 1..<match.numberOfRanges {
240
+ let range = match.range(at: rangeIdx)
241
+ if range.location != NSNotFound, let columnNameRange = Range(range, in: whereClause) {
242
+ let columnName = String(whereClause[columnNameRange])
243
+ columnNames.append(columnName)
244
+ }
245
+ }
246
+ }
247
+
248
+ return columnNames
249
+
250
+ } catch {
251
+ let msg = "Error creating regular expression: " +
252
+ "\(error.localizedDescription)"
253
+ throw UtilsSQLStatementError.extractColumnNames(message: msg)
254
+ }
255
+ }
256
+
257
+ // MARK: - flattenMultilineString
258
+
259
+ class func flattenMultilineString(_ input: String) -> String {
260
+ let lines = input
261
+ .components(separatedBy: CharacterSet.newlines)
262
+ return lines.joined(separator: " ")
263
+ }
264
+
265
+ // MARK: - getStmtAndRetColNames
266
+
267
+ class func getStmtAndRetColNames(sqlStmt: String, retMode: String)
268
+ -> [String: String] {
269
+ let retWord = "RETURNING"
270
+
271
+ var retStmtNames: [String: String] = [:]
272
+ retStmtNames["stmt"] = sqlStmt
273
+ retStmtNames["names"] = ""
274
+ if let range = sqlStmt.uppercased().range(of: retWord) {
275
+ let prefix = sqlStmt.prefix(upTo: range.lowerBound)
276
+ retStmtNames["stmt"] = "\(prefix);"
277
+ retStmtNames["names"] = ""
278
+ if retMode.prefix(2) == "wA" {
279
+ let suffix = sqlStmt.suffix(from: range.upperBound)
280
+ let names =
281
+ "\(suffix)".trimmingLeadingAndTrailingSpaces()
282
+ if names.suffix(1) == ";" {
283
+ retStmtNames["names"] = String(names.dropLast())
284
+ }
285
+ }
286
+
287
+ }
288
+ return retStmtNames
289
+ }
290
+
291
+ // MARK: - extractCombinedPrimaryKey
292
+
293
+ class func extractCombinedPrimaryKey(from whereClause: String)
294
+ -> [[String]]? {
295
+ // Regular expression pattern to match the combined primary
296
+ // key comparison with IN operator or without it
297
+ // meaning = operator
298
+ let pattern = #"WHERE\s*\((.+?)\)\s*(?:=|IN)\s*\((.+?)\)"#
299
+
300
+ guard let regex = try? NSRegularExpression(pattern: pattern,
301
+ options: []) else {
302
+ print("Invalid regular expression pattern.")
303
+ return nil
304
+ }
305
+ let range = NSRange(location: 0,
306
+ length: whereClause.utf16.count)
307
+ let matches = regex.matches(in: whereClause, options: [],
308
+ range: range)
309
+ var primaryKeySets: [[String]] = []
310
+
311
+ for match in matches {
312
+
313
+ let keysRange = Range(match.range(at: 1), in: whereClause)!
314
+ let keysString = String(whereClause[keysRange])
315
+ let keys = keysString.split(separator: ",").map {
316
+ String($0.trimmingCharacters(in: .whitespaces)) }
317
+ primaryKeySets.append(keys)
318
+ }
319
+ return primaryKeySets.isEmpty ? nil : primaryKeySets
320
+ }
321
+
322
+ // MARK: - getWhereStatementForCombinedPK
323
+
324
+ class func getWhereStmtForCombinedPK(whStmt: String,
325
+ withRefs: [String],
326
+ colNames: [String],
327
+ keys: [[String]])
328
+ -> String {
329
+ var retWhere: String = whStmt
330
+ var repKeys: [String] = []
331
+ for grpKeys in keys {
332
+ if grpKeys == withRefs {
333
+ repKeys = colNames
334
+ } else {
335
+ repKeys = withRefs
336
+ }
337
+ for (index, key) in grpKeys.enumerated() {
338
+ retWhere = replaceAllString(originalStr: retWhere,
339
+ searchStr: key,
340
+ replaceStr: repKeys[index])
341
+ }
342
+ }
343
+ return retWhere
344
+ }
345
+
346
+ // MARK: - replaceAllString
347
+
348
+ class func replaceAllString(originalStr: String, searchStr: String,
349
+ replaceStr: String) -> String {
350
+ let modifiedStr = originalStr
351
+ .replacingOccurrences(of: searchStr, with: replaceStr)
352
+ return modifiedStr
353
+ }
354
+
355
+ // MARK: - replaceString
356
+
357
+ class func replaceString(originalStr: String, searchStr: String,
358
+ replaceStr: String) -> String {
359
+ var modifiedStr = originalStr
360
+ if let range = originalStr.range(of: searchStr) {
361
+ modifiedStr.replaceSubrange(range, with: replaceStr)
362
+ }
363
+ return modifiedStr
364
+ }
365
+
366
+ // MARK: - getWhereStmtForNonCombinedPK
367
+
368
+ class func getWhereStmtForNonCombinedPK(whStmt: String,
369
+ withRefs: [String],
370
+ colNames: [String])
371
+ -> String {
372
+ var whereStmt = ""
373
+ var stmt: String = String(whStmt.stringRange(
374
+ fromIdx: 6,
375
+ toIdx: whStmt.count))
376
+ for (idx, wRf) in withRefs.enumerated() {
377
+ var colType: String = "withRefsNames"
378
+ var idxs: [Int] = stmt.indicesOf(string: wRf)
379
+ if idxs.count == 0 {
380
+ idxs = stmt.indicesOf(string: colNames[idx])
381
+ colType = "colNames"
382
+ }
383
+ if idxs.count > 0 {
384
+ var valStr: String = ""
385
+ let indicesEqual: [Int] = stmt
386
+ .indicesOf(string: "=",
387
+ fromIdx: idxs[0])
388
+
389
+ if indicesEqual.count > 0 {
390
+ let indicesAnd: [Int] = stmt
391
+ .indicesOf(string: "AND",
392
+ fromIdx: indicesEqual[0])
393
+ if indicesAnd.count > 0 {
394
+ valStr = String(stmt.stringRange(
395
+ fromIdx: indicesEqual[0] + 1,
396
+ toIdx: indicesAnd[0] - 1))
397
+ stmt = String(stmt.stringRange(
398
+ fromIdx: indicesAnd[0] + 3,
399
+ toIdx: stmt.count))
400
+ } else {
401
+ valStr = String(stmt.stringRange(
402
+ fromIdx: indicesEqual[0] + 1,
403
+ toIdx: stmt.count))
404
+ }
405
+ if idx > 0 {
406
+ whereStmt += " AND "
407
+ }
408
+ if colType == "withRefsNames" {
409
+ whereStmt += colNames[idx] + " = " + valStr
410
+ } else {
411
+ whereStmt += withRefs[idx] + " = " + valStr
412
+ }
413
+ }
414
+ }
415
+ }
416
+ whereStmt = "WHERE " + whereStmt
417
+ return whereStmt
418
+ }
419
+
420
+ // MARK: - updateWhere
421
+
422
+ class func updateWhere(whStmt: String, withRefs: [String],
423
+ colNames: [String]) -> String {
424
+ var whereStmt = ""
425
+ if whStmt.count <= 0 {
426
+ return whereStmt
427
+ }
428
+ if whStmt.uppercased().prefix(5) != "WHERE" {
429
+ return whereStmt
430
+ }
431
+ if withRefs.count == colNames.count {
432
+ // get whereStmt for primary combined key
433
+ if let keys: [[String]] = UtilsSQLStatement
434
+ .extractCombinedPrimaryKey(from: whStmt) {
435
+ whereStmt = UtilsSQLStatement
436
+ .getWhereStmtForCombinedPK(whStmt: whStmt,
437
+ withRefs: withRefs,
438
+ colNames: colNames,
439
+ keys: keys)
440
+ } else {
441
+ // get for non primary combined key
442
+ whereStmt = UtilsSQLStatement
443
+ .getWhereStmtForNonCombinedPK(whStmt: whStmt,
444
+ withRefs: withRefs,
445
+ colNames: colNames)
446
+ }
447
+ }
448
+ return whereStmt
449
+ }
450
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capacitor-community/sqlite",
3
- "version": "5.0.6",
3
+ "version": "5.0.7-1",
4
4
  "description": "Community plugin for native & electron SQLite databases",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",
@@ -95,6 +95,6 @@
95
95
  }
96
96
  },
97
97
  "dependencies": {
98
- "jeep-sqlite": "^2.3.8"
98
+ "jeep-sqlite": "^2.4.1"
99
99
  }
100
100
  }