Skip to content

Commit e94b3fa

Browse files
committed
wip
1 parent 09a677d commit e94b3fa

File tree

13 files changed

+1043
-921
lines changed

13 files changed

+1043
-921
lines changed

Sources/StructuredQueriesCore/Triggers.swift

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@ import Foundation
22
import IssueReporting
33

44
extension Table {
5-
/// A `CREATE TEMPORARY TRIGGER` statement.
5+
/// A `CREATE TEMPORARY TRIGGER` statement that executes after a database event.
66
///
7-
/// > Important: TODO: explain how implicit names are handled and how trigger helpers should always take file/line/column. and put in name/file/line/column parameters.
7+
/// > Important: A name for the trigger is automatically derived from the arguments if one is not
8+
/// > provided. If you build your own trigger helper that call this function, then your helper
9+
/// > should also take fileID, line and column arguments and pass them to this function.
810
///
911
/// - Parameters:
1012
/// - name: The trigger's name. By default a unique name is generated depending using the table,
@@ -27,14 +29,63 @@ extension Table {
2729
name: name,
2830
ifNotExists: ifNotExists,
2931
operation: operation,
32+
when: .after,
3033
fileID: fileID,
3134
line: line,
3235
column: column
3336
)
3437
}
3538

36-
// TODO: write tests on these below:
39+
/// A `CREATE TEMPORARY TRIGGER` statement that executes before a database event.
40+
///
41+
/// > Important: A name for the trigger is automatically derived from the arguments if one is not
42+
/// > provided. If you build your own trigger helper that call this function, then your helper
43+
/// > should also take fileID, line and column arguments and pass them to this function.
44+
///
45+
/// - Parameters:
46+
/// - name: The trigger's name. By default a unique name is generated depending using the table,
47+
/// operation, and source location.
48+
/// - ifNotExists: Adds an `IF NOT EXISTS` clause to the `CREATE TRIGGER` statement.
49+
/// - operation: The trigger's operation.
50+
/// - fileID: The source `#fileID` associated with the trigger.
51+
/// - line: The source `#line` associated with the trigger.
52+
/// - column: The source `#column` associated with the trigger.
53+
/// - Returns: A temporary trigger.
54+
public static func createTemporaryTrigger(
55+
_ name: String? = nil,
56+
ifNotExists: Bool = false,
57+
before operation: TemporaryTrigger<Self>.Operation,
58+
fileID: StaticString = #fileID,
59+
line: UInt = #line,
60+
column: UInt = #column
61+
) -> TemporaryTrigger<Self> {
62+
TemporaryTrigger(
63+
name: name,
64+
ifNotExists: ifNotExists,
65+
operation: operation,
66+
when: .before,
67+
fileID: fileID,
68+
line: line,
69+
column: column
70+
)
71+
}
3772

73+
/// A `CREATE TEMPORARY TRIGGER` statement that applies additional updates to a row that has just
74+
/// been updated.
75+
///
76+
/// > Important: A name for the trigger is automatically derived from the arguments if one is not
77+
/// > provided. If you build your own trigger helper that call this function, then your helper
78+
/// > should also take fileID, line and column arguments and pass them to this function.
79+
///
80+
/// - Parameters:
81+
/// - name: The trigger's name. By default a unique name is generated depending using the table,
82+
/// operation, and source location.
83+
/// - ifNotExists: Adds an `IF NOT EXISTS` clause to the `CREATE TRIGGER` statement.
84+
/// - updates: The updates to apply after the row has been updated.
85+
/// - fileID: The source `#fileID` associated with the trigger.
86+
/// - line: The source `#line` associated with the trigger.
87+
/// - column: The source `#column` associated with the trigger.
88+
/// - Returns: A temporary trigger.
3889
public static func createTemporaryTrigger(
3990
_ name: String? = nil,
4091
ifNotExists: Bool = false,
@@ -57,7 +108,22 @@ extension Table {
57108
)
58109
}
59110

60-
// TODO: Touchable protocol with Date: Touchable, UUID: Touchable, ?
111+
/// A `CREATE TEMPORARY TRIGGER` statement that updates a datetime column when a row has been updated.
112+
/// been updated.
113+
///
114+
/// > Important: A name for the trigger is automatically derived from the arguments if one is not
115+
/// > provided. If you build your own trigger helper that call this function, then your helper
116+
/// > should also take fileID, line and column arguments and pass them to this function.
117+
///
118+
/// - Parameters:
119+
/// - name: The trigger's name. By default a unique name is generated depending using the table,
120+
/// operation, and source location.
121+
/// - ifNotExists: Adds an `IF NOT EXISTS` clause to the `CREATE TRIGGER` statement.
122+
/// - date: A key path to a datetime column.
123+
/// - fileID: The source `#fileID` associated with the trigger.
124+
/// - line: The source `#line` associated with the trigger.
125+
/// - column: The source `#column` associated with the trigger.
126+
/// - Returns: A temporary trigger.
61127
public static func createTemporaryTrigger(
62128
_ name: String? = nil,
63129
ifNotExists: Bool = false,
@@ -77,17 +143,18 @@ extension Table {
77143
column: column
78144
)
79145
}
80-
81-
// TODO: createTemporaryTrigger(afterUpdateTouch: \.updatedAt)
82-
// TODO: createTemporaryTrigger(afterUpdate: { $0... }, touch: { $0... = })
83-
// TODO: createTemporaryTrigger(afterUpdate: \.self, touch: \.updatedAt)
84146
}
85147

