diff --git a/Sources/StructuredQueriesMacros/SelectionMacro.swift b/Sources/StructuredQueriesMacros/SelectionMacro.swift index 97f33a7d..5bd7504a 100644 --- a/Sources/StructuredQueriesMacros/SelectionMacro.swift +++ b/Sources/StructuredQueriesMacros/SelectionMacro.swift @@ -205,7 +205,7 @@ extension SelectionMacro: MemberMacro { return [] } let type = IdentifierTypeSyntax(name: declaration.name.trimmed) - var allColumns: [(name: TokenSyntax, type: TypeSyntax?)] = [] + var allColumns: [(name: TokenSyntax, type: TypeSyntax?, default: ExprSyntax?)] = [] var decodings: [String] = [] var decodingUnwrappings: [String] = [] var decodingAssignments: [String] = [] @@ -278,7 +278,12 @@ extension SelectionMacro: MemberMacro { .text } - allColumns.append((identifier, columnQueryValueType)) + let defaultValue: ExprSyntax? = + binding.initializer.map(\.value.trimmed) + // TODO: Revisit this with multi-column support. + // ?? (columnQueryValueType?.isOptionalType == true ? ExprSyntax(NilLiteralExprSyntax()) : nil) + + allColumns.append((identifier, columnQueryValueType, defaultValue)) let decodedType = columnQueryValueType?.asNonOptionalType() decodings.append( """ @@ -320,7 +325,13 @@ extension SelectionMacro: MemberMacro { let initArguments = allColumns - .map { "\($0): some \(moduleName).QueryExpression\($1.map { "<\($0)>" } ?? "")" } + .map { + """ + \($0): some \(moduleName).QueryExpression\ + \($1.map { "<\($0)>" } ?? "")\ + \($2.map { "= #bind(\($0))" } ?? "") + """ + } .joined(separator: ",\n") let initAssignment: [String] = allColumns diff --git a/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md b/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md index 20be5971..2b400e11 100644 --- a/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md +++ b/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md @@ -151,20 +151,25 @@ last list was deleted: One can use triggers with a `SELECT` action to invoke Swift code when an event occurs in your database. For example, suppose you want to execute a Swift function a new reminder is inserted into the database. First you must register the function with SQLite and that depends on what -SQLite driver you are using ([here][grdb-add-function] is how to do it in GRDB). +SQLite driver you are using ([here][sqlite-data-add-function] is how to do it in SQLiteData). Suppose we registered a function called `didInsertReminder`, and further suppose it takes one argument of the ID of the newly inserted reminder. Then one can invoke this function whenever a -reminder is inserted into the database with the following trigger: +reminder is inserted into the database with the following trigger: -[grdb-add-function]: https://swiftpackageindex.com/groue/grdb.swift/v7.5.0/documentation/grdb/database/add(function:) +[sqlite-data-add-function]: https://swiftpackageindex.com/pointfreeco/sqlite-data/main/documentation/sqlitedata/database-52hin/add(function:)-1z12a @Row { @Column { ```swift + @DatabaseFunction + func didInsertReminder(_ id: Int) { + // ... + } + Reminders.createTemporaryTrigger( after: .insert { new in - #sql("SELECT didInsertReminder(\(new.id))") + Values($didInsertReminder(new.id)) } ) ``` @@ -175,7 +180,7 @@ reminder is inserted into the database with the following trigger: AFTER INSERT ON "reminders" FOR EACH ROW BEGIN - SELECT didInsertReminder("new"."id") + SELECT "didInsertReminder"("new"."id") END ``` } diff --git a/Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift b/Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift index 0553b615..b0467349 100644 --- a/Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift +++ b/Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift @@ -143,5 +143,50 @@ extension SnapshotTests { """ } } + + @Test func defaults() { + assertMacro { + """ + @Selection struct Row { + var title = "" + @Column(as: [String].JSONRepresentation.self) + var notes: [String] = [] + } + """ + } expansion: { + """ + struct Row { + var title = "" + var notes: [String] = [] + + public struct Columns: StructuredQueriesCore._SelectedColumns { + public typealias QueryValue = Row + public let selection: [(aliasName: String, expression: StructuredQueriesCore.QueryFragment)] + public init( + title: some StructuredQueriesCore.QueryExpression = StructuredQueriesCore.BindQueryExpression(""), + notes: some StructuredQueriesCore.QueryExpression<[String].JSONRepresentation> = StructuredQueriesCore.BindQueryExpression([]) + ) { + self.selection = [("title", title.queryFragment), ("notes", notes.queryFragment)] + } + } + } + + extension Row: StructuredQueriesCore._Selection { + public init(decoder: inout some StructuredQueriesCore.QueryDecoder) throws { + let title = try decoder.decode(Swift.String.self) + let notes = try decoder.decode([String].JSONRepresentation.self) + guard let title else { + throw QueryDecodingError.missingRequiredColumn + } + guard let notes else { + throw QueryDecodingError.missingRequiredColumn + } + self.title = title + self.notes = notes + } + } + """ + } + } } }