diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9fbba1c5..f82a9c59 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,19 +1,13 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: "iPhone 15"
-  IOS_VERSION: "17.2"
+  IOS_SIMULATOR: "iPhone 16"
+  IOS_VERSION: "18.0"
 jobs:
   build:
-    runs-on: macos-14
+    runs-on: macos-15
     steps:
     - uses: actions/checkout@v2
-    - name: "Select Xcode"
-      # Currently only works with Xcode 14.2:
-      # https://github.com/CocoaPods/CocoaPods/issues/11839
-      run: |
-        xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_15.3.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
@@ -82,3 +76,9 @@ jobs:
       env:
         SPM: run
       run: ./run-tests.sh
+  build-android:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Run tests
+      uses: skiptools/swift-android-action@v2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c96724fd..b77a8b07 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,10 @@
+0.15.4 (13-06-2025), [diff][diff-0.15.4]
+========================================
+* Fix cross compilation for linux on macOS fails ([#1317][])
+* Support creating tables in schema changer ([#1315][])
+* Update oldest supported platform versions ([#1280][])
+* Add CustomStringConvertible for Setter ([#1279][])
+
 0.15.3 (19-04-2024), [diff][diff-0.15.3]
 ========================================
 * Update `podspec` to include privacy manifest ([#1265][])
@@ -173,6 +180,8 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
 [diff-0.15.1]: https://github.com/stephencelis/SQLite.swift/compare/0.15.0...0.15.1
 [diff-0.15.2]: https://github.com/stephencelis/SQLite.swift/compare/0.15.1...0.15.2
+[diff-0.15.3]: https://github.com/stephencelis/SQLite.swift/compare/0.15.2...0.15.3
+[diff-0.15.4]: https://github.com/stephencelis/SQLite.swift/compare/0.15.3...0.15.4
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -259,3 +268,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
 [#1260]: https://github.com/stephencelis/SQLite.swift/pull/1260
 [#1265]: https://github.com/stephencelis/SQLite.swift/pull/1265
+[#1279]: https://github.com/stephencelis/SQLite.swift/pull/1279
+[#1280]: https://github.com/stephencelis/SQLite.swift/pull/1280
+[#1315]: https://github.com/stephencelis/SQLite.swift/pull/1315
+[#1317]: https://github.com/stephencelis/SQLite.swift/pull/1317
\ No newline at end of file
diff --git a/Documentation/Index.md b/Documentation/Index.md
index fcd2d642..0e8261f2 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -63,6 +63,7 @@
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
       - [Renaming/Dropping Tables](#renamingdropping-tables)
+      - [Creating Tables](#creating-tables)      
     - [Indexes](#indexes)
       - [Creating Indexes](#creating-indexes)
       - [Dropping Indexes](#dropping-indexes)
@@ -108,7 +109,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
   ]
   ```
 
@@ -129,7 +130,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.3
+    github "stephencelis/SQLite.swift" ~> 0.15.4
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +160,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.3'
+        pod 'SQLite.swift', '~> 0.15.4'
     end
     ```
 
@@ -173,7 +174,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.3'
+  pod 'SQLite.swift/standalone', '~> 0.15.4'
 end
 ```
 
@@ -183,7 +184,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.3'
+  pod 'SQLite.swift/standalone', '~> 0.15.4'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +200,7 @@ If you want to use [SQLCipher][] with SQLite.swift you can require the
 target 'YourAppTargetName' do
   # Make sure you only require the subspec, otherwise you app might link against
   # the system SQLite, which means the SQLCipher-specific methods won't work.
-  pod 'SQLite.swift/SQLCipher', '~> 0.15.3'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.4'
 end
 ```
 
@@ -1583,6 +1584,17 @@ try schemaChanger.rename(table: "users", to: "users_new")
 try schemaChanger.drop(table: "emails", ifExists: false)
 ```
 
+#### Creating Tables
+
+```swift
+let schemaChanger = SchemaChanger(connection: db)
+
+try schemaChanger.create(table: "users") { table in 
+    table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+    table.add(column: .init(name: "name", type: .TEXT, nullable: false))            
+}
+``` 
+
 ### Indexes
 
 
diff --git a/Documentation/Upgrading.md b/Documentation/Upgrading.md
index f2cc2ecb..0e12aacf 100644
--- a/Documentation/Upgrading.md
+++ b/Documentation/Upgrading.md
@@ -4,6 +4,8 @@
 
 - `Expression.asSQL()` is no longer available. Expressions now implement `CustomStringConvertible`,
   where `description` returns the SQL.
-- `Statement.prepareRowIterator()` is now longer available. Instead, use the methods
+- `Statement.prepareRowIterator()` is no longer available. Instead, use the methods
   of the same name on `Connection`.
 - `Connection.registerTokenizer` is no longer available to register custom FTS4 tokenizers.
+- `Setter.asSQL()` is no longer available. Instead, Setter now implement `CustomStringConvertible`,
+  where `description` returns the SQL.
diff --git a/Gemfile b/Gemfile
index eb6036cd..2770e85d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,4 @@
 source "https://rubygems.org"
 
-gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
+# https://github.com/CocoaPods/CocoaPods/pull/12816
+gem 'cocoapods', :git => 'https://github.com/jberkel/CocoaPods.git', branch: 'watchos-fourflusher'
diff --git a/Gemfile.lock b/Gemfile.lock
index a58783ea..7dac2c18 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,12 +1,12 @@
 GIT
-  remote: https://github.com/SagarSDagdu/CocoaPods.git
-  revision: d96f491f79abd2804d1359c5228cce404dd365b7
-  tag: 1.15.2.1-sagard
+  remote: https://github.com/jberkel/CocoaPods.git
+  revision: 32a90c184bc5dc9ec8b7b9b8ad08e98b7253dec2
+  branch: watchos-fourflusher
   specs:
-    cocoapods (1.15.2.1.pre.sagard)
+    cocoapods (1.16.2)
       addressable (~> 2.8)
       claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.15.2)
+      cocoapods-core (= 1.16.2)
       cocoapods-deintegrate (>= 1.0.3, < 2.0)
       cocoapods-downloader (>= 2.1, < 3.0)
       cocoapods-plugins (>= 1.0.0, < 2.0)
@@ -19,8 +19,8 @@ GIT
       gh_inspector (~> 1.0)
       molinillo (~> 0.8.0)
       nap (~> 1.0)
-      ruby-macho (>= 2.3.0, < 3.0)
-      xcodeproj (>= 1.23.0, < 2.0)
+      ruby-macho (~> 4.1.0)
+      xcodeproj (>= 1.27.0, < 2.0)
 
 GEM
   remote: https://rubygems.org/
@@ -29,26 +29,29 @@ GEM
       base64
       nkf
       rexml
-    activesupport (7.1.3.2)
+    activesupport (7.2.2.1)
       base64
+      benchmark (>= 0.3)
       bigdecimal
-      concurrent-ruby (~> 1.0, >= 1.0.2)
+      concurrent-ruby (~> 1.0, >= 1.3.1)
       connection_pool (>= 2.2.5)
       drb
       i18n (>= 1.6, < 2)
+      logger (>= 1.4.2)
       minitest (>= 5.1)
-      mutex_m
-      tzinfo (~> 2.0)
-    addressable (2.8.6)
-      public_suffix (>= 2.0.2, < 6.0)
+      securerandom (>= 0.3)
+      tzinfo (~> 2.0, >= 2.0.5)
+    addressable (2.8.7)
+      public_suffix (>= 2.0.2, < 7.0)
     algoliasearch (1.27.5)
       httpclient (~> 2.8, >= 2.8.3)
       json (>= 1.5.1)
     atomos (0.1.3)
     base64 (0.2.0)
-    bigdecimal (3.1.7)
+    benchmark (0.4.0)
+    bigdecimal (3.1.9)
     claide (1.1.0)
-    cocoapods-core (1.15.2)
+    cocoapods-core (1.16.2)
       activesupport (>= 5.0, < 8)
       addressable (~> 2.8)
       algoliasearch (~> 1.0)
@@ -68,41 +71,45 @@ GEM
       netrc (~> 0.11)
     cocoapods-try (1.2.0)
     colored2 (3.1.2)
-    concurrent-ruby (1.2.3)
-    connection_pool (2.4.1)
-    drb (2.2.1)
+    concurrent-ruby (1.3.5)
+    connection_pool (2.5.3)
+    drb (2.2.3)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
-    ffi (1.16.3)
+    ffi (1.17.2)
+    ffi (1.17.2-arm64-darwin)
     fourflusher (2.3.1)
     fuzzy_match (2.0.4)
     gh_inspector (1.1.3)
-    httpclient (2.8.3)
-    i18n (1.14.4)
+    httpclient (2.9.0)
+      mutex_m
+    i18n (1.14.7)
       concurrent-ruby (~> 1.0)
-    json (2.7.2)
-    minitest (5.22.3)
+    json (2.12.0)
+    logger (1.7.0)
+    minitest (5.25.5)
     molinillo (0.8.0)
-    mutex_m (0.2.0)
-    nanaimo (0.3.0)
+    mutex_m (0.3.0)
+    nanaimo (0.4.0)
     nap (1.1.0)
     netrc (0.11.0)
     nkf (0.2.0)
     public_suffix (4.0.7)
-    rexml (3.2.6)
-    ruby-macho (2.5.1)
+    rexml (3.4.1)
+    ruby-macho (4.1.0)
+    securerandom (0.4.1)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
     tzinfo (2.0.6)
       concurrent-ruby (~> 1.0)
-    xcodeproj (1.24.0)
+    xcodeproj (1.27.0)
       CFPropertyList (>= 2.3.3, < 4.0)
       atomos (~> 0.1.3)
       claide (>= 1.0.2, < 2.0)
       colored2 (~> 3.1)
-      nanaimo (~> 0.3.0)
-      rexml (~> 3.2.4)
+      nanaimo (~> 0.4.0)
+      rexml (>= 3.3.6, < 4.0)
 
 PLATFORMS
   arm64-darwin-23
@@ -112,4 +119,4 @@ DEPENDENCIES
   cocoapods!
 
 BUNDLED WITH
-   2.5.4
+   2.6.9
diff --git a/Makefile b/Makefile
index 73b33f97..52a25a12 100644
--- a/Makefile
+++ b/Makefile
@@ -35,7 +35,7 @@ lint: $(SWIFTLINT)
 	$< --strict
 
 lint-fix: $(SWIFTLINT)
-	$< lint fix
+	$< --fix
 
 clean:
 	$(XCODEBUILD) $(BUILD_ARGUMENTS) clean
diff --git a/Package.swift b/Package.swift
index 238661ae..56925d18 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,13 +1,45 @@
 // swift-tools-version:5.9
 import PackageDescription
 
+let deps: [Package.Dependency] = [
+    .github("swiftlang/swift-toolchain-sqlite", exact: "1.0.4")
+]
+
+let targets: [Target] = [
+    .target(
+        name: "SQLite",
+        dependencies: [
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows, .android]))
+        ],
+        exclude: [
+            "Info.plist"
+        ]
+    )
+]
+
+let testTargets: [Target] = [
+    .testTarget(
+        name: "SQLiteTests",
+        dependencies: [
+            "SQLite"
+        ],
+        path: "Tests/SQLiteTests",
+        exclude: [
+            "Info.plist"
+        ],
+        resources: [
+            .copy("Resources")
+        ]
+    )
+]
+
 let package = Package(
     name: "SQLite.swift",
     platforms: [
-        .iOS(.v11),
+        .iOS(.v12),
         .macOS(.v10_13),
         .watchOS(.v4),
-        .tvOS(.v11),
+        .tvOS(.v12),
         .visionOS(.v1)
     ],
     products: [
@@ -16,34 +48,13 @@ let package = Package(
             targets: ["SQLite"]
         )
     ],
-    targets: [
-        .target(
-            name: "SQLite",
-            exclude: [
-                "Info.plist"
-            ]
-        ),
-        .testTarget(
-            name: "SQLiteTests",
-            dependencies: [
-                "SQLite"
-            ],
-            path: "Tests/SQLiteTests",
-            exclude: [
-                "Info.plist"
-            ],
-            resources: [
-                .copy("Resources")
-            ]
-        )
-    ]
+    dependencies: deps,
+    targets: targets + testTargets
 )
 
-#if os(Linux)
-package.dependencies = [
-    .package(url: "https://github.com/stephencelis/CSQLite.git", from: "0.0.3")
-]
-package.targets.first?.dependencies += [
-    .product(name: "CSQLite", package: "CSQLite")
-]
-#endif
+extension Package.Dependency {
+
+    static func github(_ repo: String, exact ver: Version) -> Package.Dependency {
+        .package(url: "https://github.com/\(repo)", exact: ver)
+    }
+}
diff --git a/README.md b/README.md
index a4d713b6..a52b7ae5 100644
--- a/README.md
+++ b/README.md
@@ -42,9 +42,9 @@ do {
     let db = try Connection("path/to/db.sqlite3")
 
     let users = Table("users")
-    let id = Expression<Int64>("id")
-    let name = Expression<String?>("name")
-    let email = Expression<String>("email")
+    let id = SQLite.Expression<Int64>("id")
+    let name = SQLite.Expression<String?>("name")
+    let email = SQLite.Expression<String>("email")
 
     try db.run(users.create { t in
         t.column(id, primaryKey: true)
@@ -83,6 +83,9 @@ do {
 }
 ```
 
+Note that `Expression` should be written as `SQLite.Expression` to avoid
+conflicts with the `SwiftUI.Expression` if you are using SwiftUI too.
+
 SQLite.swift also works as a lightweight, Swift-friendly wrapper over the C
 API.
 
@@ -128,7 +131,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
   ]
   ```
 
@@ -152,7 +155,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.3
+    github "stephencelis/SQLite.swift" ~> 0.15.4
     ```
 
  3. Run `carthage update` and
@@ -183,7 +186,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.0'
+        pod 'SQLite.swift', '~> 0.15.0'
     end
     ```
 
@@ -226,8 +229,6 @@ device:
 
 ## Communication
 
-[See the planning document] for a roadmap and existing feature requests.
-
 [Read the contributing guidelines][]. The _TL;DR_ (but please; _R_):
 
  - Need **help** or have a **general question**? [Ask on Stack
@@ -235,7 +236,6 @@ device:
  - Found a **bug** or have a **feature request**? [Open an issue][].
  - Want to **contribute**? [Submit a pull request][].
 
-[See the planning document]: /Documentation/Planning.md
 [Read the contributing guidelines]: ./CONTRIBUTING.md#contributing
 [Ask on Stack Overflow]: https://stackoverflow.com/questions/tagged/sqlite.swift
 [Open an issue]: https://github.com/stephencelis/SQLite.swift/issues/new
@@ -266,7 +266,6 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
 
  - [GRDB](https://github.com/groue/GRDB.swift)
  - [SQLiteDB](https://github.com/FahimF/SQLiteDB)
- - [Squeal](https://github.com/nerdyc/Squeal)
 
 [Swift]: https://swift.org/
 [SQLite3]: https://www.sqlite.org
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 184ce23e..73c6f705 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -1,6 +1,6 @@
 Pod::Spec.new do |s|
   s.name             = "SQLite.swift"
-  s.version          = "0.15.3"
+  s.version          = "0.15.4"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
@@ -18,17 +18,11 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '12.0'
-  tvos_deployment_target = '12.0'
-  osx_deployment_target = '10.13'
-  watchos_deployment_target = '4.0'
-  visionos_deployment_target = '1.0'
-
-  s.ios.deployment_target = ios_deployment_target
-  s.tvos.deployment_target = tvos_deployment_target
-  s.osx.deployment_target = osx_deployment_target
-  s.watchos.deployment_target = watchos_deployment_target
-  s.visionos.deployment_target = visionos_deployment_target
+  s.ios.deployment_target = '12.0'
+  s.tvos.deployment_target = '12.0'
+  s.osx.deployment_target = '10.13'
+  s.watchos.deployment_target = '4.0'
+  s.visionos.deployment_target = '1.0'
 
   s.subspec 'standard' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
@@ -36,18 +30,9 @@ Pod::Spec.new do |s|
     ss.library = 'sqlite3'
     ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    ss.visionos.deployment_target = visionos_deployment_target
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 
@@ -62,22 +47,20 @@ Pod::Spec.new do |s|
     }
     ss.dependency 'sqlite3'
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    ss.visionos.deployment_target = visionos_deployment_target
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 
   s.subspec 'SQLCipher' do |ss|
+    # Disable unsupported visionOS
+    # https://github.com/sqlcipher/sqlcipher/issues/483
+    ss.ios.deployment_target = s.deployment_target(:ios)
+    ss.tvos.deployment_target = s.deployment_target(:tvos)
+    ss.osx.deployment_target = s.deployment_target(:osx)
+    ss.watchos.deployment_target = s.deployment_target(:watchos)
+
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
 
@@ -87,18 +70,9 @@ Pod::Spec.new do |s|
     }
     ss.dependency 'SQLCipher', '>= 4.0.0'
     
-    ss.ios.deployment_target = ios_deployment_target
-    ss.tvos.deployment_target = tvos_deployment_target
-    ss.osx.deployment_target = osx_deployment_target
-    ss.watchos.deployment_target = watchos_deployment_target
-    #ss.visionos.deployment_target = visionos_deployment_target # Not supported by SQLCipher for now
-
     ss.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
       test_spec.source_files = 'Tests/SQLiteTests/*.swift'
-      test_spec.ios.deployment_target = ios_deployment_target
-      test_spec.tvos.deployment_target = tvos_deployment_target
-      test_spec.osx.deployment_target = osx_deployment_target
     end
   end
 end
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index a2d7edbe..ec0423e9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1539,7 +1539,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1562,7 +1561,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = appletvos;
@@ -1614,7 +1612,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1639,7 +1636,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = watchos;
@@ -1664,7 +1660,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1689,7 +1684,6 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1778,6 +1772,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.4;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_NAME = "";
@@ -1837,6 +1832,7 @@
 				GCC_WARN_UNUSED_VARIABLE = YES;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
+				MARKETING_VERSION = 0.15.4;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
 				SDKROOT = iphoneos;
@@ -1866,7 +1862,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1890,7 +1885,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1940,7 +1934,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1966,7 +1959,6 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
-				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 0eebbdd5..023acc08 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -28,8 +28,8 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index 4eea76c3..a1abb74a 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -3,8 +3,8 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 8a25e51d..0c674ee6 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -3,8 +3,8 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index f2c3b781..57521f83 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -28,8 +28,8 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index ee59e5d1..9a72e4c5 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -2,8 +2,8 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 6cb2e5d3..82d535b9 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -26,8 +26,8 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index c27ccf05..e3c84589 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -26,8 +26,8 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index af7b5e27..6fae532a 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -43,19 +43,30 @@ public class SchemaChanger: CustomStringConvertible {
 
     public enum Operation {
         case addColumn(ColumnDefinition)
+        case addIndex(IndexDefinition, ifNotExists: Bool)
         case dropColumn(String)
+        case dropIndex(String, ifExists: Bool)
         case renameColumn(String, String)
         case renameTable(String)
+        case createTable(columns: [ColumnDefinition], ifNotExists: Bool)
 
         /// Returns non-nil if the operation can be executed with a simple SQL statement
         func toSQL(_ table: String, version: SQLiteVersion) -> String? {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
+            case .addIndex(let definition, let ifNotExists):
+                return definition.toSQL(ifNotExists: ifNotExists)
             case .renameColumn(let from, let to) where SQLiteFeature.renameColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) RENAME COLUMN \(from.quote()) TO \(to.quote())"
             case .dropColumn(let column) where SQLiteFeature.dropColumn.isSupported(by: version):
                 return "ALTER TABLE \(table.quote()) DROP COLUMN \(column.quote())"
+            case .dropIndex(let name, let ifExists):
+                return "DROP INDEX \(ifExists ? " IF EXISTS " : "") \(name.quote())"
+            case .createTable(let columns, let ifNotExists):
+                return "CREATE TABLE \(ifNotExists ? " IF NOT EXISTS " : "") \(table.quote()) (" +
+                    columns.map { $0.toSQL() }.joined(separator: ", ") +
+                ")"
             default: return nil
             }
         }
@@ -89,7 +100,7 @@ public class SchemaChanger: CustomStringConvertible {
     public class AlterTableDefinition {
         fileprivate var operations: [Operation] = []
 
-        let name: String
+        public let name: String
 
         init(name: String) {
             self.name = name
@@ -99,21 +110,73 @@ public class SchemaChanger: CustomStringConvertible {
             operations.append(.addColumn(column))
         }
 
+        public func add(index: IndexDefinition, ifNotExists: Bool = false) {
+            operations.append(.addIndex(index, ifNotExists: ifNotExists))
+        }
+
         public func drop(column: String) {
             operations.append(.dropColumn(column))
         }
 
+        public func drop(index: String, ifExists: Bool = false) {
+            operations.append(.dropIndex(index, ifExists: ifExists))
+        }
+
         public func rename(column: String, to: String) {
             operations.append(.renameColumn(column, to))
         }
     }
 
+    public class CreateTableDefinition {
+        fileprivate var columnDefinitions: [ColumnDefinition] = []
+        fileprivate var indexDefinitions: [IndexDefinition] = []
+
+        let name: String
+        let ifNotExists: Bool
+
+        init(name: String, ifNotExists: Bool) {
+            self.name = name
+            self.ifNotExists = ifNotExists
+        }
+
+        public func add(column: ColumnDefinition) {
+            columnDefinitions.append(column)
+        }
+
+        public func add<T>(expression: Expression<T>) where T: Value {
+            add(column: .init(name: columnName(for: expression), type: .init(expression: expression), nullable: false))
+        }
+
+        public func add<T>(expression: Expression<T?>) where T: Value {
+            add(column: .init(name: columnName(for: expression), type: .init(expression: expression), nullable: true))
+        }
+
+        public func add(index: IndexDefinition) {
+            indexDefinitions.append(index)
+        }
+
+        var operations: [Operation] {
+            precondition(!columnDefinitions.isEmpty)
+            return [
+                .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
+            ] + indexDefinitions.map { .addIndex($0, ifNotExists: ifNotExists) }
+        }
+
+        private func columnName<T>(for expression: Expression<T>) -> String {
+            switch LiteralValue(expression.template) {
+            case .stringLiteral(let string): return string
+            default: fatalError("expression is not a literal string value")
+            }
+        }
+    }
+
     private let connection: Connection
     private let schemaReader: SchemaReader
     private let version: SQLiteVersion
     static let tempPrefix = "tmp_"
     typealias Block = () throws -> Void
     public typealias AlterTableDefinitionBlock = (AlterTableDefinition) -> Void
+    public typealias CreateTableDefinitionBlock = (CreateTableDefinition) -> Void
 
     struct Options: OptionSet {
         let rawValue: Int
@@ -141,6 +204,15 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
+    public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
+        let createTableDefinition = CreateTableDefinition(name: table, ifNotExists: ifNotExists)
+        block(createTableDefinition)
+
+        for operation in createTableDefinition.operations {
+            try run(table: table, operation: operation)
+        }
+    }
+
     public func drop(table: String, ifExists: Bool = true) throws {
         try dropTable(table, ifExists: ifExists)
     }
@@ -151,6 +223,12 @@ public class SchemaChanger: CustomStringConvertible {
         try connection.run("ALTER TABLE \(table.quote()) RENAME TO \(to.quote())")
     }
 
+    // Runs arbitrary SQL. Should only be used if no predefined operations exist.
+    @discardableResult
+    public func run(_ sql: String, _ bindings: Binding?...) throws -> Statement {
+        return try connection.run(sql, bindings)
+    }
+
     private func run(table: String, operation: Operation) throws {
         try operation.validate()
 
@@ -263,7 +341,9 @@ extension TableDefinition {
     func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
+        case .createTable, .addIndex, .dropIndex: fatalError()
         case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
+
         case .dropColumn(let column):
             return TableDefinition(name: name,
                 columns: columns.filter { $0.name != column },
@@ -280,3 +360,13 @@ extension TableDefinition {
         }
     }
 }
+
+extension ColumnDefinition.Affinity {
+    init<T>(expression: Expression<T>) where T: Value {
+        self.init(T.declaredDatatype)
+    }
+
+    init<T>(expression: Expression<T?>) where T: Value {
+        self.init(T.declaredDatatype)
+    }
+}
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 80f9e199..d7803999 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -93,7 +93,7 @@ public struct ColumnDefinition: Equatable {
         // swiftlint:disable:next force_try
         static let pattern = try! NSRegularExpression(pattern: "PRIMARY KEY\\s*(?:ASC|DESC)?\\s*(?:ON CONFLICT (\\w+)?)?\\s*(AUTOINCREMENT)?")
 
-        init(autoIncrement: Bool = true, onConflict: OnConflict? = nil) {
+        public init(autoIncrement: Bool = true, onConflict: OnConflict? = nil) {
             self.autoIncrement = autoIncrement
             self.onConflict = onConflict
         }
@@ -117,17 +117,31 @@ public struct ColumnDefinition: Equatable {
     }
 
     public struct ForeignKey: Equatable {
-        let table: String
-        let column: String
-        let primaryKey: String?
+        let fromColumn: String
+        let toTable: String
+        // when null, use primary key of "toTable"
+        let toColumn: String?
         let onUpdate: String?
         let onDelete: String?
+
+        public init(toTable: String, toColumn: String? = nil, onUpdate: String? = nil, onDelete: String? = nil) {
+            self.init(fromColumn: "", toTable: toTable, toColumn: toColumn, onUpdate: onUpdate, onDelete: onDelete)
+        }
+
+        public init(fromColumn: String, toTable: String, toColumn: String? = nil, onUpdate: String? = nil, onDelete: String? = nil) {
+            self.fromColumn = fromColumn
+            self.toTable = toTable
+            self.toColumn = toColumn
+            self.onUpdate = onUpdate
+            self.onDelete = onDelete
+        }
     }
 
     public let name: String
     public let primaryKey: PrimaryKey?
     public let type: Affinity
     public let nullable: Bool
+    public let unique: Bool
     public let defaultValue: LiteralValue
     public let references: ForeignKey?
 
@@ -135,12 +149,14 @@ public struct ColumnDefinition: Equatable {
                 primaryKey: PrimaryKey? = nil,
                 type: Affinity,
                 nullable: Bool = true,
+                unique: Bool = false,
                 defaultValue: LiteralValue = .NULL,
                 references: ForeignKey? = nil) {
         self.name = name
         self.primaryKey = primaryKey
         self.type = type
         self.nullable = nullable
+        self.unique = unique
         self.defaultValue = defaultValue
         self.references = references
     }
@@ -244,16 +260,18 @@ public struct IndexDefinition: Equatable {
 
     public enum Order: String { case ASC, DESC }
 
-    public init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil, orders: [String: Order]? = nil) {
+    public init(table: String, name: String, unique: Bool = false, columns: [String], `where`: String? = nil,
+                orders: [String: Order]? = nil, origin: Origin? = nil) {
         self.table = table
         self.name = name
         self.unique = unique
         self.columns = columns
         self.where = `where`
         self.orders = orders
+        self.origin = origin
     }
 
-    init (table: String, name: String, unique: Bool, columns: [String], indexSQL: String?) {
+    init (table: String, name: String, unique: Bool, columns: [String], indexSQL: String?, origin: Origin? = nil) {
         func wherePart(sql: String) -> String? {
             IndexDefinition.whereRe.firstMatch(in: sql, options: [], range: NSRange(location: 0, length: sql.count)).map {
                 (sql as NSString).substring(with: $0.range(at: 1))
@@ -270,12 +288,16 @@ public struct IndexDefinition: Equatable {
                         return memo2
             }
         }
+
+        let orders = indexSQL.flatMap(orders)
+
         self.init(table: table,
                   name: name,
                   unique: unique,
                   columns: columns,
                   where: indexSQL.flatMap(wherePart),
-                  orders: indexSQL.flatMap(orders))
+                  orders: (orders?.isEmpty ?? false) ? nil : orders,
+                  origin: origin)
     }
 
     public let table: String
@@ -284,6 +306,13 @@ public struct IndexDefinition: Equatable {
     public let columns: [String]
     public let `where`: String?
     public let orders: [String: Order]?
+    public let origin: Origin?
+
+    public enum Origin: String {
+        case uniqueConstraint = "u" // index created from a "CREATE TABLE (... UNIQUE)" column constraint
+        case createIndex = "c"      // index created explicitly via "CREATE INDEX ..."
+        case primaryKey = "pk"      // index created from a "CREATE TABLE PRIMARY KEY" column constraint
+    }
 
     enum IndexError: LocalizedError {
         case tooLong(String, String)
@@ -297,6 +326,13 @@ public struct IndexDefinition: Equatable {
         }
     }
 
+    // Indices with names of the form "sqlite_autoindex_TABLE_N" that are used to implement UNIQUE and PRIMARY KEY
+    // constraints on ordinary tables.
+    // https://sqlite.org/fileformat2.html#intschema
+    var isInternal: Bool {
+        name.starts(with: "sqlite_autoindex_")
+    }
+
     func validate() throws {
         if name.count > IndexDefinition.maxIndexLength {
             throw IndexError.tooLong(name, table)
@@ -345,6 +381,7 @@ extension ColumnDefinition {
             defaultValue.map { "DEFAULT \($0)" },
             primaryKey.map { $0.toSQL() },
             nullable ? nil : "NOT NULL",
+            unique ? "UNIQUE" : nil,
             references.map { $0.toSQL() }
         ].compactMap { $0 }
          .joined(separator: " ")
@@ -376,8 +413,8 @@ extension ColumnDefinition.ForeignKey {
     func toSQL() -> String {
         ([
             "REFERENCES",
-            table.quote(),
-            primaryKey.map { "(\($0.quote()))" },
+            toTable.quote(),
+            toColumn.map { "(\($0.quote()))" },
             onUpdate.map { "ON UPDATE \($0)" },
             onDelete.map { "ON DELETE \($0)" }
         ] as [String?]).compactMap { $0 }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 1989ad6f..995cd4d7 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -19,9 +19,9 @@ public class SchemaReader {
         }
 
         let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
-            Dictionary(grouping: try foreignKeys(table: table), by: { $0.column })
+            Dictionary(grouping: try foreignKeys(table: table), by: { $0.fromColumn })
 
-        return try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
+        let columnDefinitions = try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
             .map { (row: Row) -> ColumnDefinition in
                 ColumnDefinition(
                     name: row[TableInfoTable.nameColumn],
@@ -29,10 +29,27 @@ public class SchemaReader {
                         try parsePrimaryKey(column: row[TableInfoTable.nameColumn]) : nil,
                     type: ColumnDefinition.Affinity(row[TableInfoTable.typeColumn]),
                     nullable: row[TableInfoTable.notNullColumn] == 0,
+                    unique: false,
                     defaultValue: LiteralValue(row[TableInfoTable.defaultValueColumn]),
                     references: foreignKeys[row[TableInfoTable.nameColumn]]?.first
                 )
             }
+
+        let internalIndexes = try indexDefinitions(table: table).filter { $0.isInternal }
+        return columnDefinitions.map { definition in
+            if let index = internalIndexes.first(where: { $0.columns.contains(definition.name) }), index.origin == .uniqueConstraint {
+
+                ColumnDefinition(name: definition.name,
+                                 primaryKey: definition.primaryKey,
+                                 type: definition.type,
+                                 nullable: definition.nullable,
+                                 unique: true,
+                                 defaultValue: definition.defaultValue,
+                                 references: definition.references)
+            } else {
+                definition
+            }
+        }
     }
 
     public func objectDefinitions(name: String? = nil,
@@ -66,27 +83,26 @@ public class SchemaReader {
                 .first
         }
 
-        func columns(name: String) throws -> [String] {
+        func indexInfos(name: String) throws -> [IndexInfo] {
             try connection.prepareRowIterator("PRAGMA index_info(\(name.quote()))")
                 .compactMap { row in
-                    row[IndexInfoTable.nameColumn]
+                    IndexInfo(name: row[IndexInfoTable.nameColumn],
+                              columnRank: row[IndexInfoTable.seqnoColumn],
+                              columnRankWithinTable: row[IndexInfoTable.cidColumn])
+
                 }
         }
 
         return try connection.prepareRowIterator("PRAGMA index_list(\(table.quote()))")
             .compactMap { row -> IndexDefinition? in
                 let name = row[IndexListTable.nameColumn]
-                guard !name.starts(with: "sqlite_") else {
-                    // Indexes SQLite creates implicitly for internal use start with "sqlite_".
-                    // See https://www.sqlite.org/fileformat2.html#intschema
-                    return nil
-                }
                 return IndexDefinition(
                     table: table,
                     name: name,
                     unique: row[IndexListTable.uniqueColumn] == 1,
-                    columns: try columns(name: name),
-                    indexSQL: try indexSQL(name: name)
+                    columns: try indexInfos(name: name).compactMap { $0.name },
+                    indexSQL: try indexSQL(name: name),
+                    origin: IndexDefinition.Origin(rawValue: row[IndexListTable.originColumn])
                 )
             }
     }
@@ -95,9 +111,9 @@ public class SchemaReader {
         try connection.prepareRowIterator("PRAGMA foreign_key_list(\(table.quote()))")
             .map { row in
                 ColumnDefinition.ForeignKey(
-                    table: row[ForeignKeyListTable.tableColumn],
-                    column: row[ForeignKeyListTable.fromColumn],
-                    primaryKey: row[ForeignKeyListTable.toColumn],
+                    fromColumn: row[ForeignKeyListTable.fromColumn],
+                    toTable: row[ForeignKeyListTable.tableColumn],
+                    toColumn: row[ForeignKeyListTable.toColumn],
                     onUpdate: row[ForeignKeyListTable.onUpdateColumn] == TableBuilder.Dependency.noAction.rawValue
                         ? nil : row[ForeignKeyListTable.onUpdateColumn],
                     onDelete: row[ForeignKeyListTable.onDeleteColumn] == TableBuilder.Dependency.noAction.rawValue
@@ -123,6 +139,15 @@ public class SchemaReader {
             objectDefinitions(name: name, type: .table, temp: true)
         ).compactMap(\.sql).first
     }
+
+    struct IndexInfo {
+        let name: String?
+        // The rank of the column within the index. (0 means left-most.)
+        let columnRank: Int
+        // The rank of the column within the table being indexed.
+        // A value of -1 means rowid and a value of -2 means that an expression is being used
+        let columnRankWithinTable: Int
+    }
 }
 
 private enum SchemaTable {
@@ -159,11 +184,12 @@ private enum TableInfoTable {
 
 private enum IndexInfoTable {
     // The rank of the column within the index. (0 means left-most.)
-    static let seqnoColumn = Expression<Int64>("seqno")
+    static let seqnoColumn = Expression<Int>("seqno")
     // The rank of the column within the table being indexed.
     // A value of -1 means rowid and a value of -2 means that an expression is being used.
-    static let cidColumn = Expression<Int64>("cid")
-    // The name of the column being indexed. This columns is NULL if the column is the rowid or an expression.
+    static let cidColumn = Expression<Int>("cid")
+    // The name of the column being indexed.
+    // This columns is NULL if the column is the rowid or an expression.
     static let nameColumn = Expression<String?>("name")
 }
 
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index 2a83665c..6162fcc7 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1055,7 +1055,7 @@ extension Connection {
             }
 
             func expandGlob(_ namespace: Bool) -> (QueryType) throws -> Void {
-                { (queryType: QueryType) throws -> Void in
+                { (queryType: QueryType) throws in
                     var query = type(of: queryType).init(queryType.clauses.from.name, database: queryType.clauses.from.database)
                     query.clauses.select = queryType.clauses.select
                     query.clauses.with = strip(queryType.clauses.with)
diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index e162cf34..919042ab 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -136,7 +136,7 @@ extension Table {
 
     // MARK: - CREATE INDEX
 
-    public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
+    public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
         let clauses: [Expressible?] = [
             create("INDEX", indexName(columns), unique ? .unique : nil, ifNotExists),
             Expression<Void>(literal: "ON"),
@@ -147,12 +147,20 @@ extension Table {
         return " ".join(clauses.compactMap { $0 }).asSQL()
     }
 
+    public func createIndex(_ columns: Expressible..., unique: Bool = false, ifNotExists: Bool = false) -> String {
+        return createIndex(Array(columns), unique: unique, ifNotExists: ifNotExists)
+    }
+
     // MARK: - DROP INDEX
 
-    public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
+    public func dropIndex(_ columns: [Expressible], ifExists: Bool = false) -> String {
         drop("INDEX", indexName(columns), ifExists)
     }
 
+    public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
+        dropIndex(Array(columns), ifExists: ifExists)
+    }
+
     fileprivate func indexName(_ columns: [Expressible]) -> Expressible {
         let string = (["index", clauses.from.name, "on"] + columns.map { $0.expression.template }).joined(separator: " ").lowercased()
 
diff --git a/Sources/SQLite/Typed/Setter.swift b/Sources/SQLite/Typed/Setter.swift
index 7910cab8..8dc8a0e0 100644
--- a/Sources/SQLite/Typed/Setter.swift
+++ b/Sources/SQLite/Typed/Setter.swift
@@ -75,6 +75,12 @@ extension Setter: Expressible {
 
 }
 
+extension Setter: CustomStringConvertible {
+    public var description: String {
+        asSQL()
+    }
+}
+
 public func <-<V: Value>(column: Expression<V>, value: Expression<V>) -> Setter {
     Setter(column: column, value: value)
 }
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 6521211a..a5a4afc4 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -15,7 +15,7 @@ let package = Package(
         // for testing from same repository
         .package(path: "../..")
         // normally this would be:
-        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.4")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index 940a30ca..0e185da5 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -6,8 +6,8 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
@@ -19,7 +19,7 @@ class ConnectionAttachTests: SQLiteTestCase {
         try db.attach(.inMemory, as: schemaName)
 
         let table = Table("attached_users", database: schemaName)
-        let name = Expression<String>("string")
+        let name = SQLite.Expression<String>("string")
 
         // create a table, insert some data
         try db.run(table.create { builder in
@@ -41,7 +41,7 @@ class ConnectionAttachTests: SQLiteTestCase {
         try db.attach(.uri(testDb, parameters: [.mode(.readOnly)]), as: schemaName)
 
         let table = Table("tests", database: schemaName)
-        let email = Expression<String>("email")
+        let email = SQLite.Expression<String>("email")
 
         let rows = try db.prepare(table.select(email)).map { $0[email] }
         XCTAssertEqual(["foo@bar.com"], rows)
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index 2bcdb6af..d1d4ab04 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -6,8 +6,8 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 6a1d94ae..da50974a 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -7,8 +7,8 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/CoreFunctionsTests.swift b/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
index e03e3769..b866c4e9 100644
--- a/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
+++ b/Tests/SQLiteTests/Core/CoreFunctionsTests.swift
@@ -12,8 +12,8 @@ class CoreFunctionsTests: XCTestCase {
     }
 
     func test_random_generatesExpressionWithRandomFunction() {
-        assertSQL("random()", Expression<Int64>.random())
-        assertSQL("random()", Expression<Int>.random())
+        assertSQL("random()", SQLite.Expression<Int64>.random())
+        assertSQL("random()", SQLite.Expression<Int>.random())
     }
 
     func test_length_wrapsStringExpressionWithLengthFunction() {
@@ -38,14 +38,14 @@ class CoreFunctionsTests: XCTestCase {
         assertSQL("(\"string\" LIKE '%\\%' ESCAPE '\\')", string.like("%\\%", escape: "\\"))
         assertSQL("(\"stringOptional\" LIKE '_\\_' ESCAPE '\\')", stringOptional.like("_\\_", escape: "\\"))
 
-        assertSQL("(\"string\" LIKE \"a\")", string.like(Expression<String>("a")))
-        assertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(Expression<String>("a")))
+        assertSQL("(\"string\" LIKE \"a\")", string.like(SQLite.Expression<String>("a")))
+        assertSQL("(\"stringOptional\" LIKE \"a\")", stringOptional.like(SQLite.Expression<String>("a")))
 
-        assertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(Expression<String>("a"), escape: "\\"))
-        assertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"string\" LIKE \"a\" ESCAPE '\\')", string.like(SQLite.Expression<String>("a"), escape: "\\"))
+        assertSQL("(\"stringOptional\" LIKE \"a\" ESCAPE '\\')", stringOptional.like(SQLite.Expression<String>("a"), escape: "\\"))
 
-        assertSQL("('string' LIKE \"a\")", "string".like(Expression<String>("a")))
-        assertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(Expression<String>("a"), escape: "\\"))
+        assertSQL("('string' LIKE \"a\")", "string".like(SQLite.Expression<String>("a")))
+        assertSQL("('string' LIKE \"a\" ESCAPE '\\')", "string".like(SQLite.Expression<String>("a"), escape: "\\"))
     }
 
     func test_glob_buildsExpressionWithGlobOperator() {
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index d3c8bb1f..03415f3a 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -6,8 +6,8 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 5f212505..3c90d941 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -5,8 +5,8 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
@@ -27,7 +27,7 @@ class StatementTests: SQLiteTestCase {
 
     func test_zero_sized_blob_returns_null() throws {
         let blobs = Table("blobs")
-        let blobColumn = Expression<Blob>("blob_column")
+        let blobColumn = SQLite.Expression<Blob>("blob_column")
         try db.run(blobs.create { $0.column(blobColumn) })
         try db.run(blobs.insert(blobColumn <- Blob(bytes: [])))
         let blobValue = try db.scalar(blobs.select(blobColumn).limit(1, offset: 0))
@@ -38,7 +38,7 @@ class StatementTests: SQLiteTestCase {
         let names = ["a", "b", "c"]
         try insertUsers(names)
 
-        let emailColumn = Expression<String>("email")
+        let emailColumn = SQLite.Expression<String>("email")
         let statement = try db.prepare("SELECT email FROM users")
         let emails = try statement.prepareRowIterator().map { $0[emailColumn] }
 
diff --git a/Tests/SQLiteTests/Extensions/FTS4Tests.swift b/Tests/SQLiteTests/Extensions/FTS4Tests.swift
index 5e595007..f7258fb5 100644
--- a/Tests/SQLiteTests/Extensions/FTS4Tests.swift
+++ b/Tests/SQLiteTests/Extensions/FTS4Tests.swift
@@ -35,9 +35,9 @@ class FTS4Tests: XCTestCase {
     }
 
     func test_match_onVirtualTableAsExpression_compilesMatchExpression() {
-        assertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as Expression<Bool>)
-        assertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as Expression<Bool>)
-        assertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as Expression<Bool?>)
+        assertSQL("(\"virtual_table\" MATCH 'string')", virtualTable.match("string") as SQLite.Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"string\")", virtualTable.match(string) as SQLite.Expression<Bool>)
+        assertSQL("(\"virtual_table\" MATCH \"stringOptional\")", virtualTable.match(stringOptional) as SQLite.Expression<Bool?>)
     }
 
     func test_match_onVirtualTableAsQueryType_compilesMatchExpression() {
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index da9ba8dc..b3b9e617 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -3,15 +3,15 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
 @testable import SQLite
 
 class FTSIntegrationTests: SQLiteTestCase {
-    let email = Expression<String>("email")
+    let email = SQLite.Expression<String>("email")
     let index = VirtualTable("index")
 
     private func createIndex() throws {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 9f8e21f4..f5a6de42 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -92,7 +92,7 @@ class SchemaChangerTests: SQLiteTestCase {
     }
 
     func test_add_column() throws {
-        let column = Expression<String>("new_column")
+        let column = SQLite.Expression<String>("new_column")
         let newColumn = ColumnDefinition(name: "new_column",
                                          type: .TEXT,
                                          nullable: true,
@@ -124,6 +124,78 @@ class SchemaChangerTests: SQLiteTestCase {
         }
     }
 
+    func test_add_index() throws {
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: .init(table: table.name, name: "age_index", unique: false, columns: ["age"], indexSQL: nil))
+        }
+
+        let indexes = try schema.indexDefinitions(table: "users").filter { !$0.isInternal }
+        XCTAssertEqual([
+            IndexDefinition(table: "users",
+                            name: "age_index",
+                            unique: false,
+                            columns: ["age"],
+                            where: nil,
+                            orders: nil,
+                            origin: .createIndex)
+        ], indexes)
+    }
+
+    func test_add_index_if_not_exists() throws {
+        let index = IndexDefinition(table: "users", name: "age_index", unique: false, columns: ["age"], indexSQL: nil)
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: index)
+        }
+
+        try schemaChanger.alter(table: "users") { table in
+            table.add(index: index, ifNotExists: true)
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.alter(table: "users") { table in
+                table.add(index: index, ifNotExists: false)
+            }
+        )
+    }
+
+    func test_drop_index() throws {
+        try db.execute("""
+            CREATE INDEX age_index ON users(age)
+        """)
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index")
+        }
+        let indexes = try schema.indexDefinitions(table: "users").filter { !$0.isInternal }
+        XCTAssertEqual(0, indexes.count)
+    }
+
+    func test_drop_index_if_exists() throws {
+        try db.execute("""
+            CREATE INDEX age_index ON users(age)
+        """)
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index")
+        }
+
+        try schemaChanger.alter(table: "users") { table in
+            table.drop(index: "age_index", ifExists: true)
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.alter(table: "users") { table in
+                table.drop(index: "age_index", ifExists: false)
+            }
+        ) { error in
+            if case Result.error(let message, _, _) =  error {
+                XCTAssertEqual(message, "no such index: age_index")
+            } else {
+                XCTFail("unexpected error \(error)")
+            }
+        }
+    }
+
     func test_drop_table() throws {
         try schemaChanger.drop(table: "users")
         XCTAssertThrowsError(try db.scalar(users.count)) { error in
@@ -154,4 +226,158 @@ class SchemaChangerTests: SQLiteTestCase {
         let users_new = Table("users_new")
         XCTAssertEqual((try db.scalar(users_new.count)) as Int, 1)
     }
+
+    func test_create_table() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "name", type: .TEXT, nullable: false, unique: true))
+            table.add(column: .init(name: "age", type: .INTEGER))
+
+            table.add(index: .init(table: table.name,
+                                   name: "nameIndex",
+                                   unique: true,
+                                   columns: ["name"],
+                                   where: nil,
+                                   orders: nil))
+        }
+
+        // make sure new table can be queried
+        let foo = Table("foo")
+        XCTAssertEqual((try db.scalar(foo.count)) as Int, 0)
+
+        let columns = try schema.columnDefinitions(table: "foo")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: true, onConflict: nil),
+                             type: .INTEGER,
+                             nullable: true,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "name",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             unique: true,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: true,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: nil)
+        ])
+
+        let indexes = try schema.indexDefinitions(table: "foo").filter { !$0.isInternal }
+        XCTAssertEqual(indexes, [
+            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil, origin: .createIndex)
+        ])
+    }
+
+    func test_create_table_add_column_expression() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(expression: SQLite.Expression<String>("name"))
+            table.add(expression: SQLite.Expression<Int>("age"))
+            table.add(expression: SQLite.Expression<Double?>("salary"))
+        }
+
+        let columns = try schema.columnDefinitions(table: "foo")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(name: "name",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "salary",
+                             primaryKey: nil,
+                             type: .REAL,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil)
+            ])
+    }
+
+    func test_create_table_if_not_exists() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        try schemaChanger.create(table: "foo", ifNotExists: true) { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        XCTAssertThrowsError(
+            try schemaChanger.create(table: "foo", ifNotExists: false) { table in
+                table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            }
+        ) { error in
+            if case Result.error(_, let code, _) = error {
+                XCTAssertEqual(code, 1)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
+
+    func test_create_table_if_not_exists_with_index() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "name", type: .TEXT))
+            table.add(index: .init(table: "foo", name: "name_index", unique: true, columns: ["name"], indexSQL: nil))
+        }
+
+        // ifNotExists needs to apply to index creation as well
+        try schemaChanger.create(table: "foo", ifNotExists: true) { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(index: .init(table: "foo", name: "name_index", unique: true, columns: ["name"], indexSQL: nil))
+        }
+    }
+
+    func test_create_table_with_foreign_key_reference() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+        }
+
+        try schemaChanger.create(table: "bars") { table in
+            table.add(column: .init(name: "id", primaryKey: .init(autoIncrement: true), type: .INTEGER))
+            table.add(column: .init(name: "foo_id",
+                                    type: .INTEGER,
+                                    nullable: false,
+                                    references: .init(toTable: "foo", toColumn: "id")))
+        }
+
+        let barColumns = try schema.columnDefinitions(table: "bars")
+
+        XCTAssertEqual([
+            ColumnDefinition(name: "id",
+                             primaryKey: .init(autoIncrement: true, onConflict: nil),
+                             type: .INTEGER,
+                             nullable: true,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: nil),
+
+            ColumnDefinition(name: "foo_id",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: false,
+                             unique: false,
+                             defaultValue: .NULL,
+                             references: .init(fromColumn: "foo_id", toTable: "foo", toColumn: "id", onUpdate: nil, onDelete: nil))
+        ], barColumns)
+    }
+
+    func test_run_arbitrary_sql() throws {
+        try schemaChanger.run("DROP TABLE users")
+        XCTAssertEqual(0, try schema.objectDefinitions(name: "users", type: .table).count)
+    }
 }
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index 8b7e27e3..1c648bd8 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -11,7 +11,7 @@ class ColumnDefinitionTests: XCTestCase {
 
         ("\"other_id\" INTEGER NOT NULL REFERENCES \"other_table\" (\"some_id\")",
         ColumnDefinition(name: "other_id", primaryKey: nil, type: .INTEGER, nullable: false, defaultValue: .NULL,
-                         references: .init(table: "other_table", column: "", primaryKey: "some_id", onUpdate: nil, onDelete: nil))),
+                         references: .init(fromColumn: "", toTable: "other_table", toColumn: "some_id", onUpdate: nil, onDelete: nil))),
 
         ("\"text\" TEXT",
         ColumnDefinition(name: "text", primaryKey: nil, type: .TEXT, nullable: true, defaultValue: .NULL, references: nil)),
@@ -245,9 +245,9 @@ class ForeignKeyDefinitionTests: XCTestCase {
     func test_toSQL() {
         XCTAssertEqual(
             ColumnDefinition.ForeignKey(
-                table: "foo",
-                column: "bar",
-                primaryKey: "bar_id",
+                fromColumn: "bar",
+                toTable: "foo",
+                toColumn: "bar_id",
                 onUpdate: nil,
                 onDelete: "SET NULL"
             ).toSQL(), """
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index d57ebff7..8963070f 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -18,41 +18,48 @@ class SchemaReaderTests: SQLiteTestCase {
                              primaryKey: .init(autoIncrement: false, onConflict: nil),
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "email",
                              primaryKey: nil,
                              type: .TEXT,
                              nullable: false,
+                             unique: true,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "age",
                              primaryKey: nil,
                              type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "salary",
                              primaryKey: nil,
                              type: .REAL,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil),
             ColumnDefinition(name: "admin",
                              primaryKey: nil,
                              type: .NUMERIC,
                              nullable: false,
+                             unique: false,
                              defaultValue: .numericLiteral("0"),
                              references: nil),
             ColumnDefinition(name: "manager_id",
                              primaryKey: nil, type: .INTEGER,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
-                             references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
+                             references: .init(fromColumn: "manager_id", toTable: "users", toColumn: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
                              type: .NUMERIC,
                              nullable: true,
+                             unique: false,
                              defaultValue: .NULL,
                              references: nil)
         ])
@@ -68,6 +75,24 @@ class SchemaReaderTests: SQLiteTestCase {
                 primaryKey: .init(autoIncrement: true, onConflict: .IGNORE),
                 type: .INTEGER,
                 nullable: true,
+                unique: false,
+                defaultValue: .NULL,
+                references: nil)
+            ]
+        )
+    }
+
+    func test_columnDefinitions_parses_unique() throws {
+        try db.run("CREATE TABLE t (name TEXT UNIQUE)")
+
+        let columns = try schemaReader.columnDefinitions(table: "t")
+        XCTAssertEqual(columns, [
+            ColumnDefinition(
+                name: "name",
+                primaryKey: nil,
+                type: .TEXT,
+                nullable: true,
+                unique: true,
                 defaultValue: .NULL,
                 references: nil)
             ]
@@ -128,13 +153,13 @@ class SchemaReaderTests: SQLiteTestCase {
     }
 
     func test_indexDefinitions_no_index() throws {
-        let indexes = try schemaReader.indexDefinitions(table: "users")
+        let indexes = try schemaReader.indexDefinitions(table: "users").filter { !$0.isInternal }
         XCTAssertTrue(indexes.isEmpty)
     }
 
     func test_indexDefinitions_with_index() throws {
         try db.run("CREATE UNIQUE INDEX index_users ON users (age DESC) WHERE age IS NOT NULL")
-        let indexes = try schemaReader.indexDefinitions(table: "users")
+        let indexes = try schemaReader.indexDefinitions(table: "users").filter { !$0.isInternal }
 
         XCTAssertEqual(indexes, [
             IndexDefinition(
@@ -143,7 +168,8 @@ class SchemaReaderTests: SQLiteTestCase {
                 unique: true,
                 columns: ["age"],
                 where: "age IS NOT NULL",
-                orders: ["age": .DESC]
+                orders: ["age": .DESC],
+                origin: .createIndex
             )
         ])
     }
@@ -163,12 +189,12 @@ class SchemaReaderTests: SQLiteTestCase {
 
         try db.run(linkTable.create(block: { definition in
             definition.column(idColumn, primaryKey: .autoincrement)
-            definition.column(testIdColumn, unique: false, check: nil, references: users, Expression<Int64>("id"))
+            definition.column(testIdColumn, unique: false, check: nil, references: users, SQLite.Expression<Int64>("id"))
         }))
 
         let foreignKeys = try schemaReader.foreignKeys(table: "test_links")
         XCTAssertEqual(foreignKeys, [
-            .init(table: "users", column: "test_id", primaryKey: "id", onUpdate: nil, onDelete: nil)
+            .init(fromColumn: "test_id", toTable: "users", toColumn: "id", onUpdate: nil, onDelete: nil)
         ])
     }
 
@@ -238,7 +264,7 @@ class SchemaReaderTests: SQLiteTestCase {
     }
 
     func test_objectDefinitions_indexes() throws {
-        let emailIndex = users.createIndex(Expression<String>("email"), unique: false, ifNotExists: true)
+        let emailIndex = users.createIndex(SQLite.Expression<String>("email"), unique: false, ifNotExists: true)
         try db.run(emailIndex)
 
         let indexes = try schemaReader.objectDefinitions(type: .index)
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index e2da5927..e62b2a23 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -74,29 +74,29 @@ class SQLiteTestCase: XCTestCase {
 
 }
 
-let bool = Expression<Bool>("bool")
-let boolOptional = Expression<Bool?>("boolOptional")
+let bool = SQLite.Expression<Bool>("bool")
+let boolOptional = SQLite.Expression<Bool?>("boolOptional")
 
-let data = Expression<Blob>("blob")
-let dataOptional = Expression<Blob?>("blobOptional")
+let data = SQLite.Expression<Blob>("blob")
+let dataOptional = SQLite.Expression<Blob?>("blobOptional")
 
-let date = Expression<Date>("date")
-let dateOptional = Expression<Date?>("dateOptional")
+let date = SQLite.Expression<Date>("date")
+let dateOptional = SQLite.Expression<Date?>("dateOptional")
 
-let double = Expression<Double>("double")
-let doubleOptional = Expression<Double?>("doubleOptional")
+let double = SQLite.Expression<Double>("double")
+let doubleOptional = SQLite.Expression<Double?>("doubleOptional")
 
-let int = Expression<Int>("int")
-let intOptional = Expression<Int?>("intOptional")
+let int = SQLite.Expression<Int>("int")
+let intOptional = SQLite.Expression<Int?>("intOptional")
 
-let int64 = Expression<Int64>("int64")
-let int64Optional = Expression<Int64?>("int64Optional")
+let int64 = SQLite.Expression<Int64>("int64")
+let int64Optional = SQLite.Expression<Int64?>("int64Optional")
 
-let string = Expression<String>("string")
-let stringOptional = Expression<String?>("stringOptional")
+let string = SQLite.Expression<String>("string")
+let stringOptional = SQLite.Expression<String?>("stringOptional")
 
-let uuid = Expression<UUID>("uuid")
-let uuidOptional = Expression<UUID?>("uuidOptional")
+let uuid = SQLite.Expression<UUID>("uuid")
+let uuidOptional = SQLite.Expression<UUID?>("uuidOptional")
 
 let testUUIDValue = UUID(uuidString: "E621E1F8-C36C-495A-93FC-0C247A3E6E5F")!
 
@@ -164,14 +164,4 @@ struct TestOptionalCodable: Codable, Equatable {
     let double: Double?
     let date: Date?
     let uuid: UUID?
-
-    init(int: Int?, string: String?, bool: Bool?, float: Float?, double: Double?, date: Date?, uuid: UUID?) {
-        self.int = int
-        self.string = string
-        self.bool = bool
-        self.float = float
-        self.double = double
-        self.date = date
-        self.uuid = uuid
-    }
 }
diff --git a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
index 71cbba9c..73a0767f 100644
--- a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
@@ -7,8 +7,8 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
index 0f46b380..8598b6fb 100644
--- a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
@@ -5,8 +5,8 @@ import SQLite
 #if !os(Linux)
 
 class CustomFunctionNoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = () -> Expression<String>
-    typealias FunctionResultOptional          = () -> Expression<String?>
+    typealias FunctionNoOptional              = () -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = () -> SQLite.Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) {
@@ -26,10 +26,10 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithOneArgTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>) -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>) -> Expression<String?>
-    typealias FunctionLeftResultOptional      = (Expression<String?>) -> Expression<String?>
+    typealias FunctionNoOptional              = (SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionLeftOptional            = (SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = (SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionLeftResultOptional      = (SQLite.Expression<String?>) -> SQLite.Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a in
@@ -65,14 +65,14 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>, Expression<String>) -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>) -> Expression<String>
-    typealias FunctionRightOptional           = (Expression<String>, Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>, Expression<String>) -> Expression<String?>
-    typealias FunctionLeftRightOptional       = (Expression<String?>, Expression<String?>) -> Expression<String>
-    typealias FunctionLeftResultOptional      = (Expression<String?>, Expression<String>) -> Expression<String?>
-    typealias FunctionRightResultOptional     = (Expression<String>, Expression<String?>) -> Expression<String?>
-    typealias FunctionLeftRightResultOptional = (Expression<String?>, Expression<String?>) -> Expression<String?>
+    typealias FunctionNoOptional              = (SQLite.Expression<String>, SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionLeftOptional            = (SQLite.Expression<String?>, SQLite.Expression<String>) -> SQLite.Expression<String>
+    typealias FunctionRightOptional           = (SQLite.Expression<String>, SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionResultOptional          = (SQLite.Expression<String>, SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionLeftRightOptional       = (SQLite.Expression<String?>, SQLite.Expression<String?>) -> SQLite.Expression<String>
+    typealias FunctionLeftResultOptional      = (SQLite.Expression<String?>, SQLite.Expression<String>) -> SQLite.Expression<String?>
+    typealias FunctionRightResultOptional     = (SQLite.Expression<String>, SQLite.Expression<String?>) -> SQLite.Expression<String?>
+    typealias FunctionLeftRightResultOptional = (SQLite.Expression<String?>, SQLite.Expression<String?>) -> SQLite.Expression<String?>
 
     func testNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) { a, b in
diff --git a/Tests/SQLiteTests/Typed/ExpressionTests.swift b/Tests/SQLiteTests/Typed/ExpressionTests.swift
index 147d62e9..1b97fe94 100644
--- a/Tests/SQLiteTests/Typed/ExpressionTests.swift
+++ b/Tests/SQLiteTests/Typed/ExpressionTests.swift
@@ -4,17 +4,17 @@ import XCTest
 class ExpressionTests: XCTestCase {
 
     func test_asSQL_expression_bindings() {
-        let expression = Expression<String>("foo ? bar", ["baz"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["baz"])
         XCTAssertEqual(expression.asSQL(), "foo 'baz' bar")
     }
 
     func test_asSQL_expression_bindings_quoting() {
-        let expression = Expression<String>("foo ? bar", ["'baz'"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["'baz'"])
         XCTAssertEqual(expression.asSQL(), "foo '''baz''' bar")
     }
 
     func test_expression_custom_string_convertible() {
-        let expression = Expression<String>("foo ? bar", ["baz"])
+        let expression = SQLite.Expression<String>("foo ? bar", ["baz"])
         XCTAssertEqual(expression.asSQL(), expression.description)
     }
 
@@ -24,12 +24,12 @@ class ExpressionTests: XCTestCase {
     }
 
     func test_init_literal() {
-        let expression = Expression<String>(literal: "literal")
+        let expression = SQLite.Expression<String>(literal: "literal")
         XCTAssertEqual(expression.template, "literal")
     }
 
     func test_init_identifier() {
-        let expression = Expression<String>("identifier")
+        let expression = SQLite.Expression<String>("identifier")
         XCTAssertEqual(expression.template, "\"identifier\"")
     }
 }
diff --git a/Tests/SQLiteTests/Typed/OperatorsTests.swift b/Tests/SQLiteTests/Typed/OperatorsTests.swift
index 370b910b..7a5d83da 100644
--- a/Tests/SQLiteTests/Typed/OperatorsTests.swift
+++ b/Tests/SQLiteTests/Typed/OperatorsTests.swift
@@ -356,7 +356,7 @@ class OperatorsTests: XCTestCase {
     }
 
     func test_precedencePreserved() {
-        let n = Expression<Int>(value: 1)
+        let n = SQLite.Expression<Int>(value: 1)
         assertSQL("(((1 = 1) AND (1 = 1)) OR (1 = 1))", (n == n && n == n) || n == n)
         assertSQL("((1 = 1) AND ((1 = 1) OR (1 = 1)))", n == n && (n == n || n == n))
     }
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 7ac72d0c..cde5a0c3 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -3,8 +3,8 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
@@ -12,9 +12,9 @@ import SQLite3
 
 class QueryIntegrationTests: SQLiteTestCase {
 
-    let id = Expression<Int64>("id")
-    let email = Expression<String>("email")
-    let age = Expression<Int>("age")
+    let id = SQLite.Expression<Int64>("id")
+    let email = SQLite.Expression<String>("email")
+    let age = SQLite.Expression<Int>("age")
 
     override func setUpWithError() throws {
         try super.setUpWithError()
@@ -24,7 +24,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     // MARK: -
 
     func test_select() throws {
-        let managerId = Expression<Int64>("manager_id")
+        let managerId = SQLite.Expression<Int64>("manager_id")
         let managers = users.alias("managers")
 
         let alice = try db.run(users.insert(email <- "alice@example.com"))
@@ -39,7 +39,7 @@ class QueryIntegrationTests: SQLiteTestCase {
         let names = ["a", "b", "c"]
         try insertUsers(names)
 
-        let emailColumn = Expression<String>("email")
+        let emailColumn = SQLite.Expression<String>("email")
         let emails = try db.prepareRowIterator(users).map { $0[emailColumn] }
 
         XCTAssertEqual(names.map({ "\($0)@example.com" }), emails.sorted())
@@ -55,7 +55,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     }
 
     func test_select_optional() throws {
-        let managerId = Expression<Int64?>("manager_id")
+        let managerId = SQLite.Expression<Int64?>("manager_id")
         let managers = users.alias("managers")
 
         let alice = try db.run(users.insert(email <- "alice@example.com"))
@@ -69,15 +69,15 @@ class QueryIntegrationTests: SQLiteTestCase {
     func test_select_codable() throws {
         let table = Table("codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int>("int"))
-            builder.column(Expression<String>("string"))
-            builder.column(Expression<Bool>("bool"))
-            builder.column(Expression<Double>("float"))
-            builder.column(Expression<Double>("double"))
-            builder.column(Expression<Date>("date"))
-            builder.column(Expression<UUID>("uuid"))
-            builder.column(Expression<String?>("optional"))
-            builder.column(Expression<Data>("sub"))
+            builder.column(SQLite.Expression<Int>("int"))
+            builder.column(SQLite.Expression<String>("string"))
+            builder.column(SQLite.Expression<Bool>("bool"))
+            builder.column(SQLite.Expression<Double>("float"))
+            builder.column(SQLite.Expression<Double>("double"))
+            builder.column(SQLite.Expression<Date>("date"))
+            builder.column(SQLite.Expression<UUID>("uuid"))
+            builder.column(SQLite.Expression<String?>("optional"))
+            builder.column(SQLite.Expression<Data>("sub"))
         })
 
         let value1 = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
@@ -133,13 +133,13 @@ class QueryIntegrationTests: SQLiteTestCase {
     func test_insert_many_encodables() throws {
         let table = Table("codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int?>("int"))
-            builder.column(Expression<String?>("string"))
-            builder.column(Expression<Bool?>("bool"))
-            builder.column(Expression<Double?>("float"))
-            builder.column(Expression<Double?>("double"))
-            builder.column(Expression<Date?>("date"))
-            builder.column(Expression<UUID?>("uuid"))
+            builder.column(SQLite.Expression<Int?>("int"))
+            builder.column(SQLite.Expression<String?>("string"))
+            builder.column(SQLite.Expression<Bool?>("bool"))
+            builder.column(SQLite.Expression<Double?>("float"))
+            builder.column(SQLite.Expression<Double?>("double"))
+            builder.column(SQLite.Expression<Date?>("date"))
+            builder.column(SQLite.Expression<UUID?>("uuid"))
         })
 
         let value1 = TestOptionalCodable(int: 5, string: "6", bool: true, float: 7, double: 8,
@@ -161,9 +161,9 @@ class QueryIntegrationTests: SQLiteTestCase {
 
         let table = Table("custom_codable")
         try db.run(table.create { builder in
-            builder.column(Expression<Int?>("myInt"))
-            builder.column(Expression<String?>("myString"))
-            builder.column(Expression<String?>("myOptionalArray"))
+            builder.column(SQLite.Expression<Int?>("myInt"))
+            builder.column(SQLite.Expression<String?>("myString"))
+            builder.column(SQLite.Expression<String?>("myOptionalArray"))
         })
 
         let customType = TestTypeWithOptionalArray(myInt: 13, myString: "foo", myOptionalArray: [1, 2, 3])
@@ -216,22 +216,22 @@ class QueryIntegrationTests: SQLiteTestCase {
         let actualIDs = try db.prepare(query1.union(query2)).map { $0[id] }
         XCTAssertEqual(expectedIDs, actualIDs)
 
-        let query3 = users.select(users[*], Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
-        let query4 = users.select(users[*], Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
+        let query3 = users.select(users[*], SQLite.Expression<Int>(literal: "1 AS weight")).filter(email == "sally@example.com")
+        let query4 = users.select(users[*], SQLite.Expression<Int>(literal: "2 AS weight")).filter(email == "alice@example.com")
 
-        let sql = query3.union(query4).order(Expression<Int>(literal: "weight")).asSQL()
+        let sql = query3.union(query4).order(SQLite.Expression<Int>(literal: "weight")).asSQL()
         XCTAssertEqual(sql,
         """
         SELECT "users".*, 1 AS weight FROM "users" WHERE ("email" = 'sally@example.com') UNION \
         SELECT "users".*, 2 AS weight FROM "users" WHERE ("email" = 'alice@example.com') ORDER BY weight
         """)
 
-        let orderedIDs = try db.prepare(query3.union(query4).order(Expression<Int>(literal: "weight"), email)).map { $0[id] }
+        let orderedIDs = try db.prepare(query3.union(query4).order(SQLite.Expression<Int>(literal: "weight"), email)).map { $0[id] }
         XCTAssertEqual(Array(expectedIDs.reversed()), orderedIDs)
     }
 
     func test_no_such_column() throws {
-        let doesNotExist = Expression<String>("doesNotExist")
+        let doesNotExist = SQLite.Expression<String>("doesNotExist")
         try insertUser("alice")
         let row = try db.pluck(users.filter(email == "alice@example.com"))!
 
@@ -272,15 +272,15 @@ class QueryIntegrationTests: SQLiteTestCase {
     // https://github.com/stephencelis/SQLite.swift/issues/285
     func test_order_by_random() throws {
         try insertUsers(["a", "b", "c'"])
-        let result = Array(try db.prepare(users.select(email).order(Expression<Int>.random()).limit(1)))
+        let result = Array(try db.prepare(users.select(email).order(SQLite.Expression<Int>.random()).limit(1)))
         XCTAssertEqual(1, result.count)
     }
 
     func test_with_recursive() throws {
         let nodes = Table("nodes")
-        let id = Expression<Int64>("id")
-        let parent = Expression<Int64?>("parent")
-        let value = Expression<Int64>("value")
+        let id = SQLite.Expression<Int64>("id")
+        let parent = SQLite.Expression<Int64?>("parent")
+        let value = SQLite.Expression<Int64>("value")
 
         try db.run(nodes.create { builder in
             builder.column(id)
@@ -320,7 +320,7 @@ class QueryIntegrationTests: SQLiteTestCase {
     /// Verify that `*` is properly expanded in a SELECT statement following a WITH clause.
     func test_with_glob_expansion() throws {
         let names = Table("names")
-        let name = Expression<String>("name")
+        let name = SQLite.Expression<String>("name")
         try db.run(names.create { builder in
             builder.column(email)
             builder.column(name)
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index f698c621..257b5245 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -3,8 +3,8 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
-import CSQLite
+#elseif canImport(SwiftToolchainCSQLite)
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
@@ -13,19 +13,19 @@ import SQLite3
 class QueryTests: XCTestCase {
 
     let users = Table("users")
-    let id = Expression<Int64>("id")
-    let email = Expression<String>("email")
-    let age = Expression<Int?>("age")
-    let admin = Expression<Bool>("admin")
-    let optionalAdmin = Expression<Bool?>("admin")
+    let id = SQLite.Expression<Int64>("id")
+    let email = SQLite.Expression<String>("email")
+    let age = SQLite.Expression<Int?>("age")
+    let admin = SQLite.Expression<Bool>("admin")
+    let optionalAdmin = SQLite.Expression<Bool?>("admin")
 
     let posts = Table("posts")
-    let userId = Expression<Int64>("user_id")
-    let categoryId = Expression<Int64>("category_id")
-    let published = Expression<Bool>("published")
+    let userId = SQLite.Expression<Int64>("user_id")
+    let categoryId = SQLite.Expression<Int64>("category_id")
+    let published = SQLite.Expression<Bool>("published")
 
     let categories = Table("categories")
-    let tag = Expression<String>("tag")
+    let tag = SQLite.Expression<String>("tag")
 
     func test_select_withExpression_compilesSelectClause() {
         assertSQL("SELECT \"email\" FROM \"users\"", users.select(email))
@@ -217,7 +217,7 @@ class QueryTests: XCTestCase {
     }
 
     func test_alias_aliasesTable() {
-        let managerId = Expression<Int64>("manager_id")
+        let managerId = SQLite.Expression<Int64>("manager_id")
 
         let managers = users.alias("managers")
 
@@ -422,7 +422,7 @@ class QueryTests: XCTestCase {
 
     func test_upsert_encodable() throws {
         let emails = Table("emails")
-        let string = Expression<String>("string")
+        let string = SQLite.Expression<String>("string")
         let value = TestCodable(int: 1, string: "2", bool: true, float: 3, double: 4,
                                 date: Date(timeIntervalSince1970: 0), uuid: testUUIDValue, optional: nil, sub: nil)
         let insert = try emails.upsert(value, onConflictOf: string)
diff --git a/Tests/SQLiteTests/Typed/RowTests.swift b/Tests/SQLiteTests/Typed/RowTests.swift
index 14fa373b..1aed00a7 100644
--- a/Tests/SQLiteTests/Typed/RowTests.swift
+++ b/Tests/SQLiteTests/Typed/RowTests.swift
@@ -5,49 +5,49 @@ class RowTests: XCTestCase {
 
     public func test_get_value() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<String>("foo"))
+        let result = try row.get(SQLite.Expression<String>("foo"))
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_subscript() {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = row[Expression<String>("foo")]
+        let result = row[SQLite.Expression<String>("foo")]
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<String?>("foo"))
+        let result = try row.get(SQLite.Expression<String?>("foo"))
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional_subscript() {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = row[Expression<String?>("foo")]
+        let result = row[SQLite.Expression<String?>("foo")]
 
         XCTAssertEqual("value", result)
     }
 
     public func test_get_value_optional_nil() throws {
         let row = Row(["\"foo\"": 0], [nil])
-        let result = try row.get(Expression<String?>("foo"))
+        let result = try row.get(SQLite.Expression<String?>("foo"))
 
         XCTAssertNil(result)
     }
 
     public func test_get_value_optional_nil_subscript() {
         let row = Row(["\"foo\"": 0], [nil])
-        let result = row[Expression<String?>("foo")]
+        let result = row[SQLite.Expression<String?>("foo")]
 
         XCTAssertNil(result)
     }
 
     public func test_get_type_mismatch_throws_unexpected_null_value() {
         let row = Row(["\"foo\"": 0], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("foo"))) { error in
             if case QueryError.unexpectedNullValue(let name) = error {
                 XCTAssertEqual("\"foo\"", name)
             } else {
@@ -58,13 +58,13 @@ class RowTests: XCTestCase {
 
     public func test_get_type_mismatch_optional_returns_nil() throws {
         let row = Row(["\"foo\"": 0], ["value"])
-        let result = try row.get(Expression<Int?>("foo"))
+        let result = try row.get(SQLite.Expression<Int?>("foo"))
         XCTAssertNil(result)
     }
 
     public func test_get_non_existent_column_throws_no_such_column() {
         let row = Row(["\"foo\"": 0], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("bar"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("bar"))) { error in
             if case QueryError.noSuchColumn(let name, let columns) = error {
                 XCTAssertEqual("\"bar\"", name)
                 XCTAssertEqual(["\"foo\""], columns)
@@ -76,7 +76,7 @@ class RowTests: XCTestCase {
 
     public func test_get_ambiguous_column_throws() {
         let row = Row(["table1.\"foo\"": 0, "table2.\"foo\"": 1], ["value"])
-        XCTAssertThrowsError(try row.get(Expression<Int>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<Int>("foo"))) { error in
             if case QueryError.ambiguousColumn(let name, let columns) = error {
                 XCTAssertEqual("\"foo\"", name)
                 XCTAssertEqual(["table1.\"foo\"", "table2.\"foo\""], columns.sorted())
@@ -107,7 +107,7 @@ class RowTests: XCTestCase {
         }
 
         let row = Row(["\"foo\"": 0], [Blob(bytes: [])])
-        XCTAssertThrowsError(try row.get(Expression<MyType>("foo"))) { error in
+        XCTAssertThrowsError(try row.get(SQLite.Expression<MyType>("foo"))) { error in
             if case MyType.MyError.failed = error {
                 XCTAssertTrue(true)
             } else {
diff --git a/Tests/SQLiteTests/Typed/SelectTests.swift b/Tests/SQLiteTests/Typed/SelectTests.swift
index 52d5bb6b..5fa3cd30 100644
--- a/Tests/SQLiteTests/Typed/SelectTests.swift
+++ b/Tests/SQLiteTests/Typed/SelectTests.swift
@@ -24,10 +24,10 @@ class SelectTests: SQLiteTestCase {
         let usersData = Table("users_name")
         let users = Table("users")
 
-        let name = Expression<String>("name")
-        let id = Expression<Int64>("id")
-        let userID = Expression<Int64>("user_id")
-        let email = Expression<String>("email")
+        let name = SQLite.Expression<String>("name")
+        let id = SQLite.Expression<Int64>("id")
+        let userID = SQLite.Expression<Int64>("user_id")
+        let email = SQLite.Expression<String>("email")
 
         try insertUser("Joey")
         try db.run(usersData.insert(
diff --git a/Tests/SQLiteTests/Typed/SetterTests.swift b/Tests/SQLiteTests/Typed/SetterTests.swift
index 938dd013..05da57a4 100644
--- a/Tests/SQLiteTests/Typed/SetterTests.swift
+++ b/Tests/SQLiteTests/Typed/SetterTests.swift
@@ -134,4 +134,7 @@ class SetterTests: XCTestCase {
         assertSQL("\"intOptional\" = (\"intOptional\" - 1)", intOptional--)
     }
 
+    func test_setter_custom_string_convertible() {
+        XCTAssertEqual("\"int\" = \"int\"", (int <- int).description)
+    }
 }
diff --git a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
index 6ded152b..01e88297 100644
--- a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
+++ b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
@@ -34,13 +34,13 @@ class WindowFunctionsTests: XCTestCase {
     func test_lag_wrapsExpressionWithOverClause() {
         assertSQL("lag(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lag(int.desc))
         assertSQL("lag(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 7, int.desc))
-        assertSQL("lag(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 1, default: Expression<Int>(value: 3), int.desc))
+        assertSQL("lag(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lag(offset: 1, default: SQLite.Expression<Int>(value: 3), int.desc))
     }
 
     func test_lead_wrapsExpressionWithOverClause() {
         assertSQL("lead(\"int\", 0) OVER (ORDER BY \"int\" DESC)", int.lead(int.desc))
         assertSQL("lead(\"int\", 7) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 7, int.desc))
-        assertSQL("lead(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 1, default: Expression<Int>(value: 3), int.desc))
+        assertSQL("lead(\"int\", 1, 3) OVER (ORDER BY \"int\" DESC)", int.lead(offset: 1, default: SQLite.Expression<Int>(value: 3), int.desc))
     }
 
     func test_firstValue_wrapsExpressionWithOverClause() {
diff --git a/run-tests.sh b/run-tests.sh
index 1bf8de96..3ffba810 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,15 +8,18 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     bundle install
-    if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      bundle exec pod lib lint --no-subspecs --fail-fast
-    else
-      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
-    fi
+    case "$VALIDATOR_SUBSPEC" in
+      none)
+        bundle exec pod lib lint --no-subspecs --fail-fast
+        ;;
+      *)
+        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+        ;;
+      esac
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"
 elif [ -n "$SPM" ]; then
-    cd Tests/SPM && swift ${SPM}
+    cd Tests/SPM && swift "${SPM}"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
     swift ${PACKAGE_MANAGER_COMMAND}
 fi
