169 lines
7.5 KiB
Swift
169 lines
7.5 KiB
Swift
import Foundation
|
|
import DataLiteC
|
|
|
|
/// A base class representing a custom SQLite function.
|
|
///
|
|
/// This class provides a framework for defining custom functions in SQLite. Subclasses must
|
|
/// override specific properties and methods to define the function's behavior, including
|
|
/// its name, argument count, and options.
|
|
///
|
|
/// To create a custom SQLite function, you should subclass either ``Scalar`` or
|
|
/// ``Aggregate`` depending on whether your function is a scalar function (returns
|
|
/// a single result) or an aggregate function (returns a result accumulated from multiple
|
|
/// rows). The subclass will then override the necessary properties and methods to implement
|
|
/// the function's behavior.
|
|
///
|
|
/// ## Topics
|
|
///
|
|
/// ### Base Function Classes
|
|
///
|
|
/// - ``Aggregate``
|
|
/// - ``Scalar``
|
|
///
|
|
/// ### Custom Function Classes
|
|
///
|
|
/// - ``Regexp``
|
|
open class Function {
|
|
// MARK: - Properties
|
|
|
|
/// The number of arguments that the custom SQLite function accepts.
|
|
///
|
|
/// This property must be overridden by subclasses to specify how many arguments
|
|
/// the function expects. The value should be a positive integer representing the
|
|
/// number of arguments, or zero if the function does not accept arguments.
|
|
open class var argc: Int32 {
|
|
fatalError("Subclasses must override this property to specify the number of arguments.")
|
|
}
|
|
|
|
/// The name of the custom SQLite function.
|
|
///
|
|
/// This property must be overridden by subclasses to provide the name that the SQLite
|
|
/// engine will use to identify the function. The name should be a valid SQLite function
|
|
/// name according to SQLite naming conventions.
|
|
open class var name: String {
|
|
fatalError("Subclasses must override this property to provide the function name.")
|
|
}
|
|
|
|
/// The options for the custom SQLite function.
|
|
///
|
|
/// This property must be overridden by subclasses to specify options such as whether the
|
|
/// function is deterministic or not. Options are represented as a bitmask of `Function.Options`.
|
|
open class var options: Options {
|
|
fatalError("Subclasses must override this property to specify function options.")
|
|
}
|
|
|
|
/// The encoding used by the function, which defaults to UTF-8.
|
|
///
|
|
/// This is used to set the encoding for text data in the custom SQLite function. The default
|
|
/// encoding is UTF-8, but this can be modified if necessary. This encoding is combined with
|
|
/// the function's options to configure the function.
|
|
class var encoding: Function.Options {
|
|
Function.Options(rawValue: SQLITE_UTF8)
|
|
}
|
|
|
|
/// The combined options for the custom SQLite function.
|
|
///
|
|
/// This property combines the function's options with the encoding. The result is used when
|
|
/// registering the function with SQLite. This property is derived from `options` and `encoding`.
|
|
class var opts: Int32 {
|
|
var options = options
|
|
options.insert(encoding)
|
|
return options.rawValue
|
|
}
|
|
|
|
// MARK: - Methods
|
|
|
|
/// Installs the custom SQLite function into the specified database connection.
|
|
///
|
|
/// Subclasses must override this method to provide the implementation for installing
|
|
/// the function into the SQLite database. This typically involves registering the function
|
|
/// with SQLite using `sqlite3_create_function_v2` or similar APIs.
|
|
///
|
|
/// - Parameter connection: A pointer to the SQLite database connection where the function
|
|
/// will be installed.
|
|
/// - Throws: An error if the function installation fails. The method will throw an exception
|
|
/// if the installation cannot be completed successfully.
|
|
class func install(db connection: OpaquePointer) throws(Connection.Error) {
|
|
fatalError("Subclasses must override this method to implement function installation.")
|
|
}
|
|
|
|
/// Uninstalls the custom SQLite function from the specified database connection.
|
|
///
|
|
/// This method unregisters the function from the SQLite database using `sqlite3_create_function_v2`
|
|
/// with `NULL` for the function implementations. This effectively removes the function from the
|
|
/// database.
|
|
///
|
|
/// - Parameter connection: A pointer to the SQLite database connection from which the function
|
|
/// will be uninstalled.
|
|
/// - Throws: An error if the function uninstallation fails. An exception is thrown if the function
|
|
/// cannot be removed successfully.
|
|
class func uninstall(db connection: OpaquePointer) throws(Connection.Error) {
|
|
let status = sqlite3_create_function_v2(
|
|
connection,
|
|
name, argc, opts,
|
|
nil, nil, nil, nil, nil
|
|
)
|
|
if status != SQLITE_OK {
|
|
throw Connection.Error(connection)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Functions
|
|
|
|
/// Sets the result of an SQLite query as a text string.
|
|
///
|
|
/// This function sets the result of the query to the specified text string. SQLite will store
|
|
/// this string inside the database as the result of the custom function.
|
|
///
|
|
/// - Parameters:
|
|
/// - ctx: A pointer to the SQLite context that provides information about the current query.
|
|
/// - string: A `String` that will be returned as the result of the query.
|
|
///
|
|
/// - Note: The `SQLITE_TRANSIENT` flag is used, meaning that SQLite makes a copy of the passed
|
|
/// data. This ensures that the string remains valid after the function execution is completed.
|
|
func sqlite3_result_text(_ ctx: OpaquePointer!, _ string: String) {
|
|
sqlite3_result_text(ctx, string, -1, SQLITE_TRANSIENT)
|
|
}
|
|
|
|
/// Sets the result of an SQLite query as binary data (BLOB).
|
|
///
|
|
/// This function sets the result of the query to the specified binary data. This is useful for
|
|
/// returning non-textual data such as images or other binary content from a custom function.
|
|
///
|
|
/// - Parameters:
|
|
/// - ctx: A pointer to the SQLite context that provides information about the current query.
|
|
/// - data: A `Data` object representing the binary data to be returned as the result.
|
|
///
|
|
/// - Note: The `SQLITE_TRANSIENT` flag is used, ensuring that SQLite makes a copy of the binary
|
|
/// data. This prevents issues related to memory management if the original data is modified
|
|
/// or deallocated after the function completes.
|
|
func sqlite3_result_blob(_ ctx: OpaquePointer!, _ data: Data) {
|
|
data.withUnsafeBytes {
|
|
sqlite3_result_blob(ctx, $0.baseAddress, Int32($0.count), SQLITE_TRANSIENT)
|
|
}
|
|
}
|
|
|
|
/// Sets the result of an SQLite query based on the `SQLiteRawValue` type.
|
|
///
|
|
/// This function sets the result of the query according to the type of the provided value. It can
|
|
/// handle integers, floating-point numbers, strings, binary data, or `NULL` values.
|
|
///
|
|
/// - Parameters:
|
|
/// - ctx: A pointer to the SQLite context that provides information about the current query.
|
|
/// - value: A `SQLiteRawValue` that represents the result to be returned. If the value is `nil`,
|
|
/// the result will be set to `NULL`.
|
|
///
|
|
/// - Note: The function uses a `switch` statement to determine the type of the value and then
|
|
/// calls the appropriate SQLite function to set the result. This ensures that the correct SQLite
|
|
/// result type is used based on the provided value.
|
|
func sqlite3_result_value(_ ctx: OpaquePointer!, _ value: SQLiteRawValue?) {
|
|
switch value ?? .null {
|
|
case .int(let value): sqlite3_result_int64(ctx, value)
|
|
case .real(let value): sqlite3_result_double(ctx, value)
|
|
case .text(let value): sqlite3_result_text(ctx, value)
|
|
case .blob(let value): sqlite3_result_blob(ctx, value)
|
|
case .null: sqlite3_result_null(ctx)
|
|
}
|
|
}
|