-
-
Notifications
You must be signed in to change notification settings - Fork 744
/
Copy pathDatabaseError.swift
625 lines (591 loc) · 31.6 KB
/
DatabaseError.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
// Import C SQLite functions
#if GRDBCIPHER
import SQLCipher
#elseif SWIFT_PACKAGE
import GRDBSQLite
#elseif !GRDBCUSTOMSQLITE && !GRDBCIPHER
import SQLite3
#endif
import Foundation
/// An SQLite result code.
///
/// Related SQLite documentation: <https://www.sqlite.org/rescode.html>
public struct ResultCode: RawRepresentable, Equatable {
/// The raw SQLite result code.
public let rawValue: CInt
/// Creates a `ResultCode` from a raw SQLite result code.
public init(rawValue: CInt) {
self.rawValue = rawValue
}
/// A primary result code limited to the least significant 8 bits.
///
/// For example:
///
/// ```swift
/// let resultCode = .SQLITE_CONSTRAINT_FOREIGNKEY
/// resultCode.primaryResultCode == .SQLITE_CONSTRAINT // true
/// ```
public var primaryResultCode: ResultCode {
ResultCode(rawValue: rawValue & 0xFF)
}
var isPrimary: Bool { self == primaryResultCode }
/// Returns true if the code on the left matches the code on the right.
///
/// Primary result codes match themselves and their extended result codes,
/// while extended result codes match only themselves:
///
/// For example:
///
/// ```swift
/// switch error.extendedResultCode {
/// case .SQLITE_CONSTRAINT_FOREIGNKEY: // foreign key constraint error
/// case .SQLITE_CONSTRAINT: // any other constraint error
/// default: // any other database error
/// }
/// ```
public static func ~= (pattern: ResultCode, code: ResultCode) -> Bool {
if pattern.isPrimary {
return pattern == code.primaryResultCode
} else {
return pattern == code
}
}
// Primary Result codes
// https://www.sqlite.org/rescode.html#primary_result_code_list
// swiftlint:disable operator_usage_whitespace
public static let SQLITE_OK = ResultCode(rawValue: 0) // Successful result
public static let SQLITE_ERROR = ResultCode(rawValue: 1) // SQL error or missing database
public static let SQLITE_INTERNAL = ResultCode(rawValue: 2) // Internal logic error in SQLite
public static let SQLITE_PERM = ResultCode(rawValue: 3) // Access permission denied
public static let SQLITE_ABORT = ResultCode(rawValue: 4) // Callback routine requested an abort
public static let SQLITE_BUSY = ResultCode(rawValue: 5) // The database file is locked
public static let SQLITE_LOCKED = ResultCode(rawValue: 6) // A table in the database is locked
public static let SQLITE_NOMEM = ResultCode(rawValue: 7) // A malloc() failed
public static let SQLITE_READONLY = ResultCode(rawValue: 8) // Attempt to write a readonly database
public static let SQLITE_INTERRUPT = ResultCode(rawValue: 9) // Operation terminated by sqlite3_interrupt()
public static let SQLITE_IOERR = ResultCode(rawValue: 10) // Some kind of disk I/O error occurred
public static let SQLITE_CORRUPT = ResultCode(rawValue: 11) // The database disk image is malformed
public static let SQLITE_NOTFOUND = ResultCode(rawValue: 12) // Unknown opcode in sqlite3_file_control()
public static let SQLITE_FULL = ResultCode(rawValue: 13) // Insertion failed because database is full
public static let SQLITE_CANTOPEN = ResultCode(rawValue: 14) // Unable to open the database file
public static let SQLITE_PROTOCOL = ResultCode(rawValue: 15) // Database lock protocol error
public static let SQLITE_EMPTY = ResultCode(rawValue: 16) // Database is empty
public static let SQLITE_SCHEMA = ResultCode(rawValue: 17) // The database schema changed
public static let SQLITE_TOOBIG = ResultCode(rawValue: 18) // String or BLOB exceeds size limit
public static let SQLITE_CONSTRAINT = ResultCode(rawValue: 19) // Abort due to constraint violation
public static let SQLITE_MISMATCH = ResultCode(rawValue: 20) // Data type mismatch
public static let SQLITE_MISUSE = ResultCode(rawValue: 21) // Library used incorrectly
public static let SQLITE_NOLFS = ResultCode(rawValue: 22) // Uses OS features not supported on host
public static let SQLITE_AUTH = ResultCode(rawValue: 23) // Authorization denied
public static let SQLITE_FORMAT = ResultCode(rawValue: 24) // Auxiliary database format error
public static let SQLITE_RANGE = ResultCode(rawValue: 25) // 2nd parameter to sqlite3_bind out of range
public static let SQLITE_NOTADB = ResultCode(rawValue: 26) // File opened that is not a database file
public static let SQLITE_NOTICE = ResultCode(rawValue: 27) // Notifications from sqlite3_log()
public static let SQLITE_WARNING = ResultCode(rawValue: 28) // Warnings from sqlite3_log()
public static let SQLITE_ROW = ResultCode(rawValue: 100) // sqlite3_step() has another row ready
public static let SQLITE_DONE = ResultCode(rawValue: 101) // sqlite3_step() has finished executing
// swiftlint:enable operator_usage_whitespace
// Extended Result Code
// https://www.sqlite.org/rescode.html#extended_result_code_list
// swiftlint:disable operator_usage_whitespace line_length
public static let SQLITE_ERROR_MISSING_COLLSEQ = ResultCode(rawValue: (SQLITE_ERROR.rawValue | (1<<8)))
public static let SQLITE_ERROR_RETRY = ResultCode(rawValue: (SQLITE_ERROR.rawValue | (2<<8)))
public static let SQLITE_ERROR_SNAPSHOT = ResultCode(rawValue: (SQLITE_ERROR.rawValue | (3<<8)))
public static let SQLITE_IOERR_READ = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (1<<8)))
public static let SQLITE_IOERR_SHORT_READ = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (2<<8)))
public static let SQLITE_IOERR_WRITE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (3<<8)))
public static let SQLITE_IOERR_FSYNC = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (4<<8)))
public static let SQLITE_IOERR_DIR_FSYNC = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (5<<8)))
public static let SQLITE_IOERR_TRUNCATE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (6<<8)))
public static let SQLITE_IOERR_FSTAT = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (7<<8)))
public static let SQLITE_IOERR_UNLOCK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (8<<8)))
public static let SQLITE_IOERR_RDLOCK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (9<<8)))
public static let SQLITE_IOERR_DELETE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (10<<8)))
public static let SQLITE_IOERR_BLOCKED = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (11<<8)))
public static let SQLITE_IOERR_NOMEM = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (12<<8)))
public static let SQLITE_IOERR_ACCESS = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (13<<8)))
public static let SQLITE_IOERR_CHECKRESERVEDLOCK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (14<<8)))
public static let SQLITE_IOERR_LOCK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (15<<8)))
public static let SQLITE_IOERR_CLOSE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (16<<8)))
public static let SQLITE_IOERR_DIR_CLOSE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (17<<8)))
public static let SQLITE_IOERR_SHMOPEN = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (18<<8)))
public static let SQLITE_IOERR_SHMSIZE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (19<<8)))
public static let SQLITE_IOERR_SHMLOCK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (20<<8)))
public static let SQLITE_IOERR_SHMMAP = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (21<<8)))
public static let SQLITE_IOERR_SEEK = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (22<<8)))
public static let SQLITE_IOERR_DELETE_NOENT = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (23<<8)))
public static let SQLITE_IOERR_MMAP = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (24<<8)))
public static let SQLITE_IOERR_GETTEMPPATH = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (25<<8)))
public static let SQLITE_IOERR_CONVPATH = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (26<<8)))
public static let SQLITE_IOERR_VNODE = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (27<<8)))
public static let SQLITE_IOERR_AUTH = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (28<<8)))
public static let SQLITE_IOERR_BEGIN_ATOMIC = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (29<<8)))
public static let SQLITE_IOERR_COMMIT_ATOMIC = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (30<<8)))
public static let SQLITE_IOERR_ROLLBACK_ATOMIC = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (31<<8)))
public static let SQLITE_IOERR_DATA = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (32<<8)))
public static let SQLITE_IOERR_CORRUPTFS = ResultCode(rawValue: (SQLITE_IOERR.rawValue | (33<<8)))
public static let SQLITE_LOCKED_SHAREDCACHE = ResultCode(rawValue: (SQLITE_LOCKED.rawValue | (1<<8)))
public static let SQLITE_LOCKED_VTAB = ResultCode(rawValue: (SQLITE_LOCKED.rawValue | (2<<8)))
public static let SQLITE_BUSY_RECOVERY = ResultCode(rawValue: (SQLITE_BUSY.rawValue | (1<<8)))
public static let SQLITE_BUSY_SNAPSHOT = ResultCode(rawValue: (SQLITE_BUSY.rawValue | (2<<8)))
public static let SQLITE_BUSY_TIMEOUT = ResultCode(rawValue: (SQLITE_BUSY.rawValue | (3<<8)))
public static let SQLITE_CANTOPEN_NOTEMPDIR = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (1<<8)))
public static let SQLITE_CANTOPEN_ISDIR = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (2<<8)))
public static let SQLITE_CANTOPEN_FULLPATH = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (3<<8)))
public static let SQLITE_CANTOPEN_CONVPATH = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (4<<8)))
public static let SQLITE_CANTOPEN_DIRTYWAL = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (5<<8))) /* Not Used */
public static let SQLITE_CANTOPEN_SYMLINK = ResultCode(rawValue: (SQLITE_CANTOPEN.rawValue | (6<<8)))
public static let SQLITE_CORRUPT_VTAB = ResultCode(rawValue: (SQLITE_CORRUPT.rawValue | (1<<8)))
public static let SQLITE_CORRUPT_SEQUENCE = ResultCode(rawValue: (SQLITE_CORRUPT.rawValue | (2<<8)))
public static let SQLITE_CORRUPT_INDEX = ResultCode(rawValue: (SQLITE_CORRUPT.rawValue | (3<<8)))
public static let SQLITE_READONLY_RECOVERY = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (1<<8)))
public static let SQLITE_READONLY_CANTLOCK = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (2<<8)))
public static let SQLITE_READONLY_ROLLBACK = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (3<<8)))
public static let SQLITE_READONLY_DBMOVED = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (4<<8)))
public static let SQLITE_READONLY_CANTINIT = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (5<<8)))
public static let SQLITE_READONLY_DIRECTORY = ResultCode(rawValue: (SQLITE_READONLY.rawValue | (6<<8)))
public static let SQLITE_ABORT_ROLLBACK = ResultCode(rawValue: (SQLITE_ABORT.rawValue | (2<<8)))
public static let SQLITE_CONSTRAINT_CHECK = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (1<<8)))
public static let SQLITE_CONSTRAINT_COMMITHOOK = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (2<<8)))
public static let SQLITE_CONSTRAINT_FOREIGNKEY = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (3<<8)))
public static let SQLITE_CONSTRAINT_FUNCTION = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (4<<8)))
public static let SQLITE_CONSTRAINT_NOTNULL = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (5<<8)))
public static let SQLITE_CONSTRAINT_PRIMARYKEY = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (6<<8)))
public static let SQLITE_CONSTRAINT_TRIGGER = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (7<<8)))
public static let SQLITE_CONSTRAINT_UNIQUE = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (8<<8)))
public static let SQLITE_CONSTRAINT_VTAB = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (9<<8)))
public static let SQLITE_CONSTRAINT_ROWID = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (10<<8)))
public static let SQLITE_CONSTRAINT_PINNED = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (11<<8)))
public static let SQLITE_CONSTRAINT_DATATYPE = ResultCode(rawValue: (SQLITE_CONSTRAINT.rawValue | (12<<8)))
public static let SQLITE_NOTICE_RECOVER_WAL = ResultCode(rawValue: (SQLITE_NOTICE.rawValue | (1<<8)))
public static let SQLITE_NOTICE_RECOVER_ROLLBACK = ResultCode(rawValue: (SQLITE_NOTICE.rawValue | (2<<8)))
public static let SQLITE_WARNING_AUTOINDEX = ResultCode(rawValue: (SQLITE_WARNING.rawValue | (1<<8)))
public static let SQLITE_AUTH_USER = ResultCode(rawValue: (SQLITE_AUTH.rawValue | (1<<8)))
public static let SQLITE_OK_LOAD_PERMANENTLY = ResultCode(rawValue: (SQLITE_OK.rawValue | (1<<8)))
public static let SQLITE_OK_SYMLINK = ResultCode(rawValue: (SQLITE_OK.rawValue | (2<<8)))
// swiftlint:enable operator_usage_whitespace line_length
}
extension ResultCode {
/// Returns true if the code on the left matches the error on the right.
///
/// Primary result codes match themselves and their extended result codes,
/// while extended result codes match only themselves.
///
/// For example:
///
/// ```swift
/// do {
/// try ...
/// } catch ResultCode.SQLITE_CONSTRAINT_FOREIGNKEY {
/// // foreign key constraint error
/// } catch ResultCode.SQLITE_CONSTRAINT {
/// // any other constraint error
/// } catch {
/// // any other database error
/// }
/// ```
public static func ~= (lhs: Self, rhs: Error) -> Bool {
guard let error = rhs as? DatabaseError else { return false }
return lhs ~= error.extendedResultCode
}
}
extension ResultCode: CustomStringConvertible {
var errorString: String? {
String(cString: sqlite3_errstr(rawValue))
}
public var description: String {
if let errorString {
return "\(rawValue) (\(errorString))"
} else {
return "\(rawValue)"
}
}
}
extension ResultCode: Sendable { }
/// A `DatabaseError` describes an SQLite error.
///
/// For example:
///
/// ```swift
/// do {
/// try player.insert(db)
/// } catch let error as DatabaseError {
/// print(error) // prints debugging information
///
/// switch error {
/// case DatabaseError.SQLITE_CONSTRAINT_FOREIGNKEY:
/// // foreign key constraint error
/// case DatabaseError.SQLITE_CONSTRAINT:
/// // any other constraint error
/// default:
/// // any other database error
/// }
/// }
/// ```
///
/// The above example can also be written in a shorter way:
///
/// ```swift
/// do {
/// try player.insert(db)
/// } catch DatabaseError.SQLITE_CONSTRAINT_FOREIGNKEY {
/// // foreign key constraint error
/// } catch DatabaseError.SQLITE_CONSTRAINT {
/// // any other constraint error
/// } catch {
/// // any other database error
/// }
/// ```
///
/// Related SQLite documentation: <https://www.sqlite.org/rescode.html>
///
/// ## Topics
///
/// ### Creating DatabaseError
///
/// - ``init(resultCode:message:sql:arguments:publicStatementArguments:)``
/// - ``ResultCode``
///
/// ### Error Information
///
/// - ``arguments``
/// - ``extendedResultCode``
/// - ``isInterruptionError``
/// - ``message``
/// - ``resultCode``
/// - ``sql``
///
/// ### Converting DatabaseError to String
///
/// - ``description``
/// - ``expandedDescription``
public struct DatabaseError: Error {
/// The SQLite primary result code.
///
/// This property returns a "primary result code", that is to say the least
/// significant 8 bits of any SQLite result code.
///
/// For example:
///
/// ```swift
/// do {
/// ...
/// } catch let error as DatabaseError where error.resultCode == .SQL_CONSTRAINT {
/// // A constraint error
/// }
/// ```
///
/// The above example can also be written in a shorter way:
///
/// ```swift
/// do {
/// ...
/// } catch DatabaseError.SQL_CONSTRAINT {
/// // A constraint error
/// }
/// ```
///
/// See also ``extendedResultCode``.
///
/// Related SQLite documentation: <https://www.sqlite.org/rescode.html>
public var resultCode: ResultCode {
extendedResultCode.primaryResultCode
}
/// The SQLite extended error code.
///
/// For example:
///
/// ```swift
/// do {
/// ...
/// } catch let error as DatabaseError where error.extendedResultCode == .SQLITE_CONSTRAINT_FOREIGNKEY {
/// // A foreign key constraint error
/// }
/// ```
///
/// The above example can also be written in a shorter way:
///
/// ```swift
/// do {
/// ...
/// } catch DatabaseError.SQLITE_CONSTRAINT_FOREIGNKEY {
/// // A foreign key constraint error
/// }
/// ```
///
/// See also ``resultCode``.
///
/// Related SQLite documentation: <https://www.sqlite.org/rescode.html>
public let extendedResultCode: ResultCode
/// The SQLite error message.
public let message: String?
/// The SQL query that yielded the error.
public let sql: String?
/// The query arguments that yielded the error.
public let arguments: StatementArguments?
/// See Configuration.publicStatementArguments
var publicStatementArguments: Bool
/// Creates a `DatabaseError`.
///
/// - parameters:
/// - resultCode: A ResultCode (defaults to .SQLITE_ERROR).
/// - message: An eventual error message. If nil, the error message is
/// derived from the result code.
/// - sql: An eventual SQL string.
/// - arguments: Eventual statement arguments.
/// - publicStatementArguments: If false (the default), statement
/// arguments are not visible in the error's ``description`` property.
public init(
resultCode: ResultCode = .SQLITE_ERROR,
message: String? = nil,
sql: String? = nil,
arguments: StatementArguments? = nil,
publicStatementArguments: Bool = false)
{
self.extendedResultCode = resultCode
self.message = message ?? resultCode.errorString
self.sql = sql
self.arguments = arguments
self.publicStatementArguments = publicStatementArguments
}
/// Creates a Database Error with a raw CInt result code.
///
/// This initializer is not public because library user is not supposed to
/// be exposed to raw result codes.
@usableFromInline
init(resultCode: CInt, message: String? = nil, sql: String? = nil) {
self.init(
resultCode: ResultCode(rawValue: resultCode),
message: message,
sql: sql)
}
/// Creates a Database Error with a raw CInt result code.
///
/// This initializer is not public because library user is not supposed to
/// be exposed to raw result codes.
@usableFromInline
init(
resultCode: CInt,
message: String? = nil,
sql: String? = nil,
arguments: StatementArguments?,
publicStatementArguments: Bool)
{
self.init(
resultCode: ResultCode(rawValue: resultCode),
message: message,
sql: sql,
arguments: arguments,
publicStatementArguments: publicStatementArguments)
}
static func noSuchTable(_ tableName: String) -> Self {
DatabaseError(message: "no such table: \(tableName)")
}
static func noSuchSchema(_ schemaName: String) -> Self {
DatabaseError(message: "no such schema: \(schemaName)")
}
}
extension DatabaseError {
static func connectionIsClosed() -> Self {
DatabaseError(resultCode: .SQLITE_MISUSE, message: "Connection is closed")
}
static func snapshotIsLost() -> Self {
DatabaseError(resultCode: .SQLITE_ABORT, message: "Snapshot is lost.")
}
}
// Support for `catch DatabaseError.SQLITE_XXX`
extension DatabaseError {
public static let SQLITE_OK = ResultCode.SQLITE_OK
public static let SQLITE_ERROR = ResultCode.SQLITE_ERROR
public static let SQLITE_INTERNAL = ResultCode.SQLITE_INTERNAL
public static let SQLITE_PERM = ResultCode.SQLITE_PERM
public static let SQLITE_ABORT = ResultCode.SQLITE_ABORT
public static let SQLITE_BUSY = ResultCode.SQLITE_BUSY
public static let SQLITE_LOCKED = ResultCode.SQLITE_LOCKED
public static let SQLITE_NOMEM = ResultCode.SQLITE_NOMEM
public static let SQLITE_READONLY = ResultCode.SQLITE_READONLY
public static let SQLITE_INTERRUPT = ResultCode.SQLITE_INTERRUPT
public static let SQLITE_IOERR = ResultCode.SQLITE_IOERR
public static let SQLITE_CORRUPT = ResultCode.SQLITE_CORRUPT
public static let SQLITE_NOTFOUND = ResultCode.SQLITE_NOTFOUND
public static let SQLITE_FULL = ResultCode.SQLITE_FULL
public static let SQLITE_CANTOPEN = ResultCode.SQLITE_CANTOPEN
public static let SQLITE_PROTOCOL = ResultCode.SQLITE_PROTOCOL
public static let SQLITE_EMPTY = ResultCode.SQLITE_EMPTY
public static let SQLITE_SCHEMA = ResultCode.SQLITE_SCHEMA
public static let SQLITE_TOOBIG = ResultCode.SQLITE_TOOBIG
public static let SQLITE_CONSTRAINT = ResultCode.SQLITE_CONSTRAINT
public static let SQLITE_MISMATCH = ResultCode.SQLITE_MISMATCH
public static let SQLITE_MISUSE = ResultCode.SQLITE_MISUSE
public static let SQLITE_NOLFS = ResultCode.SQLITE_NOLFS
public static let SQLITE_AUTH = ResultCode.SQLITE_AUTH
public static let SQLITE_FORMAT = ResultCode.SQLITE_FORMAT
public static let SQLITE_RANGE = ResultCode.SQLITE_RANGE
public static let SQLITE_NOTADB = ResultCode.SQLITE_NOTADB
public static let SQLITE_NOTICE = ResultCode.SQLITE_NOTICE
public static let SQLITE_WARNING = ResultCode.SQLITE_WARNING
public static let SQLITE_ROW = ResultCode.SQLITE_ROW
public static let SQLITE_DONE = ResultCode.SQLITE_DONE
public static let SQLITE_ERROR_MISSING_COLLSEQ = ResultCode.SQLITE_ERROR_MISSING_COLLSEQ
public static let SQLITE_ERROR_RETRY = ResultCode.SQLITE_ERROR_RETRY
public static let SQLITE_ERROR_SNAPSHOT = ResultCode.SQLITE_ERROR_SNAPSHOT
public static let SQLITE_IOERR_READ = ResultCode.SQLITE_IOERR_READ
public static let SQLITE_IOERR_SHORT_READ = ResultCode.SQLITE_IOERR_SHORT_READ
public static let SQLITE_IOERR_WRITE = ResultCode.SQLITE_IOERR_WRITE
public static let SQLITE_IOERR_FSYNC = ResultCode.SQLITE_IOERR_FSYNC
public static let SQLITE_IOERR_DIR_FSYNC = ResultCode.SQLITE_IOERR_DIR_FSYNC
public static let SQLITE_IOERR_TRUNCATE = ResultCode.SQLITE_IOERR_TRUNCATE
public static let SQLITE_IOERR_FSTAT = ResultCode.SQLITE_IOERR_FSTAT
public static let SQLITE_IOERR_UNLOCK = ResultCode.SQLITE_IOERR_UNLOCK
public static let SQLITE_IOERR_RDLOCK = ResultCode.SQLITE_IOERR_RDLOCK
public static let SQLITE_IOERR_DELETE = ResultCode.SQLITE_IOERR_DELETE
public static let SQLITE_IOERR_BLOCKED = ResultCode.SQLITE_IOERR_BLOCKED
public static let SQLITE_IOERR_NOMEM = ResultCode.SQLITE_IOERR_NOMEM
public static let SQLITE_IOERR_ACCESS = ResultCode.SQLITE_IOERR_ACCESS
public static let SQLITE_IOERR_CHECKRESERVEDLOCK = ResultCode.SQLITE_IOERR_CHECKRESERVEDLOCK
public static let SQLITE_IOERR_LOCK = ResultCode.SQLITE_IOERR_LOCK
public static let SQLITE_IOERR_CLOSE = ResultCode.SQLITE_IOERR_CLOSE
public static let SQLITE_IOERR_DIR_CLOSE = ResultCode.SQLITE_IOERR_DIR_CLOSE
public static let SQLITE_IOERR_SHMOPEN = ResultCode.SQLITE_IOERR_SHMOPEN
public static let SQLITE_IOERR_SHMSIZE = ResultCode.SQLITE_IOERR_SHMSIZE
public static let SQLITE_IOERR_SHMLOCK = ResultCode.SQLITE_IOERR_SHMLOCK
public static let SQLITE_IOERR_SHMMAP = ResultCode.SQLITE_IOERR_SHMMAP
public static let SQLITE_IOERR_SEEK = ResultCode.SQLITE_IOERR_SEEK
public static let SQLITE_IOERR_DELETE_NOENT = ResultCode.SQLITE_IOERR_DELETE_NOENT
public static let SQLITE_IOERR_MMAP = ResultCode.SQLITE_IOERR_MMAP
public static let SQLITE_IOERR_GETTEMPPATH = ResultCode.SQLITE_IOERR_GETTEMPPATH
public static let SQLITE_IOERR_CONVPATH = ResultCode.SQLITE_IOERR_CONVPATH
public static let SQLITE_IOERR_VNODE = ResultCode.SQLITE_IOERR_VNODE
public static let SQLITE_IOERR_AUTH = ResultCode.SQLITE_IOERR_AUTH
public static let SQLITE_IOERR_BEGIN_ATOMIC = ResultCode.SQLITE_IOERR_BEGIN_ATOMIC
public static let SQLITE_IOERR_COMMIT_ATOMIC = ResultCode.SQLITE_IOERR_COMMIT_ATOMIC
public static let SQLITE_IOERR_ROLLBACK_ATOMIC = ResultCode.SQLITE_IOERR_ROLLBACK_ATOMIC
public static let SQLITE_IOERR_DATA = ResultCode.SQLITE_IOERR_DATA
public static let SQLITE_IOERR_CORRUPTFS = ResultCode.SQLITE_IOERR_CORRUPTFS
public static let SQLITE_LOCKED_SHAREDCACHE = ResultCode.SQLITE_LOCKED_SHAREDCACHE
public static let SQLITE_LOCKED_VTAB = ResultCode.SQLITE_LOCKED_VTAB
public static let SQLITE_BUSY_RECOVERY = ResultCode.SQLITE_BUSY_RECOVERY
public static let SQLITE_BUSY_SNAPSHOT = ResultCode.SQLITE_BUSY_SNAPSHOT
public static let SQLITE_BUSY_TIMEOUT = ResultCode.SQLITE_BUSY_TIMEOUT
public static let SQLITE_CANTOPEN_NOTEMPDIR = ResultCode.SQLITE_CANTOPEN_NOTEMPDIR
public static let SQLITE_CANTOPEN_ISDIR = ResultCode.SQLITE_CANTOPEN_ISDIR
public static let SQLITE_CANTOPEN_FULLPATH = ResultCode.SQLITE_CANTOPEN_FULLPATH
public static let SQLITE_CANTOPEN_CONVPATH = ResultCode.SQLITE_CANTOPEN_CONVPATH
public static let SQLITE_CANTOPEN_DIRTYWAL = ResultCode.SQLITE_CANTOPEN_DIRTYWAL
public static let SQLITE_CANTOPEN_SYMLINK = ResultCode.SQLITE_CANTOPEN_SYMLINK
public static let SQLITE_CORRUPT_VTAB = ResultCode.SQLITE_CORRUPT_VTAB
public static let SQLITE_CORRUPT_SEQUENCE = ResultCode.SQLITE_CORRUPT_SEQUENCE
public static let SQLITE_CORRUPT_INDEX = ResultCode.SQLITE_CORRUPT_INDEX
public static let SQLITE_READONLY_RECOVERY = ResultCode.SQLITE_READONLY_RECOVERY
public static let SQLITE_READONLY_CANTLOCK = ResultCode.SQLITE_READONLY_CANTLOCK
public static let SQLITE_READONLY_ROLLBACK = ResultCode.SQLITE_READONLY_ROLLBACK
public static let SQLITE_READONLY_DBMOVED = ResultCode.SQLITE_READONLY_DBMOVED
public static let SQLITE_READONLY_CANTINIT = ResultCode.SQLITE_READONLY_CANTINIT
public static let SQLITE_READONLY_DIRECTORY = ResultCode.SQLITE_READONLY_DIRECTORY
public static let SQLITE_ABORT_ROLLBACK = ResultCode.SQLITE_ABORT_ROLLBACK
public static let SQLITE_CONSTRAINT_CHECK = ResultCode.SQLITE_CONSTRAINT_CHECK
public static let SQLITE_CONSTRAINT_COMMITHOOK = ResultCode.SQLITE_CONSTRAINT_COMMITHOOK
public static let SQLITE_CONSTRAINT_FOREIGNKEY = ResultCode.SQLITE_CONSTRAINT_FOREIGNKEY
public static let SQLITE_CONSTRAINT_FUNCTION = ResultCode.SQLITE_CONSTRAINT_FUNCTION
public static let SQLITE_CONSTRAINT_NOTNULL = ResultCode.SQLITE_CONSTRAINT_NOTNULL
public static let SQLITE_CONSTRAINT_PRIMARYKEY = ResultCode.SQLITE_CONSTRAINT_PRIMARYKEY
public static let SQLITE_CONSTRAINT_TRIGGER = ResultCode.SQLITE_CONSTRAINT_TRIGGER
public static let SQLITE_CONSTRAINT_UNIQUE = ResultCode.SQLITE_CONSTRAINT_UNIQUE
public static let SQLITE_CONSTRAINT_VTAB = ResultCode.SQLITE_CONSTRAINT_VTAB
public static let SQLITE_CONSTRAINT_ROWID = ResultCode.SQLITE_CONSTRAINT_ROWID
public static let SQLITE_CONSTRAINT_PINNED = ResultCode.SQLITE_CONSTRAINT_PINNED
public static let SQLITE_CONSTRAINT_DATATYPE = ResultCode.SQLITE_CONSTRAINT_DATATYPE
public static let SQLITE_NOTICE_RECOVER_WAL = ResultCode.SQLITE_NOTICE_RECOVER_WAL
public static let SQLITE_NOTICE_RECOVER_ROLLBACK = ResultCode.SQLITE_NOTICE_RECOVER_ROLLBACK
public static let SQLITE_WARNING_AUTOINDEX = ResultCode.SQLITE_WARNING_AUTOINDEX
public static let SQLITE_AUTH_USER = ResultCode.SQLITE_AUTH_USER
public static let SQLITE_OK_LOAD_PERMANENTLY = ResultCode.SQLITE_OK_LOAD_PERMANENTLY
public static let SQLITE_OK_SYMLINK = ResultCode.SQLITE_OK_SYMLINK
}
extension DatabaseError {
/// A boolean value indicating if the error has code
/// `SQLITE_ABORT` or `SQLITE_INTERRUPT`.
///
/// Such an error can be thrown when a database has been interrupted, or
/// when the database is suspended.
///
/// See ``DatabaseReader/interrupt()`` and ``Database/suspendNotification``
/// for more information.
public var isInterruptionError: Bool {
switch resultCode {
case .SQLITE_ABORT, .SQLITE_INTERRUPT:
return true
default:
return false
}
}
}
extension DatabaseError: CustomStringConvertible {
/// The error description.
///
/// For example:
///
/// SQLite error 19: NOT NULL constraint failed: player.score
/// - while executing `UPDATE player SET score = ? WHERE email = ?
///
/// The format of the error description may change between GRDB releases,
/// without notice: don't have your application rely on any specific format.
public var description: String {
var description = "SQLite error \(resultCode.rawValue)"
if let message {
description += ": \(message)"
}
if let sql {
description += " - while executing `\(sql.trimmedSQLStatement)`"
}
if publicStatementArguments, let arguments, !arguments.isEmpty {
description += " with arguments \(arguments)"
}
return description
}
/// The error description, where bound parameters, if present, are visible.
///
/// For example:
///
/// SQLite error 19: NOT NULL constraint failed: player.score
/// - while executing `UPDATE player SET score = ? WHERE email = ?
/// with arguments [nil, "[email protected]"]
///
/// The format of the error description may change between GRDB releases,
/// without notice: don't have your application rely on any specific format.
///
/// - warning: It is your responsibility to prevent sensitive
/// information from leaking in unexpected locations, so use this
/// property with care.
public var expandedDescription: String {
var description = "SQLite error \(resultCode.rawValue)"
if let message {
description += ": \(message)"
}
if let sql {
description += " - while executing `\(sql.trimmedSQLStatement)`"
}
if let arguments, !arguments.isEmpty {
description += " with arguments \(arguments)"
}
return description
}
}
extension DatabaseError: CustomNSError {
/// Part of the `CustomNSError` conformance.
///
/// Returns `GRDB.DatabaseError`.
public static var errorDomain: String { "GRDB.DatabaseError" }
/// Part of the `CustomNSError` conformance.
///
/// Returns the ``extendedResultCode``.
public var errorCode: Int { Int(extendedResultCode.rawValue) }
/// Part of the `CustomNSError` conformance.
public var errorUserInfo: [String: Any] {
var userInfo = [NSLocalizedDescriptionKey: description]
if let message {
userInfo[NSLocalizedFailureReasonErrorKey] = message
}
return userInfo
}
}