Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,22 @@ public final class SQLCreateTriggerBuilder: SQLQueryBuilder {
self.createTrigger.orderTriggerName = otherTriggerName
return self
}


@inlinable
@discardableResult
public func orReplace(orReplace: Bool = true) -> Self {
self.createTrigger.orReplace = orReplace
return self
}


@inlinable
@discardableResult
public func ifNotExists(ifNotExists: Bool = true) -> Self {
self.createTrigger.ifNotExists = ifNotExists
return self
}
}

extension SQLDatabase {
Expand Down
10 changes: 10 additions & 0 deletions Sources/SQLKit/Database/SQLDialect.swift
Original file line number Diff line number Diff line change
Expand Up @@ -368,6 +368,16 @@ public struct SQLTriggerSyntax: Sendable {
public static var conditionRequiresParentheses: Self {
.init(rawValue: 1 << 9)
}

/// Indicates support for `OR REPLACE` syntax.
public static var supportsOrReplace: Self {
.init(rawValue: 1 << 10)
}

/// Indicates support for an `IF NOT EXISTS` syntax.
public static var supportsIfNotExists: Self {
.init(rawValue: 1 << 11)
}
}

/// Describes specific feature support for `CREATE TRIGGER` syntax.
Expand Down
23 changes: 22 additions & 1 deletion Sources/SQLKit/Expressions/Queries/SQLCreateTrigger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public struct SQLCreateTrigger: SQLExpression {

/// `true` if the new trigger will be a constraint trigger, if supported.
public var isConstraint: Bool

/// Include `OR REPLACE` to the trigger syntax.
public var orReplace: Bool

/// Whether to include `IF NOT EXISTS` syntax.
public var ifNotExists: Bool

/// The ordering of the trigger's execution relative to the triggering event.
///
Expand Down Expand Up @@ -122,6 +128,8 @@ public struct SQLCreateTrigger: SQLExpression {
self.when = when
self.event = event
self.isConstraint = false
self.orReplace = false
self.ifNotExists = false
}

/// Create a new trigger creation query.
Expand Down Expand Up @@ -162,10 +170,23 @@ public struct SQLCreateTrigger: SQLExpression {
if syntax.contains(.supportsConstraints), self.isConstraint {
$0.append("CONSTRAINT")
}

if self.orReplace, syntax.contains(.supportsOrReplace) {
$0.append("OR REPLACE")
}

if let definer = self.definer, syntax.contains(.supportsDefiner) {
$0.append("DEFINER =", definer)
}
$0.append("TRIGGER", self.name)

$0.append("TRIGGER")

if self.ifNotExists, syntax.contains(.supportsIfNotExists) {
$0.append("IF NOT EXISTS")
}

$0.append(self.name)

$0.append(self.when, self.event)
if let columns = self.columns, !columns.isEmpty, syntax.contains(.supportsUpdateColumns) {
$0.append("OF", SQLList(columns))
Expand Down
20 changes: 17 additions & 3 deletions Tests/SQLKitTests/SQLCreateDropTriggerTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ final class SQLCreateDropTriggerTests: XCTestCase {
}

func testMySqlTriggerCreates() {
self.db._dialect.triggerSyntax = .init(create: [.supportsBody, .supportsOrder, .supportsDefiner, .requiresForEachRow])
self.db._dialect.triggerSyntax = .init(create: [.supportsBody, .supportsOrder, .supportsDefiner, .requiresForEachRow, .supportsIfNotExists])

let builder = self.db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
.body(self.body.map { SQLRaw($0) })
Expand All @@ -44,20 +44,30 @@ final class SQLCreateDropTriggerTests: XCTestCase {
of: builder,
is: "CREATE DEFINER = 'foo@bar' TRIGGER ``foo`` BEFORE INSERT ON ``planet`` FOR EACH ROW PRECEDES ``other`` BEGIN \(self.body.joined(separator: " ")) END;"
)
XCTAssertSerialization(
of: self.db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
.ifNotExists(),
is: "CREATE TRIGGER IF NOT EXISTS ``foo`` BEFORE INSERT ON ``planet`` FOR EACH ROW"
)
}

func testSqliteTriggerCreates() {
self.db._dialect.triggerSyntax = .init(create: [.supportsBody, .supportsCondition])
self.db._dialect.triggerSyntax = .init(create: [.supportsBody, .supportsCondition, .supportsIfNotExists])
XCTAssertSerialization(
of: self.db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
.body(self.body.map { SQLRaw($0) })
.condition("\(ident: "foo") = \(ident: "bar")" as SQLQueryString),
is: "CREATE TRIGGER ``foo`` BEFORE INSERT ON ``planet`` WHEN ``foo`` = ``bar`` BEGIN \(self.body.joined(separator: " ")) END;"
)
XCTAssertSerialization(
of: self.db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
.ifNotExists(),
is: "CREATE TRIGGER IF NOT EXISTS ``foo`` BEFORE INSERT ON ``planet``"
)
}

func testPostgreSqlTriggerCreates() {
self.db._dialect.triggerSyntax = .init(create: [.supportsForEach, .postgreSQLChecks, .supportsCondition, .conditionRequiresParentheses, .supportsConstraints])
self.db._dialect.triggerSyntax = .init(create: [.supportsForEach, .postgreSQLChecks, .supportsCondition, .conditionRequiresParentheses, .supportsConstraints, .supportsOrReplace])
XCTAssertSerialization(
of: self.db.create(trigger: "foo", table: "planet", when: .after, event: .insert)
.each(.row)
Expand All @@ -80,6 +90,10 @@ final class SQLCreateDropTriggerTests: XCTestCase {
.procedure("qwer"),
is: "CREATE TRIGGER ``foo`` INSTEAD OF UPDATE ON ``planet`` FOR EACH ROW EXECUTE PROCEDURE ``qwer``"
)
XCTAssertSerialization(
of: self.db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
.orReplace(),
is: "CREATE OR REPLACE TRIGGER ``foo`` BEFORE INSERT ON ``planet``")
}

func testPostgreSqlTriggerCreateWithColumns() {
Expand Down