From 7107315f936aeb492d8680ae2e9588d55bbe3728 Mon Sep 17 00:00:00 2001 From: Gwynne Raskind Date: Tue, 14 May 2024 01:03:42 -0500 Subject: [PATCH] Fix API breakages --- .../FluentSQLiteConfiguration.swift | 69 +++++++++++++++++-- .../SQLiteRow+Database.swift | 27 ++++++++ 2 files changed, 91 insertions(+), 5 deletions(-) diff --git a/Sources/FluentSQLiteDriver/FluentSQLiteConfiguration.swift b/Sources/FluentSQLiteDriver/FluentSQLiteConfiguration.swift index aa811bf..373641b 100644 --- a/Sources/FluentSQLiteDriver/FluentSQLiteConfiguration.swift +++ b/Sources/FluentSQLiteDriver/FluentSQLiteConfiguration.swift @@ -2,15 +2,74 @@ import NIO import FluentKit import SQLiteKit import AsyncKit +import Logging + +// Hint: Yes, I know what default arguments are. This ridiculous spelling out of each alternative avoids public API +// breakage from adding the defaults. And yes, `maxConnectionsPerEventLoop` is not forwarded on purpose, it's not +// an oversight or an omission. We no longer support it for SQLite because increasing it past one causes thread +// conntention but can never increase parallelism. extension DatabaseConfigurationFactory { + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite(_ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10)) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: .init(), dataDecoder: .init(), sqlLogLevel: .debug) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), dataEncoder: SQLiteDataEncoder + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: dataEncoder, dataDecoder: .init(), sqlLogLevel: .debug) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), dataDecoder: SQLiteDataDecoder + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: .init(), dataDecoder: dataDecoder, sqlLogLevel: .debug) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), + dataEncoder: SQLiteDataEncoder, dataDecoder: SQLiteDataDecoder + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: dataEncoder, dataDecoder: dataDecoder, sqlLogLevel: .debug) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), sqlLogLevel: Logger.Level? + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: .init(), dataDecoder: .init(), sqlLogLevel: sqlLogLevel) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), + dataEncoder: SQLiteDataEncoder, sqlLogLevel: Logger.Level? + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: dataEncoder, dataDecoder: .init(), sqlLogLevel: sqlLogLevel) + } + /// Shorthand for ``sqlite(_:maxConnectionsPerEventLoop:connectionPoolTimeout:dataEncoder:dataDecoder:sqlLogLevel:)``. + public static func sqlite( + _ config: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, connectionPoolTimeout: TimeAmount = .seconds(10), + dataDecoder: SQLiteDataDecoder, sqlLogLevel: Logger.Level? + ) -> Self { + self.sqlite(config, connectionPoolTimeout: connectionPoolTimeout, dataEncoder: .init(), dataDecoder: dataDecoder, sqlLogLevel: sqlLogLevel) + } + + /// Return a configuration factory using the provided parameters. + /// + /// - Parameters: + /// - configuration: The underlying `SQLiteConfiguration`. + /// - maxConnnectionsPerEventLoop: Ignored. The value is always treated as 1. + /// - dataEncoder: An ``SQLiteDataEncoder`` used to translate bound query parameters into `SQLiteData` values. + /// - dataDecoder: An ``SQLiteDataDecoder`` used to translate `SQLiteData` values into output values. + /// - queryLogLevel: The level at which SQL queries issued through the Fluent or SQLKit interfaces will be logged. + /// - Returns: A configuration factory, public static func sqlite( - _ configuration: SQLiteConfiguration = .init(storage: .memory), + _ configuration: SQLiteConfiguration = .memory, maxConnectionsPerEventLoop: Int = 1, - connectionPoolTimeout: NIO.TimeAmount = .seconds(10), - dataEncoder: SQLiteDataEncoder = .init(), - dataDecoder: SQLiteDataDecoder = .init(), - sqlLogLevel: Logger.Level = .debug + connectionPoolTimeout: TimeAmount = .seconds(10), + dataEncoder: SQLiteDataEncoder, + dataDecoder: SQLiteDataDecoder, + sqlLogLevel: Logger.Level? ) -> Self { .init { FluentSQLiteConfiguration( diff --git a/Sources/FluentSQLiteDriver/SQLiteRow+Database.swift b/Sources/FluentSQLiteDriver/SQLiteRow+Database.swift index 247ee34..cfe40df 100644 --- a/Sources/FluentSQLiteDriver/SQLiteRow+Database.swift +++ b/Sources/FluentSQLiteDriver/SQLiteRow+Database.swift @@ -50,3 +50,30 @@ private struct SQLRowDatabaseOutput: DatabaseOutput { try self.row.decode(column: self.adjust(key: key), as: T.self) } } + +/// A legacy deprecated conformance of `SQLiteRow` directly to `DatabaseOutput`. This interface exists solely +/// because its absence would be a public API break. +/// +/// Do not use these methods. +@available(*, deprecated, message: "Do not use this conformance.") +extension SQLiteNIO.SQLiteRow: FluentKit.DatabaseOutput { + // See `DatabaseOutput.schema(_:)`. + public func schema(_ schema: String) -> any DatabaseOutput { + self.databaseOutput().schema(schema) + } + + // See `DatabaseOutput.contains(_:)`. + public func contains(_ key: FieldKey) -> Bool { + self.databaseOutput().contains(key) + } + + // See `DatabaseOutput.decodeNil(_:)`. + public func decodeNil(_ key: FieldKey) throws -> Bool { + try self.databaseOutput().decodeNil(key) + } + + // See `DatabaseOutput.decode(_:as:)`. + public func decode(_ key: FieldKey, as: T.Type) throws -> T { + try self.databaseOutput().decode(key, as: T.self) + } +}