diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 838af7f77..f971a4c54 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -71,6 +71,8 @@ jobs: VALIDATOR_SUBSPEC: SQLCipher run: ./run-tests.sh - name: "Run tests (tuist)" + env: + TUIST_USE_SWIFTERPM: '0' run: | brew update brew install tuist diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift index 7048b68ea..dbe3aedaa 100644 --- a/Sources/SQLite/Typed/Operators.swift +++ b/Sources/SQLite/Typed/Operators.swift @@ -558,45 +558,45 @@ public func <=(lhs: V, rhs: Expression) -> Expression where } public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) BETWEEN ? AND ?)", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) } public func ~=(lhs: ClosedRange, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) BETWEEN ? AND ?", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) BETWEEN ? AND ?)", rhs.bindings + [lhs.lowerBound.datatypeValue, lhs.upperBound.datatypeValue]) } public func ~=(lhs: Range, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", + Expression("(\(rhs.template) >= ? AND \(rhs.template) < ?)", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: Range, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) >= ? AND \(rhs.template) < ?", + Expression("(\(rhs.template) >= ? AND \(rhs.template) < ?)", rhs.bindings + [lhs.lowerBound.datatypeValue] + rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: PartialRangeThrough, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) <= ?)", rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: PartialRangeThrough, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) <= ?", rhs.bindings + [lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) <= ?)", rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: PartialRangeUpTo, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) < ?)", rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: PartialRangeUpTo, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) < ?", rhs.bindings + [lhs.upperBound.datatypeValue]) + Expression("(\(rhs.template) < ?)", rhs.bindings + [lhs.upperBound.datatypeValue]) } public func ~=(lhs: PartialRangeFrom, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue]) + Expression("(\(rhs.template) >= ?)", rhs.bindings + [lhs.lowerBound.datatypeValue]) } public func ~=(lhs: PartialRangeFrom, rhs: Expression) -> Expression where V.Datatype: Comparable & Value { - Expression("\(rhs.template) >= ?", rhs.bindings + [lhs.lowerBound.datatypeValue]) + Expression("(\(rhs.template) >= ?)", rhs.bindings + [lhs.lowerBound.datatypeValue]) } // MARK: - diff --git a/Tests/SQLiteTests/Schema/SchemaTests.swift b/Tests/SQLiteTests/Schema/SchemaTests.swift index 4f4a49d1d..b291d97e1 100644 --- a/Tests/SQLiteTests/Schema/SchemaTests.swift +++ b/Tests/SQLiteTests/Schema/SchemaTests.swift @@ -290,6 +290,17 @@ class SchemaTests: XCTestCase { ) } + // https://github.com/stephencelis/SQLite.swift/issues/1056 + // A column-level CHECK built from a range pattern (BETWEEN) must wrap its + // condition in parentheses, just like every other check condition does, + // otherwise SQLite rejects the generated `CREATE TABLE` statement. + func test_column_withRangeCheck_compilesValidCheckConstraint() { + XCTAssertEqual( + "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL CHECK (\"int64\" BETWEEN 0 AND 26))", + table.create { t in t.column(int64, check: 0...26 ~= int64) } + ) + } + func test_column_withIntegerExpression_compilesPrimaryKeyAutoincrementColumnDefinitionExpression() { XCTAssertEqual( "CREATE TABLE \"table\" (\"int64\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", diff --git a/Tests/SQLiteTests/Typed/OperatorsTests.swift b/Tests/SQLiteTests/Typed/OperatorsTests.swift index 7a5d83dac..f912860a3 100644 --- a/Tests/SQLiteTests/Typed/OperatorsTests.swift +++ b/Tests/SQLiteTests/Typed/OperatorsTests.swift @@ -274,38 +274,38 @@ class OperatorsTests: XCTestCase { } func test_patternMatchingOperator_withComparableCountableClosedRange_buildsBetweenBooleanExpression() { - assertSQL("\"int\" BETWEEN 0 AND 5", 0...5 ~= int) - assertSQL("\"intOptional\" BETWEEN 0 AND 5", 0...5 ~= intOptional) + assertSQL("(\"int\" BETWEEN 0 AND 5)", 0...5 ~= int) + assertSQL("(\"intOptional\" BETWEEN 0 AND 5)", 0...5 ~= intOptional) } func test_patternMatchingOperator_withComparableClosedRange_buildsBetweenBooleanExpression() { - assertSQL("\"double\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= double) - assertSQL("\"doubleOptional\" BETWEEN 1.2 AND 4.5", 1.2...4.5 ~= doubleOptional) + assertSQL("(\"double\" BETWEEN 1.2 AND 4.5)", 1.2...4.5 ~= double) + assertSQL("(\"doubleOptional\" BETWEEN 1.2 AND 4.5)", 1.2...4.5 ~= doubleOptional) } func test_patternMatchingOperator_withComparableRange_buildsBooleanExpression() { - assertSQL("\"double\" >= 1.2 AND \"double\" < 4.5", 1.2..<4.5 ~= double) - assertSQL("\"doubleOptional\" >= 1.2 AND \"doubleOptional\" < 4.5", 1.2..<4.5 ~= doubleOptional) + assertSQL("(\"double\" >= 1.2 AND \"double\" < 4.5)", 1.2..<4.5 ~= double) + assertSQL("(\"doubleOptional\" >= 1.2 AND \"doubleOptional\" < 4.5)", 1.2..<4.5 ~= doubleOptional) } func test_patternMatchingOperator_withComparablePartialRangeThrough_buildsBooleanExpression() { - assertSQL("\"double\" <= 4.5", ...4.5 ~= double) - assertSQL("\"doubleOptional\" <= 4.5", ...4.5 ~= doubleOptional) + assertSQL("(\"double\" <= 4.5)", ...4.5 ~= double) + assertSQL("(\"doubleOptional\" <= 4.5)", ...4.5 ~= doubleOptional) } func test_patternMatchingOperator_withComparablePartialRangeUpTo_buildsBooleanExpression() { - assertSQL("\"double\" < 4.5", ..<4.5 ~= double) - assertSQL("\"doubleOptional\" < 4.5", ..<4.5 ~= doubleOptional) + assertSQL("(\"double\" < 4.5)", ..<4.5 ~= double) + assertSQL("(\"doubleOptional\" < 4.5)", ..<4.5 ~= doubleOptional) } func test_patternMatchingOperator_withComparablePartialRangeFrom_buildsBooleanExpression() { - assertSQL("\"double\" >= 4.5", 4.5... ~= double) - assertSQL("\"doubleOptional\" >= 4.5", 4.5... ~= doubleOptional) + assertSQL("(\"double\" >= 4.5)", 4.5... ~= double) + assertSQL("(\"doubleOptional\" >= 4.5)", 4.5... ~= doubleOptional) } func test_patternMatchingOperator_withComparableClosedRangeString_buildsBetweenBooleanExpression() { - assertSQL("\"string\" BETWEEN 'a' AND 'b'", "a"..."b" ~= string) - assertSQL("\"stringOptional\" BETWEEN 'a' AND 'b'", "a"..."b" ~= stringOptional) + assertSQL("(\"string\" BETWEEN 'a' AND 'b')", "a"..."b" ~= string) + assertSQL("(\"stringOptional\" BETWEEN 'a' AND 'b')", "a"..."b" ~= stringOptional) } func test_doubleAndOperator_withBooleanExpressions_buildsCompoundExpression() { @@ -373,7 +373,7 @@ class OperatorsTests: XCTestCase { let begin = Date(timeIntervalSince1970: 0) let end = Date(timeIntervalSince1970: 5000) assertSQL( - "\"date\" >= '1970-01-01T00:00:00.000' AND \"date\" < '1970-01-01T01:23:20.000'", + "(\"date\" >= '1970-01-01T00:00:00.000' AND \"date\" < '1970-01-01T01:23:20.000')", (begin..