From adbca0bddb627cf0c6f8544fb80b2439fff94e49 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Mon, 18 Jun 2018 17:13:45 -0400 Subject: [PATCH 1/9] implement latest SQL + FluentSQL updates --- Package.swift | 3 +- Sources/FluentSQL/Exports.swift | 2 + Sources/FluentSQL/FluentSQLQuery.swift | 31 ++ Sources/FluentSQL/FluentSQLSchema.swift | 23 + Sources/FluentSQL/SQL+Contains.swift | 41 ++ Sources/FluentSQL/SQL+JoinSupporting.swift | 27 ++ Sources/FluentSQL/SQL+QuerySupporting.swift | 337 +++++++++++++++ Sources/FluentSQL/SQL+SchemaBuilder.swift | 13 + Sources/FluentSQL/SQL+SchemaSupporting.swift | 107 +++++ Sources/FluentSQLite/Exports.swift | 3 +- Sources/FluentSQLite/FluentSQLiteQuery.swift | 54 +++ Sources/FluentSQLite/FluentSQLiteSchema.swift | 34 ++ ...> SQLiteDataTypeStaticRepresentable.swift} | 38 +- .../SQLiteDatabase+Contains.swift | 29 -- .../SQLiteDatabase+JoinSupporting.swift | 24 +- .../SQLiteDatabase+QuerySupporting.swift | 401 +++--------------- .../SQLiteDatabase+SchemaSupporting.swift | 156 ++----- .../FluentSQLite/SQLiteSQLSerializer.swift | 4 - Sources/FluentSQLite/SQLiteTypes.swift | 6 +- .../SQLiteBenchmarkTests.swift | 3 +- 20 files changed, 790 insertions(+), 546 deletions(-) create mode 100644 Sources/FluentSQL/Exports.swift create mode 100644 Sources/FluentSQL/FluentSQLQuery.swift create mode 100644 Sources/FluentSQL/FluentSQLSchema.swift create mode 100644 Sources/FluentSQL/SQL+Contains.swift create mode 100644 Sources/FluentSQL/SQL+JoinSupporting.swift create mode 100644 Sources/FluentSQL/SQL+QuerySupporting.swift create mode 100644 Sources/FluentSQL/SQL+SchemaBuilder.swift create mode 100644 Sources/FluentSQL/SQL+SchemaSupporting.swift create mode 100644 Sources/FluentSQLite/FluentSQLiteQuery.swift create mode 100644 Sources/FluentSQLite/FluentSQLiteSchema.swift rename Sources/FluentSQLite/{SQLiteFieldTypes.swift => SQLiteDataTypeStaticRepresentable.swift} (57%) delete mode 100644 Sources/FluentSQLite/SQLiteDatabase+Contains.swift delete mode 100644 Sources/FluentSQLite/SQLiteSQLSerializer.swift diff --git a/Package.swift b/Package.swift index b5acbff..fb85049 100644 --- a/Package.swift +++ b/Package.swift @@ -20,7 +20,8 @@ let package = Package( .package(url: "https://github.com/vapor/sqlite.git", from: "3.0.0-rc"), ], targets: [ - .target(name: "FluentSQLite", dependencies: ["Async", "Fluent", "Service", "SQLite"]), + .target(name: "FluentSQL", dependencies: ["Fluent", "SQL"]), + .target(name: "FluentSQLite", dependencies: ["Async", "FluentSQL", "Service", "SQLite"]), .testTarget(name: "FluentSQLiteTests", dependencies: ["FluentBenchmark", "FluentSQLite", "SQLite"]), ] ) diff --git a/Sources/FluentSQL/Exports.swift b/Sources/FluentSQL/Exports.swift new file mode 100644 index 0000000..3ed0ea0 --- /dev/null +++ b/Sources/FluentSQL/Exports.swift @@ -0,0 +1,2 @@ +@_exported import Fluent +@_exported import SQL diff --git a/Sources/FluentSQL/FluentSQLQuery.swift b/Sources/FluentSQL/FluentSQLQuery.swift new file mode 100644 index 0000000..8a9c3f5 --- /dev/null +++ b/Sources/FluentSQL/FluentSQLQuery.swift @@ -0,0 +1,31 @@ +public protocol FluentSQLQuery { + associatedtype Statement: FluentSQLQueryStatement + associatedtype Expression: SQLExpression + associatedtype Join: SQLJoin + associatedtype OrderBy: SQLOrderBy + associatedtype TableIdentifier: SQLTableIdentifier + associatedtype SelectExpression: SQLSelectExpression + associatedtype RowDecoder: SQLRowDecoder + + var statement: Statement { get set } + var table: TableIdentifier { get set } + var keys: [SelectExpression] { get set } + var predicate: Expression? { get set } + var joins: [Join] { get set } + var orderBy: [OrderBy] { get set } + var limit: Int? { get set } + var offset: Int? { get set } + var values: [String: Expression] { get set } + + var defaultBinaryOperator: Expression.BinaryOperator { get set } + + static func query(_ statement: Statement, _ table: TableIdentifier) -> Self +} + +public protocol FluentSQLQueryStatement { + static var insert: Self { get } + static var select: Self { get } + static var update: Self { get } + static var delete: Self { get } + var isInsert: Bool { get } +} diff --git a/Sources/FluentSQL/FluentSQLSchema.swift b/Sources/FluentSQL/FluentSQLSchema.swift new file mode 100644 index 0000000..1985086 --- /dev/null +++ b/Sources/FluentSQL/FluentSQLSchema.swift @@ -0,0 +1,23 @@ +public protocol FluentSQLSchema { + associatedtype Statement: FluentSQLSchemaStatement + associatedtype TableIdentifier: SQLTableIdentifier + associatedtype ColumnDefinition: SQLColumnDefinition + associatedtype TableConstraint: SQLTableConstraint + associatedtype ColumnIdentifier: SQLColumnIdentifier + + var statement: Statement { get set } + var table: TableIdentifier { get set } + var columns: [ColumnDefinition] { get set } + var deleteColumns: [ColumnIdentifier] { get set } + var constraints: [TableConstraint] { get set } + var deleteConstraints: [TableConstraint] { get set } + + static func schema(_ statement: Statement, _ table: TableIdentifier) -> Self +} + + +public protocol FluentSQLSchemaStatement { + static var createTable: Self { get } + static var alterTable: Self { get } + static var dropTable: Self { get } +} diff --git a/Sources/FluentSQL/SQL+Contains.swift b/Sources/FluentSQL/SQL+Contains.swift new file mode 100644 index 0000000..4413438 --- /dev/null +++ b/Sources/FluentSQL/SQL+Contains.swift @@ -0,0 +1,41 @@ +infix operator ~= +/// Has prefix +public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, ["%" + rhs]) +} +/// Has prefix +public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, ["%" + rhs]) +} + +infix operator =~ +/// Has suffix. +public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, [rhs + "%"]) +} +/// Has suffix. +public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, [rhs + "%"]) +} + +infix operator ~~ +/// Contains. +public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, ["%" + rhs + "%"]) +} +/// Contains. +public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator + where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator +{ + return .make(lhs, .like, ["%" + rhs + "%"]) +} diff --git a/Sources/FluentSQL/SQL+JoinSupporting.swift b/Sources/FluentSQL/SQL+JoinSupporting.swift new file mode 100644 index 0000000..28d647d --- /dev/null +++ b/Sources/FluentSQL/SQL+JoinSupporting.swift @@ -0,0 +1,27 @@ +extension JoinSupporting where + QueryJoin: SQLJoin, + QueryJoinMethod == QueryJoin.Method, + QueryJoin.Expression.ColumnIdentifier.TableIdentifier == QueryJoin.TableIdentifier +{ + /// See `JoinSupporting`. + public static func queryJoin(_ method: QueryJoinMethod, base: QueryJoin.Expression.ColumnIdentifier, joined: QueryJoin.Expression.ColumnIdentifier) -> QueryJoin { + guard let table = joined.table else { + fatalError("Cannot join column without a table identifier: \(joined).") + } + return .join(method, table, .binary(.column(base), .equal, .column(joined))) + } +} + +extension JoinSupporting where QueryJoinMethod: SQLJoinMethod { + /// See `JoinSupporting`. + public static var queryJoinMethodDefault: QueryJoinMethod { + return .default + } +} + +extension JoinSupporting where Query: FluentSQLQuery, QueryJoin == Query.Join { + /// See `JoinSupporting`. + public static func queryJoinApply(_ join: QueryJoin, to query: inout Query) { + query.joins.append(join) + } +} diff --git a/Sources/FluentSQL/SQL+QuerySupporting.swift b/Sources/FluentSQL/SQL+QuerySupporting.swift new file mode 100644 index 0000000..02bd1df --- /dev/null +++ b/Sources/FluentSQL/SQL+QuerySupporting.swift @@ -0,0 +1,337 @@ +extension QuerySupporting where Query: FluentSQLQuery { + /// See `QuerySupporting`. + public static func query(_ entity: String) -> Query { + return .query(.select, .table(.identifier(entity))) + } + + /// See `QuerySupporting`. + public static func queryEntity(for query: Query) -> String { + return query.table.identifier.string + } +} + +extension QuerySupporting where QueryAction: FluentSQLQueryStatement { + /// See `QuerySupporting`. + public static var queryActionCreate: QueryAction { + return .insert + } + + /// See `QuerySupporting`. + public static var queryActionRead: QueryAction { + return .select + } + + /// See `QuerySupporting`. + public static var queryActionUpdate: QueryAction { + return .update + } + + /// See `QuerySupporting`. + public static var queryActionDelete: QueryAction { + return .delete + } + + /// See `QuerySupporting`. + public static func queryActionIsCreate(_ action: QueryAction) -> Bool { + return action.isInsert + } +} + +extension QuerySupporting where QueryFilterMethod: SQLBinaryOperator { + /// See `QuerySupporting`. + public static var queryFilterMethodEqual: QueryFilterMethod { + return .equal + } + + /// See `QuerySupporting`. + public static var queryFilterMethodNotEqual: QueryFilterMethod { + return .notEqual + } + + /// See `QuerySupporting`. + public static var queryFilterMethodGreaterThan: QueryFilterMethod { + return .greaterThan + } + + /// See `QuerySupporting`. + public static var queryFilterMethodLessThan: QueryFilterMethod { + return .lessThan + } + + /// See `QuerySupporting`. + public static var queryFilterMethodGreaterThanOrEqual: QueryFilterMethod { + return .greaterThanOrEqual + } + + /// See `QuerySupporting`. + public static var queryFilterMethodLessThanOrEqual: QueryFilterMethod { + return .lessThanOrEqual + } + + /// See `QuerySupporting`. + public static var queryFilterMethodInSubset: QueryFilterMethod { + return .in + } + + /// See `QuerySupporting`. + public static var queryFilterMethodNotInSubset: QueryFilterMethod { + return .notIn + } +} + +extension QuerySupporting where QueryFilterRelation: SQLBinaryOperator { + /// See `QuerySupporting`. + public static var queryFilterRelationAnd: QueryFilterRelation { + return .and + } + + /// See `QuerySupporting`. + public static var queryFilterRelationOr: QueryFilterRelation { + return .or + } +} + +// MARK: QuerySort + +extension QuerySupporting where QuerySortDirection: SQLDirection { + /// See `QuerySupporting`. + public static var querySortDirectionAscending: QuerySortDirection { + return .ascending + } + + /// See `QuerySupporting`. + public static var querySortDirectionDescending: QuerySortDirection { + return .descending + } +} + +extension QuerySupporting where + QuerySort: SQLOrderBy, + QuerySortDirection == QuerySort.Direction, + QueryField == QuerySort.Expression.ColumnIdentifier +{ + /// See `QuerySupporting`. + public static func querySort(_ column: QueryField, _ direction: QuerySortDirection) -> QuerySort { + return .orderBy(.column(column), direction) + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QuerySort == Query.OrderBy { + /// See `QuerySupporting`. + public static func querySortApply(_ orderBy: QuerySort, to query: inout Query) { + query.orderBy.append(orderBy) + } +} + +// MARK: Aggregate + +extension QuerySupporting where QueryAggregate == String, Query: FluentSQLQuery { + /// See `QuerySupporting`. + public static var queryAggregateCount: QueryAggregate { + return "COUNT" + } + + /// See `QuerySupporting`. + public static var queryAggregateSum: QueryAggregate { + return "SUM" + } + + /// See `QuerySupporting`. + public static var queryAggregateAverage: QueryAggregate { + return "AVG" + } + + /// See `QuerySupporting`. + public static var queryAggregateMinimum: QueryAggregate { + return "MIN" + } + + /// See `QuerySupporting`. + public static var queryAggregateMaximum: QueryAggregate { + return "MAX" + } +} + +extension QuerySupporting where + QueryAggregate == String, + QueryKey: SQLSelectExpression, + QueryKey.Expression == QueryKey.Expression.Function.Argument.Expression +{ + /// See `QuerySupporting`. + public static func queryAggregate(_ name: QueryAggregate, _ fields: [QueryKey]) -> QueryKey { + let args: [QueryKey.Expression.Function.Argument] = fields.compactMap { expr in + if expr.isAll { + return .all + } else if let (expr, _) = expr.expression { + return .expression(expr) + } else { + return nil + } + } + return .expression(.function(.function(name, args)), alias: .identifier("fluentAggregate")) + } + +} + +// MARK: QueryRange + +extension QuerySupporting where Query: FluentSQLQuery { + /// See `QuerySupporting`. + public static func queryRangeApply(lower: Int, upper: Int?, to query: inout Query) { + if let upper = upper { + query.limit = upper - lower + query.offset = lower + } else { + query.offset = lower + } + } +} + +// MARK: QueryKey + +extension QuerySupporting where QueryKey: SQLSelectExpression { + /// See `QuerySupporting`. + public static var queryKeyAll: QueryKey { + return .all + } +} + +extension QuerySupporting where QueryKey: SQLSelectExpression, QueryField == QueryKey.Expression.ColumnIdentifier { + /// See `QuerySupporting`. + public static func queryKey(_ field: QueryField) -> QueryKey { + return .expression(.column(field), alias: nil) + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryKey == Query.SelectExpression { + /// See `QuerySupporting`. + public static func queryKeyApply(_ key: QueryKey, to query: inout Query) { + query.keys.append(key) + } +} + +// MARK: QueryFilter + +extension QuerySupporting where + QueryFilter: SQLExpression, + QueryField == QueryFilter.ColumnIdentifier, + QueryFilterMethod == QueryFilter.BinaryOperator, + QueryFilterValue == QueryFilter +{ + /// See `QuerySupporting`. + public static func queryFilter(_ field: QueryField, _ method: QueryFilterMethod, _ value: QueryFilterValue) -> QueryFilter { + return .binary(.column(field), method, value) + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryFilter == Query.Expression { + /// See `QuerySupporting`. + public static func queryFilters(for query: Query) -> [QueryFilter] { + switch query.predicate { + case .none: return [] + case .some(let wrapped): return [wrapped] + } + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryFilter == Query.Expression, QueryFilter.BinaryOperator: Equatable { + /// See `QuerySupporting`. + public static func queryFilterApply(_ filter: QueryFilter, to query: inout Query) { + switch query.defaultBinaryOperator { + case .or: query.predicate |= filter + default: query.predicate &= filter + } + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryFilterRelation == Query.Expression.BinaryOperator { + /// See `QuerySupporting`. + public static func queryDefaultFilterRelation(_ relation: QueryFilterRelation, on query: inout Query) { + query.defaultBinaryOperator = relation + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryAction == Query.Statement { + /// See `QuerySupporting`. + public static func queryActionApply(_ action: QueryAction, to query: inout Query) { + query.statement = action + } +} + + +extension QuerySupporting where Query: FluentSQLQuery, QueryField: SQLColumnIdentifier { + /// See `QuerySupporting`. + public static func queryDataSet(_ field: QueryField, to data: E, on query: inout Query) + where E: Encodable + { + query.values[field.identifier.string] = .bind(.encodable(data)) + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryData == Dictionary { + /// See `QuerySupporting`. + public static func queryDataApply(_ data: QueryData, to query: inout Query) { + query.values = data + } +} + +extension QuerySupporting where QueryField: SQLColumnIdentifier { + /// See `QuerySupporting`. + public static func queryField(_ property: FluentProperty) -> QueryField { + return .column(property.entity.flatMap { .table(.identifier($0)) }, .identifier(property.path[0])) + } +} + +extension QuerySupporting where QueryFilterValue: SQLExpression { + /// See `QuerySupporting`. + public static var queryFilterValueNil: QueryFilterValue { + return .literal(.null) + } +} + +extension QuerySupporting where QueryFilterValue: SQLExpression { + /// See `QuerySupporting`. + public static func queryFilterValue(_ encodables: [E]) -> QueryFilterValue + where E: Encodable + { + return .group(encodables.map { .bind(.encodable($0)) }) + } +} + +extension QuerySupporting where Query: FluentSQLQuery, QueryData == Dictionary { + /// See `QuerySupporting`. + public static func queryEncode(_ value: E, entity: String) throws -> QueryData where E : Encodable { + return SQLQueryEncoder(Query.Expression.self).encode(value) + } +} + +extension QuerySupporting where QueryFilter: SQLExpression, QueryFilterRelation == QueryFilter.BinaryOperator, QueryFilter.BinaryOperator: Equatable { + /// See `QuerySupporting`. + public static func queryFilterGroup(_ relation: QueryFilterRelation, _ filters: [QueryFilter]) -> QueryFilter { + var current: QueryFilter? + for next in filters { + switch relation { + case .or: current |= next + case .and: current &= next + default: break + } + } + if let predicate = current { + return .group([predicate]) + } else { + return .group([]) + } + } +} + +extension QuerySupporting where Query: FluentSQLQuery, Output == Query.RowDecoder.Row { + /// See `QuerySupporting`. + public static func queryDecode(_ output: Output, entity: String, as decodable: D.Type, on conn: Connection) -> Future where D : Decodable { + do { + let row = try Query.RowDecoder.init().decode(D.self, from: output, table: .table(.identifier(entity))) + return conn.future(row) + } catch { + return conn.future(error: error) + } + } +} diff --git a/Sources/FluentSQL/SQL+SchemaBuilder.swift b/Sources/FluentSQL/SQL+SchemaBuilder.swift new file mode 100644 index 0000000..5700176 --- /dev/null +++ b/Sources/FluentSQL/SQL+SchemaBuilder.swift @@ -0,0 +1,13 @@ +extension SchemaBuilder where + Model.Database.Schema: FluentSQLSchema, + Model.Database.SchemaField == Model.Database.Schema.ColumnDefinition +{ + public func field( + for key: KeyPath, + type: Model.Database.Schema.ColumnDefinition.DataType, + _ constraints: Model.Database.Schema.ColumnDefinition.ColumnConstraint... + ) { + let property = FluentProperty.keyPath(key) + self.field(.columnDefinition(.column(nil, .identifier(property.path[0])), type, constraints)) + } +} diff --git a/Sources/FluentSQL/SQL+SchemaSupporting.swift b/Sources/FluentSQL/SQL+SchemaSupporting.swift new file mode 100644 index 0000000..c18ba3c --- /dev/null +++ b/Sources/FluentSQL/SQL+SchemaSupporting.swift @@ -0,0 +1,107 @@ +extension SchemaSupporting where SchemaAction: FluentSQLSchemaStatement { + /// See `SchemaSupporting`. + public static var schemaActionCreate: SchemaAction { + return .createTable + } + + /// See `SchemaSupporting`. + public static var schemaActionUpdate: SchemaAction { + return .alterTable + } + + /// See `SchemaSupporting`. + public static var schemaActionDelete: SchemaAction { + return .dropTable + } +} + +extension SchemaSupporting where Schema: FluentSQLSchema, SchemaAction == Schema.Statement { + /// See `SchemaSupporting`. + public static func schemaCreate(_ action: SchemaAction, _ entity: String) -> Schema { + return .schema(action, .table(.identifier(entity))) + } +} + +extension SchemaSupporting where + SchemaField: SQLColumnDefinition, + SchemaFieldType == SchemaField.DataType, + QueryField == SchemaField.ColumnIdentifier +{ + /// See `SchemaSupporting`. + public static func schemaField(_ field: QueryField, _ type: SchemaFieldType) -> SchemaField { + return .columnDefinition(field, type, []) + } +} + +extension SchemaSupporting where Schema: FluentSQLSchema, SchemaField == Schema.ColumnDefinition { + /// See `SchemaSupporting`. + public static func schemaFieldCreate(_ field: SchemaField, to query: inout Schema) { + query.columns.append(field) + } +} + +extension SchemaSupporting where Schema: FluentSQLSchema, QueryField == Schema.ColumnIdentifier { + /// See `SchemaSupporting`. + public static func schemaFieldDelete(_ field: QueryField, to query: inout Schema) { + query.deleteColumns.append(field) + } +} + + +extension SchemaSupporting where Schema: FluentSQLSchema, SchemaConstraint == Schema.TableConstraint { + /// See `SchemaSupporting`. + public static func schemaConstraintCreate(_ constraint: SchemaConstraint, to query: inout Schema) { + query.constraints.append(constraint) + } + + /// See `SchemaSupporting`. + public static func schemaConstraintDelete(_ constraint: SchemaConstraint, to query: inout Schema) { + query.deleteConstraints.append(constraint) + } +} + +public protocol SQLConstraintIdentifierNormalizer { + static func normalizeSQLConstraintIdentifier(_ identifier: String) -> String +} + +extension SchemaSupporting where + SchemaConstraint: SQLTableConstraint, + QueryField: SQLColumnIdentifier, + QueryField.Identifier == SchemaConstraint.Algorithm.Identifier, + SchemaReferenceAction == SchemaConstraint.Algorithm.ForeignKey.ConflictResolution, + QueryField.TableIdentifier == SchemaConstraint.Algorithm.ForeignKey.TableIdentifier, + QueryField.Identifier == SchemaConstraint.Algorithm.ForeignKey.Identifier, + Self: SQLConstraintIdentifierNormalizer +{ + /// See `SchemaSupporting`. + public static func schemaReference(from: QueryField, to: QueryField, onUpdate: SchemaReferenceAction?, onDelete: SchemaReferenceAction?) -> SchemaConstraint { + guard let foreignTable = to.table else { + fatalError("Cannot create reference to column without table identifier: \(to).") + } + guard let localTable = from.table else { + fatalError("Cannot create reference from column without table identifier: \(from).") + } + let uid = "\(localTable.identifier.string).\(from.identifier.string)+\(foreignTable.identifier.string).\(to.identifier.string)" + return .constraint( + .foreignKey([from.identifier], .foreignKey(foreignTable, [to.identifier], onDelete: onDelete, onUpdate: onUpdate)), + .identifier("fk:\(normalizeSQLConstraintIdentifier(uid))") + ) + } +} + +extension SchemaSupporting where + SchemaConstraint: SQLTableConstraint, + QueryField: SQLColumnIdentifier, + QueryField.Identifier == SchemaConstraint.Algorithm.Identifier, + Self: SQLConstraintIdentifierNormalizer +{ + /// See `SchemaSupporting`. + public static func schemaUnique(on: [QueryField]) -> SchemaConstraint { + let uid = on.map { $0.identifier.string }.joined(separator: "+") + return .constraint(.unique(on.map { $0.identifier }), .identifier("uq:\(normalizeSQLConstraintIdentifier(uid))")) + } +} + +extension SchemaSupporting { + +} diff --git a/Sources/FluentSQLite/Exports.swift b/Sources/FluentSQLite/Exports.swift index 883e343..45da0a8 100644 --- a/Sources/FluentSQLite/Exports.swift +++ b/Sources/FluentSQLite/Exports.swift @@ -1,7 +1,6 @@ -@_exported import Fluent +@_exported import FluentSQL @_exported import SQLite - //extension SQLiteDatabase: QuerySupporting { // /// See `SQLDatabase`. // public typealias QueryJoin = SQLQuery.DML.Join diff --git a/Sources/FluentSQLite/FluentSQLiteQuery.swift b/Sources/FluentSQLite/FluentSQLiteQuery.swift new file mode 100644 index 0000000..591b561 --- /dev/null +++ b/Sources/FluentSQLite/FluentSQLiteQuery.swift @@ -0,0 +1,54 @@ +public enum FluentSQLiteQueryStatement: FluentSQLQueryStatement { + public static var insert: FluentSQLiteQueryStatement { return ._insert } + public static var select: FluentSQLiteQueryStatement { return ._select } + public static var update: FluentSQLiteQueryStatement { return ._update } + public static var delete: FluentSQLiteQueryStatement { 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 FluentSQLiteQuery: FluentSQLQuery { + public typealias Statement = FluentSQLiteQueryStatement + public typealias TableIdentifier = SQLiteTableIdentifier + public typealias Expression = SQLiteExpression + public typealias SelectExpression = SQLiteSelectExpression + public typealias Join = SQLiteJoin + public typealias OrderBy = SQLiteOrderBy + public typealias RowDecoder = SQLiteRowDecoder + + 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 limit: Int? + public var offset: Int? + public var defaultBinaryOperator: GenericSQLBinaryOperator + + public static func query(_ statement: Statement, _ table: TableIdentifier) -> FluentSQLiteQuery { + return .init( + statement: statement, + table: table, + keys: [], + values: [:], + joins: [], + predicate: nil, + orderBy: [], + limit: nil, + offset: nil, + defaultBinaryOperator: .and + ) + } +} diff --git a/Sources/FluentSQLite/FluentSQLiteSchema.swift b/Sources/FluentSQLite/FluentSQLiteSchema.swift new file mode 100644 index 0000000..e82e047 --- /dev/null +++ b/Sources/FluentSQLite/FluentSQLiteSchema.swift @@ -0,0 +1,34 @@ +public enum FluentSQLiteSchemaStatement: FluentSQLSchemaStatement { + public static var createTable: FluentSQLiteSchemaStatement { return ._createTable } + public static var alterTable: FluentSQLiteSchemaStatement { return ._alterTable } + public static var dropTable: FluentSQLiteSchemaStatement { return ._dropTable } + + case _createTable + case _alterTable + case _dropTable +} + +public struct FluentSQLiteSchema: FluentSQLSchema { + public typealias Statement = FluentSQLiteSchemaStatement + public typealias TableIdentifier = SQLiteTableIdentifier + public typealias ColumnDefinition = SQLiteColumnDefinition + public typealias TableConstraint = SQLiteTableConstraint + + public var statement: Statement + public var table: TableIdentifier + public var columns: [SQLiteColumnDefinition] + public var deleteColumns: [SQLiteColumnIdentifier] + public var constraints: [SQLiteTableConstraint] + public var deleteConstraints: [SQLiteTableConstraint] + + public static func schema(_ statement: Statement, _ table: TableIdentifier) -> FluentSQLiteSchema { + return .init( + statement: statement, + table: table, + columns: [], + deleteColumns: [], + constraints: [], + deleteConstraints: [] + ) + } +} diff --git a/Sources/FluentSQLite/SQLiteFieldTypes.swift b/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift similarity index 57% rename from Sources/FluentSQLite/SQLiteFieldTypes.swift rename to Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift index 557c2fa..fc15c32 100644 --- a/Sources/FluentSQLite/SQLiteFieldTypes.swift +++ b/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift @@ -3,7 +3,7 @@ /// Types conforming to this protocol can be automatically migrated by `FluentSQLite`. /// /// See `SQLiteType` for more information. -public protocol SQLiteFieldTypeStaticRepresentable { +public protocol SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. static var sqliteFieldType: SQLiteDataType { get } } @@ -13,18 +13,18 @@ extension FixedWidthInteger { public static var sqliteFieldType: SQLiteDataType { return .integer } } -extension UInt: SQLiteFieldTypeStaticRepresentable { } -extension UInt8: SQLiteFieldTypeStaticRepresentable { } -extension UInt16: SQLiteFieldTypeStaticRepresentable { } -extension UInt32: SQLiteFieldTypeStaticRepresentable { } -extension UInt64: SQLiteFieldTypeStaticRepresentable { } -extension Int: SQLiteFieldTypeStaticRepresentable { } -extension Int8: SQLiteFieldTypeStaticRepresentable { } -extension Int16: SQLiteFieldTypeStaticRepresentable { } -extension Int32: SQLiteFieldTypeStaticRepresentable { } -extension Int64: SQLiteFieldTypeStaticRepresentable { } +extension UInt: SQLiteDataTypeStaticRepresentable { } +extension UInt8: SQLiteDataTypeStaticRepresentable { } +extension UInt16: SQLiteDataTypeStaticRepresentable { } +extension UInt32: SQLiteDataTypeStaticRepresentable { } +extension UInt64: SQLiteDataTypeStaticRepresentable { } +extension Int: SQLiteDataTypeStaticRepresentable { } +extension Int8: SQLiteDataTypeStaticRepresentable { } +extension Int16: SQLiteDataTypeStaticRepresentable { } +extension Int32: SQLiteDataTypeStaticRepresentable { } +extension Int64: SQLiteDataTypeStaticRepresentable { } -extension Date: SQLiteFieldTypeStaticRepresentable { +extension Date: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return Double.sqliteFieldType } } @@ -34,30 +34,30 @@ extension BinaryFloatingPoint { public static var sqliteFieldType: SQLiteDataType { return .real } } -extension Float: SQLiteFieldTypeStaticRepresentable { } -extension Double: SQLiteFieldTypeStaticRepresentable { } +extension Float: SQLiteDataTypeStaticRepresentable { } +extension Double: SQLiteDataTypeStaticRepresentable { } -extension Bool: SQLiteFieldTypeStaticRepresentable { +extension Bool: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return Int.sqliteFieldType } } -extension UUID: SQLiteFieldTypeStaticRepresentable { +extension UUID: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return .blob } } -extension Data: SQLiteFieldTypeStaticRepresentable { +extension Data: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return .blob } } -extension String: SQLiteFieldTypeStaticRepresentable { +extension String: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return .text } } -extension URL: SQLiteFieldTypeStaticRepresentable { +extension URL: SQLiteDataTypeStaticRepresentable { /// See `SQLiteFieldTypeStaticRepresentable`. public static var sqliteFieldType: SQLiteDataType { return String.sqliteFieldType } } diff --git a/Sources/FluentSQLite/SQLiteDatabase+Contains.swift b/Sources/FluentSQLite/SQLiteDatabase+Contains.swift deleted file mode 100644 index f025582..0000000 --- a/Sources/FluentSQLite/SQLiteDatabase+Contains.swift +++ /dev/null @@ -1,29 +0,0 @@ -infix operator ~= -/// Has prefix -public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), ["%" + rhs]) -} -/// Has prefix -public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), ["%" + rhs]) -} - -infix operator =~ -/// Has suffix. -public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), [rhs + "%"]) -} -/// Has suffix. -public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), [rhs + "%"]) -} - -infix operator ~~ -/// Contains. -public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), ["%" + rhs + "%"]) -} -/// Contains. -public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator { - return .make(lhs, .compare(.like), ["%" + rhs + "%"]) -} diff --git a/Sources/FluentSQLite/SQLiteDatabase+JoinSupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+JoinSupporting.swift index 964fb62..915d506 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+JoinSupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+JoinSupporting.swift @@ -1,27 +1,7 @@ extension SQLiteDatabase: JoinSupporting { /// See `JoinSupporting`. - public typealias QueryJoin = SQLiteQuery.JoinClause.Join + public typealias QueryJoin = SQLiteJoin /// See `JoinSupporting`. - public typealias QueryJoinMethod = SQLiteQuery.JoinClause.Join.Operator - - /// See `JoinSupporting`. - public static var queryJoinMethodDefault: SQLiteQuery.JoinClause.Join.Operator { - return .inner - } - - /// See `JoinSupporting`. - public static func queryJoin(_ method: SQLiteQuery.JoinClause.Join.Operator, base: SQLiteQuery.QualifiedColumnName, joined: SQLiteQuery.QualifiedColumnName) -> SQLiteQuery.JoinClause.Join { - return .init( - natural: false, - method, - table: .table(.init(table: .init(table: .init(name: joined.table!)))), - constraint: .condition(.binary(.column(base), .equal, .column(joined))) - ) - } - - /// See `JoinSupporting`. - public static func queryJoinApply(_ join: SQLiteQuery.JoinClause.Join, to query: inout SQLiteQuery.FluentQuery) { - query.joins.append(join) - } + public typealias QueryJoinMethod = SQLiteJoinMethod } diff --git a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift index 6641532..152861b 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift @@ -1,163 +1,91 @@ -extension SQLiteQuery { - public struct FluentQuery { - public enum Statement { - case insert - case select - case update - case delete - } - - public enum Comparison { - case binary(Expression.BinaryOperator) - case subset(Expression.SubsetOperator) - case compare(Expression.Compare.Operator) - } - - public var statement: Statement - public var table: TableName - public var joins: [JoinClause.Join] - public var keys: [SQLiteQuery.Select.ResultColumn] - public var values: [String: SQLiteQuery.Expression] - public var predicate: Expression? - public var orderBy: [SQLiteQuery.OrderBy] - public var limit: Int? - public var offset: Int? - public var defaultRelation: Expression.BinaryOperator - - public init(_ statement: Statement, table: TableName) { - self.statement = statement - self.table = table - self.joins = [] - self.keys = [] - self.values = [:] - self.predicate = nil - self.orderBy = [] - self.limit = nil - self.offset = nil - defaultRelation = .and - } - } -} - extension SQLiteDatabase: QuerySupporting { /// See `QuerySupporting`. - public typealias Query = SQLiteQuery.FluentQuery + public typealias Query = FluentSQLiteQuery /// See `QuerySupporting`. public typealias Output = [SQLiteColumn: SQLiteData] /// See `QuerySupporting`. - public typealias QueryAction = SQLiteQuery.FluentQuery.Statement + public typealias QueryAction = FluentSQLiteQueryStatement /// See `QuerySupporting`. public typealias QueryAggregate = String /// See `QuerySupporting`. - public typealias QueryData = [String: SQLiteQuery.Expression] + public typealias QueryData = [String: SQLiteExpression] /// See `QuerySupporting`. - public typealias QueryField = SQLiteQuery.QualifiedColumnName + public typealias QueryField = SQLiteColumnIdentifier /// See `QuerySupporting`. - public typealias QueryFilterMethod = SQLiteQuery.FluentQuery.Comparison + public typealias QueryFilterMethod = SQLiteBinaryOperator /// See `QuerySupporting`. - public typealias QueryFilterValue = SQLiteQuery.Expression + public typealias QueryFilterValue = SQLiteExpression /// See `QuerySupporting`. - public typealias QueryFilter = SQLiteQuery.Expression + public typealias QueryFilter = SQLiteExpression /// See `QuerySupporting`. - public typealias QueryFilterRelation = SQLiteQuery.Expression.BinaryOperator + public typealias QueryFilterRelation = SQLiteBinaryOperator /// See `QuerySupporting`. - public typealias QueryKey = SQLiteQuery.Select.ResultColumn + public typealias QueryKey = SQLiteSelectExpression /// See `QuerySupporting`. - public typealias QuerySort = SQLiteQuery.OrderBy + public typealias QuerySort = SQLiteOrderBy /// See `QuerySupporting`. - public typealias QuerySortDirection = SQLiteQuery.Direction - - public static func query(_ entity: String) -> SQLiteQuery.FluentQuery { - return .init(.select, table: .init(name: entity)) - } - - public static func queryEntity(for query: SQLiteQuery.FluentQuery) -> String { - return query.table.name - } + public typealias QuerySortDirection = SQLiteDirection - public static func queryExecute(_ fluent: SQLiteQuery.FluentQuery, on conn: SQLiteConnection, into handler: @escaping ([SQLiteColumn : SQLiteData], SQLiteConnection) throws -> ()) -> Future { + /// See `QuerySupporting`. + public static func queryExecute(_ fluent: Query, on conn: SQLiteConnection, into handler: @escaping ([SQLiteColumn : SQLiteData], SQLiteConnection) throws -> ()) -> Future { let query: SQLiteQuery switch fluent.statement { - case .insert: - // filter out all `NULL` values, no need to insert them since - // they could override default values that we want to keep - let values = fluent.values.filter { (key, val) in - switch val { - case .literal(let literal) where literal == .null: return false - default: return true + case ._insert: + var insert: SQLiteInsert = .insert(fluent.table) + var values: [SQLiteExpression] = [] + 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) } - query = .insert(.init( - with: nil, - conflictResolution: nil, - table: .init(table: fluent.table, alias: nil), - columns: values.keys.map { .init($0) }, - values: .values([.init(values.values)]), - upsert: nil - )) - case .select: - var table: SQLiteQuery.TableOrSubquery - switch fluent.joins.count { - case 0: table = .table(.init(table: .init(table: fluent.table, alias: nil), indexing: nil)) - default: - table = .joinClause(.init( - table: .table(.init(table: .init(table: fluent.table, alias: nil), indexing: nil)), - joins: fluent.joins - )) + insert.values.append(values) + query = .insert(insert) + case ._select: + var select: SQLiteSelect = .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 + query = .select(select) + case ._update: + var update: SQLiteUpdate = .update(fluent.table) + update.table = fluent.table + update.values = fluent.values.map { val in + return (.identifier(val.key), val.value) } - - query = .select(.init( - with: nil, - distinct: nil, - columns: fluent.keys.isEmpty ? [.all(nil)] : fluent.keys, - tables: [table], - predicate: fluent.predicate, - orderBy: fluent.orderBy - )) - case .update: - query = .update(.init( - with: nil, - conflictResolution: nil, - table: .init(table: .init(table: fluent.table, alias: nil), indexing: nil), - values: .init(columns: fluent.values.map { (col, expr) in - return .init(columns: [.init(col)], value: expr) - }, predicate: nil), - predicate: fluent.predicate - )) - case .delete: - query = .delete(.init( - with: nil, - table: .init(table: .init(table: fluent.table, alias: nil), indexing: nil), - predicate: fluent.predicate - )) + update.predicate = fluent.predicate + query = .update(update) + case ._delete: + var delete: SQLiteDelete = .delete(fluent.table) + delete.predicate = fluent.predicate + query = .delete(delete) } return conn.query(query) { try handler($0, conn) } } - public static func queryDecode(_ output: [SQLiteColumn : SQLiteData], entity: String, as decodable: D.Type, on conn: SQLiteConnection) -> Future where D : Decodable { - do { - return try conn.future(output.decode(D.self, from: entity)) - } catch { - return conn.future(error: error) - } - } - - public static func queryEncode(_ encodable: E, entity: String) throws -> [String: SQLiteQuery.Expression] where E : Encodable { - return try SQLiteQueryEncoder().encode(encodable) - } - + /// See `QuerySupporting`. public static func modelEvent(event: ModelEvent, model: M, on conn: SQLiteConnection) -> EventLoopFuture where SQLiteDatabase == M.Database, M : Model { var copy = model switch event { @@ -166,225 +94,22 @@ extension SQLiteDatabase: QuerySupporting { copy.fluentID = UUID() as? M.ID } case .didCreate: - if M.ID.self is Int.Type { - copy.fluentID = conn.lastAutoincrementID as? M.ID + if let intType = M.ID.self as? Int64Initializable.Type, copy.fluentID == nil { + copy.fluentID = conn.lastAutoincrementID.flatMap { intType.init($0) as? M.ID } } default: break } return conn.future(copy) } - - public static var queryActionCreate: SQLiteQuery.FluentQuery.Statement { - return .insert - } - - public static var queryActionRead: SQLiteQuery.FluentQuery.Statement { - return .select - } - - public static var queryActionUpdate: SQLiteQuery.FluentQuery.Statement { - return .update - } - - public static var queryActionDelete: SQLiteQuery.FluentQuery.Statement { - return .delete - } - - public static func queryActionIsCreate(_ action: SQLiteQuery.FluentQuery.Statement) -> Bool { - switch action { - case .insert: return true - default: return false - } - } - - public static func queryActionApply(_ action: SQLiteQuery.FluentQuery.Statement, to query: inout SQLiteQuery.FluentQuery) { - query.statement = action - } - - public static var queryAggregateCount: String { - return "COUNT" - } - - public static var queryAggregateSum: String { - return "SUM" - } - - public static var queryAggregateAverage: String { - return "AVG" - } - - public static var queryAggregateMinimum: String { - return "MIN" - } - - public static var queryAggregateMaximum: String { - return "MAX" - } - - public static func queryDataSet(_ field: SQLiteQuery.QualifiedColumnName, to data: E, on query: inout SQLiteQuery.FluentQuery) - where E: Encodable - { - query.values[field.name.string] = try! .bind(data) - } - - public static func queryDataApply(_ data: [String: SQLiteQuery.Expression], to query: inout SQLiteQuery.FluentQuery) { - query.values = data - } - - public static func queryField(_ property: FluentProperty) -> SQLiteQuery.QualifiedColumnName { - return .init(schema: nil, table: property.entity, name: .init(property.path[0])) - } - - public static var queryFilterMethodEqual: SQLiteQuery.FluentQuery.Comparison { - return .binary(.equal) - } - - public static var queryFilterMethodNotEqual: SQLiteQuery.FluentQuery.Comparison { - return .binary(.notEqual) - } - - public static var queryFilterMethodGreaterThan: SQLiteQuery.FluentQuery.Comparison { - return .binary(.greaterThan) - } - - public static var queryFilterMethodLessThan: SQLiteQuery.FluentQuery.Comparison { - return .binary(.lessThan) - } - - public static var queryFilterMethodGreaterThanOrEqual: SQLiteQuery.FluentQuery.Comparison { - return .binary(.greaterThanOrEqual) - } - - public static var queryFilterMethodLessThanOrEqual: SQLiteQuery.FluentQuery.Comparison { - return .binary(.lessThanOrEqual) - } - - public static var queryFilterMethodInSubset: SQLiteQuery.FluentQuery.Comparison { - return .subset(.in) - } - - public static var queryFilterMethodNotInSubset: SQLiteQuery.FluentQuery.Comparison { - return .subset(.notIn) - } - - public static func queryFilterValue(_ encodables: [E]) -> SQLiteQuery.Expression - where E: Encodable - { - return try! .expressions(encodables.map { try .bind($0) }) - } - - public static var queryFilterValueNil: SQLiteQuery.Expression { - return .literal(.null) - } - - public static func queryFilter(_ field: SQLiteQuery.QualifiedColumnName, _ method: SQLiteQuery.FluentQuery.Comparison, _ value: SQLiteQuery.Expression) -> SQLiteQuery.Expression { - switch method { - case .binary(let binary): return .binary(.column(field), binary, value) - case .compare(let compare): return .compare(.init(.column(field), not: false, compare, value, escape: nil)) - case .subset(let subset): return .subset(.column(field), subset, .expressions([value])) - } - - } - - public static func queryFilters(for query: SQLiteQuery.FluentQuery) -> [SQLiteQuery.Expression] { - switch query.predicate { - case .none: return [] - case .some(let wrapped): return [wrapped] - } - } - - public static func queryFilterApply(_ filter: SQLiteQuery.Expression, to query: inout SQLiteQuery.FluentQuery) { - switch query.defaultRelation { - case .or: query.predicate |= filter - default: query.predicate &= filter - } - } - - public static var queryFilterRelationAnd: SQLiteQuery.Expression.BinaryOperator { - return .and - } - - public static var queryFilterRelationOr: SQLiteQuery.Expression.BinaryOperator { - return .or - } - - - public static func queryDefaultFilterRelation(_ relation: SQLiteQuery.Expression.BinaryOperator, on query: inout SQLiteQuery.FluentQuery) { - query.defaultRelation = relation - } - - public static func queryFilterGroup(_ relation: SQLiteQuery.Expression.BinaryOperator, _ filters: [SQLiteQuery.Expression]) -> SQLiteQuery.Expression { - var current: SQLiteQuery.Expression? - for next in filters { - switch relation { - case .or: current |= next - case .and: current &= next - default: break - } - } - if let predicate = current { - return .expressions([predicate]) - } else { - return .expressions([]) - } - } - - public static var queryKeyAll: SQLiteQuery.Select.ResultColumn { - return .all(nil) - } - - public static func queryAggregate(_ aggregate: String, _ fields: [SQLiteQuery.Select.ResultColumn]) -> SQLiteQuery.Select.ResultColumn { - let parameters: SQLiteQuery.Expression.Function.Parameters - switch fields.count { - case 1: - switch fields[0] { - case .all: parameters = .all - case .expression(let expr, _): parameters = .expressions(distinct: false, [expr]) - } - default: - parameters = .expressions(distinct: false, fields.compactMap { field in - switch field { - case .all: return nil - case .expression(let expr, _): return expr - } - }) - } - return .expression(.function(.init( - name: aggregate, - parameters: parameters - )), alias: "fluentAggregate") - } - - public static func queryKey(_ field: SQLiteQuery.QualifiedColumnName) -> SQLiteQuery.Select.ResultColumn { - return .expression(.column(field), alias: nil) - } - - public static func queryKeyApply(_ key: SQLiteQuery.Select.ResultColumn, to query: inout SQLiteQuery.FluentQuery) { - query.keys.append(key) - } - - public static func queryRangeApply(lower: Int, upper: Int?, to query: inout SQLiteQuery.FluentQuery) { - if let upper = upper { - query.limit = upper - lower - query.offset = lower - } else { - query.offset = lower - } - } - - public static func querySort(_ column: SQLiteQuery.QualifiedColumnName, _ direction: SQLiteQuery.Direction) -> SQLiteQuery.OrderBy { - return .init(expression: .column(column), direction: direction) - } - - public static var querySortDirectionAscending: SQLiteQuery.Direction { - return .ascending - } - - public static var querySortDirectionDescending: SQLiteQuery.Direction { - return .descending - } - - public static func querySortApply(_ orderBy: SQLiteQuery.OrderBy, to query: inout SQLiteQuery.FluentQuery) { - query.orderBy.append(orderBy) - } } + +internal protocol Int64Initializable { + init(_ int64: Int64) +} + +extension Int: Int64Initializable { } +extension UInt: Int64Initializable { } +extension Int64: Int64Initializable { } +extension UInt64: Int64Initializable { } +extension Int32: Int64Initializable { } +extension UInt32: Int64Initializable { } diff --git a/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift index 3727a4c..d64f1fb 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift @@ -1,67 +1,33 @@ -extension SQLiteQuery { - public struct FluentSchema { - public enum Statement { - case create - case alter - case drop - } - - public var statement: Statement - public var table: TableName - public var columns: [SQLiteQuery.ColumnDefinition] - public var constraints: [SQLiteQuery.TableConstraint] - public init(_ statement: Statement, table: TableName) { - self.statement = statement - self.table = table - self.columns = [] - self.constraints = [] - } +extension SQLiteDatabase: SQLConstraintIdentifierNormalizer { + /// See `SQLConstraintIdentifierNormalizer`. + public static func normalizeSQLConstraintIdentifier(_ identifier: String) -> String { + return identifier } } extension SQLiteDatabase: SchemaSupporting { /// See `SchemaSupporting`. - public typealias Schema = SQLiteQuery.FluentSchema - - /// See `SchemaSupporting`. - public typealias SchemaAction = SQLiteQuery.FluentSchema.Statement - - /// See `SchemaSupporting`. - public typealias SchemaField = SQLiteQuery.ColumnDefinition - - /// See `SchemaSupporting`. - public typealias SchemaFieldType = SQLiteQuery.TypeName - - /// See `SchemaSupporting`. - public typealias SchemaConstraint = SQLiteQuery.TableConstraint + public typealias Schema = FluentSQLiteSchema /// See `SchemaSupporting`. - public typealias SchemaReferenceAction = SQLiteQuery.ForeignKeyReference.Action + public typealias SchemaAction = FluentSQLiteSchemaStatement /// See `SchemaSupporting`. - public static var schemaActionCreate: SQLiteQuery.FluentSchema.Statement { - return .create - } + public typealias SchemaField = SQLiteColumnDefinition /// See `SchemaSupporting`. - public static var schemaActionUpdate: SQLiteQuery.FluentSchema.Statement { - return .alter - } + public typealias SchemaFieldType = SQLiteDataType /// See `SchemaSupporting`. - public static var schemaActionDelete: SQLiteQuery.FluentSchema.Statement { - return .drop - } + public typealias SchemaConstraint = SQLiteTableConstraint /// See `SchemaSupporting`. - public static func schemaCreate(_ action: SQLiteQuery.FluentSchema.Statement, _ entity: String) -> SQLiteQuery.FluentSchema { - return .init(action, table: .init(name: entity)) - } + public typealias SchemaReferenceAction = SQLiteConflictResolution /// See `SchemaSupporting`. - public static func schemaField(for type: Any.Type, isIdentifier: Bool, _ field: SQLiteQuery.QualifiedColumnName) -> SQLiteQuery.ColumnDefinition { + public static func schemaField(for type: Any.Type, isIdentifier: Bool, _ field: QueryField) -> SchemaField { var type = type - var constraints: [SQLiteQuery.ColumnConstraint] = [] + var constraints: [SQLiteColumnConstraint] = [] if let optional = type as? AnyOptionalType.Type { type = optional.anyWrappedType @@ -69,12 +35,12 @@ extension SQLiteDatabase: SchemaSupporting { constraints.append(.notNull) } - let typeName: SQLiteQuery.TypeName - if let sqlite = type as? SQLiteFieldTypeStaticRepresentable.Type { + let typeName: SQLiteDataType + if let sqlite = type as? SQLiteDataTypeStaticRepresentable.Type { switch sqlite.sqliteFieldType { - case .blob: typeName = .none + case .blob: typeName = .blob case .integer: typeName = .integer - case .null: typeName = .none + case .null: typeName = .null case .real: typeName = .real case .text: typeName = .text } @@ -84,85 +50,23 @@ extension SQLiteDatabase: SchemaSupporting { if isIdentifier { constraints.append(.notNull) - switch typeName { - case .integer: constraints.append(.primaryKey(autoIncrement: true)) - default: constraints.append(.primaryKey(autoIncrement: false)) - } + // SQLite should not use AUTOINCREMENT for INTEGER PRIMARY KEY since it is an alias for ROWID + constraints.append(.primaryKey(autoIncrement: false)) } - return .init(name: field.name, typeName: typeName, constraints: constraints) - } - - /// See `SchemaSupporting`. - public static func schemaField(_ field: SQLiteQuery.QualifiedColumnName, _ type: SQLiteQuery.TypeName) -> SQLiteQuery.ColumnDefinition { - return .init(name: field.name, typeName: type, constraints: []) - } - - /// See `SchemaSupporting`. - public static func schemaFieldCreate(_ field: SQLiteQuery.ColumnDefinition, to query: inout SQLiteQuery.FluentSchema) { - query.columns.append(field) + return .columnDefinition(field, typeName, constraints) } /// See `SchemaSupporting`. - public static func schemaFieldDelete(_ field: SQLiteQuery.QualifiedColumnName, to query: inout SQLiteQuery.FluentSchema) { - fatalError("SQLite does not support deleting columns from tables.") - } - - /// See `SchemaSupporting`. - public static func schemaReference(from: SQLiteQuery.QualifiedColumnName, to: SQLiteQuery.QualifiedColumnName, onUpdate: SQLiteQuery.ForeignKeyReference.Action?, onDelete: SQLiteQuery.ForeignKeyReference.Action?) -> SQLiteQuery.TableConstraint { - return .init( - name: "fk", - value: .foreignKey(.init( - columns: [from.name], - reference: .init( - foreignTable: .init(name: to.table!), - foreignColumns: [to.name], - onDelete: onDelete, - onUpdate: onUpdate, - match: nil, - deferrence: nil - ) - )) - ) - } - - /// See `SchemaSupporting`. - public static func schemaUnique(on: [SQLiteQuery.QualifiedColumnName]) -> SQLiteQuery.TableConstraint { - return .init( - name: "uq", - value: .unique(.init( - columns: on.map { .init(value: .column($0.name)) }, - conflictResolution: nil - )) - ) - } - - /// See `SchemaSupporting`. - public static func schemaConstraintCreate(_ constraint: SQLiteQuery.TableConstraint, to query: inout SQLiteQuery.FluentSchema) { - query.constraints.append(constraint) - } - - /// See `SchemaSupporting`. - public static func schemaConstraintDelete(_ constraint: SQLiteQuery.TableConstraint, to query: inout SQLiteQuery.FluentSchema) { - fatalError("SQLite does not support deleting constraints from tables.") - } - - /// See `SchemaSupporting`. - public static func schemaExecute(_ fluent: SQLiteQuery.FluentSchema, on conn: SQLiteConnection) -> Future { + public static func schemaExecute(_ fluent: Schema, on conn: SQLiteConnection) -> Future { let query: SQLiteQuery switch fluent.statement { - case .create: - query = .createTable(.init( - temporary: false, - ifNotExists: false, - table: fluent.table, - source: .schema(.init( - columns: fluent.columns, - tableConstraints: fluent.constraints, - withoutRowID: false - )) - )) - case .alter: + case ._createTable: + var createTable: SQLiteCreateTable = .createTable(fluent.table) + createTable.columns = fluent.columns + createTable.tableConstraints = fluent.constraints + query = ._createTable(createTable) + case ._alterTable: guard fluent.columns.count == 1 && fluent.constraints.count == 0 else { /// See https://www.sqlite.org/lang_altertable.html fatalError("SQLite only supports adding one (1) column in an ALTER query.") @@ -171,11 +75,9 @@ extension SQLiteDatabase: SchemaSupporting { table: fluent.table, value: .addColumn(fluent.columns[0]) )) - case .drop: - query = .dropTable(.init( - table: fluent.table, - ifExists: false - )) + case ._dropTable: + let dropTable: SQLiteDropTable = .dropTable(fluent.table) + query = ._dropTable(dropTable) } return conn.query(query).transform(to: ()) } diff --git a/Sources/FluentSQLite/SQLiteSQLSerializer.swift b/Sources/FluentSQLite/SQLiteSQLSerializer.swift deleted file mode 100644 index 98e6273..0000000 --- a/Sources/FluentSQLite/SQLiteSQLSerializer.swift +++ /dev/null @@ -1,4 +0,0 @@ -///// A SQLite flavored SQL serializer. -//final class SQLiteSQLSerializer: SQLSerializer { -// init() { } -//} diff --git a/Sources/FluentSQLite/SQLiteTypes.swift b/Sources/FluentSQLite/SQLiteTypes.swift index 8bb4dac..5667f20 100644 --- a/Sources/FluentSQLite/SQLiteTypes.swift +++ b/Sources/FluentSQLite/SQLiteTypes.swift @@ -3,7 +3,7 @@ /// This protocol defines which `SQLiteFieldType` (TEXT, BLOB, etc) a type uses and how it converts to/from `SQLiteData`. /// /// See `SQLiteEnumType` and `SQLiteJSONType` for more specialized use-cases. -public typealias SQLiteType = Codable & SQLiteFieldTypeStaticRepresentable & SQLiteDataConvertible +public typealias SQLiteType = Codable & SQLiteDataTypeStaticRepresentable & SQLiteDataConvertible // MARK: JSON @@ -71,8 +71,8 @@ public typealias SQLiteEnumType = SQLiteType & ReflectionDecodable & RawRepresen /// Provides a default `SQLiteFieldTypeStaticRepresentable` implementation where the type is also /// `RawRepresentable` by a `SQLiteFieldTypeStaticRepresentable` type. -extension SQLiteFieldTypeStaticRepresentable - where Self: RawRepresentable, Self.RawValue: SQLiteFieldTypeStaticRepresentable +extension SQLiteDataTypeStaticRepresentable + where Self: RawRepresentable, Self.RawValue: SQLiteDataTypeStaticRepresentable { /// Use the `RawValue`'s `SQLiteFieldType`. /// diff --git a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift index c0f34c7..2040b78 100644 --- a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift +++ b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift @@ -4,6 +4,7 @@ import FluentBenchmark import FluentSQLite import SQLite import XCTest +import FluentSQL final class SQLiteBenchmarkTests: XCTestCase { var benchmarker: Benchmarker! @@ -266,7 +267,7 @@ final class SQLiteBenchmarkTests: XCTestCase { return SQLiteDatabase.create(User.self, on: conn) { builder in builder.field(for: \.id, isIdentifier: true) builder.field(for: \.name) - builder.field(.init(name: "test", typeName: .text, constraints: [.default(.literal("foo"))])) + builder.field(for: \.test, type: .text, .default(.literal("foo"))) } } From 2a66326191e7dc7ded233b4ec5cce9e4a93f5d70 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Mon, 18 Jun 2018 17:15:09 -0400 Subject: [PATCH 2/9] remove commented code --- Sources/FluentSQLite/Exports.swift | 146 ----------------------------- 1 file changed, 146 deletions(-) diff --git a/Sources/FluentSQLite/Exports.swift b/Sources/FluentSQLite/Exports.swift index 45da0a8..314ccff 100644 --- a/Sources/FluentSQLite/Exports.swift +++ b/Sources/FluentSQLite/Exports.swift @@ -1,148 +1,2 @@ @_exported import FluentSQL @_exported import SQLite - -//extension SQLiteDatabase: QuerySupporting { -// /// See `SQLDatabase`. -// public typealias QueryJoin = SQLQuery.DML.Join -// -// /// See `SQLDatabase`. -// public typealias QueryJoinMethod = SQLQuery.DML.Join.Method -// -// /// See `SQLDatabase`. -// public typealias Query = SQLQuery.DML -// -// /// See `SQLDatabase`. -// public typealias Output = [SQLiteColumn: SQLiteData] -// -// /// See `SQLDatabase`. -// public typealias QueryAction = SQLQuery.DML.Statement -// -// /// See `SQLDatabase`. -// public typealias QueryAggregate = String -// -// /// See `SQLDatabase`. -// public typealias QueryData = [SQLQuery.DML.Column: SQLQuery.DML.Value] -// -// /// See `SQLDatabase`. -// public typealias QueryField = SQLQuery.DML.Column -// -// /// See `SQLDatabase`. -// public typealias QueryFilterMethod = SQLQuery.DML.Predicate.Comparison -// -// /// See `SQLDatabase`. -// public typealias QueryFilterValue = SQLQuery.DML.Value -// -// /// See `SQLDatabase`. -// public typealias QueryFilter = SQLQuery.DML.Predicate -// -// /// See `SQLDatabase`. -// public typealias QueryFilterRelation = SQLQuery.DML.Predicate.Relation -// -// /// See `SQLDatabase`. -// public typealias QueryKey = SQLQuery.DML.Key -// -// /// See `SQLDatabase`. -// public typealias QuerySort = SQLQuery.DML.OrderBy -// -// /// See `SQLDatabase`. -// public typealias QuerySortDirection = SQLQuery.DML.OrderBy.Direction -// -// /// See `SQLDatabase`. -// public static func queryExecute(_ dml: SQLQuery.DML, on conn: SQLiteConnection, into handler: @escaping ([SQLiteColumn: SQLiteData], SQLiteConnection) throws -> ()) -> Future { -// // always cache the names first -// return conn.query(.init(.dml(dml))) { row in -// try handler(row, conn) -// } -// } -// -// /// See `SQLDatabase`. -// public static func queryDecode(_ data: [SQLiteColumn: SQLiteData], entity: String, as decodable: D.Type, on conn: SQLiteConnection) -> Future -// where D: Decodable -// { -// do { -// let decoded = try SQLiteRowDecoder().decode(D.self, from: data, table: entity) -// return conn.future(decoded) -// } catch { -// return conn.future(error: error) -// } -// } -// -// /// See `SQLDatabase`. -// public static func schemaColumnType(for type: Any.Type, primaryKey: Bool) -> SQLQuery.DDL.ColumnDefinition.ColumnType { -// var sqliteType: SQLiteFieldTypeStaticRepresentable.Type? -// -// if let optionalType = type as? AnyOptionalType.Type { -// sqliteType = optionalType.anyWrappedType as? SQLiteFieldTypeStaticRepresentable.Type -// } else { -// sqliteType = type as? SQLiteFieldTypeStaticRepresentable.Type -// } -// -// if let type = sqliteType { -// var name: String -// var attributes: [String] = [] -// -// switch type.sqliteFieldType { -// case .blob: name = "BLOB" -// case .integer: name = "INTEGER" -// case .null: name = "NULL" -// case .real: name = "REAL" -// case .text: name = "TEXT" -// } -// -// if primaryKey { -// attributes.append("PRIMARY KEY") -// } -// -// return .init(name: name, attributes: attributes) -// } else { -// fatalError("Unsupported SQLite type: \(type).") -// } -// } -// -// /// See `SQLDatabase`. -// public static func schemaExecute(_ ddl: SQLQuery.DDL, on conn: SQLiteConnection) -> EventLoopFuture { -// // always cache the names first -// return conn.query(.init(.ddl(ddl))).transform(to: ()) -// } -// -// /// See `SQLSupporting`. -// public static func enableForeignKeys(on conn: SQLiteConnection) -> Future { -// return conn.query("PRAGMA foreign_keys = ON;").transform(to: ()) -// } -// -// /// See `SQLSupporting`. -// public static func disableForeignKeys(on conn: SQLiteConnection) -> Future { -// return conn.query("PRAGMA foreign_keys = OFF;").transform(to: ()) -// } -// -// /// See `SQLSupporting`. -// public static func modelEvent(event: ModelEvent, model: M, on conn: SQLiteConnection) -> Future where SQLiteDatabase == M.Database, M: Model { -// var copy = model -// switch event { -// case .willCreate: -// if M.ID.self is UUID.Type { -// copy.fluentID = UUID() as? M.ID -// } -// case .didCreate: -// if M.ID.self is Int.Type { -// copy.fluentID = conn.lastAutoincrementID as? M.ID -// } -// default: break -// } -// return conn.future(copy) -// } -// -// /// See `SQLSupporting`. -// public static func transactionExecute(_ transaction: @escaping (SQLiteConnection) throws -> Future, on conn: SQLiteConnection) -> Future { -// return conn.query("BEGIN TRANSACTION").flatMap { _ -> Future in -// return try transaction(conn).flatMap { res -> Future in -// return conn.query("COMMIT TRANSACTION").transform(to: res) -// }.catchFlatMap { err -> Future in -// return conn.query("ROLLBACK TRANSACTION").map { query -> T in -// // still fail even tho rollback succeeded -// throw err -// } -// } -// } -// } -//} From e0d6594a2c99e51aa2648523986919d474c42b45 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Mon, 18 Jun 2018 23:20:34 -0400 Subject: [PATCH 3/9] sql 2.0 protocols --- Package.swift | 5 +- Sources/FluentSQL/Exports.swift | 2 - Sources/FluentSQL/FluentSQLQuery.swift | 31 -- Sources/FluentSQL/FluentSQLSchema.swift | 23 -- Sources/FluentSQL/SQL+Contains.swift | 41 --- Sources/FluentSQL/SQL+JoinSupporting.swift | 27 -- Sources/FluentSQL/SQL+QuerySupporting.swift | 337 ------------------ Sources/FluentSQL/SQL+SchemaBuilder.swift | 13 - Sources/FluentSQL/SQL+SchemaSupporting.swift | 107 ------ .../SQLiteDataTypeStaticRepresentable.swift | 62 ---- 10 files changed, 2 insertions(+), 646 deletions(-) delete mode 100644 Sources/FluentSQL/Exports.swift delete mode 100644 Sources/FluentSQL/FluentSQLQuery.swift delete mode 100644 Sources/FluentSQL/FluentSQLSchema.swift delete mode 100644 Sources/FluentSQL/SQL+Contains.swift delete mode 100644 Sources/FluentSQL/SQL+JoinSupporting.swift delete mode 100644 Sources/FluentSQL/SQL+QuerySupporting.swift delete mode 100644 Sources/FluentSQL/SQL+SchemaBuilder.swift delete mode 100644 Sources/FluentSQL/SQL+SchemaSupporting.swift diff --git a/Package.swift b/Package.swift index fb85049..8eb1e24 100644 --- a/Package.swift +++ b/Package.swift @@ -11,16 +11,15 @@ let package = Package( .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-rc"), + .package(url: "https://github.com/vapor/fluent.git", .branch("sql")), // 📦 Dependency injection / inversion of control framework. .package(url: "https://github.com/vapor/service.git", from: "1.0.0"), // 🔵 SQLite 3 wrapper for Swift - .package(url: "https://github.com/vapor/sqlite.git", from: "3.0.0-rc"), + .package(url: "https://github.com/vapor/sqlite.git", .branch("sql")), ], targets: [ - .target(name: "FluentSQL", dependencies: ["Fluent", "SQL"]), .target(name: "FluentSQLite", dependencies: ["Async", "FluentSQL", "Service", "SQLite"]), .testTarget(name: "FluentSQLiteTests", dependencies: ["FluentBenchmark", "FluentSQLite", "SQLite"]), ] diff --git a/Sources/FluentSQL/Exports.swift b/Sources/FluentSQL/Exports.swift deleted file mode 100644 index 3ed0ea0..0000000 --- a/Sources/FluentSQL/Exports.swift +++ /dev/null @@ -1,2 +0,0 @@ -@_exported import Fluent -@_exported import SQL diff --git a/Sources/FluentSQL/FluentSQLQuery.swift b/Sources/FluentSQL/FluentSQLQuery.swift deleted file mode 100644 index 8a9c3f5..0000000 --- a/Sources/FluentSQL/FluentSQLQuery.swift +++ /dev/null @@ -1,31 +0,0 @@ -public protocol FluentSQLQuery { - associatedtype Statement: FluentSQLQueryStatement - associatedtype Expression: SQLExpression - associatedtype Join: SQLJoin - associatedtype OrderBy: SQLOrderBy - associatedtype TableIdentifier: SQLTableIdentifier - associatedtype SelectExpression: SQLSelectExpression - associatedtype RowDecoder: SQLRowDecoder - - var statement: Statement { get set } - var table: TableIdentifier { get set } - var keys: [SelectExpression] { get set } - var predicate: Expression? { get set } - var joins: [Join] { get set } - var orderBy: [OrderBy] { get set } - var limit: Int? { get set } - var offset: Int? { get set } - var values: [String: Expression] { get set } - - var defaultBinaryOperator: Expression.BinaryOperator { get set } - - static func query(_ statement: Statement, _ table: TableIdentifier) -> Self -} - -public protocol FluentSQLQueryStatement { - static var insert: Self { get } - static var select: Self { get } - static var update: Self { get } - static var delete: Self { get } - var isInsert: Bool { get } -} diff --git a/Sources/FluentSQL/FluentSQLSchema.swift b/Sources/FluentSQL/FluentSQLSchema.swift deleted file mode 100644 index 1985086..0000000 --- a/Sources/FluentSQL/FluentSQLSchema.swift +++ /dev/null @@ -1,23 +0,0 @@ -public protocol FluentSQLSchema { - associatedtype Statement: FluentSQLSchemaStatement - associatedtype TableIdentifier: SQLTableIdentifier - associatedtype ColumnDefinition: SQLColumnDefinition - associatedtype TableConstraint: SQLTableConstraint - associatedtype ColumnIdentifier: SQLColumnIdentifier - - var statement: Statement { get set } - var table: TableIdentifier { get set } - var columns: [ColumnDefinition] { get set } - var deleteColumns: [ColumnIdentifier] { get set } - var constraints: [TableConstraint] { get set } - var deleteConstraints: [TableConstraint] { get set } - - static func schema(_ statement: Statement, _ table: TableIdentifier) -> Self -} - - -public protocol FluentSQLSchemaStatement { - static var createTable: Self { get } - static var alterTable: Self { get } - static var dropTable: Self { get } -} diff --git a/Sources/FluentSQL/SQL+Contains.swift b/Sources/FluentSQL/SQL+Contains.swift deleted file mode 100644 index 4413438..0000000 --- a/Sources/FluentSQL/SQL+Contains.swift +++ /dev/null @@ -1,41 +0,0 @@ -infix operator ~= -/// Has prefix -public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, ["%" + rhs]) -} -/// Has prefix -public func ~= (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, ["%" + rhs]) -} - -infix operator =~ -/// Has suffix. -public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, [rhs + "%"]) -} -/// Has suffix. -public func =~ (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, [rhs + "%"]) -} - -infix operator ~~ -/// Contains. -public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, ["%" + rhs + "%"]) -} -/// Contains. -public func ~~ (lhs: KeyPath, rhs: String) -> FilterOperator - where D: QuerySupporting, D.QueryFilterMethod: SQLBinaryOperator -{ - return .make(lhs, .like, ["%" + rhs + "%"]) -} diff --git a/Sources/FluentSQL/SQL+JoinSupporting.swift b/Sources/FluentSQL/SQL+JoinSupporting.swift deleted file mode 100644 index 28d647d..0000000 --- a/Sources/FluentSQL/SQL+JoinSupporting.swift +++ /dev/null @@ -1,27 +0,0 @@ -extension JoinSupporting where - QueryJoin: SQLJoin, - QueryJoinMethod == QueryJoin.Method, - QueryJoin.Expression.ColumnIdentifier.TableIdentifier == QueryJoin.TableIdentifier -{ - /// See `JoinSupporting`. - public static func queryJoin(_ method: QueryJoinMethod, base: QueryJoin.Expression.ColumnIdentifier, joined: QueryJoin.Expression.ColumnIdentifier) -> QueryJoin { - guard let table = joined.table else { - fatalError("Cannot join column without a table identifier: \(joined).") - } - return .join(method, table, .binary(.column(base), .equal, .column(joined))) - } -} - -extension JoinSupporting where QueryJoinMethod: SQLJoinMethod { - /// See `JoinSupporting`. - public static var queryJoinMethodDefault: QueryJoinMethod { - return .default - } -} - -extension JoinSupporting where Query: FluentSQLQuery, QueryJoin == Query.Join { - /// See `JoinSupporting`. - public static func queryJoinApply(_ join: QueryJoin, to query: inout Query) { - query.joins.append(join) - } -} diff --git a/Sources/FluentSQL/SQL+QuerySupporting.swift b/Sources/FluentSQL/SQL+QuerySupporting.swift deleted file mode 100644 index 02bd1df..0000000 --- a/Sources/FluentSQL/SQL+QuerySupporting.swift +++ /dev/null @@ -1,337 +0,0 @@ -extension QuerySupporting where Query: FluentSQLQuery { - /// See `QuerySupporting`. - public static func query(_ entity: String) -> Query { - return .query(.select, .table(.identifier(entity))) - } - - /// See `QuerySupporting`. - public static func queryEntity(for query: Query) -> String { - return query.table.identifier.string - } -} - -extension QuerySupporting where QueryAction: FluentSQLQueryStatement { - /// See `QuerySupporting`. - public static var queryActionCreate: QueryAction { - return .insert - } - - /// See `QuerySupporting`. - public static var queryActionRead: QueryAction { - return .select - } - - /// See `QuerySupporting`. - public static var queryActionUpdate: QueryAction { - return .update - } - - /// See `QuerySupporting`. - public static var queryActionDelete: QueryAction { - return .delete - } - - /// See `QuerySupporting`. - public static func queryActionIsCreate(_ action: QueryAction) -> Bool { - return action.isInsert - } -} - -extension QuerySupporting where QueryFilterMethod: SQLBinaryOperator { - /// See `QuerySupporting`. - public static var queryFilterMethodEqual: QueryFilterMethod { - return .equal - } - - /// See `QuerySupporting`. - public static var queryFilterMethodNotEqual: QueryFilterMethod { - return .notEqual - } - - /// See `QuerySupporting`. - public static var queryFilterMethodGreaterThan: QueryFilterMethod { - return .greaterThan - } - - /// See `QuerySupporting`. - public static var queryFilterMethodLessThan: QueryFilterMethod { - return .lessThan - } - - /// See `QuerySupporting`. - public static var queryFilterMethodGreaterThanOrEqual: QueryFilterMethod { - return .greaterThanOrEqual - } - - /// See `QuerySupporting`. - public static var queryFilterMethodLessThanOrEqual: QueryFilterMethod { - return .lessThanOrEqual - } - - /// See `QuerySupporting`. - public static var queryFilterMethodInSubset: QueryFilterMethod { - return .in - } - - /// See `QuerySupporting`. - public static var queryFilterMethodNotInSubset: QueryFilterMethod { - return .notIn - } -} - -extension QuerySupporting where QueryFilterRelation: SQLBinaryOperator { - /// See `QuerySupporting`. - public static var queryFilterRelationAnd: QueryFilterRelation { - return .and - } - - /// See `QuerySupporting`. - public static var queryFilterRelationOr: QueryFilterRelation { - return .or - } -} - -// MARK: QuerySort - -extension QuerySupporting where QuerySortDirection: SQLDirection { - /// See `QuerySupporting`. - public static var querySortDirectionAscending: QuerySortDirection { - return .ascending - } - - /// See `QuerySupporting`. - public static var querySortDirectionDescending: QuerySortDirection { - return .descending - } -} - -extension QuerySupporting where - QuerySort: SQLOrderBy, - QuerySortDirection == QuerySort.Direction, - QueryField == QuerySort.Expression.ColumnIdentifier -{ - /// See `QuerySupporting`. - public static func querySort(_ column: QueryField, _ direction: QuerySortDirection) -> QuerySort { - return .orderBy(.column(column), direction) - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QuerySort == Query.OrderBy { - /// See `QuerySupporting`. - public static func querySortApply(_ orderBy: QuerySort, to query: inout Query) { - query.orderBy.append(orderBy) - } -} - -// MARK: Aggregate - -extension QuerySupporting where QueryAggregate == String, Query: FluentSQLQuery { - /// See `QuerySupporting`. - public static var queryAggregateCount: QueryAggregate { - return "COUNT" - } - - /// See `QuerySupporting`. - public static var queryAggregateSum: QueryAggregate { - return "SUM" - } - - /// See `QuerySupporting`. - public static var queryAggregateAverage: QueryAggregate { - return "AVG" - } - - /// See `QuerySupporting`. - public static var queryAggregateMinimum: QueryAggregate { - return "MIN" - } - - /// See `QuerySupporting`. - public static var queryAggregateMaximum: QueryAggregate { - return "MAX" - } -} - -extension QuerySupporting where - QueryAggregate == String, - QueryKey: SQLSelectExpression, - QueryKey.Expression == QueryKey.Expression.Function.Argument.Expression -{ - /// See `QuerySupporting`. - public static func queryAggregate(_ name: QueryAggregate, _ fields: [QueryKey]) -> QueryKey { - let args: [QueryKey.Expression.Function.Argument] = fields.compactMap { expr in - if expr.isAll { - return .all - } else if let (expr, _) = expr.expression { - return .expression(expr) - } else { - return nil - } - } - return .expression(.function(.function(name, args)), alias: .identifier("fluentAggregate")) - } - -} - -// MARK: QueryRange - -extension QuerySupporting where Query: FluentSQLQuery { - /// See `QuerySupporting`. - public static func queryRangeApply(lower: Int, upper: Int?, to query: inout Query) { - if let upper = upper { - query.limit = upper - lower - query.offset = lower - } else { - query.offset = lower - } - } -} - -// MARK: QueryKey - -extension QuerySupporting where QueryKey: SQLSelectExpression { - /// See `QuerySupporting`. - public static var queryKeyAll: QueryKey { - return .all - } -} - -extension QuerySupporting where QueryKey: SQLSelectExpression, QueryField == QueryKey.Expression.ColumnIdentifier { - /// See `QuerySupporting`. - public static func queryKey(_ field: QueryField) -> QueryKey { - return .expression(.column(field), alias: nil) - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryKey == Query.SelectExpression { - /// See `QuerySupporting`. - public static func queryKeyApply(_ key: QueryKey, to query: inout Query) { - query.keys.append(key) - } -} - -// MARK: QueryFilter - -extension QuerySupporting where - QueryFilter: SQLExpression, - QueryField == QueryFilter.ColumnIdentifier, - QueryFilterMethod == QueryFilter.BinaryOperator, - QueryFilterValue == QueryFilter -{ - /// See `QuerySupporting`. - public static func queryFilter(_ field: QueryField, _ method: QueryFilterMethod, _ value: QueryFilterValue) -> QueryFilter { - return .binary(.column(field), method, value) - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryFilter == Query.Expression { - /// See `QuerySupporting`. - public static func queryFilters(for query: Query) -> [QueryFilter] { - switch query.predicate { - case .none: return [] - case .some(let wrapped): return [wrapped] - } - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryFilter == Query.Expression, QueryFilter.BinaryOperator: Equatable { - /// See `QuerySupporting`. - public static func queryFilterApply(_ filter: QueryFilter, to query: inout Query) { - switch query.defaultBinaryOperator { - case .or: query.predicate |= filter - default: query.predicate &= filter - } - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryFilterRelation == Query.Expression.BinaryOperator { - /// See `QuerySupporting`. - public static func queryDefaultFilterRelation(_ relation: QueryFilterRelation, on query: inout Query) { - query.defaultBinaryOperator = relation - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryAction == Query.Statement { - /// See `QuerySupporting`. - public static func queryActionApply(_ action: QueryAction, to query: inout Query) { - query.statement = action - } -} - - -extension QuerySupporting where Query: FluentSQLQuery, QueryField: SQLColumnIdentifier { - /// See `QuerySupporting`. - public static func queryDataSet(_ field: QueryField, to data: E, on query: inout Query) - where E: Encodable - { - query.values[field.identifier.string] = .bind(.encodable(data)) - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryData == Dictionary { - /// See `QuerySupporting`. - public static func queryDataApply(_ data: QueryData, to query: inout Query) { - query.values = data - } -} - -extension QuerySupporting where QueryField: SQLColumnIdentifier { - /// See `QuerySupporting`. - public static func queryField(_ property: FluentProperty) -> QueryField { - return .column(property.entity.flatMap { .table(.identifier($0)) }, .identifier(property.path[0])) - } -} - -extension QuerySupporting where QueryFilterValue: SQLExpression { - /// See `QuerySupporting`. - public static var queryFilterValueNil: QueryFilterValue { - return .literal(.null) - } -} - -extension QuerySupporting where QueryFilterValue: SQLExpression { - /// See `QuerySupporting`. - public static func queryFilterValue(_ encodables: [E]) -> QueryFilterValue - where E: Encodable - { - return .group(encodables.map { .bind(.encodable($0)) }) - } -} - -extension QuerySupporting where Query: FluentSQLQuery, QueryData == Dictionary { - /// See `QuerySupporting`. - public static func queryEncode(_ value: E, entity: String) throws -> QueryData where E : Encodable { - return SQLQueryEncoder(Query.Expression.self).encode(value) - } -} - -extension QuerySupporting where QueryFilter: SQLExpression, QueryFilterRelation == QueryFilter.BinaryOperator, QueryFilter.BinaryOperator: Equatable { - /// See `QuerySupporting`. - public static func queryFilterGroup(_ relation: QueryFilterRelation, _ filters: [QueryFilter]) -> QueryFilter { - var current: QueryFilter? - for next in filters { - switch relation { - case .or: current |= next - case .and: current &= next - default: break - } - } - if let predicate = current { - return .group([predicate]) - } else { - return .group([]) - } - } -} - -extension QuerySupporting where Query: FluentSQLQuery, Output == Query.RowDecoder.Row { - /// See `QuerySupporting`. - public static func queryDecode(_ output: Output, entity: String, as decodable: D.Type, on conn: Connection) -> Future where D : Decodable { - do { - let row = try Query.RowDecoder.init().decode(D.self, from: output, table: .table(.identifier(entity))) - return conn.future(row) - } catch { - return conn.future(error: error) - } - } -} diff --git a/Sources/FluentSQL/SQL+SchemaBuilder.swift b/Sources/FluentSQL/SQL+SchemaBuilder.swift deleted file mode 100644 index 5700176..0000000 --- a/Sources/FluentSQL/SQL+SchemaBuilder.swift +++ /dev/null @@ -1,13 +0,0 @@ -extension SchemaBuilder where - Model.Database.Schema: FluentSQLSchema, - Model.Database.SchemaField == Model.Database.Schema.ColumnDefinition -{ - public func field( - for key: KeyPath, - type: Model.Database.Schema.ColumnDefinition.DataType, - _ constraints: Model.Database.Schema.ColumnDefinition.ColumnConstraint... - ) { - let property = FluentProperty.keyPath(key) - self.field(.columnDefinition(.column(nil, .identifier(property.path[0])), type, constraints)) - } -} diff --git a/Sources/FluentSQL/SQL+SchemaSupporting.swift b/Sources/FluentSQL/SQL+SchemaSupporting.swift deleted file mode 100644 index c18ba3c..0000000 --- a/Sources/FluentSQL/SQL+SchemaSupporting.swift +++ /dev/null @@ -1,107 +0,0 @@ -extension SchemaSupporting where SchemaAction: FluentSQLSchemaStatement { - /// See `SchemaSupporting`. - public static var schemaActionCreate: SchemaAction { - return .createTable - } - - /// See `SchemaSupporting`. - public static var schemaActionUpdate: SchemaAction { - return .alterTable - } - - /// See `SchemaSupporting`. - public static var schemaActionDelete: SchemaAction { - return .dropTable - } -} - -extension SchemaSupporting where Schema: FluentSQLSchema, SchemaAction == Schema.Statement { - /// See `SchemaSupporting`. - public static func schemaCreate(_ action: SchemaAction, _ entity: String) -> Schema { - return .schema(action, .table(.identifier(entity))) - } -} - -extension SchemaSupporting where - SchemaField: SQLColumnDefinition, - SchemaFieldType == SchemaField.DataType, - QueryField == SchemaField.ColumnIdentifier -{ - /// See `SchemaSupporting`. - public static func schemaField(_ field: QueryField, _ type: SchemaFieldType) -> SchemaField { - return .columnDefinition(field, type, []) - } -} - -extension SchemaSupporting where Schema: FluentSQLSchema, SchemaField == Schema.ColumnDefinition { - /// See `SchemaSupporting`. - public static func schemaFieldCreate(_ field: SchemaField, to query: inout Schema) { - query.columns.append(field) - } -} - -extension SchemaSupporting where Schema: FluentSQLSchema, QueryField == Schema.ColumnIdentifier { - /// See `SchemaSupporting`. - public static func schemaFieldDelete(_ field: QueryField, to query: inout Schema) { - query.deleteColumns.append(field) - } -} - - -extension SchemaSupporting where Schema: FluentSQLSchema, SchemaConstraint == Schema.TableConstraint { - /// See `SchemaSupporting`. - public static func schemaConstraintCreate(_ constraint: SchemaConstraint, to query: inout Schema) { - query.constraints.append(constraint) - } - - /// See `SchemaSupporting`. - public static func schemaConstraintDelete(_ constraint: SchemaConstraint, to query: inout Schema) { - query.deleteConstraints.append(constraint) - } -} - -public protocol SQLConstraintIdentifierNormalizer { - static func normalizeSQLConstraintIdentifier(_ identifier: String) -> String -} - -extension SchemaSupporting where - SchemaConstraint: SQLTableConstraint, - QueryField: SQLColumnIdentifier, - QueryField.Identifier == SchemaConstraint.Algorithm.Identifier, - SchemaReferenceAction == SchemaConstraint.Algorithm.ForeignKey.ConflictResolution, - QueryField.TableIdentifier == SchemaConstraint.Algorithm.ForeignKey.TableIdentifier, - QueryField.Identifier == SchemaConstraint.Algorithm.ForeignKey.Identifier, - Self: SQLConstraintIdentifierNormalizer -{ - /// See `SchemaSupporting`. - public static func schemaReference(from: QueryField, to: QueryField, onUpdate: SchemaReferenceAction?, onDelete: SchemaReferenceAction?) -> SchemaConstraint { - guard let foreignTable = to.table else { - fatalError("Cannot create reference to column without table identifier: \(to).") - } - guard let localTable = from.table else { - fatalError("Cannot create reference from column without table identifier: \(from).") - } - let uid = "\(localTable.identifier.string).\(from.identifier.string)+\(foreignTable.identifier.string).\(to.identifier.string)" - return .constraint( - .foreignKey([from.identifier], .foreignKey(foreignTable, [to.identifier], onDelete: onDelete, onUpdate: onUpdate)), - .identifier("fk:\(normalizeSQLConstraintIdentifier(uid))") - ) - } -} - -extension SchemaSupporting where - SchemaConstraint: SQLTableConstraint, - QueryField: SQLColumnIdentifier, - QueryField.Identifier == SchemaConstraint.Algorithm.Identifier, - Self: SQLConstraintIdentifierNormalizer -{ - /// See `SchemaSupporting`. - public static func schemaUnique(on: [QueryField]) -> SchemaConstraint { - let uid = on.map { $0.identifier.string }.joined(separator: "+") - return .constraint(.unique(on.map { $0.identifier }), .identifier("uq:\(normalizeSQLConstraintIdentifier(uid))")) - } -} - -extension SchemaSupporting { - -} diff --git a/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift b/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift index fc15c32..8b13789 100644 --- a/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift +++ b/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift @@ -1,63 +1 @@ -/// A type that is capable of being represented by a `SQLiteFieldType`. -/// -/// Types conforming to this protocol can be automatically migrated by `FluentSQLite`. -/// -/// See `SQLiteType` for more information. -public protocol SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - static var sqliteFieldType: SQLiteDataType { get } -} -extension FixedWidthInteger { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return .integer } -} - -extension UInt: SQLiteDataTypeStaticRepresentable { } -extension UInt8: SQLiteDataTypeStaticRepresentable { } -extension UInt16: SQLiteDataTypeStaticRepresentable { } -extension UInt32: SQLiteDataTypeStaticRepresentable { } -extension UInt64: SQLiteDataTypeStaticRepresentable { } -extension Int: SQLiteDataTypeStaticRepresentable { } -extension Int8: SQLiteDataTypeStaticRepresentable { } -extension Int16: SQLiteDataTypeStaticRepresentable { } -extension Int32: SQLiteDataTypeStaticRepresentable { } -extension Int64: SQLiteDataTypeStaticRepresentable { } - -extension Date: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return Double.sqliteFieldType } -} - -extension BinaryFloatingPoint { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return .real } -} - -extension Float: SQLiteDataTypeStaticRepresentable { } -extension Double: SQLiteDataTypeStaticRepresentable { } - -extension Bool: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return Int.sqliteFieldType } -} - -extension UUID: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return .blob } -} - -extension Data: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return .blob } -} - -extension String: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return .text } -} - -extension URL: SQLiteDataTypeStaticRepresentable { - /// See `SQLiteFieldTypeStaticRepresentable`. - public static var sqliteFieldType: SQLiteDataType { return String.sqliteFieldType } -} From ed8d7e1ad7ad42b85d322bf650b737594e28d90b Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Mon, 18 Jun 2018 23:24:11 -0400 Subject: [PATCH 4/9] latest vapor/sqlite updates --- Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift | 1 - Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift | 4 ++-- Sources/FluentSQLite/SQLiteTypes.swift | 4 ++-- Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) delete mode 100644 Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift diff --git a/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift b/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift deleted file mode 100644 index 8b13789..0000000 --- a/Sources/FluentSQLite/SQLiteDataTypeStaticRepresentable.swift +++ /dev/null @@ -1 +0,0 @@ - diff --git a/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift index d64f1fb..ad9a6ff 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+SchemaSupporting.swift @@ -37,7 +37,7 @@ extension SQLiteDatabase: SchemaSupporting { let typeName: SQLiteDataType if let sqlite = type as? SQLiteDataTypeStaticRepresentable.Type { - switch sqlite.sqliteFieldType { + switch sqlite.sqliteDataType { case .blob: typeName = .blob case .integer: typeName = .integer case .null: typeName = .null @@ -51,7 +51,7 @@ extension SQLiteDatabase: SchemaSupporting { if isIdentifier { constraints.append(.notNull) // SQLite should not use AUTOINCREMENT for INTEGER PRIMARY KEY since it is an alias for ROWID - constraints.append(.primaryKey(autoIncrement: false)) + constraints.append(.primaryKey(default: nil)) } return .columnDefinition(field, typeName, constraints) diff --git a/Sources/FluentSQLite/SQLiteTypes.swift b/Sources/FluentSQLite/SQLiteTypes.swift index 5667f20..0165334 100644 --- a/Sources/FluentSQLite/SQLiteTypes.swift +++ b/Sources/FluentSQLite/SQLiteTypes.swift @@ -29,7 +29,7 @@ extension SQLiteJSONType { /// Use the `Data`'s `SQLiteFieldType` to store the JSON-encoded data. /// /// See `SQLiteFieldTypeStaticRepresentable.sqliteFieldType` for more information. - public static var sqliteFieldType: SQLiteDataType { return Data.sqliteFieldType } + public static var sqliteDataType: SQLiteDataType { return Data.sqliteDataType } /// JSON-encode `Self` to `Data`. /// @@ -77,7 +77,7 @@ extension SQLiteDataTypeStaticRepresentable /// Use the `RawValue`'s `SQLiteFieldType`. /// /// See `SQLiteFieldTypeStaticRepresentable.sqliteFieldType` for more information. - public static var sqliteFieldType: SQLiteDataType { return RawValue.sqliteFieldType } + public static var sqliteDataType: SQLiteDataType { return RawValue.sqliteDataType } } /// Provides a default `SQLiteDataConvertible` implementation where the type is also diff --git a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift index 2040b78..867c1f9 100644 --- a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift +++ b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift @@ -310,7 +310,7 @@ final class SQLiteBenchmarkTests: XCTestCase { do { var databases = DatabasesConfig() try! databases.add(database: SQLiteDatabase(storage: .memory), as: .sqlite) - databases.enableReferebces(on: .sqlite) + databases.enableReferences(on: .sqlite) let group = MultiThreadedEventLoopGroup(numberOfThreads: 1) let dbs = try databases.resolve(on: BasicContainer(config: .init(), environment: .testing, services: .init(), on: group)) sqlite = try dbs.requireDatabase(for: .sqlite).newConnectionPool(config: .init(maxConnections: 4), on: group) From 509c3865aaca8a8b457afde325df55bc7f95bba2 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 19 Jun 2018 02:36:09 -0400 Subject: [PATCH 5/9] pure codable enum test --- Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift index 867c1f9..eed0e26 100644 --- a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift +++ b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift @@ -137,7 +137,7 @@ final class SQLiteBenchmarkTests: XCTestCase { } func testSQLiteEnums() throws { - enum PetType: Int, SQLiteEnumType, CaseIterable { + enum PetType: Int, Codable, Swift.CaseIterable { static let allCases: [PetType] = [.cat, .dog] case cat, dog } From 70241368b13b9a1bd48a38020e727bea13f7e14d Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 19 Jun 2018 11:09:40 -0400 Subject: [PATCH 6/9] update to latest fluent benchmarks --- .../SQLiteDatabase+QuerySupporting.swift | 2 + .../SQLiteBenchmarkTests.swift | 65 ++----------------- 2 files changed, 7 insertions(+), 60 deletions(-) diff --git a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift index 152861b..cb73039 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift @@ -68,6 +68,8 @@ extension SQLiteDatabase: QuerySupporting { select.joins = fluent.joins select.predicate = fluent.predicate select.orderBy = fluent.orderBy + select.limit = fluent.limit + select.offset = fluent.offset query = .select(select) case ._update: var update: SQLiteUpdate = .update(fluent.table) diff --git a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift index eed0e26..2de6a0a 100644 --- a/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift +++ b/Tests/FluentSQLiteTests/SQLiteBenchmarkTests.swift @@ -16,50 +16,10 @@ final class SQLiteBenchmarkTests: XCTestCase { benchmarker = try! Benchmarker(database, on: group, onFail: XCTFail) } - func testSchema() throws { - try benchmarker.benchmarkSchema() + func testBenchmark() throws { + try benchmarker.runAll() } - - func testModels() throws { - try benchmarker.benchmarkModels_withSchema() - } - - func testRelations() throws { - try benchmarker.benchmarkRelations_withSchema() - } - - func testTimestampable() throws { - try benchmarker.benchmarkTimestampable_withSchema() - } - - func testTransactions() throws { - try benchmarker.benchmarkTransactions_withSchema() - } - - func testChunking() throws { - try benchmarker.benchmarkChunking_withSchema() - } - - func testAutoincrement() throws { - try benchmarker.benchmarkAutoincrement_withSchema() - } - - func testCache() throws { - try benchmarker.benchmarkCache_withSchema() - } - - func testJoins() throws { - try benchmarker.benchmarkJoins_withSchema() - } - - func testSoftDeletable() throws { - try benchmarker.benchmarkSoftDeletable_withSchema() - } - - func testReferentialActions() throws { - try benchmarker.benchmarkReferentialActions_withSchema() - } - + func testMinimumViableModelDeclaration() throws { /// NOTE: these must never fail to build struct Foo: SQLiteModel { @@ -83,10 +43,6 @@ final class SQLiteBenchmarkTests: XCTestCase { var name: String } } - - func testIndexSupporting() throws { - try benchmarker.benchmarkIndexSupporting_withSchema() - } func testContains() throws { struct User: SQLiteModel, SQLiteMigration { @@ -137,7 +93,7 @@ final class SQLiteBenchmarkTests: XCTestCase { } func testSQLiteEnums() throws { - enum PetType: Int, Codable, Swift.CaseIterable { + enum PetType: Int, Codable, CaseIterable { static let allCases: [PetType] = [.cat, .dog] case cat, dog } @@ -332,19 +288,8 @@ final class SQLiteBenchmarkTests: XCTestCase { } static let allTests = [ - ("testSchema", testSchema), - ("testModels", testModels), - ("testRelations", testRelations), - ("testTimestampable", testTimestampable), - ("testTransactions", testTransactions), - ("testChunking", testChunking), - ("testAutoincrement", testAutoincrement), - ("testCache", testCache), - ("testJoins", testJoins), - ("testSoftDeletable", testSoftDeletable), - ("testReferentialActions", testReferentialActions), + ("testBenchmark", testBenchmark), ("testMinimumViableModelDeclaration", testMinimumViableModelDeclaration), - ("testIndexSupporting", testIndexSupporting), ("testUUIDPivot", testUUIDPivot), ("testSQLiteEnums", testSQLiteEnums), ("testSQLiteJSON", testSQLiteJSON), From 2854fa7dcc2714ce4218a9bbbc44094ad6e3d8a6 Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 19 Jun 2018 19:12:24 -0400 Subject: [PATCH 7/9] use tags --- Package.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Package.swift b/Package.swift index 8eb1e24..87c017d 100644 --- a/Package.swift +++ b/Package.swift @@ -11,13 +11,13 @@ let package = Package( .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", .branch("sql")), + .package(url: "https://github.com/vapor/fluent.git", from: "3.0.0-rc"), // 📦 Dependency injection / inversion of control framework. .package(url: "https://github.com/vapor/service.git", from: "1.0.0"), // 🔵 SQLite 3 wrapper for Swift - .package(url: "https://github.com/vapor/sqlite.git", .branch("sql")), + .package(url: "https://github.com/vapor/sqlite.git", from: "3.0.0-rc"), ], targets: [ .target(name: "FluentSQLite", dependencies: ["Async", "FluentSQL", "Service", "SQLite"]), From c1680e56d0436e8b36435bed9af94f4d6c74687e Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 19 Jun 2018 19:13:51 -0400 Subject: [PATCH 8/9] add group by property --- Sources/FluentSQLite/FluentSQLiteQuery.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sources/FluentSQLite/FluentSQLiteQuery.swift b/Sources/FluentSQLite/FluentSQLiteQuery.swift index 591b561..0f26c28 100644 --- a/Sources/FluentSQLite/FluentSQLiteQuery.swift +++ b/Sources/FluentSQLite/FluentSQLiteQuery.swift @@ -24,6 +24,7 @@ public struct FluentSQLiteQuery: FluentSQLQuery { public typealias SelectExpression = SQLiteSelectExpression public typealias Join = SQLiteJoin public typealias OrderBy = SQLiteOrderBy + public typealias GroupBy = SQLiteGroupBy public typealias RowDecoder = SQLiteRowDecoder public var statement: Statement @@ -33,6 +34,7 @@ public struct FluentSQLiteQuery: FluentSQLQuery { 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 defaultBinaryOperator: GenericSQLBinaryOperator @@ -46,6 +48,7 @@ public struct FluentSQLiteQuery: FluentSQLQuery { joins: [], predicate: nil, orderBy: [], + groupBy: [], limit: nil, offset: nil, defaultBinaryOperator: .and From 380f7e9f1afa0090a87c03727a5a3d586f76fb9c Mon Sep 17 00:00:00 2001 From: tanner0101 Date: Tue, 19 Jun 2018 19:16:23 -0400 Subject: [PATCH 9/9] forward group by during query conversion --- Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift index cb73039..1d2301d 100644 --- a/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift +++ b/Sources/FluentSQLite/SQLiteDatabase+QuerySupporting.swift @@ -68,6 +68,7 @@ extension SQLiteDatabase: QuerySupporting { 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)