From cc09c1da4ea9bac9c5e89f306b94dcce616bad8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Fri, 18 Oct 2024 17:29:21 +0200 Subject: [PATCH 1/7] Mark SQLError as Sendable It is. --- .../RecordGeneration/GenerateDatabaseSupport.swift | 6 ++++-- Sources/Lighter/Utilities/SQLError.swift | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift index 2a6447c..675cedd 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseSupport.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // import LighterCodeGenAST @@ -194,7 +194,9 @@ extension EnlighterASTGenerator { func generateSQLError(name: String = "SQLError") -> Struct { return Struct( public: options.public, name: name, - conformances: [.name("Swift.Error"), .name("Equatable")], + conformances: [ + .name("Swift.Error"), .name("Equatable"), .name("Sendable") + ], variables: [ .let(public: options.public, "code" , .int32, comment: "The SQLite3 error code (`sqlite3_errcode`)."), diff --git a/Sources/Lighter/Utilities/SQLError.swift b/Sources/Lighter/Utilities/SQLError.swift index b550f0d..438ff74 100644 --- a/Sources/Lighter/Utilities/SQLError.swift +++ b/Sources/Lighter/Utilities/SQLError.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022-2023 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // import func SQLite3.sqlite3_errcode @@ -50,3 +50,6 @@ public struct SQLError: Swift.Error, Equatable { self.message = sqlite3_errmsg(db).flatMap(String.init(cString:)) } } +#if swift(>=5.5) +extension SQLError: Sendable {} +#endif From b4fff574ea1a91187f39780f48e090610121e296 Mon Sep 17 00:00:00 2001 From: gibachan Date: Tue, 22 Oct 2024 08:02:16 +0900 Subject: [PATCH 2/7] Fix to prevent conflicts between database and table names --- .../Libraries/LighterGeneration/GenModel/SchemaInit.swift | 3 ++- .../EntityGenTests/ASTDatabaseStructGenerationTests.swift | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Plugins/Libraries/LighterGeneration/GenModel/SchemaInit.swift b/Plugins/Libraries/LighterGeneration/GenModel/SchemaInit.swift index a3aeb96..5199bd1 100644 --- a/Plugins/Libraries/LighterGeneration/GenModel/SchemaInit.swift +++ b/Plugins/Libraries/LighterGeneration/GenModel/SchemaInit.swift @@ -11,6 +11,7 @@ public extension DatabaseInfo { var entities = [ EntityInfo ]() entities.reserveCapacity(schema.tables.count + schema.views.count) + let dbName = schema.tables.contains(where: { $0.name == name }) ? "\(name)DB" : name for table in schema.tables { let isWithoutRowID = table.isTableWithoutRowID let indices = schema.indices [table.name] ?? [] @@ -56,7 +57,7 @@ public extension DatabaseInfo { entities.append(entity) } - self.init(name: name, userVersion: schema.userVersion, entities: entities) + self.init(name: dbName, userVersion: schema.userVersion, entities: entities) } } diff --git a/Tests/EntityGenTests/ASTDatabaseStructGenerationTests.swift b/Tests/EntityGenTests/ASTDatabaseStructGenerationTests.swift index 89c2557..040a711 100644 --- a/Tests/EntityGenTests/ASTDatabaseStructGenerationTests.swift +++ b/Tests/EntityGenTests/ASTDatabaseStructGenerationTests.swift @@ -228,4 +228,11 @@ final class ASTDatabaseStructGenerationTests: XCTestCase { XCTAssertTrue(source.contains( "[ Person.self, Address.self, AFancyTestTable.self ]")) } + + func testResolveConflictedDatabaseName() throws { + let name = try XCTUnwrap(Fixtures.addressSchema.tables.first?.name) + let dbInfo = DatabaseInfo(name: name, schema: Fixtures.addressSchema) + + XCTAssertEqual(dbInfo.name, "\(name)DB") + } } From 45f232d6090ce17e327cc47a4b67d7190b6956bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 26 Oct 2024 21:44:41 +0200 Subject: [PATCH 3/7] Move Sendables to 5.5 guarded extension ... --- .../LighterCodeGenAST/Nodes/Expression.swift | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift index 92e2aae..d466671 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift @@ -82,12 +82,12 @@ public indirect enum Expression: Equatable { * * Used as the value for ``Expression/functionCall(_:)``. */ -public struct FunctionCall: Equatable, Sendable { +public struct FunctionCall: Equatable { /** * A parameter passed as part of the function call. */ - public struct Parameter: Equatable, Sendable { + public struct Parameter: Equatable { /// The keyword/label of the parameter, can be `nil` if it is a wildcard /// (unlabled) parameter. @@ -105,7 +105,7 @@ public struct FunctionCall: Equatable, Sendable { /** * A trailing closure attached to a function call. */ - public struct TrailingClosure: Equatable, Sendable { + public struct TrailingClosure: Equatable { /// The parameter list of the trailing closure (e.g. `( a, b ) in`). public let parameters: [ String ] @@ -282,5 +282,8 @@ public extension Expression { } #if swift(>=5.5) -extension Expression : Sendable {} +extension Expression : Sendable {} +extension FunctionCall : Sendable {} +extension FunctionCall.Parameter : Sendable {} +extension FunctionCall.TrailingClosure : Sendable {} #endif From ef365f75c09ffe1bc9c628f339540d224d6bce68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 26 Oct 2024 21:45:44 +0200 Subject: [PATCH 4/7] Use proper constants for Sendable structures Originally replaced them w/ var's to get around the Sendable issues. Not required once the types are Sendable, which they are. --- .../LighterCodeGenAST/Nodes/Expression.swift | 15 ++++++++---- .../Nodes/TypeReference.swift | 24 +++++++++---------- .../GenerateDatabaseStruct.swift | 3 +-- .../GenerateRawFunctions.swift | 8 +++---- 4 files changed, 28 insertions(+), 22 deletions(-) diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift index d466671..d024aa1 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift @@ -153,20 +153,27 @@ public struct FunctionCall: Equatable { public extension Expression { /// `nil` - static var `nil` : Self { .literal(.nil) } + static let `nil` = Self.literal(.nil) /// Bool `true` - static var `true` : Self { .literal(.true) } + static let `true` = Self.literal(.true) /// Bool `false` - static var `false` : Self { .literal(.false) } - + static let `false` = Self.literal(.false) + + /// `$0` + static let closureArg0 = Self.raw("$0") + /// A literal integer (`42`). + @inlinable static func integer(_ value: Int) -> Self { .literal(.integer(value)) } /// A literal double (`13.37`). + @inlinable static func double (_ value: Double) -> Self { .literal(.double (value)) } /// A literal string (`"Them Bones"`). + @inlinable static func string (_ value: String) -> Self { .literal(.string (value)) } /// An array of `UInt8` integers (i.e. a data literal). + @inlinable static func integerArray(_ value: [ UInt8 ]) -> Self { .literal(.integerArray(value.map { Int($0) })) } diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeReference.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeReference.swift index 5c3d4f6..e5f3865 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeReference.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/TypeReference.swift @@ -43,38 +43,38 @@ public indirect enum TypeReference: Equatable { public extension TypeReference { /// Swift `Int`. - static var int : TypeReference { .name("Int") } + static let int = TypeReference.name("Int") /// Swift `String`. - static var string : TypeReference { .name("String") } + static let string = TypeReference.name("String") /// Swift `Double`. - static var double : TypeReference { .name("Double") } + static let double = TypeReference.name("Double") /// Swift `Bool`. - static var bool : TypeReference { .name("Bool") } + static let bool = TypeReference.name("Bool") // common in SQLite /// Swift `Int32`. - static var int32 : TypeReference { .name("Int32") } + static let int32 = TypeReference.name("Int32") /// Swift `Int64`. - static var int64 : TypeReference { .name("Int64") } + static let int64 = TypeReference.name("Int64") } public extension TypeReference { /// Swift `[ UInt8 ]`. Aka `Data`, w/o the need for Foundation. - static var uint8Array : TypeReference { .array(.name("UInt8")) } + static let uint8Array = TypeReference.array(.name("UInt8")) } public extension TypeReference { /// A Foundation `URL`. - static var url : TypeReference { .name("URL") } + static let url = TypeReference.name("URL") /// A Foundation `Decimal` number. - static var decimal : TypeReference { .name("Decimal") } + static let decimal = TypeReference.name("Decimal") /// A Foundation `Date`. - static var date : TypeReference { .name("Date") } + static let date = TypeReference.name("Date") /// A Foundation `Data`. - static var data : TypeReference { .name("Data") } + static let data = TypeReference.name("Data") /// A Foundation `UUID`. - static var uuid : TypeReference { .name("UUID") } + static let uuid = TypeReference .name("UUID") } #if swift(>=5.5) diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift index 7d70c76..ce02cf1 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateDatabaseStruct.swift @@ -603,7 +603,7 @@ extension EnlighterASTGenerator { } } - fileprivate static var defaultSQLiteDateFormatterExpression : Expression { + fileprivate static let defaultSQLiteDateFormatterExpression : Expression = Expression.inlineClosureCall([ .let("formatter", is: .call(name: "DateFormatter")), .set(instance: "formatter", "dateFormat", @@ -613,5 +613,4 @@ extension EnlighterASTGenerator { [ ("identifier", .string("en_US_POSIX"))])), .return(.variable("formatter")) ]) - } } diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRawFunctions.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRawFunctions.swift index 7723f0b..76a9ab8 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRawFunctions.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRawFunctions.swift @@ -148,7 +148,7 @@ extension EnlighterASTGenerator { // MARK: - Helpers - fileprivate static var stepAndReturnError : [ Statement ] { [ + fileprivate static let stepAndReturnError : [ Statement ] = [ .let("rc", is: .call(name: "sqlite3_step", .variable("statement"))), .return( .conditional( @@ -157,7 +157,7 @@ extension EnlighterASTGenerator { .variable("SQLITE_OK") ) ) - ] } + ] private func prepareSQL(_ schemaSQLProperty: String, for entity: EntityInfo) -> [ Statement ] @@ -166,13 +166,13 @@ extension EnlighterASTGenerator { schemaSQLProperty])) ] + Self.prepareSQL } - static var prepareSQL : [ Statement ] { [ + static let prepareSQL : [ Statement ] = [ // Could use `Self` for record attached funcs .var("handle", type: .name("OpaquePointer?")), .raw("guard sqlite3_prepare_v2(db, sql, -1, &handle, nil) == SQLITE_OK,"), .raw(" let statement = handle else { return sqlite3_errcode(db) }"), .raw("defer { sqlite3_finalize(statement) }") - ] } + ] func functionName(for entity: EntityInfo, operation: String) -> String { switch options.rawFunctions { From f163279551b5a3bb855dd6788d34aa4f5315d4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 26 Oct 2024 21:51:25 +0200 Subject: [PATCH 5/7] Use proper constants for Sendable structures Expressions this time. SQLExpression is already marked Sendable, so no need to restate in implementations. --- .../Predicates/SQLColumnComparisonPredicate.swift | 8 ++++++-- Sources/Lighter/Predicates/SQLColumnValuePredicate.swift | 6 +++++- Sources/Lighter/Predicates/SQLTruePredicate.swift | 9 +++------ 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/Sources/Lighter/Predicates/SQLColumnComparisonPredicate.swift b/Sources/Lighter/Predicates/SQLColumnComparisonPredicate.swift index 5d2e509..01b1ae3 100644 --- a/Sources/Lighter/Predicates/SQLColumnComparisonPredicate.swift +++ b/Sources/Lighter/Predicates/SQLColumnComparisonPredicate.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // /** @@ -20,7 +20,7 @@ public struct SQLColumnComparisonPredicate: SQLPredicate L.Value == R.Value { - public enum ComparisonOperator: String, Sendable { + public enum ComparisonOperator: String { /** * Check whether the ``SQLColumn`` is the same like the other column @@ -111,3 +111,7 @@ public struct SQLColumnComparisonPredicate: SQLPredicate builder.append(builder.sqlString(for: rhs)) } } + +#if swift(>=5.5) +extension SQLColumnComparisonPredicate.ComparisonOperator : Sendable {} +#endif diff --git a/Sources/Lighter/Predicates/SQLColumnValuePredicate.swift b/Sources/Lighter/Predicates/SQLColumnValuePredicate.swift index 4ef5e7c..1b8ab6f 100644 --- a/Sources/Lighter/Predicates/SQLColumnValuePredicate.swift +++ b/Sources/Lighter/Predicates/SQLColumnValuePredicate.swift @@ -20,7 +20,7 @@ import struct Foundation.Data */ public struct SQLColumnValuePredicate: SQLPredicate { - public enum ComparisonOperator: String, Sendable { + public enum ComparisonOperator: String { /** * Check whether the ``SQLColumn`` is the same like the given value. @@ -353,3 +353,7 @@ public struct SQLColumnValuePredicate: SQLPredicate { } private let specialLikeChars : Set = [ "_", "%", "'", "\"" ] + +#if swift(>=5.5) +extension SQLColumnValuePredicate.ComparisonOperator : Sendable {} +#endif diff --git a/Sources/Lighter/Predicates/SQLTruePredicate.swift b/Sources/Lighter/Predicates/SQLTruePredicate.swift index ee88d60..770de98 100644 --- a/Sources/Lighter/Predicates/SQLTruePredicate.swift +++ b/Sources/Lighter/Predicates/SQLTruePredicate.swift @@ -8,8 +8,7 @@ */ public struct SQLTruePredicate: SQLPredicate { - @inlinable - static var shared : SQLTruePredicate { SQLTruePredicate() } + public static let shared = SQLTruePredicate() // MARK: - SQL Generation @@ -34,10 +33,8 @@ extension SQLPredicate where Self == SQLTruePredicate { */ public struct SQLBoolPredicate: SQLPredicate { - @inlinable - public static var `true` : Self { Self(true) } - @inlinable - public static var `false` : Self { Self(false) } + public static let `true` = Self(true) + public static let `false` = Self(false) public let value : Bool From e354ce0790a7ef36c65834e5449c8d1d8695e6f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 26 Oct 2024 21:55:51 +0200 Subject: [PATCH 6/7] Add a "closure" expression We had inlineClosureCall, but no specific expression for closures. --- .../LighterCodeGenAST/Generation/GenExpressions.swift | 10 +++++++++- .../Libraries/LighterCodeGenAST/Nodes/Expression.swift | 5 ++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Plugins/Libraries/LighterCodeGenAST/Generation/GenExpressions.swift b/Plugins/Libraries/LighterCodeGenAST/Generation/GenExpressions.swift index 1c938d5..c3f2d52 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Generation/GenExpressions.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Generation/GenExpressions.swift @@ -15,7 +15,7 @@ public extension CodeGenerator { case .literal, .variableReference, .variablePath, .keyPathLookup, .keyPath, .functionCall, .selfInit, .typeInit, - .cast, .inlineClosureCall: + .cast, .inlineClosureCall, .closure: return string(for: expression) } } @@ -103,6 +103,12 @@ public extension CodeGenerator { case .forceUnwrap(let expression): return "\(string(for: expression, wrapIfComplex: true))!" + case .closure(let statements): + return nestedGeneration { + indentedCodeBlock(addSpaceIfMissing: false) { + generateStatements(statements) + } + } case .inlineClosureCall(let statements): return nestedGeneration { indentedCodeBlock(endSuffix: "()", addSpaceIfMissing: false) { @@ -143,6 +149,8 @@ public extension CodeGenerator { append(string(for: expression)) case .varargs: // indent better append(string(for: expression)) + case .closure: // indent better + append(string(for: expression)) case .inlineClosureCall: // indent better append(string(for: expression)) case .functionCall(let call): diff --git a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift index d024aa1..1525a8c 100644 --- a/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift +++ b/Plugins/Libraries/LighterCodeGenAST/Nodes/Expression.swift @@ -73,7 +73,10 @@ public indirect enum Expression: Equatable { /// `a!` case forceUnwrap(Expression) - /// `{ return 46 + 2 }` + /// `{ return 46 + 2 }` (w/o calling it) + case closure([ Statement ]) + + /// `{ return 46 + 2 }()` (calling it) case inlineClosureCall([ Statement ]) } From 83050ef7c5e0b38f24132a2e0ce30dc0da2e734c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Helge=20He=C3=9F?= Date: Sat, 26 Oct 2024 22:01:14 +0200 Subject: [PATCH 7/7] Properly generate optional string-Date fields Should fix issue #37 --- .../RecordGeneration/GenerateRecordStatementBind.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStatementBind.swift b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStatementBind.swift index e2a68ab..210192c 100644 --- a/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStatementBind.swift +++ b/Plugins/Libraries/LighterGeneration/RecordGeneration/GenerateRecordStatementBind.swift @@ -1,6 +1,6 @@ // // Created by Helge Heß. -// Copyright © 2022 ZeeZide GmbH. +// Copyright © 2022-2024 ZeeZide GmbH. // import LighterCodeGenAST @@ -590,10 +590,10 @@ extension EnlighterASTGenerator { !optional // but still yields an optional if dateformatter is nil ? .call(name: "\(database.name).dateFormatter?.string", parameters: [ ( "from", ivar(propertyName) ) ]) - : .flatMap(expression: ivar(propertyName), map: .call( - name: "\(database.name).dateFormatter?.string", - parameters: [ ( "from", .raw("$0") ) ] - )) + : .flatMap(expression: ivar(propertyName), map: .closure([ + .call(name: "\(database.name).dateFormatter?.string", + parameters: [ ( "from", .closureArg0 ) ]) + ])) ) ], trailing: ( [ "s" ], [ .ifSwitch( (