diff --git a/Package.swift b/Package.swift index 6f6e922..d546ba8 100644 --- a/Package.swift +++ b/Package.swift @@ -1,24 +1,17 @@ -// swift-tools-version:4.0 +// swift-tools-version:5.0 import PackageDescription let package = Package( - name: "FluentPostgreSQL", + name: "fluent-postgres-driver", products: [ - // Swift ORM for PostgreSQL (built on top of Fluent ORM framework) - .library(name: "FluentPostgreSQL", targets: ["FluentPostgreSQL"]), + .library(name: "FluentPostgresDriver", targets: ["FluentPostgresDriver"]), ], dependencies: [ - // 🌎 Utility package containing tools for byte manipulation, Codable, OS APIs, and debugging. - .package(url: "https://github.com/vapor/core.git", from: "3.0.0"), - - // Swift ORM framework (queries, models, and relations) for building NoSQL and SQL database integrations. - .package(url: "https://github.com/vapor/fluent.git", from: "3.0.0"), - - // 🐘 Non-blocking, event-driven Swift client for PostgreSQL. - .package(url: "https://github.com/vapor/postgresql.git", from: "1.0.0"), + .package(url: "https://github.com/vapor/fluent-kit.git", .branch("master")), + .package(url: "https://github.com/vapor/postgresql.git", .branch("master")), ], targets: [ - .target(name: "FluentPostgreSQL", dependencies: ["Async", "FluentSQL", "PostgreSQL"]), - .testTarget(name: "FluentPostgreSQLTests", dependencies: ["FluentBenchmark", "FluentPostgreSQL"]), + .target(name: "FluentPostgresDriver", dependencies: ["FluentKit", "FluentSQL", "PostgresKit"]), + .testTarget(name: "FluentPostgresDriverTests", dependencies: ["FluentBenchmark", "FluentPostgresDriver"]), ] ) diff --git a/README.md b/README.md index acd1243..4e63d78 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,6 @@ Continuous Integration - Swift 4.1 + Swift 5

