Skip to content

Commit

Permalink
Fix column affinity parsing to match how SQLite determines affinity
Browse files Browse the repository at this point in the history
See https://www.sqlite.org/datatype3.html#determination_of_column_affinity
for how SQLite determines column affinity.
  • Loading branch information
stefansaasen committed Jun 7, 2023
1 parent 1b1eba0 commit fabbe8c
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 5 deletions.
14 changes: 13 additions & 1 deletion Sources/SQLite/Schema/SchemaDefinitions.swift
Expand Up @@ -57,7 +57,19 @@ public struct ColumnDefinition: Equatable {
}

init(_ string: String) {
self = Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? .TEXT
let test = string.uppercased()
// https://sqlite.org/datatype3.html#determination_of_column_affinity
if test.contains("INT") { // Rule 1
self = .INTEGER
} else if ["CHAR", "CLOB", "TEXT"].first(where: {test.contains($0)}) != nil { // Rule 2
self = .TEXT
} else if string.contains("BLOB") { // Rule 3
self = .BLOB
} else if ["REAL", "FLOA", "DOUB"].first(where: {test.contains($0)}) != nil { // Rule 4
self = .REAL
} else { // Rule 5
self = .NUMERIC
}
}
}

Expand Down
64 changes: 62 additions & 2 deletions Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
Expand Up @@ -71,8 +71,68 @@ class AffinityTests: XCTestCase {
XCTAssertEqual(ColumnDefinition.Affinity("NUMERIC"), .NUMERIC)
}

func test_returns_TEXT_for_unknown_type() {
XCTAssertEqual(ColumnDefinition.Affinity("baz"), .TEXT)
// [Determination Of Column Affinity](https://sqlite.org/datatype3.html#determination_of_column_affinity)
// Rule 1
func testIntegerAffinity() {
let declared = [
"INT",
"INTEGER",
"TINYINT",
"SMALLINT",
"MEDIUMINT",
"BIGINT",
"UNSIGNED BIG INT",
"INT2",
"INT8"
]
XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .INTEGER}))
}

// Rule 2
func testTextAffinity() {
let declared = [
"CHARACTER(20)",
"VARCHAR(255)",
"VARYING CHARACTER(255)",
"NCHAR(55)",
"NATIVE CHARACTER(70)",
"NVARCHAR(100)",
"TEXT",
"CLOB"
]
XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .TEXT}))
}

// Rule 3
func testBlobAffinity() {
XCTAssertEqual(ColumnDefinition.Affinity("BLOB"), .BLOB)
}

// Rule 4
func testRealAffinity() {
let declared = [
"REAL",
"DOUBLE",
"DOUBLE PRECISION",
"FLOAT"
]
XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .REAL}))
}

// Rule 5
func testNumericAffinity() {
let declared = [
"NUMERIC",
"DECIMAL(10,5)",
"BOOLEAN",
"DATE",
"DATETIME"
]
XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .NUMERIC}))
}

func test_returns_NUMERIC_for_unknown_type() {
XCTAssertEqual(ColumnDefinition.Affinity("baz"), .NUMERIC)
}
}

Expand Down
4 changes: 2 additions & 2 deletions Tests/SQLiteTests/Schema/SchemaReaderTests.swift
Expand Up @@ -40,7 +40,7 @@ class SchemaReaderTests: SQLiteTestCase {
references: nil),
ColumnDefinition(name: "admin",
primaryKey: nil,
type: .TEXT,
type: .NUMERIC,
nullable: false,
defaultValue: .numericLiteral("0"),
references: nil),
Expand All @@ -51,7 +51,7 @@ class SchemaReaderTests: SQLiteTestCase {
references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
ColumnDefinition(name: "created_at",
primaryKey: nil,
type: .TEXT,
type: .NUMERIC,
nullable: true,
defaultValue: .NULL,
references: nil)
Expand Down

0 comments on commit fabbe8c

Please sign in to comment.