Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions Sources/StructuredQueriesMacros/SelectionMacro.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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] = []
Expand Down Expand Up @@ -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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We unfortunately can't automatically do = #bind(nil) because outer-joined tables aren't bindable:

var reminder: Reminder?

But if/when we figure out #108 we could revisit this.


allColumns.append((identifier, columnQueryValueType, defaultValue))
let decodedType = columnQueryValueType?.asNonOptionalType()
decodings.append(
"""
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
)
```
Expand All @@ -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
```
}
Expand Down
45 changes: 45 additions & 0 deletions Tests/StructuredQueriesMacrosTests/SelectionMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<Swift.String> = 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
}
}
"""
}
}
}
}