diff --git a/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md b/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md index 53847eb7..20be5971 100644 --- a/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md +++ b/Sources/StructuredQueriesSQLiteCore/Documentation.docc/Articles/Triggers.md @@ -188,6 +188,7 @@ reminder is inserted into the database with the following trigger: - ``StructuredQueriesCore/Table/createTemporaryTrigger(_:ifNotExists:after:fileID:line:column:)`` - ``StructuredQueriesCore/Table/createTemporaryTrigger(_:ifNotExists:before:fileID:line:column:)`` +- ``StructuredQueriesCore/Table/createTemporaryTrigger(_:ifNotExists:insteadOf:fileID:line:column:)`` ### Touching records diff --git a/Sources/StructuredQueriesSQLiteCore/Triggers.swift b/Sources/StructuredQueriesSQLiteCore/Triggers.swift index ce9bfcfc..64edf5b6 100644 --- a/Sources/StructuredQueriesSQLiteCore/Triggers.swift +++ b/Sources/StructuredQueriesSQLiteCore/Triggers.swift @@ -74,6 +74,42 @@ extension Table { ) } + /// A `CREATE TEMPORARY TRIGGER` statement that executes instead of a database view event. + /// + /// See for more information. + /// + /// > Important: A name for the trigger is automatically derived from the arguments if one is not + /// > provided. If you build your own trigger helper that call this function, then your helper + /// > should also take `fileID`, `line` and `column` arguments and pass them to this function. + /// + /// - Parameters: + /// - name: The trigger's name. By default a unique name is generated depending using the table, + /// operation, and source location. + /// - ifNotExists: Adds an `IF NOT EXISTS` clause to the `CREATE TRIGGER` statement. + /// - operation: The trigger's operation. + /// - fileID: The source `#fileID` associated with the trigger. + /// - line: The source `#line` associated with the trigger. + /// - column: The source `#column` associated with the trigger. + /// - Returns: A temporary trigger. + public static func createTemporaryTrigger( + _ name: String? = nil, + ifNotExists: Bool = false, + insteadOf operation: TemporaryTrigger.Operation, + fileID: StaticString = #fileID, + line: UInt = #line, + column: UInt = #column + ) -> TemporaryTrigger { + TemporaryTrigger( + name: name, + ifNotExists: ifNotExists, + operation: operation, + when: .insteadOf, + fileID: fileID, + line: line, + column: column + ) + } + /// A `CREATE TEMPORARY TRIGGER` statement that applies additional updates to a row that has just /// been updated. /// @@ -252,6 +288,7 @@ public struct TemporaryTrigger: Sendable, Statement { fileprivate enum When: QueryFragment { case before = "BEFORE" case after = "AFTER" + case insteadOf = "INSTEAD OF" } /// The database event used in a trigger. diff --git a/Tests/StructuredQueriesTests/ViewsTests.swift b/Tests/StructuredQueriesTests/ViewsTests.swift index 131df7da..f978ab44 100644 --- a/Tests/StructuredQueriesTests/ViewsTests.swift +++ b/Tests/StructuredQueriesTests/ViewsTests.swift @@ -54,6 +54,59 @@ extension SnapshotTests { └────────────────────────┘ """ } + assertQuery( + CompletedReminder.createTemporaryTrigger( + insteadOf: .insert { new in + Reminder.insert { + ($0.title, $0.isCompleted, $0.remindersListID) + } values: { + (new.title, true, 1) + } + } + ) + ) { + """ + CREATE TEMPORARY TRIGGER + "after_insert_on_completedReminders@StructuredQueriesTests/ViewsTests.swift:58:49" + INSTEAD OF INSERT ON "completedReminders" + FOR EACH ROW BEGIN + INSERT INTO "reminders" + ("title", "isCompleted", "remindersListID") + VALUES + ("new"."title", 1, 1); + END + """ + } + assertQuery( + CompletedReminder.insert(\.title) { "Already done" } + ) { + """ + INSERT INTO "completedReminders" + ("title") + VALUES + ('Already done') + """ + } + // NB: Can't use 'RETURNING' above due to a SQLite bug where 'reminderID' is 'NULL'. + assertQuery( + CompletedReminder.order { $0.reminderID.desc() }.limit(1) + ) { + """ + SELECT "completedReminders"."reminderID", "completedReminders"."title" + FROM "completedReminders" + ORDER BY "completedReminders"."reminderID" DESC + LIMIT 1 + """ + } results: { + """ + ┌─────────────────────────┐ + │ CompletedReminder( │ + │ reminderID: 11, │ + │ title: "Already done" │ + │ ) │ + └─────────────────────────┘ + """ + } assertQuery( query.drop() ) {