Skip to main content
DatabaseValue represents a value as stored in SQLite, preserving its exact storage type.

Overview

DatabaseValue wraps SQLite’s five storage classes:
let nullValue = DatabaseValue.null
let intValue = 42.databaseValue
let doubleValue = 3.14.databaseValue
let stringValue = "Arthur".databaseValue  
let blobValue = Data([1, 2, 3]).databaseValue
Most of the time you’ll work with Swift types directly. Use DatabaseValue when you need to preserve exact SQLite storage information.

Storage Classes

Storage Enum

The Storage enum represents SQLite’s five storage classes:
public enum Storage {
    case null
    case int64(Int64)
    case double(Double)
    case string(String)
    case blob(Data)
}

Accessing Storage

storage
DatabaseValue.Storage
The exact SQLite storage class and value
let value = 42.databaseValue
switch value.storage {
case .null:
    print("NULL")
case .int64(let int):
    print("Integer: \(int)")
case .double(let double):
    print("Real: \(double)")
case .string(let string):
    print("Text: \(string)")
case .blob(let data):
    print("Blob: \(data)")
}

Creating DatabaseValues

null

The NULL value:
let value = DatabaseValue.null

From DatabaseValueConvertible

Any type conforming to DatabaseValueConvertible can create a DatabaseValue:
let intValue = 42.databaseValue
let stringValue = "Arthur".databaseValue
let dateValue = Date().databaseValue
let boolValue = true.databaseValue

init(value:)

Creates a DatabaseValue from any value:
let value = DatabaseValue(value: 42)
let value = DatabaseValue(value: "text")
let value = DatabaseValue(value: Data())

init(sqliteStatement:index:)

Creates a value from a raw SQLite statement:
let value = DatabaseValue(sqliteStatement: statement, index: 0)

Checking for NULL

isNull

isNull
Bool
Whether the value is NULL
let value = DatabaseValue.null
print(value.isNull) // true

let value = 42.databaseValue
print(value.isNull) // false

Equality

DatabaseValue equality follows SQLite rules:
// Same types
1.databaseValue == 1.databaseValue // true
"text".databaseValue == "text".databaseValue // true

// Integer and Double are equal if they represent the same number
1.databaseValue == 1.0.databaseValue // true
1.databaseValue == 1.5.databaseValue // false

// NULL is never equal to anything, including itself
DatabaseValue.null == DatabaseValue.null // false

Storage Equality

For exact storage comparison, compare storage properties:
1.databaseValue.storage == 1.0.databaseValue.storage // false (int64 != double)

Fetching DatabaseValues

From Queries

try dbQueue.read { db in
    let value = try DatabaseValue.fetchOne(db, sql: """
        SELECT name FROM player WHERE id = 1
        """)
    
    if let value, !value.isNull {
        print("Name: \(value)")
    }
}

From Rows

let row = try Row.fetchOne(db, sql: "SELECT * FROM player")!
let nameValue: DatabaseValue = row["name"]

Converting from DatabaseValue

Convert DatabaseValue to Swift types:
let value = 42.databaseValue

if let int = Int.fromDatabaseValue(value) {
    print("Int: \(int)")
}

if let string = String.fromDatabaseValue(value) {
    print("String: \(string)")
}

Common Patterns

Handling Unknown Types

func handleValue(_ value: DatabaseValue) {
    switch value.storage {
    case .null:
        print("NULL")
    case .int64(let int):
        print("Integer: \(int)")
    case .double(let double):
        print("Real: \(double)")
    case .string(let string):
        print("Text: \(string)")
    case .blob(let data):
        print("Blob (\(data.count) bytes)")
    }
}

Extracting Values Safely

let value: DatabaseValue = row["score"]

if let score = Int.fromDatabaseValue(value) {
    print("Score: \(score)")
} else if value.isNull {
    print("No score")
} else {
    print("Unexpected type: \(value.storage)")
}

Working with Optional Values

let value: DatabaseValue? = row["deletedAt"]

if let value {
    if value.isNull {
        print("Not deleted (NULL)")
    } else if let date = Date.fromDatabaseValue(value) {
        print("Deleted at: \(date)")
    }
} else {
    print("Column doesn't exist")
}

Type Information

Storage Class

Get the storage class name:
func storageClassName(_ value: DatabaseValue) -> String {
    switch value.storage {
    case .null: return "NULL"
    case .int64: return "INTEGER"
    case .double: return "REAL"
    case .string: return "TEXT"
    case .blob: return "BLOB"
    }
}

Description

DatabaseValue provides custom string descriptions:
print(DatabaseValue.null) // NULL
print(42.databaseValue) // 42
print(3.14.databaseValue) // 3.14
print("text".databaseValue) // "text"
print(Data([1, 2]).databaseValue) // Data([0x01, 0x02])

Advanced Usage

Custom Types

Create DatabaseValue for custom types by conforming to DatabaseValueConvertible:
enum Status: String, DatabaseValueConvertible {
    case active, inactive
    
    var databaseValue: DatabaseValue {
        rawValue.databaseValue
    }
    
    static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Status? {
        guard let string = String.fromDatabaseValue(dbValue) else {
            return nil
        }
        return Status(rawValue: string)
    }
}

let value = Status.active.databaseValue

Binding to Statements

DatabaseValue can be bound to prepared statements:
let statement = try db.makeStatement(sql: """
    INSERT INTO player (name, score) VALUES (?, ?)
    """)

let values: [DatabaseValue] = [
    "Arthur".databaseValue,
    100.databaseValue
]

try statement.execute(arguments: StatementArguments(values))

Hashable Conformance

DatabaseValue is hashable and can be used in Sets and Dictionary keys:
let values: Set<DatabaseValue> = [
    1.databaseValue,
    "text".databaseValue,
    DatabaseValue.null
]

let dict: [DatabaseValue: String] = [
    1.databaseValue: "one",
    "key".databaseValue: "value"
]
The hash follows SQLite equality rules: 1.databaseValue and 1.0.databaseValue have the same hash.

Performance

For simple type conversions, using Swift types directly is more efficient than going through DatabaseValue:
// Better
let name: String = row["name"]

// Less efficient
let nameValue: DatabaseValue = row["name"]
let name = String.fromDatabaseValue(nameValue)

See Also

Build docs developers (and LLMs) love