439 lines
22 KiB
Swift
439 lines
22 KiB
Swift
import Foundation
|
||
import DataLiteC
|
||
|
||
extension Connection {
|
||
/// Options for controlling the connection to a SQLite database.
|
||
///
|
||
/// This type represents a set of options that can be used when opening a connection to a
|
||
/// SQLite database. Each option corresponds to one of the flags defined in the SQLite
|
||
/// library. For more details, read [Opening A New Database Connection](https://www.sqlite.org/c3ref/open.html).
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// ```swift
|
||
/// do {
|
||
/// let dbFilePath = "path/to/your/database.db"
|
||
/// let options: Connection.Options = [.readwrite, .create]
|
||
/// let connection = try Connection(path: dbFilePath, options: options)
|
||
/// print("Database connection established successfully!")
|
||
/// } catch {
|
||
/// print("Error opening database: \(error)")
|
||
/// }
|
||
/// ```
|
||
///
|
||
/// ## Topics
|
||
///
|
||
/// ### Initializers
|
||
///
|
||
/// - ``init(rawValue:)``
|
||
///
|
||
/// ### Instance Properties
|
||
///
|
||
/// - ``rawValue``
|
||
///
|
||
/// ### Type Properties
|
||
///
|
||
/// - ``readonly``
|
||
/// - ``readwrite``
|
||
/// - ``create``
|
||
/// - ``uri``
|
||
/// - ``memory``
|
||
/// - ``nomutex``
|
||
/// - ``fullmutex``
|
||
/// - ``sharedcache``
|
||
/// - ``privatecache``
|
||
/// - ``exrescode``
|
||
/// - ``nofollow``
|
||
public struct Options: OptionSet, Sendable {
|
||
// MARK: - Properties
|
||
|
||
/// An integer value representing a combination of option flags.
|
||
///
|
||
/// This property holds the raw integer representation of the selected options for the
|
||
/// SQLite database connection. Each option corresponds to a specific flag defined in the
|
||
/// SQLite library, allowing for a flexible and efficient way to specify multiple options
|
||
/// using bitwise operations. The value can be combined using the bitwise OR operator (`|`).
|
||
///
|
||
/// ```swift
|
||
/// let options = [
|
||
/// Connection.Options.readonly,
|
||
/// Connection.Options.create
|
||
/// ]
|
||
/// ```
|
||
///
|
||
/// In this example, the `rawValue` will represent a combination of the ``readonly`` and ``create`` options.
|
||
///
|
||
/// - Important: When combining options, ensure that the selected flags are compatible and do not conflict,
|
||
/// as certain combinations may lead to unexpected behavior. For example, setting both ``readonly`` and
|
||
/// ``readwrite`` is not allowed.
|
||
public var rawValue: Int32
|
||
|
||
// MARK: - Instances
|
||
|
||
/// Option: open the database for read-only access.
|
||
///
|
||
/// This option configures the SQLite database connection to be opened in read-only mode. When this
|
||
/// option is specified, the database can be accessed for querying, but any attempts to modify the
|
||
/// data (such as inserting, updating, or deleting records) will result in an error. If the specified
|
||
/// database file does not already exist, an error will also be returned.
|
||
///
|
||
/// This is particularly useful when you want to ensure that your application does not accidentally
|
||
/// modify the database, or when you need to work with a database that is being shared among multiple
|
||
/// processes or applications that require read-only access.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `readonly` option when opening a database connection, as shown in the example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.readonly]
|
||
/// let connection = try Connection(path: dbFilePath, options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: If you attempt to write to a read-only database, an error will be thrown.
|
||
///
|
||
/// - Note: Ensure that the database file exists before opening it in read-only mode, as the connection
|
||
/// will fail if the file does not exist.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [opening a new database connection](https://www.sqlite.org/c3ref/open.html).
|
||
public static let readonly = Self(rawValue: SQLITE_OPEN_READONLY)
|
||
|
||
/// Option: open the database for reading and writing.
|
||
///
|
||
/// This option configures the SQLite database connection to be opened in read-write mode. When this
|
||
/// option is specified, the database can be accessed for both querying and modifying data. This means
|
||
/// you can perform operations such as inserting, updating, or deleting records in addition to reading.
|
||
///
|
||
/// If the database file does not exist, an error will be returned. If the file is write-protected by
|
||
/// the operating system, the connection will be opened in read-only mode instead, as a fallback.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `readwrite` option when opening a database connection, as shown below:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.readwrite]
|
||
/// let connection = try Connection(path: dbFilePath, options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: If the database file does not exist, an error will be thrown.
|
||
/// - Note: If you are unable to open the database in read-write mode due to permissions, it will
|
||
/// attempt to open it in read-only mode.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [opening a new database connection](https://www.sqlite.org/c3ref/open.html).
|
||
public static let readwrite = Self(rawValue: SQLITE_OPEN_READWRITE)
|
||
|
||
/// Option: create the database if it does not exist.
|
||
///
|
||
/// This option instructs SQLite to create a new database file if it does not already exist. If the
|
||
/// specified database file already exists, the connection will open that existing database instead.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `create` option when opening a database connection, as shown below:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.create]
|
||
/// let connection = try Connection(path: dbFilePath, options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: If the database file exists, it will be opened normally, and no new file will be created.
|
||
/// - Note: If the database file does not exist, a new file will be created at the specified path.
|
||
///
|
||
/// This option is often used in conjunction with other options, such as `readwrite`, to ensure that a
|
||
/// new database can be created and written to right away.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [opening a new database connection](https://www.sqlite.org/c3ref/open.html).
|
||
public static let create = Self(rawValue: SQLITE_OPEN_CREATE)
|
||
|
||
/// Option: specify a URI for opening the database.
|
||
///
|
||
/// This option allows the filename provided to be interpreted as a Uniform Resource Identifier (URI).
|
||
/// When this flag is set, SQLite will parse the filename as a URI, enabling the use of URI features
|
||
/// such as special encoding and various URI schemes.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `uri` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.uri]
|
||
/// let connection = try Connection(path: "file:///path/to/database.db", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: Using this option allows you to take advantage of SQLite's URI capabilities, such as
|
||
/// specifying various parameters in the URI (e.g., caching, locking, etc.).
|
||
/// - Note: If this option is not set, the filename will be treated as a simple path without URI
|
||
/// interpretation.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [opening a new database connection](https://www.sqlite.org/c3ref/open.html).
|
||
public static let uri = Self(rawValue: SQLITE_OPEN_URI)
|
||
|
||
/// Option: open the database in memory.
|
||
///
|
||
/// This option opens the database as an in-memory database, meaning that all data is stored in RAM
|
||
/// rather than on disk. This can be useful for temporary databases or for testing purposes where
|
||
/// persistence is not required.
|
||
///
|
||
/// When using this option, the "filename" argument is ignored, but it is still used for cache-sharing
|
||
/// if shared cache mode is enabled.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `memory` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.memory]
|
||
/// let connection = try Connection(path: ":memory:", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: Since the database is stored in memory, all data will be lost when the connection is
|
||
/// closed or the program exits. Therefore, this option is best suited for scenarios where data
|
||
/// persistence is not necessary.
|
||
/// - Note: In-memory databases can be significantly faster than disk-based databases due to the
|
||
/// absence of disk I/O operations.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [opening a new database connection](https://www.sqlite.org/c3ref/open.html).
|
||
public static let memory = Self(rawValue: SQLITE_OPEN_MEMORY)
|
||
|
||
/// Option: do not use mutexes.
|
||
///
|
||
/// This option configures the new database connection to use the "multi-thread"
|
||
/// [threading mode](https://www.sqlite.org/threadsafe.html). In this mode, separate threads can
|
||
/// concurrently access SQLite, provided that each thread is utilizing a different
|
||
/// [database connection](https://www.sqlite.org/c3ref/sqlite3.html).
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `nomutex` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.nomutex]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: When using this option, ensure that each thread has its own database connection, as
|
||
/// concurrent access to the same connection is not safe.
|
||
/// - Note: This option can improve performance in multi-threaded applications by reducing the
|
||
/// overhead of mutex locking, but it may lead to undefined behavior if not used carefully.
|
||
/// - Note: If your application requires safe concurrent access to a single database connection
|
||
/// from multiple threads, consider using the ``fullmutex`` option instead.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [thread safety](https://www.sqlite.org/threadsafe.html).
|
||
public static let nomutex = Self(rawValue: SQLITE_OPEN_NOMUTEX)
|
||
|
||
/// Option: use full mutexing.
|
||
///
|
||
/// This option configures the new database connection to utilize the "serialized"
|
||
/// [threading mode](https://www.sqlite.org/threadsafe.html). In this mode, multiple threads can safely
|
||
/// attempt to access the same database connection simultaneously. Although mutexes will block any
|
||
/// actual concurrency, this mode allows for multiple threads to operate without causing data corruption
|
||
/// or undefined behavior.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `fullmutex` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.fullmutex]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: Using the `fullmutex` option is recommended when you need to ensure thread safety when
|
||
/// multiple threads access the same database connection.
|
||
/// - Note: This option may introduce some performance overhead due to the locking mechanisms in place.
|
||
/// If your application is designed for high concurrency and can manage separate connections per thread,
|
||
/// consider using the ``nomutex`` option for better performance.
|
||
/// - Note: It's essential to be aware of potential deadlocks if multiple threads are competing for the
|
||
/// same resources. Proper design can help mitigate these risks.
|
||
///
|
||
/// For more details, refer to the SQLite documentation on
|
||
/// [thread safety](https://www.sqlite.org/threadsafe.html).
|
||
public static let fullmutex = Self(rawValue: SQLITE_OPEN_FULLMUTEX)
|
||
|
||
/// Option: use a shared cache.
|
||
///
|
||
/// This option enables the database to be opened in [shared cache](https://www.sqlite.org/sharedcache.html)
|
||
/// mode. In this mode, multiple database connections can share cached data, potentially improving
|
||
/// performance when accessing the same database from different connections.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `sharedcache` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.sharedcache]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: **Discouraged Usage**: The use of shared cache mode is
|
||
/// [discouraged](https://www.sqlite.org/sharedcache.html#dontuse). It may lead to unpredictable behavior,
|
||
/// especially in applications with complex threading models or multiple database connections.
|
||
///
|
||
/// - Note: **Build Variability**: Shared cache capabilities may be omitted from many builds of SQLite.
|
||
/// If your SQLite build does not support shared cache, this option will be a no-op, meaning it will
|
||
/// have no effect on the behavior of your database connection.
|
||
///
|
||
/// - Note: **Performance Considerations**: While shared cache can improve performance by reducing memory
|
||
/// usage, it may introduce complexity in managing concurrent access. Consider your application's design
|
||
/// and the potential for contention among connections when using this option.
|
||
///
|
||
/// For more information, consult the SQLite documentation on
|
||
/// [shared cache mode](https://www.sqlite.org/sharedcache.html).
|
||
public static let sharedcache = Self(rawValue: SQLITE_OPEN_SHAREDCACHE)
|
||
|
||
/// Option: use a private cache.
|
||
///
|
||
/// This option disables the use of [shared cache](https://www.sqlite.org/sharedcache.html) mode.
|
||
/// When a database is opened with this option, it uses a private cache for its connections, meaning
|
||
/// that the cached data will not be shared with other database connections.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `privatecache` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.privatecache]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: **Isolation**: Using a private cache ensures that the database connection operates in
|
||
/// isolation, preventing any caching interference from other connections. This can be beneficial
|
||
/// in multi-threaded applications where shared cache might lead to unpredictable behavior.
|
||
///
|
||
/// - Note: **Performance Impact**: While a private cache avoids the complexities associated with
|
||
/// shared caching, it may increase memory usage since each connection maintains its own cache.
|
||
/// Consider your application’s performance requirements when choosing between shared and private
|
||
/// cache options.
|
||
///
|
||
/// - Note: **Build Compatibility**: Ensure that your SQLite build supports the private cache option.
|
||
/// While most builds do, it’s always a good idea to verify if you encounter any issues.
|
||
///
|
||
/// For more information, refer to the SQLite documentation on
|
||
/// [shared cache mode](https://www.sqlite.org/sharedcache.html).
|
||
public static let privatecache = Self(rawValue: SQLITE_OPEN_PRIVATECACHE)
|
||
|
||
/// Option: use extended result code mode.
|
||
///
|
||
/// This option enables "extended result code mode" for the database connection. When this mode is
|
||
/// enabled, SQLite provides additional error codes that can help in diagnosing issues that may
|
||
/// arise during database operations.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `exrescode` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.exrescode]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Benefits
|
||
///
|
||
/// - **Improved Error Handling**: By using extended result codes, you can get more granular
|
||
/// information about errors, which can be particularly useful for debugging and error handling
|
||
/// in your application.
|
||
///
|
||
/// - **Detailed Diagnostics**: Extended result codes may provide context about the failure,
|
||
/// allowing for more targeted troubleshooting and resolution of issues.
|
||
///
|
||
/// ### Considerations
|
||
///
|
||
/// - **Compatibility**: Make sure your version of SQLite supports extended result codes. This
|
||
/// option should be available in most modern builds of SQLite.
|
||
///
|
||
/// For more information, refer to the SQLite documentation on
|
||
/// [extended result codes](https://www.sqlite.org/rescode.html).
|
||
public static let exrescode = Self(rawValue: SQLITE_OPEN_EXRESCODE)
|
||
|
||
/// Option: do not follow symbolic links when opening a file.
|
||
///
|
||
/// When this option is enabled, the database filename must not contain a symbolic link. If the
|
||
/// filename refers to a symbolic link, an error will be returned when attempting to open the
|
||
/// database.
|
||
///
|
||
/// ### Usage
|
||
///
|
||
/// You can specify the `nofollow` option when opening a database connection. Here’s an example:
|
||
///
|
||
/// ```swift
|
||
/// let options: Connection.Options = [.nofollow]
|
||
/// let connection = try Connection(path: "myDatabase.sqlite", options: options)
|
||
/// ```
|
||
///
|
||
/// ### Benefits
|
||
///
|
||
/// - **Increased Security**: By disallowing symbolic links, you reduce the risk of unintended
|
||
/// file access or manipulation through links that may point to unexpected locations.
|
||
///
|
||
/// - **File Integrity**: Ensures that the database connection directly references the intended
|
||
/// file without any indirection that symbolic links could introduce.
|
||
///
|
||
/// ### Considerations
|
||
///
|
||
/// - **Filesystem Limitations**: This option may limit your ability to use symbolic links in
|
||
/// your application. Make sure this behavior is acceptable for your use case.
|
||
///
|
||
/// For more information, refer to the SQLite documentation on [file opening](https://www.sqlite.org/c3ref/open.html).
|
||
public static let nofollow = Self(rawValue: SQLITE_OPEN_NOFOLLOW)
|
||
|
||
// MARK: - Inits
|
||
|
||
/// Initializes a set of options for connecting to a SQLite database.
|
||
///
|
||
/// This initializer allows you to create a combination of option flags that dictate how the
|
||
/// database connection will behave. The `rawValue` parameter should be an integer that
|
||
/// represents one or more options, combined using a bitwise OR operation.
|
||
///
|
||
/// - Parameter rawValue: An integer value representing a combination of option flags. This
|
||
/// value can be constructed using the predefined options, e.g., `SQLITE_OPEN_READWRITE |
|
||
/// SQLITE_OPEN_CREATE`.
|
||
///
|
||
/// ### Example
|
||
///
|
||
/// You can create a set of options as follows:
|
||
///
|
||
/// ```swift
|
||
/// let options = Connection.Options(
|
||
/// rawValue: SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE
|
||
/// )
|
||
/// ```
|
||
///
|
||
/// In this example, the `options` variable will have both the ``readwrite`` and
|
||
/// ``create`` options enabled, allowing for read/write access and creating the database if
|
||
/// it does not exist.
|
||
///
|
||
/// ### Important Notes
|
||
///
|
||
/// - Note: Be cautious when combining options, as some combinations may lead to conflicts or
|
||
/// unintended behavior (e.g., ``readonly`` and ``readwrite`` cannot be set together).
|
||
public init(rawValue: Int32) {
|
||
self.rawValue = rawValue
|
||
}
|
||
}
|
||
}
|