diff --git a/Sources/FluentPostgreSQL/Deprecated.swift b/Sources/FluentPostgreSQL/Deprecated.swift deleted file mode 100644 index 637118d..0000000 --- a/Sources/FluentPostgreSQL/Deprecated.swift +++ /dev/null @@ -1 +0,0 @@ -/// Nothing here yet... diff --git a/Sources/FluentPostgreSQL/Exports.swift b/Sources/FluentPostgreSQL/Exports.swift deleted file mode 100644 index ad305f2..0000000 --- a/Sources/FluentPostgreSQL/Exports.swift +++ /dev/null @@ -1,2 +0,0 @@ -@_exported import FluentSQL -@_exported import PostgreSQL diff --git a/Sources/FluentPostgreSQL/Fluent+PostgreSQLUpsert.swift b/Sources/FluentPostgreSQL/Fluent+PostgreSQLUpsert.swift deleted file mode 100644 index 15a1b81..0000000 --- a/Sources/FluentPostgreSQL/Fluent+PostgreSQLUpsert.swift +++ /dev/null @@ -1,21 +0,0 @@ -extension _PostgreSQLModel { - /// Creates the model or updates it depending on whether a model with the same ID already exists. - public func create(orUpdate: Bool, on conn: DatabaseConnectable) -> Future { - return Self.query(on: conn).create(orUpdate: orUpdate, self) - } -} - -extension QueryBuilder where Result: _PostgreSQLModel, Result.Database == Database { - /// Creates the model or updates it depending on whether a model with the same ID already exists. - public func create(orUpdate: Bool, _ model: Result) -> Future { - if orUpdate { - let row = SQLQueryEncoder(PostgreSQLExpression.self).encode(model) - let values = row.map { row -> (PostgreSQLIdentifier, PostgreSQLExpression) in - return (.identifier(row.key), row.value) - } - self.query.upsert = .upsert([.keyPath(Result.idKey)], values) - } - return create(model) - } - -} diff --git a/Sources/FluentPostgreSQL/FluentPostgreSQLProvider.swift b/Sources/FluentPostgreSQL/FluentPostgreSQLProvider.swift deleted file mode 100644 index d7eda61..0000000 --- a/Sources/FluentPostgreSQL/FluentPostgreSQLProvider.swift +++ /dev/null @@ -1,52 +0,0 @@ -/// Adds Fluent PostgreSQL's services to your project. -public final class FluentPostgreSQLProvider: Provider { - /// Creates a new `FluentPostgreSQLProvider` - /// - /// - enableIdentityColumns: If true, `GENERATED BY DEFAULT AS IDENTITY` will be used. - /// `true` by default. - public init(enableIdentityColumns: Bool? = nil) { - if let enableIdentityColumns = enableIdentityColumns { - _globalEnableIdentityColumns = enableIdentityColumns - } - } - - /// See `Provider`. - public func register(_ services: inout Services) throws { - try services.register(FluentProvider()) - try services.register(PostgreSQLProvider()) - } - - /// See `Provider`. - public func willBoot(_ worker: Container) throws -> Future { - - return worker.withPooledConnection(to: .psql) { conn in - return FluentPostgreSQLProvider._setup(on: conn) - } - } - - public static func _setup(on conn: PostgreSQLConnection) -> Future { - struct Setting: Codable { - var version: String - } - return conn.select().column(.function("current_setting", [.expression(.literal(.string("server_version")))], as: .identifier("version"))).all(decoding: Setting.self).map { rows in - _serverVersion = rows[0].version - if let versionString = _serverVersion { - let pointIndex = versionString.index(of: ".") ?? versionString.endIndex - let majorVersion = versionString[.. Future { - return .done(on: worker) - } -} - -/// server version string -internal var _serverVersion: String? -/// Enabled by default -internal var _globalEnableIdentityColumns: Bool = true diff --git a/Sources/FluentPostgreSQL/FluentPostgreSQLQuery.swift b/Sources/FluentPostgreSQL/FluentPostgreSQLQuery.swift deleted file mode 100644 index 12bbd90..0000000 --- a/Sources/FluentPostgreSQL/FluentPostgreSQLQuery.swift +++ /dev/null @@ -1,59 +0,0 @@ -public enum FluentPostgreSQLQueryStatement: FluentSQLQueryStatement { - public static var insert: FluentPostgreSQLQueryStatement { return ._insert } - public static var select: FluentPostgreSQLQueryStatement { return ._select } - public static var update: FluentPostgreSQLQueryStatement { return ._update } - public static var delete: FluentPostgreSQLQueryStatement { return ._delete } - - public var isInsert: Bool { - switch self { - case ._insert: return true - default: return false - } - } - - case _insert - case _select - case _update - case _delete -} - -public struct FluentPostgreSQLQuery: FluentSQLQuery { - public typealias Statement = FluentPostgreSQLQueryStatement - public typealias TableIdentifier = PostgreSQLTableIdentifier - public typealias Expression = PostgreSQLExpression - public typealias SelectExpression = PostgreSQLSelectExpression - public typealias Join = PostgreSQLJoin - public typealias OrderBy = PostgreSQLOrderBy - public typealias GroupBy = PostgreSQLGroupBy - public typealias Upsert = PostgreSQLUpsert - - public var statement: Statement - public var table: TableIdentifier - public var keys: [SelectExpression] - public var values: [String : Expression] - public var joins: [Join] - public var predicate: Expression? - public var orderBy: [OrderBy] - public var groupBy: [GroupBy] - public var limit: Int? - public var offset: Int? - public var upsert: PostgreSQLUpsert? - public var defaultBinaryOperator: PostgreSQLBinaryOperator - - public static func query(_ statement: Statement, _ table: TableIdentifier) -> FluentPostgreSQLQuery { - return .init( - statement: statement, - table: table, - keys: [], - values: [:], - joins: [], - predicate: nil, - orderBy: [], - groupBy: [], - limit: nil, - offset: nil, - upsert: nil, - defaultBinaryOperator: .and - ) - } -} diff --git a/Sources/FluentPostgreSQL/FluentPostgreSQLSchema.swift b/Sources/FluentPostgreSQL/FluentPostgreSQLSchema.swift deleted file mode 100644 index 08d51fe..0000000 --- a/Sources/FluentPostgreSQL/FluentPostgreSQLSchema.swift +++ /dev/null @@ -1,34 +0,0 @@ -public enum FluentPostgreSQLSchemaStatement: FluentSQLSchemaStatement { - public static var createTable: FluentPostgreSQLSchemaStatement { return ._createTable } - public static var alterTable: FluentPostgreSQLSchemaStatement { return ._alterTable } - public static var dropTable: FluentPostgreSQLSchemaStatement { return ._dropTable } - - case _createTable - case _alterTable - case _dropTable -} - -public struct FluentPostgreSQLSchema: FluentSQLSchema { - public typealias Statement = FluentPostgreSQLSchemaStatement - public typealias TableIdentifier = PostgreSQLTableIdentifier - public typealias ColumnDefinition = PostgreSQLColumnDefinition - public typealias TableConstraint = PostgreSQLTableConstraint - - public var statement: Statement - public var table: TableIdentifier - public var columns: [PostgreSQLColumnDefinition] - public var deleteColumns: [PostgreSQLColumnIdentifier] - public var constraints: [PostgreSQLTableConstraint] - public var deleteConstraints: [PostgreSQLTableConstraint] - - public static func schema(_ statement: Statement, _ table: TableIdentifier) -> FluentPostgreSQLSchema { - return .init( - statement: statement, - table: table, - columns: [], - deleteColumns: [], - constraints: [], - deleteConstraints: [] - ) - } -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+JoinSupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+JoinSupporting.swift deleted file mode 100644 index adaff3c..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+JoinSupporting.swift +++ /dev/null @@ -1,7 +0,0 @@ -extension PostgreSQLDatabase: JoinSupporting { - /// See `SQLDatabase`. - public typealias QueryJoin = PostgreSQLJoin - - /// See `SQLDatabase`. - public typealias QueryJoinMethod = PostgreSQLJoinMethod -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+KeyedCacheSupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+KeyedCacheSupporting.swift deleted file mode 100644 index 5fef645..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+KeyedCacheSupporting.swift +++ /dev/null @@ -1 +0,0 @@ -extension PostgreSQLDatabase: KeyedCacheSupporting { } diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+MigrationSupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+MigrationSupporting.swift deleted file mode 100644 index 9c01d7e..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+MigrationSupporting.swift +++ /dev/null @@ -1 +0,0 @@ -extension PostgreSQLDatabase: MigrationSupporting { } diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift deleted file mode 100644 index 41ac665..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+QuerySupporting.swift +++ /dev/null @@ -1,118 +0,0 @@ -/// Adds ability to do basic Fluent queries using a `PostgreSQLDatabase`. -extension PostgreSQLDatabase: QuerySupporting { - /// See `SQLDatabase`. - public typealias Query = FluentPostgreSQLQuery - - /// See `SQLDatabase`. - public typealias Output = [PostgreSQLColumn: PostgreSQLData] - - /// See `SQLDatabase`. - public typealias QueryAction = FluentPostgreSQLQueryStatement - - /// See `SQLDatabase`. - public typealias QueryAggregate = String - - /// See `SQLDatabase`. - public typealias QueryData = [String: PostgreSQLExpression] - - /// See `SQLDatabase`. - public typealias QueryField = PostgreSQLColumnIdentifier - - /// See `SQLDatabase`. - public typealias QueryFilterMethod = PostgreSQLBinaryOperator - - /// See `SQLDatabase`. - public typealias QueryFilterValue = PostgreSQLExpression - - /// See `SQLDatabase`. - public typealias QueryFilter = PostgreSQLExpression - - /// See `SQLDatabase`. - public typealias QueryFilterRelation = PostgreSQLBinaryOperator - - /// See `SQLDatabase`. - public typealias QueryKey = PostgreSQLSelectExpression - - /// See `SQLDatabase`. - public typealias QuerySort = PostgreSQLOrderBy - - /// See `SQLDatabase`. - public typealias QuerySortDirection = PostgreSQLDirection - - /// See `SQLDatabase`. - public static func queryExecute( - _ fluent: FluentPostgreSQLQuery, - on conn: PostgreSQLConnection, - into handler: @escaping ([PostgreSQLColumn: PostgreSQLData], PostgreSQLConnection) throws -> () - ) -> Future { - let query: PostgreSQLQuery - switch fluent.statement { - case ._insert: - var insert: PostgreSQLInsert = .insert(fluent.table) - var values: [PostgreSQLExpression] = [] - fluent.values.forEach { row in - // filter out all `NULL` values, no need to insert them since - // they could override default values that we want to keep - switch row.value { - case ._literal(let literal): - switch literal { - case ._null: return - default: break - } - default: break - } - insert.columns.append(.column(nil, .identifier(row.key))) - values.append(row.value) - } - insert.values.append(values) - insert.upsert = fluent.upsert - insert.returning.append(.all) - query = .insert(insert) - case ._select: - var select: PostgreSQLSelect = .select() - select.columns = fluent.keys.isEmpty ? [.all] : fluent.keys - select.tables = [fluent.table] - select.joins = fluent.joins - select.predicate = fluent.predicate - select.orderBy = fluent.orderBy - select.groupBy = fluent.groupBy - select.limit = fluent.limit - select.offset = fluent.offset - query = .select(select) - case ._update: - var update: PostgreSQLUpdate = .update(fluent.table) - update.table = fluent.table - update.values = fluent.values.map { val in - return (.identifier(val.key), val.value) - } - update.predicate = fluent.predicate - query = .update(update) - case ._delete: - var delete: PostgreSQLDelete = .delete(fluent.table) - delete.predicate = fluent.predicate - query = .delete(delete) - } - return conn.query(query) { try handler($0, conn) } - } - - struct InsertMetadata: Codable where ID: Codable { - var lastval: ID - } - - /// See `QuerySupporting`. - public static func modelEvent(event: ModelEvent, model: M, on conn: PostgreSQLConnection) -> Future - where PostgreSQLDatabase == M.Database, M: Model - { - switch event { - case .willCreate: - if M.ID.self == UUID.self, model.fluentID == nil { - var model = model - model.fluentID = UUID() as? M.ID - return conn.future(model) - } - default: break - } - - return conn.future(model) - } -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+SchemaSupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+SchemaSupporting.swift deleted file mode 100644 index 1efec61..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+SchemaSupporting.swift +++ /dev/null @@ -1,132 +0,0 @@ -extension PostgreSQLDatabase: SQLConstraintIdentifierNormalizer { - /// See `SQLConstraintIdentifierNormalizer`. - public static func normalizeSQLConstraintIdentifier(_ identifier: String) -> String { - return identifier - } -} - -extension PostgreSQLDatabase: SchemaSupporting { - /// See `SchemaSupporting`. - public typealias Schema = FluentPostgreSQLSchema - - /// See `SchemaSupporting`. - public typealias SchemaAction = FluentPostgreSQLSchemaStatement - - /// See `SchemaSupporting`. - public typealias SchemaField = PostgreSQLColumnDefinition - - /// See `SchemaSupporting`. - public typealias SchemaFieldType = PostgreSQLDataType - - /// See `SchemaSupporting`. - public typealias SchemaConstraint = PostgreSQLTableConstraint - - /// See `SchemaSupporting`. - public typealias SchemaReferenceAction = PostgreSQLForeignKeyAction - - /// See `SchemaSupporting`. - public static func schemaExecute(_ fluent: FluentPostgreSQLSchema, on conn: PostgreSQLConnection) -> Future { - let query: PostgreSQLQuery - switch fluent.statement { - case ._createTable: - var createTable: PostgreSQLCreateTable = .createTable(fluent.table) - createTable.columns = fluent.columns - createTable.tableConstraints = fluent.constraints - query = ._createTable(createTable) - case ._alterTable: - var alterTable: PostgreSQLAlterTable = .alterTable(fluent.table) - alterTable.columns = fluent.columns - alterTable.constraints = fluent.constraints - alterTable.dropActions += fluent.deleteColumns.map { .init(.column, $0.identifier) } - alterTable.dropActions += fluent.deleteConstraints.map { - guard let id = $0.identifier else { - fatalError("Cannot drop constraint without identifier: \($0).") - } - return .init(.constraint, id) - } - query = ._alterTable(alterTable) - case ._dropTable: - let dropTable: PostgreSQLDropTable = .dropTable(fluent.table) - query = ._dropTable(dropTable) - } - return conn.query(query).transform(to: ()) - } - - /// See `SchemaSupporting`. - public static func schemaField(for type: Any.Type, isIdentifier: Bool, _ column: PostgreSQLColumnIdentifier) -> PostgreSQLColumnDefinition { - var constraints: [PostgreSQLColumnConstraint] = [] - var dataType: PostgreSQLDataType - - var type = type - if let optional = type as? AnyOptionalType.Type { - type = optional.anyWrappedType - if isIdentifier { - constraints.append(.notNull) - } - } else { - constraints.append(.notNull) - } - - let isArray: Bool - if let array = type as? AnyArray.Type { - type = array.anyElementType - isArray = true - } else { - isArray = false - } - - if let type = type as? PostgreSQLDataTypeStaticRepresentable.Type { - dataType = type.postgreSQLDataType - } else { - dataType = .jsonb - } - - if isIdentifier { - let pkDefault: PostgreSQLPrimaryKeyDefault? - // create a unique name for the primary key since it will be added - // as a separate index. - let unique: String - if let table = column.table { - unique = table.identifier.string + "." + column.identifier.string - } else { - unique = column.identifier.string - } - if _globalEnableIdentityColumns { - switch dataType { - case .smallint, .integer, .bigint: - pkDefault = .generated(.byDefault) - default: - pkDefault = nil - } - } else { - pkDefault = nil - switch dataType { - case .smallint: dataType = .smallserial - case .integer: dataType = .serial - case .bigint: dataType = .bigserial - default: break - } - } - constraints.append(.primaryKey(default: pkDefault, identifier: .identifier("pk:\(unique)"))) - } - - if isArray { - dataType = .array(dataType) - } - - return .columnDefinition(column, dataType, constraints) - } - - /// See `SchemaSupporting`. - public static func enableReferences(on connection: PostgreSQLConnection) -> Future { - // enabled by default - return .done(on: connection) - } - - /// See `SchemaSupporting`. - public static func disableReferences(on connection: PostgreSQLConnection) -> Future { - return Future.map(on: connection) { - throw PostgreSQLError(identifier: "disableReferences", reason: "PostgreSQL does not support disabling foreign key checks.") - } - } -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLDatabase+TransactionSupporting.swift b/Sources/FluentPostgreSQL/PostgreSQLDatabase+TransactionSupporting.swift deleted file mode 100644 index 86a7a6a..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLDatabase+TransactionSupporting.swift +++ /dev/null @@ -1,14 +0,0 @@ -extension PostgreSQLDatabase: TransactionSupporting { - /// See `TransactionSupporting`. - public static func transactionExecute(_ transaction: @escaping (PostgreSQLConnection) throws -> Future, on connection: PostgreSQLConnection) -> Future { - return connection.simpleQuery("BEGIN TRANSACTION").flatMap { results in - return try transaction(connection).flatMap { res in - return connection.simpleQuery("END TRANSACTION").transform(to: res) - }.catchFlatMap { error in - return connection.simpleQuery("ROLLBACK").map { results in - throw error - } - } - } - } -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLEnum.swift b/Sources/FluentPostgreSQL/PostgreSQLEnum.swift deleted file mode 100644 index 4de34a9..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLEnum.swift +++ /dev/null @@ -1,63 +0,0 @@ -public protocol PostgreSQLEnum: PostgreSQLExpressionRepresentable, CaseIterable, Codable, ReflectionDecodable, PostgreSQLDataTypeStaticRepresentable, RawRepresentable where Self.RawValue: LosslessStringConvertible { - static var postgreSQLEnumTypeName: String { get } -} - -public protocol PostgreSQLRawEnum: RawRepresentable, Codable, CaseIterable, ReflectionDecodable, PostgreSQLDataTypeStaticRepresentable { } - -extension PostgreSQLRawEnum where Self.RawValue: PostgreSQLDataTypeStaticRepresentable { - /// See `PostgreSQLDataTypeStaticRepresentable`. - public static var postgreSQLDataType: PostgreSQLDataType { - return RawValue.postgreSQLDataType - } -} - -extension PostgreSQLEnum { - /// See `PostgreSQLEnum`. - public static var postgreSQLEnumTypeName: String { - return String(reflecting: self) - .components(separatedBy: ".") - .dropFirst() - .joined(separator: "_") - // TODO: Determine if this should actually be uppercased. - // The PostgreSQL documentation for the ENUM type always - // shows this name being lowercased. - .uppercased() - } - - /// See `PostgreSQLDataTypeStaticRepresentable`. - public static var postgreSQLDataType: PostgreSQLDataType { - return .custom(postgreSQLEnumTypeName) - } - - /// See `PostgreSQLExpressionRepresentable`. - public var postgreSQLExpression: PostgreSQLExpression { - return .literal(.string(rawValue.description)) - } -} - -extension PostgreSQLEnum where Self: PostgreSQLMigration { - /// See `PostgreSQLMigration`. - public static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.create(enum: self, on: conn) - } - - /// See `PostgreSQLMigration`. - public static func revert(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.drop(enum: self, on: conn) - } -} - -extension PostgreSQLDatabase { - public static func create(enum: E.Type, on conn: PostgreSQLConnection) -> Future where E: PostgreSQLEnum { - let cases = E.allCases.map { "'" + $0.rawValue.description + "'" }.joined(separator: ", ") - return conn.simpleQuery("CREATE TYPE \(E.postgreSQLEnumTypeName) AS ENUM (\(cases))") - } - - public static func alter(enum: E.Type, add value: E, on conn: PostgreSQLConnection) -> Future where E: PostgreSQLEnum { - return conn.simpleQuery("ALTER TYPE \(E.postgreSQLEnumTypeName) ADD VALUE '\(value.rawValue.description)'") - } - - public static func drop(enum: E.Type, on conn: PostgreSQLConnection) -> Future where E: PostgreSQLEnum { - return conn.simpleQuery("DROP TYPE \(E.postgreSQLEnumTypeName)") - } -} diff --git a/Sources/FluentPostgreSQL/PostgreSQLModel.swift b/Sources/FluentPostgreSQL/PostgreSQLModel.swift deleted file mode 100644 index 4827f89..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLModel.swift +++ /dev/null @@ -1,22 +0,0 @@ -public protocol _PostgreSQLModel: Model, PostgreSQLTable where Self.Database == PostgreSQLDatabase { } - -extension _PostgreSQLModel { - /// See `SQLTable`. - public static var sqlTableIdentifierString: String { - return entity - } -} - -public protocol PostgreSQLModel: _PostgreSQLModel where Self.ID == Int { - /// This model's unique identifier. - var id: Int? { get set } -} - -extension PostgreSQLModel { - /// See `Model`. - public static var idKey: IDKey { return \.id } -} - -public protocol PostgreSQLPivot: Pivot, PostgreSQLModel { } - -public protocol PostgreSQLMigration: Migration where Self.Database == PostgreSQLDatabase { } diff --git a/Sources/FluentPostgreSQL/PostgreSQLStringModel.swift b/Sources/FluentPostgreSQL/PostgreSQLStringModel.swift deleted file mode 100644 index d0d2d51..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLStringModel.swift +++ /dev/null @@ -1,11 +0,0 @@ -public protocol PostgreSQLStringModel: _PostgreSQLModel where Self.ID == String { - /// This model's unique identifier. - var id: String? { get set } -} - -extension PostgreSQLStringModel { - /// See `Model.idKey` - public static var idKey: IDKey { return \.id } -} - -public protocol PostgreSQLStringPivot: Pivot, PostgreSQLStringModel { } diff --git a/Sources/FluentPostgreSQL/PostgreSQLUUIDModel.swift b/Sources/FluentPostgreSQL/PostgreSQLUUIDModel.swift deleted file mode 100644 index 6451a5f..0000000 --- a/Sources/FluentPostgreSQL/PostgreSQLUUIDModel.swift +++ /dev/null @@ -1,11 +0,0 @@ -public protocol PostgreSQLUUIDModel: _PostgreSQLModel where Self.ID == UUID { - /// This model's unique identifier. - var id: UUID? { get set } -} - -extension PostgreSQLUUIDModel { - /// See `Model.idKey` - public static var idKey: IDKey { return \.id } -} - -public protocol PostgreSQLUUIDPivot: Pivot, PostgreSQLUUIDModel { } diff --git a/Sources/FluentPostgreSQL/Utilities.swift b/Sources/FluentPostgreSQL/Utilities.swift deleted file mode 100644 index dcf9330..0000000 --- a/Sources/FluentPostgreSQL/Utilities.swift +++ /dev/null @@ -1,9 +0,0 @@ -protocol AnyArray { - static var anyElementType: Any.Type { get } -} - -extension Array: AnyArray { - static var anyElementType: Any.Type { - return Element.self - } -} diff --git a/Sources/FluentPostgresDriver/Exports.swift b/Sources/FluentPostgresDriver/Exports.swift new file mode 100644 index 0000000..78f2012 --- /dev/null +++ b/Sources/FluentPostgresDriver/Exports.swift @@ -0,0 +1,2 @@ +@_exported import FluentKit +@_exported import PostgresKit diff --git a/Sources/FluentPostgresDriver/Postgres+Fluent.swift b/Sources/FluentPostgresDriver/Postgres+Fluent.swift new file mode 100644 index 0000000..442bf54 --- /dev/null +++ b/Sources/FluentPostgresDriver/Postgres+Fluent.swift @@ -0,0 +1,69 @@ +import FluentSQL + +extension PostgresConnection: FluentDatabase { + public func execute(_ query: FluentQuery, _ onOutput: @escaping (FluentOutput) throws -> ()) -> EventLoopFuture { + return FluentSQLDatabase(delegate: PostgresConnectionSQLDelegate(self)) + .execute(query, onOutput) + } + + public func execute(_ schema: FluentSchema) -> EventLoopFuture { + return FluentSQLDatabase(delegate: PostgresConnectionSQLDelegate(self)) + .execute(schema) + } +} + +extension ConnectionPool: FluentDatabase where Source.Connection: FluentDatabase { + public var eventLoop: EventLoop { + return self.source.eventLoop + } + + public func execute(_ query: FluentQuery, _ onOutput: @escaping (FluentOutput) throws -> ()) -> EventLoopFuture { + return self.withConnection { conn in + return conn.execute(query, onOutput) + } + } + + public func execute(_ schema: FluentSchema) -> EventLoopFuture { + return self.withConnection { conn in + return conn.execute(schema) + } + } +} + + +private struct PostgresConnectionSQLDelegate: FluentSQLDatabaseDelegate { + var eventLoop: EventLoop { + return self.connection.eventLoop + } + + let connection: PostgresConnection + + var database: SQLDatabase { + return self.connection + } + + init(_ connection: PostgresConnection) { + self.connection = connection + } + + func convert(_ fluent: FluentQuery, _ sql: SQLExpression) -> SQLExpression { + switch fluent.action { + case .create: + return PostgresReturning(sql) + default: + return sql + } + } +} + +private struct PostgresReturning: SQLExpression { + let base: SQLExpression + init(_ base: SQLExpression) { + self.base = base + } + + func serialize(to serializer: inout SQLSerializer) { + self.base.serialize(to: &serializer) + serializer.write(" RETURNING *") + } +} diff --git a/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift b/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift deleted file mode 100644 index ec44c86..0000000 --- a/Tests/FluentPostgreSQLTests/FluentPostgreSQLTests.swift +++ /dev/null @@ -1,611 +0,0 @@ -import Async -import Core -import XCTest -import FluentBenchmark -import FluentPostgreSQL -import Fluent -import Foundation - -class FluentPostgreSQLTests: XCTestCase { - var benchmarker: Benchmarker! - var database: PostgreSQLDatabase! - - override func setUp() { - #if os(macOS) - let hostname = "localhost" - #else - let hostname = "psql" - #endif - - let config: PostgreSQLDatabaseConfig = .init( - hostname: hostname, - port: 5432, - username: "vapor_username", - database: "vapor_database", - password: "vapor_password" - ) - database = PostgreSQLDatabase(config: config) - let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1) - benchmarker = try! Benchmarker(database, on: eventLoop, onFail: XCTFail) - let conn = try! benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - try! FluentPostgreSQLProvider._setup(on: conn).wait() - } - - func testBenchmark() throws { - try benchmarker.runAll() - } - - func testNestedStruct() throws { - let conn = try benchmarker.pool.requestConnection().wait() - try? User.revert(on: conn).wait() - try User.prepare(on: conn).wait() - var user = User(id: nil, name: "Tanner", pet: Pet(name: "Zizek")) - user.favoriteColors = ["pink", "blue"] - user = try user.save(on: conn).wait() - if let fetched = try User.query(on: conn).first().wait() { - XCTAssertEqual(user.id, fetched.id) - XCTAssertNil(user.age) - XCTAssertEqual(fetched.favoriteColors, ["pink", "blue"]) - } else { - XCTFail() - } - try User.revert(on: conn).wait() - benchmarker.pool.releaseConnection(conn) - } - - func testMinimumViableModelDeclaration() throws { - /// NOTE: these must never fail to build - struct Foo: PostgreSQLModel { - var id: Int? - var name: String - } - final class Bar: PostgreSQLModel { - var id: Int? - var name: String - } - struct Baz: PostgreSQLUUIDModel { - var id: UUID? - var name: String - } - final class Qux: PostgreSQLUUIDModel { - var id: UUID? - var name: String - } - } - -// func testDefaultValue() throws { -// let conn = try benchmarker.pool.requestConnection().wait() -// defer { try? DefaultTest.revert(on: conn).wait() } -// try DefaultTest.prepare(on: conn).wait() -// let test = DefaultTest() -// // _ = try test.save(on: conn).await(on: eventLoop) -// let builder = test.query(on: conn) -// builder.query.data = try ["foo": "bar".convertToPostgreSQLData()] // there _must_ be a better way -// builder.query.action = .create -// try builder.run().wait() -// if let fetched = try DefaultTest.query(on: conn).first().wait() { -// XCTAssertNotNil(fetched.date?.value) -// } else { -// XCTFail() -// } -// benchmarker.pool.releaseConnection(conn) -// } - - - func testUpdate() throws { - let conn = try benchmarker.pool.requestConnection().wait() - defer { try? User.revert(on: conn).wait() } - try User.prepare(on: conn).wait() - let user = User(id: nil, name: "Tanner", pet: Pet(name: "Zizek")) - user.favoriteColors = ["pink", "blue"] - _ = try user.save(on: conn).wait() - try User.query(on: conn).update(data: ["name": "Vapor"]).wait() - benchmarker.pool.releaseConnection(conn) - } - - func testGH24() throws { - let conn = try benchmarker.pool.requestConnection().wait() - defer { try? Allergy.revert(on: conn).wait() } - try Allergy.prepare(on: conn).wait() - struct Allergy: PostgreSQLModel, Migration { - static let entity = "allergies" - var id: Int? - } - _ = try Allergy(id: 2).create(on: conn).wait() - _ = try Allergy(id: 4).create(on: conn).wait() - let stuff = try Allergy.query(on: conn).filter(\.id ~~ [1, 2, 3]).all().wait() - XCTAssertEqual(stuff.count, 1) - XCTAssertEqual(stuff.first?.id, 2) - } - - func testGH21() throws { - /// - types - enum PetType: Int, CaseIterable, ReflectionDecodable, Codable { - static let allCases: [PetType] = [.cat, .dog] - case cat = 0 - case dog = 1 - } - struct Pet: PostgreSQLModel, Migration { - static let entity = "pets" - var id: Int? - var type: PetType - var name: String - - static func prepare(on conn: PostgreSQLConnection) -> EventLoopFuture { - return PostgreSQLDatabase.create(Pet.self, on: conn) { builder in - builder.field(for: \.id) - builder.field(for: \.type, type: .bigint) - builder.field(for: \.name, type: .text) - } - } - } - - try print(Pet.reflectProperties()) - - /// - prepare db - let conn = try benchmarker.pool.requestConnection().wait() - defer { try? Pet.revert(on: conn).wait() } - try Pet.prepare(on: conn).wait() - - /// - tests - _ = try Pet(id: nil, type: .cat, name: "Ziz").save(on: conn).wait() - _ = try Pet(id: nil, type: .dog, name: "Spud").save(on: conn).wait() - let cats = try Pet.query(on: conn).filter(\.type == .cat).all().wait() - let dogs = try Pet.query(on: conn).filter(\.type == .dog).all().wait() - XCTAssertEqual(cats.count, 1) - XCTAssertEqual(cats.first?.name, "Ziz") - XCTAssertEqual(dogs.count, 1) - XCTAssertEqual(dogs.first?.name, "Spud") - } - - func testPersistsDateMillisecondPart() throws { - struct DefaultTest: PostgreSQLModel, Migration { - var id: Int? - var date: Date? - var foo: String - init() { - self.id = nil - self.date = nil - self.foo = "bar" - } - static func prepare(on conn: PostgreSQLConnection) -> EventLoopFuture { - return PostgreSQLDatabase.create(DefaultTest.self, on: conn) { builder in - builder.field(for: \.id, isIdentifier: true) - builder.field(for: \.date, type: .timestamp, .default(.literal(.numeric("current_timestamp")))) - builder.field(for: \.foo) - } - } - } - - let conn = try benchmarker.pool.requestConnection().wait() - defer { try? DefaultTest.revert(on: conn).wait() } - try DefaultTest.prepare(on: conn).wait() - _ = try DefaultTest().save(on: conn).wait() - let fetched = try DefaultTest.query(on: conn).first().wait()! - // within 1 minute - XCTAssertEqual(Date().timeIntervalSinceReferenceDate, fetched.date!.timeIntervalSinceReferenceDate, accuracy: 60) - } - - func testGH30() throws { - /// - types - struct Foo: PostgreSQLModel, Migration { - static let entity = "foos" - var id: Int? - var location: PostgreSQLPoint? - } - - /// - prepare db - let conn = try benchmarker.pool.requestConnection().wait() - defer { try? Foo.revert(on: conn).wait() } - try Foo.prepare(on: conn).wait() - - /// - tests - var foo = Foo(id: nil, location: PostgreSQLPoint(x: 1, y: 3.14)) - foo = try foo.save(on: conn).wait() - foo = try Foo.find(foo.requireID(), on: conn).wait()! - XCTAssertEqual(foo.location?.x, 1) - XCTAssertEqual(foo.location?.y, 3.14) - } - - // https://github.com/vapor/fluent-postgresql/issues/32 - func testURL() throws { - struct User: PostgreSQLModel, Migration { - static let entity = "users" - var id: Int? - var name: String - var website: URL - } - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - - try User.prepare(on: conn).wait() - defer { try! User.revert(on: conn).wait() } - - var user = User(id: nil, name: "Tanner", website: URL(string: "http://tanner.xyz")!) - user = try user.save(on: conn).wait() - - let fetched = try User.find(1, on: conn).wait() - XCTAssertEqual(fetched?.website.absoluteString, "http://tanner.xyz") - } - - func testDocs_type() throws { - enum PlanetType: String, PostgreSQLEnum, PostgreSQLMigration { - static let postgreSQLEnumTypeName = "PLANET_TYPE" - static let allCases: [PlanetType] = [.smallRocky, .gasGiant, .dwarf] - case smallRocky - case gasGiant - case dwarf - } - - struct Planet: PostgreSQLModel, PostgreSQLMigration { - var id: Int? - let name: String - let type: PlanetType - let test: String? - - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.create(Planet.self, on: conn) { builder in - builder.field(for: \.id) - builder.field(for: \.name, type: .varchar(64)) - builder.field(for: \.type) - } - } - } - - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - - try PlanetType.prepare(on: conn).wait() - // try PostgreSQLDatabase.alter(enum: PlanetType.self, add: .dwarf, on: conn).wait() - defer { try? PlanetType.revert(on: conn).wait() } - try Planet.prepare(on: conn).wait() - defer { try? Planet.revert(on: conn).wait() } - - let rows = try Planet.query(on: conn).filter(\.type == .gasGiant).all().wait() - XCTAssertEqual(rows.count, 0) - - try PostgreSQLDatabase.update(Planet.self, on: conn) { builder in - builder.field(for: \.test) - }.wait() - } - - func testContains() throws { - struct User: PostgreSQLModel, PostgreSQLMigration { - var id: Int? - var name: String - var age: Int - } - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - - try User.prepare(on: conn).wait() - defer { try! User.revert(on: conn).wait() } - - // create - let tanner1 = User(id: nil, name: "tanner", age: 23) - _ = try tanner1.save(on: conn).wait() - let tanner2 = User(id: nil, name: "ner", age: 23) - _ = try tanner2.save(on: conn).wait() - let tanner3 = User(id: nil, name: "tan", age: 23) - _ = try tanner3.save(on: conn).wait() - - let tas = try User.query(on: conn).filter(\.name, .like, "ta%").count().wait() - if tas != 2 { - XCTFail("tas == \(tas)") - } - let ers = try User.query(on: conn).filter(\.name, .like, "%er").count().wait() - if ers != 2 { - XCTFail("ers == \(tas)") - } - let annes = try User.query(on: conn).filter(\.name ~~ "anne").count().wait() - if annes != 1 { - XCTFail("annes == \(tas)") - } - let ns = try User.query(on: conn).filter(\.name ~~ "n").count().wait() - if ns != 3 { - XCTFail("ns == \(tas)") - } - - let nertan = try User.query(on: conn).filter(\.name ~~ ["ner", "tan"]).count().wait() - if nertan != 2 { - XCTFail("nertan == \(tas)") - } - - let notner = try User.query(on: conn).filter(\.name !~ ["ner"]).count().wait() - if notner != 2 { - XCTFail("nertan == \(tas)") - } - } - - func testEmptySubset() throws { - struct User: PostgreSQLModel, PostgreSQLMigration { - var id: Int? - } - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - try User.prepare(on: conn).wait() - defer { _ = try? User.revert(on: conn).wait() } - - let res = try User.query(on: conn).filter(\.id ~~ []).all().wait() - XCTAssertEqual(res.count, 0) - _ = try User.query(on: conn).filter(\.id ~~ [1]).all().wait() - _ = try User.query(on: conn).filter(\.id ~~ [1, 2]).all().wait() - _ = try User.query(on: conn).filter(\.id ~~ [1, 2, 3]).all().wait() - } - - func testSort() throws { - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - try Planet.prepare(on: conn).wait() - defer { _ = try? Planet.revert(on: conn).wait() } - - _ = try Planet(name: "Jupiter").save(on: conn).wait() - _ = try Planet(name: "Earth").save(on: conn).wait() - _ = try Planet(name: "Mars").save(on: conn).wait() - - let unordered = try Planet.query(on: conn).all().wait() - let ordered = try Planet.query(on: conn).sort(\.name).all().wait() - XCTAssertNotEqual(unordered, ordered) - } - - func testCustomFilter() throws { - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - try Planet.prepare(on: conn).wait() - defer { _ = try? Planet.revert(on: conn).wait() } - - _ = try Planet(name: "Jupiter").save(on: conn).wait() - _ = try Planet(name: "Earth").save(on: conn).wait() - _ = try Planet(name: "Mars").save(on: conn).wait() - - let earth = try Planet.query(on: conn).filter(\.name, .ilike, "earth").first().wait() - XCTAssertEqual(earth?.name, "Earth") - } - - func testCreateOrUpdate() throws { - let conn = try benchmarker.pool.requestConnection().wait() - defer { benchmarker.pool.releaseConnection(conn) } - defer { try? Planet.revert(on: conn).wait() } - try Planet.prepare(on: conn).wait() - - let a = Planet(id: 1, name: "Mars") - let b = Planet(id: 1, name: "Earth") - - _ = try a.create(orUpdate: true, on: conn).wait() - _ = try b.create(orUpdate: true, on: conn).wait() - - let c = try Planet.find(1, on: conn).wait() - XCTAssertEqual(c?.name, "Earth") - } - - func testEnumArray() throws { - enum A: Int16, Codable, CaseIterable, ReflectionDecodable { - static var allCases: [A] = [.a, .b, .c] - case a, b, c - } - struct B: PostgreSQLModel, PostgreSQLMigration { - var id: Int? - var a: [A] - - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.create(B.self, on: conn) { builder in - builder.field(for: \.id, isIdentifier: true) - builder.field(for: \.a, type: .array(.int2), .notNull) - } - } - } - - let conn = try benchmarker.pool.requestConnection().wait() - conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler()) - defer { benchmarker.pool.releaseConnection(conn) } - defer { try? B.revert(on: conn).wait() } - try B.prepare(on: conn).wait() - - let b = try B(id: nil, a: [.a, .b, .c]).save(on: conn).wait() - XCTAssertEqual(b.id, 1) - } - - func testAlterDrop() throws { - struct A: PostgreSQLMigration { - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.create(Planet.self, on: conn) { builder in - builder.field(for: \.id) - } - } - - static func revert(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.delete(Planet.self, on: conn) - } - } - struct B: PostgreSQLMigration { - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(Planet.self, on: conn) { builder in - builder.field(for: \.name) - builder.deleteField(for: \.id) - } - } - - static func revert(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(Planet.self, on: conn) { builder in - builder.deleteField(for: \.name) - builder.field(for: \.id) - } - } - } - struct C: PostgreSQLMigration { - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(Planet.self, on: conn) { builder in - builder.unique(on: \.name) - } - } - - static func revert(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(Planet.self, on: conn) { builder in - builder.deleteUnique(from: \.name) - } - } - } - - let conn = try benchmarker.pool.requestConnection().wait() - conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler()) - defer { benchmarker.pool.releaseConnection(conn) } - - try A.prepare(on: conn).wait() - defer { try? A.revert(on: conn).wait() } - try B.prepare(on: conn).wait() - defer { try? B.revert(on: conn).wait() } - try C.prepare(on: conn).wait() - defer { try? C.revert(on: conn).wait() } - } - - // https://github.com/vapor/fluent-postgresql/issues/85 - func testGH85() throws { - enum Availability: UInt8, PostgreSQLRawEnum { - static var allCases: [Availability] = [.everyday, .sunday, .monday, .tuesday, .wednesday, .thursday, .friday, .saturday] - - case everyday - case sunday - case monday - case tuesday - case wednesday - case thursday - case friday - case saturday - } - - struct Foo: PostgreSQLModel, Migration { - var id: Int? - var availability: Availability - } - - let conn = try benchmarker.pool.requestConnection().wait() - conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler()) - defer { benchmarker.pool.releaseConnection(conn) } - - try Foo.prepare(on: conn).wait() - defer { try? Foo.revert(on: conn).wait() } - - let a = Foo(id: nil, availability: .everyday) - _ = try a.save(on: conn).wait() - } - - // https://github.com/vapor/fluent-postgresql/issues/35 - func testGH35() throws { - struct Game: PostgreSQLModel, Migration { - var id: Int? - var tags: [Int64]? - } - - let conn = try benchmarker.pool.requestConnection().wait() - conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler()) - defer { benchmarker.pool.releaseConnection(conn) } - - try Game.prepare(on: conn).wait() - defer { try? Game.revert(on: conn).wait() } - - var a = Game(id: nil, tags: [1, 2, 3]) - a = try a.save(on: conn).wait() - } - - // https://github.com/vapor/fluent-postgresql/issues/54 - func testGH54() throws { - struct User: PostgreSQLModel, Migration { - var id: Int? - var username: String - } - - struct AddUserIndex: PostgreSQLMigration { - static func prepare(on conn: PostgreSQLConnection) -> Future { - return Database.update(User.self, on: conn) { builder in - builder.unique(on: \.username) - } - } - - static func revert(on conn: PostgreSQLConnection) -> Future { - return Database.update(User.self, on: conn) { builder in - builder.deleteUnique(from: \.username) - } - } - } - - let conn = try benchmarker.pool.requestConnection().wait() - conn.logger = DatabaseLogger(database: .psql, handler: PrintLogHandler()) - defer { benchmarker.pool.releaseConnection(conn) } - - try User.prepare(on: conn).wait() - defer { try? User.revert(on: conn).wait() } - try AddUserIndex.prepare(on: conn).wait() - defer { try? AddUserIndex.revert(on: conn).wait() } - } - - static let allTests = [ - ("testBenchmark", testBenchmark), - ("testNestedStruct", testNestedStruct), - ("testMinimumViableModelDeclaration", testMinimumViableModelDeclaration), - ("testGH24", testGH24), - ("testGH21", testGH21), - ("testPersistsDateMillisecondPart", testPersistsDateMillisecondPart), - ("testGH30", testGH30), - ("testURL", testURL), - ("testDocs_type", testDocs_type), - ("testContains", testContains), - ("testEmptySubset", testEmptySubset), - ("testSort", testSort), - ("testCustomFilter", testCustomFilter), - ("testCreateOrUpdate", testCreateOrUpdate), - ("testEnumArray", testEnumArray), - ("testGH85", testGH85), - ("testGH35", testGH35), - ] -} - -struct Planet: PostgreSQLModel, PostgreSQLMigration, Equatable { - var id: Int? - var name: String - init(id: Int? = nil, name: String) { - self.id = id - self.name = name - } -} - -extension PostgreSQLDataType { - static var planetType: PostgreSQLDataType { - return .custom("PLANET_TYPE") - } -} - -struct Pet: Codable { - var name: String -} - -final class User: PostgreSQLModel, Migration { - static let idKey: WritableKeyPath = \User.id - var id: Int? - var name: String - var age: Int? - var favoriteColors: [String] - var pet: Pet - - init(id: Int? = nil, name: String, pet: Pet) { - self.favoriteColors = [] - self.id = id - self.name = name - self.pet = pet - } -} - -/// Adds a new field to `User`'s table. -struct AddUsernameToUser: PostgreSQLMigration { - static func prepare(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(User.self, on: conn) { builder in - builder.field(for: \.name) - } - } - - static func revert(on conn: PostgreSQLConnection) -> Future { - return PostgreSQLDatabase.update(User.self, on: conn) { builder in - builder.deleteField(for: \.name) - } - } -} diff --git a/Tests/FluentPostgresDriverTests/FluentPostgresDriverTests.swift b/Tests/FluentPostgresDriverTests/FluentPostgresDriverTests.swift new file mode 100644 index 0000000..ae33131 --- /dev/null +++ b/Tests/FluentPostgresDriverTests/FluentPostgresDriverTests.swift @@ -0,0 +1,99 @@ +import FluentBenchmark +import FluentPostgresDriver +import XCTest + +final class FluentPostgresDriverTests: XCTestCase { + var benchmarker: FluentBenchmarker! + + func testAll() throws { + try self.benchmarker.testAll() + } + + func testCreate() throws { + try self.benchmarker.testCreate() + } + + func testRead() throws { + try self.benchmarker.testRead() + } + + func testUpdate() throws { + try self.benchmarker.testUpdate() + } + + func testDelete() throws { + try self.benchmarker.testDelete() + } + + func testEagerLoadChildren() throws { + try self.benchmarker.testEagerLoadChildren() + } + + func testEagerLoadParent() throws { + try self.benchmarker.testEagerLoadParent() + } + + func testEagerLoadParentJoin() throws { + try self.benchmarker.testEagerLoadParentJoin() + } + + func testEagerLoadSubqueryJSONEncode() throws { + try self.benchmarker.testEagerLoadSubqueryJSONEncode() + } + + func testEagerLoadJoinJSONEncode() throws { + try self.benchmarker.testEagerLoadJoinJSONEncode() + } + + func testMigrator() throws { + try self.benchmarker.testMigrator() + } + + func testMigratorError() throws { + try self.benchmarker.testMigratorError() + } + + func testJoin() throws { + try self.benchmarker.testJoin() + } + + func testBatchCreate() throws { + try self.benchmarker.testBatchCreate() + } + + func testBatchUpdate() throws { + try self.benchmarker.testBatchUpdate() + } + + func testNestedModel() throws { + try self.benchmarker.testNestedModel() + } +// +// func testWorkUnit() throws { +// try self.benchmarker.testWorkUnit() +// } + + override func setUp() { + let eventLoop = MultiThreadedEventLoopGroup(numberOfThreads: 1).next() + let hostname: String + #if os(Linux) + hostname = "psql" + #else + hostname = "localhost" + #endif + let config = PostgresDatabase.Config( + hostname: hostname, + port: 5432, + username: "vapor_username", + password: "vapor_password", + database: "vapor_database", + tlsConfig: nil + ) + let conn = try! PostgresDatabase(config: config, on: eventLoop).makeConnection().wait() + self.benchmarker = FluentBenchmarker(database: conn) + } + + static let allTests = [ + ("testAll", testAll), + ] +} diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index f34f628..fdd8105 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -1,6 +1,6 @@ import XCTest -@testable import FluentPostgreSQLTests +@testable import FluentPostgresDriverTests XCTMain([ - testCase(FluentPostgreSQLTests.allTests), + testCase(FluentPostgresDriverTests.allTests), ]) diff --git a/circle.yml b/circle.yml index b45cd10..eeb3ad0 100644 --- a/circle.yml +++ b/circle.yml @@ -1,17 +1,23 @@ version: 2 jobs: - macos: - macos: - xcode: "9.2" + 11-linux: + docker: + - image: codevapor/swift:5.0 + - image: circleci/postgres:11 + name: psql + environment: + POSTGRES_USER: vapor_username + POSTGRES_DB: vapor_database + POSTGRES_PASSWORD: vapor_password steps: - checkout - run: swift build - run: swift test - linux-10: + 10-linux: docker: - - image: codevapor/swift:4.1 - - image: circleci/postgres:latest + - image: codevapor/swift:5.0 + - image: circleci/postgres:10 name: psql environment: POSTGRES_USER: vapor_username @@ -19,15 +25,11 @@ jobs: POSTGRES_PASSWORD: vapor_password steps: - checkout - - run: - name: Compile code - command: swift build - - run: - name: Run unit tests - command: swift test - linux-9: + - run: swift build + - run: swift test + 9-linux: docker: - - image: codevapor/swift:4.1 + - image: codevapor/swift:5.0 - image: circleci/postgres:9 name: psql environment: @@ -36,37 +38,20 @@ jobs: POSTGRES_PASSWORD: vapor_password steps: - checkout - - run: - name: Compile code - command: swift build - - run: - name: Run unit tests - command: swift test + - run: swift build + - run: swift test linux-release: docker: - - image: codevapor/swift:4.1 + - image: codevapor/swift:5.0 steps: - checkout - - run: - name: Compile code with optimizations - command: swift build -c release + - run: swift build -c release workflows: version: 2 tests: jobs: - - linux-10 - - linux-9 + - 11-linux + - 10-linux + - 9-linux - linux-release - # - macos - nightly: - triggers: - - schedule: - cron: "0 0 * * *" - filters: - branches: - only: - - master - jobs: - - linux - # - macos - +