613 lines
31 KiB
Swift
613 lines
31 KiB
Swift
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")
|
|
}
|
|
}
|
|
|
|
// 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, "arthur@example.com"]
|
|
///
|
|
/// 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
|
|
}
|
|
}
|