86148
public struct TemporaryTrigger<On: Table>: Statement {
87149
public typealias From = Never
88150
public typealias Joins = ()
89151
public typealias QueryValue = ()
90152

153+
fileprivate enum When: String {
154+
case before = "BEFORE"
155+
case after = "AFTER"
156+
}
157+
91158
public struct Operation: QueryExpression {
92159
public typealias QueryValue = ()
93160

@@ -181,14 +248,14 @@ public struct TemporaryTrigger<On: Table>: Statement {
181248
private let when: QueryFragment?
182249

183250
public var queryFragment: QueryFragment {
184-
var query: QueryFragment = "AFTER"
251+
var query: QueryFragment = ""
185252
let statement: QueryFragment
186253
switch kind {
187254
case .insert(let begin):
188-
query.append(" INSERT")
255+
query.append("INSERT")
189256
statement = begin
190257
case .update(let begin, let columnNames):
191-
query.append(" UPDATE")
258+
query.append("UPDATE")
192259
if !columnNames.isEmpty {
193260
query.append(
194261
" OF \(columnNames.map { QueryFragment(quote: $0) }.joined(separator: ", "))"
@@ -220,6 +287,7 @@ public struct TemporaryTrigger<On: Table>: Statement {
220287
fileprivate let name: String?
221288
fileprivate let ifNotExists: Bool
222289
fileprivate let operation: Operation
290+
fileprivate let when: When
223291
fileprivate let fileID: StaticString
224292
fileprivate let line: UInt
225293
fileprivate let column: UInt
@@ -242,7 +310,8 @@ public struct TemporaryTrigger<On: Table>: Statement {
242310
if ifNotExists {
243311
query.append(" IF NOT EXISTS")
244312
}
245-
query.append("\(.newlineOrSpace)\(triggerName.indented())\(.newlineOrSpace)\(operation)")
313+
query.append("\(.newlineOrSpace)\(triggerName.indented())")
314+
query.append("\(.newlineOrSpace)\(raw: when.rawValue) \(operation)")
246315
return query.segments.reduce(into: QueryFragment()) {
247316
switch $1 {
248317
case .sql(let sql):

Tests/StructuredQueriesTests/CommonTableExpressionTests.swift

Lines changed: 6 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ extension SnapshotTests {
108108
.select { ($1.remindersListID, $0.title, !$0.isFlagged, true) }
109109
.limit(1)
110110
}
111-
.returning(\.self)
111+
.returning { ($0.id, $0.title) }
112112
}
113113
) {
114114
"""
@@ -123,23 +123,13 @@ extension SnapshotTests {
123123
FROM "incompleteReminders"
124124
JOIN "reminders" ON ("incompleteReminders"."title" = "reminders"."title")
125125
LIMIT 1
126-
RETURNING "id", "assignedUserID", "dueDate", "isCompleted", "isFlagged", "notes", "priority", "remindersListID", "title"
126+
RETURNING "id", "title"
127127
"""
128-
} results: {
128+
}results: {
129129
"""
130-
┌────────────────────────┐
131-
│ Reminder( │
132-
│ id: 11, │
133-
│ assignedUserID: nil, │
134-
│ dueDate: nil, │
135-
│ isCompleted: true, │
136-
│ isFlagged: true, │
137-
│ notes: "", │
138-
│ priority: nil, │
139-
│ remindersListID: 1, │
140-
│ title: "Groceries"
141-
│ ) │
142-
└────────────────────────┘
130+
┌────┬─────────────┐
131+
│ 11 │ "Groceries"
132+
└────┴─────────────┘
143133
"""
144134
}
145135
}

Tests/StructuredQueriesTests/DeleteTests.swift

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -47,23 +47,24 @@ extension SnapshotTests {
4747
"""
4848
DELETE FROM "reminders"
4949
WHERE ("reminders"."id" = 1)
50-
RETURNING "id", "assignedUserID", "dueDate", "isCompleted", "isFlagged", "notes", "priority", "remindersListID", "title"
51-
"""
52-
} results: {
53-
"""
54-
┌────────────────────────────────────────────┐
55-
│ Reminder( │
56-
│ id: 1, │
57-
│ assignedUserID: 1, │
58-
│ dueDate: Date(2001-01-01T00:00:00.000Z), │
59-
│ isCompleted: false, │
60-
│ isFlagged: false, │
61-
│ notes: "Milk, Eggs, Apples", │
62-
│ priority: nil, │
63-
│ remindersListID: 1, │
64-
│ title: "Groceries"
65-
│ ) │
66-
└────────────────────────────────────────────┘
50+
RETURNING "id", "assignedUserID", "dueDate", "isCompleted", "isFlagged", "notes", "priority", "remindersListID", "title", "updatedAt"
51+
"""
52+
}results: {
53+
"""
54+
┌─────────────────────────────────────────────┐
55+
│ Reminder( │
56+
│ id: 1, │
57+
│ assignedUserID: 1, │
58+
│ dueDate: Date(2001-01-01T00:00:00.000Z), │
59+
│ isCompleted: false, │
60+
│ isFlagged: false, │
61+
│ notes: "Milk, Eggs, Apples", │
62+
│ priority: nil, │
63+
│ remindersListID: 1, │
64+
│ title: "Groceries", │
65+
│ updatedAt: Date(2040-02-14T23:31:30.000Z) │
66+
│ ) │
67+
└─────────────────────────────────────────────┘
6768
"""
6869
}
6970
assertQuery(Reminder.count()) {

0 commit comments

Comments
 (0)