From 0d905d788e386854effc7133f1faf31fe9a3c9e2 Mon Sep 17 00:00:00 2001
From: "Pongsakorn Onsri(Ken)" <pongsakorn.onsri@nanameue.jp>
Date: Fri, 17 Mar 2023 17:21:31 +0700
Subject: [PATCH 01/95] fix Xcode build error

---
 Sources/SQLite/Core/Connection+Pragmas.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Core/Connection+Pragmas.swift b/Sources/SQLite/Core/Connection+Pragmas.swift
index 8f6d854b..2c4f0efb 100644
--- a/Sources/SQLite/Core/Connection+Pragmas.swift
+++ b/Sources/SQLite/Core/Connection+Pragmas.swift
@@ -7,7 +7,7 @@ public extension Connection {
     /// See SQLite [PRAGMA user_version](https://sqlite.org/pragma.html#pragma_user_version)
     var userVersion: UserVersion? {
         get {
-            (try? scalar("PRAGMA user_version") as? Int64).map(Int32.init)
+            (try? scalar("PRAGMA user_version") as? Int64)?.map(Int32.init)
         }
         set {
             _ = try? run("PRAGMA user_version = \(newValue ?? 0)")

From 5f96ca46c1843f26c040b467d9f87519f03a0888 Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Fri, 7 Apr 2023 15:41:11 +0200
Subject: [PATCH 02/95] Make ithe IndexDefinition properties public

The [documentation](https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md#querying-the-schema)
suggests that the index schema information can be queried. That requires
the `IndexDefinition` properties to be public.
---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 284fc4c3..b314e5c0 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -266,12 +266,12 @@ public struct IndexDefinition: Equatable {
                   orders: indexSQL.flatMap(orders))
     }
 
-    let table: String
-    let name: String
-    let unique: Bool
-    let columns: [String]
-    let `where`: String?
-    let orders: [String: Order]?
+    public let table: String
+    public let name: String
+    public let unique: Bool
+    public let columns: [String]
+    public let `where`: String?
+    public let orders: [String: Order]?
 
     enum IndexError: LocalizedError {
         case tooLong(String, String)

From 0718088b7885dfb08f8930111e13aa12fa9a55c6 Mon Sep 17 00:00:00 2001
From: fwcd <fwcdmail@gmail.com>
Date: Fri, 21 Apr 2023 16:10:41 +0200
Subject: [PATCH 03/95] Fix GitHub Actions build badge

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 4cf72d03..90333b16 100644
--- a/README.md
+++ b/README.md
@@ -275,7 +275,7 @@ Looking for something else? Try another Swift wrapper (or [FMDB][]):
 [SQLite3]: https://www.sqlite.org
 [SQLite.swift]: https://github.com/stephencelis/SQLite.swift
 
-[GitHubActionBadge]: https://img.shields.io/github/workflow/status/stephencelis/SQLite.swift/Build%20and%20test
+[GitHubActionBadge]: https://img.shields.io/github/actions/workflow/status/stephencelis/SQLite.swift/build.yml?branch=master
 
 [CocoaPodsVersionBadge]: https://img.shields.io/cocoapods/v/SQLite.swift.svg?style=flat
 [CocoaPodsVersionLink]: https://cocoapods.org/pods/SQLite.swift

From 481b531c6906e11d8efcc7c36ea89b7e572e6ce7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 18 Oct 2022 22:14:04 +0200
Subject: [PATCH 04/95] Run on macOS 12

Bump deployment targets

Disable watchOS validation

CocoaPods/CocoaPods#11558

Adjust deployment targets for Xcode 14
---
 .github/workflows/build.yml      |  4 ++--
 Package.swift                    |  8 ++++----
 SQLite.swift.podspec             |  8 ++++----
 SQLite.xcodeproj/project.pbxproj | 30 ++++++++++++++++++++++--------
 Tests/SPM/Package.swift          |  8 ++++----
 run-tests.sh                     |  4 ++--
 6 files changed, 38 insertions(+), 24 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d1659d9d..beec5ca2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,10 +2,10 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: "15.2"
+  IOS_VERSION: "16.0"
 jobs:
   build:
-    runs-on: macos-11
+    runs-on: macos-12
     steps:
     - uses: actions/checkout@v2
     - name: Install
diff --git a/Package.swift b/Package.swift
index 9664e99a..de40367a 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,10 +4,10 @@ import PackageDescription
 let package = Package(
     name: "SQLite.swift",
     platforms: [
-        .iOS(.v9),
-        .macOS(.v10_10),
-        .watchOS(.v3),
-        .tvOS(.v9)
+        .iOS(.v11),
+        .macOS(.v10_13),
+        .watchOS(.v4),
+        .tvOS(.v11)
     ],
     products: [
         .library(
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index f32282d3..4edddd67 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,10 +18,10 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '9.0'
-  tvos_deployment_target = '9.1'
-  osx_deployment_target = '10.10'
-  watchos_deployment_target = '3.0'
+  ios_deployment_target = '11.0'
+  tvos_deployment_target = '11.0'
+  osx_deployment_target = '10.13'
+  watchos_deployment_target = '4.0'
 
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 3d62e980..def32855 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1263,6 +1263,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Debug;
 		};
@@ -1284,6 +1285,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Release;
 		};
@@ -1297,6 +1299,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Debug;
 		};
@@ -1310,6 +1313,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 			};
 			name = Release;
 		};
@@ -1333,6 +1337,7 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Debug;
 		};
@@ -1356,6 +1361,7 @@
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
 				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Release;
 		};
@@ -1408,8 +1414,8 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				PRODUCT_NAME = "";
@@ -1417,10 +1423,10 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 9.1;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Debug;
 		};
@@ -1467,19 +1473,19 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
-				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
 				SDKROOT = iphoneos;
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 9.1;
+				TVOS_DEPLOYMENT_TARGET = 11.0;
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
-				WATCHOS_DEPLOYMENT_TARGET = 3.0;
+				WATCHOS_DEPLOYMENT_TARGET = 4.0;
 			};
 			name = Release;
 		};
@@ -1496,6 +1502,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1519,6 +1526,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1533,6 +1541,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1545,6 +1554,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1567,6 +1577,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1591,6 +1602,7 @@
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = macosx;
@@ -1608,6 +1620,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
@@ -1623,6 +1636,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
+				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = macosx;
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 3e329552..51f176f4 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -6,10 +6,10 @@ import PackageDescription
 let package = Package(
     name: "test",
     platforms: [
-        .iOS(.v9),
-        .macOS(.v10_10),
-        .watchOS(.v3),
-        .tvOS(.v9)
+        .iOS(.v11),
+        .macOS(.v10_13),
+        .watchOS(.v4),
+        .tvOS(.v11)
     ],
     dependencies: [
         // for testing from same repository
diff --git a/run-tests.sh b/run-tests.sh
index 49330c12..c54300ee 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,9 +8,9 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast
+      pod lib lint --no-subspecs --fail-fast --platforms=ios,osx,tvos
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=ios,osx,tvos
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From a2897d5512f897e566247808bf7d87b2f033b5e7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 12:45:26 +0200
Subject: [PATCH 05/95] Run on macos-13

---
 .github/workflows/build.yml | 4 ++--
 .gitignore                  | 1 +
 Package.swift               | 2 +-
 Tests/SPM/Package.swift     | 7 ++-----
 4 files changed, 6 insertions(+), 8 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index beec5ca2..ae806ecc 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,10 +2,10 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: iPhone 12
-  IOS_VERSION: "16.0"
+  IOS_VERSION: "16.4"
 jobs:
   build:
-    runs-on: macos-12
+    runs-on: macos-13
     steps:
     - uses: actions/checkout@v2
     - name: Install
diff --git a/.gitignore b/.gitignore
index 5882e0cb..b7d43ec9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ DerivedData
 # Swift Package Manager
 .build
 Packages/
+.swiftpm/
diff --git a/Package.swift b/Package.swift
index de40367a..70bde7ef 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.7
 import PackageDescription
 
 let package = Package(
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 51f176f4..3c446ae6 100644
--- a/Tests/SPM/Package.swift
+++ b/Tests/SPM/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.3
+// swift-tools-version:5.7
 // The swift-tools-version declares the minimum version of Swift required to build this package.
 
 import PackageDescription
@@ -18,9 +18,6 @@ let package = Package(
         // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
     ],
     targets: [
-        .target(
-            name: "test",
-            dependencies: [.product(name: "SQLite", package: "SQLite.swift")]
-	)
+        .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])
     ]
 )

From 85f1d443e9b758aaeea3fc5c9b3abef67675931b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:09:30 +0200
Subject: [PATCH 06/95] Automatically download swiftlint / xcbeautifier, lint
 fixes

---
 .gitignore                                    |  3 +
 .swiftlint.yml                                |  5 +-
 Makefile                                      | 81 +++++++++++++------
 Sources/SQLite/Core/Backup.swift              |  2 +-
 Sources/SQLite/Helpers.swift                  |  1 +
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 Sources/SQLite/Typed/Query.swift              |  2 +-
 Tests/.swiftlint.yml                          |  4 +-
 Tests/SQLiteTests/Schema/SchemaTests.swift    |  2 +
 .../Typed/CustomFunctionsTests.swift          | 16 ++--
 10 files changed, 80 insertions(+), 38 deletions(-)

diff --git a/.gitignore b/.gitignore
index b7d43ec9..e7b2ad4d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -22,6 +22,9 @@ DerivedData
 # Carthage
 /Carthage/
 
+# Makefile
+bin/
+
 # Swift Package Manager
 .build
 Packages/
diff --git a/.swiftlint.yml b/.swiftlint.yml
index d40be28e..e728e191 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -4,6 +4,7 @@ disabled_rules: # rule identifiers to exclude from running
   - large_tuple
   - closure_parameter_position
   - inclusive_language  # sqlite_master etc.
+  - blanket_disable_command
 included: # paths to include during linting. `--path` is ignored if present. takes precendence over `excluded`.
   - Sources
   - Tests
@@ -26,8 +27,8 @@ identifier_name:
     - SQLITE_TRANSIENT
 
 type_body_length:
-  warning: 260
-  error: 260
+  warning: 350
+  error: 350
 
 function_body_length:
   warning: 60
diff --git a/Makefile b/Makefile
index 74bf5d18..73b33f97 100644
--- a/Makefile
+++ b/Makefile
@@ -1,41 +1,76 @@
-BUILD_TOOL = xcodebuild
+XCODEBUILD = xcodebuild
 BUILD_SCHEME = SQLite Mac
-IOS_SIMULATOR = iPhone 12
-IOS_VERSION = 15.0
+IOS_SIMULATOR = iPhone 14
+IOS_VERSION = 16.4
+
+# tool settings
+SWIFTLINT_VERSION=0.52.2
+SWIFTLINT=bin/swiftlint-$(SWIFTLINT_VERSION)
+SWIFTLINT_URL=https://github.com/realm/SwiftLint/releases/download/$(SWIFTLINT_VERSION)/portable_swiftlint.zip
+XCBEAUTIFY_VERSION=0.20.0
+XCBEAUTIFY=bin/xcbeautify-$(XCBEAUTIFY_VERSION)
+ifeq ($(shell uname), Linux)
+	XCBEAUTIFY_PLATFORM=x86_64-unknown-linux-gnu.tar.xz
+else
+	XCBEAUTIFY_PLATFORM=universal-apple-macosx.zip
+endif
+XCBEAUTIFY_URL=https://github.com/tuist/xcbeautify/releases/download/$(XCBEAUTIFY_VERSION)/xcbeautify-$(XCBEAUTIFY_VERSION)-$(XCBEAUTIFY_PLATFORM)
+CURL_OPTS=--fail --silent -L --retry 3
+
 ifeq ($(BUILD_SCHEME),SQLite iOS)
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)" -destination "platform=iOS Simulator,name=$(IOS_SIMULATOR),OS=$(IOS_VERSION)"
 else
 	BUILD_ARGUMENTS = -scheme "$(BUILD_SCHEME)"
 endif
 
-XCPRETTY := $(shell command -v xcpretty)
-TEST_ACTIONS := clean build build-for-testing test-without-building
+test: $(XCBEAUTIFY)
+	set -o pipefail; \
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) test | $(XCBEAUTIFY)
 
-default: test
+build: $(XCBEAUTIFY)
+	set -o pipefail; \
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) | $(XCBEAUTIFY)
 
-build:
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS)
+lint: $(SWIFTLINT)
+	$< --strict
 
-lint:
-	swiftlint --strict
-lint-fix:
-	swiftlint lint fix
-
-test:
-ifdef XCPRETTY
-	@set -o pipefail && $(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS) | $(XCPRETTY) -c
-else
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS) $(TEST_ACTIONS)
-endif
+lint-fix: $(SWIFTLINT)
+	$< lint fix
 
 clean:
-	$(BUILD_TOOL) $(BUILD_ARGUMENTS) clean
+	$(XCODEBUILD) $(BUILD_ARGUMENTS) clean
 
 repl:
-	@$(BUILD_TOOL) $(BUILD_ARGUMENTS) -derivedDataPath $(TMPDIR)/SQLite.swift > /dev/null && \
-		swift -F '$(TMPDIR)/SQLite.swift/Build/Products/Debug'
+	@$(XCODEBUILD) $(BUILD_ARGUMENTS) -derivedDataPath $(TMPDIR)/SQLite.swift > /dev/null && \
+		swift repl -F '$(TMPDIR)/SQLite.swift/Build/Products/Debug'
 
 sloc:
-	@zsh -c "grep -vE '^ *//|^$$' Sources/**/*.{swift,h,m} | wc -l"
+	@zsh -c "grep -vE '^ *//|^$$' Sources/**/*.{swift,h} | wc -l"
+
+$(SWIFTLINT):
+	set -e ; \
+	curl $(CURL_OPTS) $(SWIFTLINT_URL) -o swiftlint.zip; \
+	unzip -o swiftlint.zip swiftlint; \
+	mkdir -p bin; \
+	mv swiftlint $@ && rm -f swiftlint.zip
+
+$(XCBEAUTIFY):
+	set -e; \
+	FILE=$(XCBEAUTIFY_PLATFORM); \
+	curl $(CURL_OPTS) $(XCBEAUTIFY_URL) -o $$FILE; \
+	case "$${FILE#*.}" in \
+	  "zip") \
+		unzip -o $$FILE xcbeautify; \
+		;; \
+	  "tar.xz") \
+	  	tar -xvf $$FILE xcbeautify; \
+		;; \
+	  *) \
+		echo "unknown extension $${FILE#*.}!"; \
+		exit 1; \
+		;; \
+	esac; \
+	mkdir -p bin; \
+	mv xcbeautify $@ && rm -f $$FILE;
 
 .PHONY: test clean repl sloc
diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 84ebf1e0..0eebbdd5 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -140,7 +140,7 @@ public final class Backup {
     /// - Parameter pagesToCopy: The maximal number of pages to copy in one step
     ///
     /// - Throws: `Result.Error` if step fails.
-    //
+    ///
     /// See: <https://www.sqlite.org/c3ref/backup_finish.html#sqlite3backupstep>
     public func step(pagesToCopy pages: Pages = .all) throws {
         let status = sqlite3_backup_step(handle, pages.number)
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index e3d37e11..79d057d6 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -38,6 +38,7 @@ public func *(_: Expression<Binding>?, _: Expression<Binding>?) -> Expression<Vo
     Expression(literal: "*")
 }
 
+// swiftlint:disable:next type_name
 public protocol _OptionalType {
 
     associatedtype WrappedType
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 1c26fdd6..0c02ba9f 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -13,7 +13,7 @@ public class SchemaReader {
     // column name, data type, whether or not the column can be NULL, and the default value for the column. The
     // "pk" column in the result set is zero for columns that are not part of the primary key, and is the
     // index of the column in the primary key for columns that are part of the primary key.
-    public func columnDefinitions(table: String) throws  -> [ColumnDefinition] {
+    public func columnDefinitions(table: String) throws -> [ColumnDefinition] {
         func parsePrimaryKey(column: String) throws -> ColumnDefinition.PrimaryKey? {
             try createTableSQL(name: table).flatMap { .init(sql: $0) }
         }
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index feff2f69..c59b728f 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1105,7 +1105,7 @@ extension Connection {
         return value(try scalar(expression.template, expression.bindings))
     }
 
-    public func scalar<V: Value>(_ query: Select<V?>) throws ->  V.ValueType? {
+    public func scalar<V: Value>(_ query: Select<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
         return V.fromDatatypeValue(value)
diff --git a/Tests/.swiftlint.yml b/Tests/.swiftlint.yml
index 3537085c..81d6c314 100644
--- a/Tests/.swiftlint.yml
+++ b/Tests/.swiftlint.yml
@@ -6,8 +6,8 @@ disabled_rules:
   - identifier_name
 
 type_body_length:
-  warning: 800
-  error: 800
+  warning: 1000
+  error: 1000
 
 function_body_length:
   warning: 200
diff --git a/Tests/SQLiteTests/Schema/SchemaTests.swift b/Tests/SQLiteTests/Schema/SchemaTests.swift
index a7de8bcf..4f4a49d1 100644
--- a/Tests/SQLiteTests/Schema/SchemaTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaTests.swift
@@ -72,6 +72,7 @@ class SchemaTests: XCTestCase {
     }
 
     // thoroughness test for ambiguity
+    // swiftlint:disable:next function_body_length
     func test_column_compilesColumnDefinitionExpression() {
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"int64\" INTEGER NOT NULL)",
@@ -390,6 +391,7 @@ class SchemaTests: XCTestCase {
         )
     }
 
+    // swiftlint:disable:next function_body_length
     func test_column_withStringExpression_compilesCollatedColumnDefinitionExpression() {
         XCTAssertEqual(
             "CREATE TABLE \"table\" (\"string\" TEXT NOT NULL COLLATE RTRIM)",
diff --git a/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift b/Tests/SQLiteTests/Typed/CustomFunctionsTests.swift
index c4eb51e6..0f46b380 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              = () -> Expression<String>
+    typealias FunctionResultOptional          = () -> Expression<String?>
 
     func testFunctionNoOptional() throws {
         let _: FunctionNoOptional = try db.createFunction("test", deterministic: true) {
@@ -26,9 +26,9 @@ class CustomFunctionNoArgsTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithOneArgTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>)  -> Expression<String>
+    typealias FunctionNoOptional              = (Expression<String>) -> Expression<String>
     typealias FunctionLeftOptional            = (Expression<String?>) -> Expression<String>
-    typealias FunctionResultOptional          = (Expression<String>)  -> Expression<String?>
+    typealias FunctionResultOptional          = (Expression<String>) -> Expression<String?>
     typealias FunctionLeftResultOptional      = (Expression<String?>) -> Expression<String?>
 
     func testFunctionNoOptional() throws {
@@ -65,12 +65,12 @@ class CustomFunctionWithOneArgTests: SQLiteTestCase {
 }
 
 class CustomFunctionWithTwoArgsTests: SQLiteTestCase {
-    typealias FunctionNoOptional              = (Expression<String>, Expression<String>)  -> Expression<String>
-    typealias FunctionLeftOptional            = (Expression<String?>, Expression<String>)  -> Expression<String>
+    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 FunctionResultOptional          = (Expression<String>, Expression<String>) -> Expression<String?>
     typealias FunctionLeftRightOptional       = (Expression<String?>, Expression<String?>) -> Expression<String>
-    typealias FunctionLeftResultOptional      = (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?>
 

From 4a0ab2a7445712e71ff803e020b74f9557ae64a8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:14:36 +0200
Subject: [PATCH 07/95] Fix simulator version

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ae806ecc..ac705e4e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: iPhone 12
+  IOS_SIMULATOR: "iPhone 14"
   IOS_VERSION: "16.4"
 jobs:
   build:

From e4f4ddff5f124652421d7cd99512181bed86b0e7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:22:26 +0200
Subject: [PATCH 08/95] Use newer Xcode

---
 .github/workflows/build.yml | 10 ++++------
 1 file changed, 4 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ac705e4e..fbe4ec49 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -8,13 +8,11 @@ jobs:
     runs-on: macos-13
     steps:
     - uses: actions/checkout@v2
-    - name: Install
+    - name: "Select Xcode"
       run: |
-        gem update bundler
-        gem install xcpretty --no-document
-        brew update
-        brew outdated carthage || brew upgrade carthage
-        brew outdated swiftlint || brew upgrade swiftlint
+        xcode-select -p
+        xcode-select -s /Applications/Xcode-14.3.app/Contents/Developer
+        xcode-select -p
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 1bc58a5477cc47856d1e6a31b834f1cc0f66c00b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:28:43 +0200
Subject: [PATCH 09/95] Fix path

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index fbe4ec49..8c893c77 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,7 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        xcode-select -s /Applications/Xcode-14.3.app/Contents/Developer
+        xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
         xcode-select -p
     - name: "Lint"
       run: make lint

From 79bab4610b74cc7a8dee118a6b9ebce769aaec61 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 15:33:04 +0200
Subject: [PATCH 10/95] sudo

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8c893c77..15f325ed 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -11,7 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
         xcode-select -p
     - name: "Lint"
       run: make lint

From b028fcdda1489a3bb0c738f8809154f9c4be7f5b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 17:48:05 +0200
Subject: [PATCH 11/95] Try Xcode 14.2

---
 .github/workflows/build.yml | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 15f325ed..242a9021 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 14"
-  IOS_VERSION: "16.4"
+  IOS_VERSION: "16.2"
 jobs:
   build:
     runs-on: macos-13
@@ -11,8 +11,7 @@ jobs:
     - name: "Select Xcode"
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_14.3.app/Contents/Developer
-        xcode-select -p
+        sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 101109848902bb2c7061286c86f15a638d6d5a17 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 22:28:40 +0200
Subject: [PATCH 12/95] Update docs

---
 .github/workflows/build.yml |  2 ++
 CHANGELOG.md                |  5 +++++
 Documentation/Index.md      | 31 ++++++++++++++-----------------
 README.md                   |  5 +----
 4 files changed, 22 insertions(+), 21 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 242a9021..9cc2f56e 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,8 @@ jobs:
     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_14.2.app/Contents/Developer
diff --git a/CHANGELOG.md b/CHANGELOG.md
index fc759118..970b4ca1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+0.15.0 (unreleased)
+========================================
+
+* New minimum deployment targets: iOS/tvOS 11.0, watchOS 4.0
+
 0.14.1 (01-11-2022), [diff][diff-0.14.1]
 ========================================
 
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 583d5def..5d72aa4b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -77,9 +77,6 @@
 
 ## Installation
 
-> _Note:_ SQLite.swift requires Swift 5 (and
-> [Xcode 10.2](https://developer.apple.com/xcode/downloads/)) or greater.
-
 ### Swift Package Manager
 
 The [Swift Package Manager][] is a tool for managing the distribution of
@@ -1142,11 +1139,11 @@ let query = managers
 chain.with(chain, recursive: true, as: query)
 // WITH RECURSIVE
 //   "chain" AS (
-//     SELECT * FROM "managers" WHERE "id" = 8 
-//     UNION 
-//     SELECT * from "chain" 
+//     SELECT * FROM "managers" WHERE "id" = 8
+//     UNION
+//     SELECT * from "chain"
 //     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
-//   ) 
+//   )
 // SELECT * FROM "chain"
 ```
 
@@ -1156,7 +1153,7 @@ Column names and a materialization hint can optionally be provided.
 // Add a "level" column to the query representing manager's position in the chain
 let level = Expression<Int64>("level")
 
-let queryWithLevel = 
+let queryWithLevel =
     managers
         .select(id, managerId, 0)
         .where(id == 8)
@@ -1166,18 +1163,18 @@ let queryWithLevel =
                 .join(managers, on: chain[managerId] == managers[id])
         )
 
-chain.with(chain, 
-           columns: [id, managerId, level], 
+chain.with(chain,
+           columns: [id, managerId, level],
            recursive: true,
            hint: .materialize,
            as: queryWithLevel)
 // WITH RECURSIVE
 //   "chain" ("id", "manager_id", "level") AS MATERIALIZED (
-//     SELECT ("id", "manager_id", 0) FROM "managers" WHERE "id" = 8 
-//     UNION 
-//     SELECT ("manager"."id", "manager"."manager_id", "level" + 1) FROM "chain" 
+//     SELECT ("id", "manager_id", 0) FROM "managers" WHERE "id" = 8
+//     UNION
+//     SELECT ("manager"."id", "manager"."manager_id", "level" + 1) FROM "chain"
 //     JOIN "managers" ON "chain"."manager_id" = "managers"."id"
-//   ) 
+//   )
 // SELECT * FROM "chain"
 ```
 
@@ -1266,7 +1263,7 @@ let count = try db.scalar(users.filter(name != nil).count)
 We can upsert rows into a table by calling a [query’s](#queries) `upsert`
 function with a list of [setters](#setters)—typically [typed column
 expressions](#expressions) and values (which can also be expressions)—each
-joined by the `<-` operator. Upserting is like inserting, except if there is a 
+joined by the `<-` operator. Upserting is like inserting, except if there is a
 conflict on the specified column value, SQLite will perform an update on the row instead.
 
 ```swift
@@ -1957,7 +1954,7 @@ for row in stmt.bind(kUTTypeImage) { /* ... */ }
 ```
 
 > _Note:_ Prepared queries can be reused, and long lived prepared queries should be `reset()` after each use. Otherwise, the transaction (either [implicit or explicit](https://www.sqlite.org/lang_transaction.html#implicit_versus_explicit_transactions)) will be held open until the query is reset or finalized. This can affect performance. Statements are reset automatically during `deinit`.
-> 
+>
 > ```swift
 > someObj.statement = try db.prepare("SELECT * FROM attachments WHERE typeConformsTo(UTI, ?)")
 > for row in someObj.statement.bind(kUTTypeImage) { /* ... */ }
@@ -2134,7 +2131,7 @@ using the following functions.
         }
     }
     ```
-    
+
   - `run` prepares a single `Statement` object from a SQL string, optionally
     binds values to it (using the statement’s `bind` function), executes,
     and returns the statement.
diff --git a/README.md b/README.md
index 90333b16..e6e5a894 100644
--- a/README.md
+++ b/README.md
@@ -90,7 +90,7 @@ API.
 // Wrap everything in a do...catch to handle errors
 do {
     // ...
-    
+
     let stmt = try db.prepare("INSERT INTO users (email) VALUES (?)")
     for email in ["betty@icloud.com", "cathy@icloud.com"] {
         try stmt.run(email)
@@ -119,9 +119,6 @@ interactively, from the Xcode project’s playground.
 
 ## Installation
 
-> _Note:_ Version 0.11.6 and later requires Swift 5 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.2) or greater.
-> Version 0.11.5 requires Swift 4.2 (and [Xcode](https://developer.apple.com/xcode/downloads/) 10.1) or greater.
-
 ### Swift Package Manager
 
 The [Swift Package Manager][] is a tool for managing the distribution of

From ab3c598db29a7a2db3e06e9642736cb18e670a0e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sun, 21 May 2023 23:59:35 +0200
Subject: [PATCH 13/95] Use shorthand optional binding

---
 .swiftlint.yml                                |  2 ++
 Sources/SQLite/Core/Connection+Attach.swift   |  2 +-
 Sources/SQLite/Core/Connection.swift          | 18 +++++------
 Sources/SQLite/Core/Result.swift              |  4 +--
 Sources/SQLite/Extensions/FTS4.swift          | 16 +++++-----
 Sources/SQLite/Extensions/FTS5.swift          |  6 ++--
 Sources/SQLite/Helpers.swift                  |  2 +-
 Sources/SQLite/Schema/SchemaDefinitions.swift |  2 +-
 Sources/SQLite/Schema/SchemaReader.swift      |  4 +--
 Sources/SQLite/Typed/Coding.swift             | 12 ++++----
 Sources/SQLite/Typed/CoreFunctions.swift      | 30 +++++++++----------
 Sources/SQLite/Typed/Operators.swift          | 16 +++++-----
 12 files changed, 58 insertions(+), 56 deletions(-)

diff --git a/.swiftlint.yml b/.swiftlint.yml
index e728e191..1bb21cd5 100644
--- a/.swiftlint.yml
+++ b/.swiftlint.yml
@@ -1,3 +1,5 @@
+opt_in_rules:
+  - shorthand_optional_binding
 disabled_rules: # rule identifiers to exclude from running
   - todo
   - operator_whitespace
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 47e6af5e..8a25e51d 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -13,7 +13,7 @@ extension Connection {
     #if SQLITE_SWIFT_SQLCIPHER
     /// See https://www.zetetic.net/sqlcipher/sqlcipher-api/#attach
     public func attach(_ location: Location, as schemaName: String, key: String? = nil) throws {
-        if let key = key {
+        if let key {
             try run("ATTACH DATABASE ? AS ? KEY ?", location.description, schemaName, key)
         } else {
             try run("ATTACH DATABASE ? AS ?", location.description, schemaName)
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 26cadea4..f2c3b781 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -414,7 +414,7 @@ public final class Connection {
     ///   times it’s been called for this lock. If it returns `true`, it will
     ///   try again. If it returns `false`, no further attempts will be made.
     public func busyHandler(_ callback: ((_ tries: Int) -> Bool)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_busy_handler(handle, nil, nil)
             busyHandler = nil
             return
@@ -449,7 +449,7 @@ public final class Connection {
     @available(watchOS, deprecated: 3.0)
     @available(tvOS, deprecated: 10.0)
     fileprivate func trace_v1(_ callback: ((String) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_trace(handle, nil /* xCallback */, nil /* pCtx */)
             trace = nil
             return
@@ -458,7 +458,7 @@ public final class Connection {
             callback(String(cString: pointer.assumingMemoryBound(to: UInt8.self)))
         }
         sqlite3_trace(handle, { (context: UnsafeMutableRawPointer?, SQL: UnsafePointer<Int8>?) in
-                    if let context = context, let SQL = SQL {
+                    if let context, let SQL {
                         unsafeBitCast(context, to: Trace.self)(SQL)
                     }
             },
@@ -469,7 +469,7 @@ public final class Connection {
 
     @available(iOS 10.0, OSX 10.12, tvOS 10.0, watchOS 3.0, *)
     fileprivate func trace_v2(_ callback: ((String) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             // If the X callback is NULL or if the M mask is zero, then tracing is disabled.
             sqlite3_trace_v2(handle, 0 /* mask */, nil /* xCallback */, nil /* pCtx */)
             trace = nil
@@ -485,7 +485,7 @@ public final class Connection {
                  // callback was invoked. The C argument is a copy of the context pointer.
                  // The P and X arguments are pointers whose meanings depend on T.
                  (_: UInt32, context: UnsafeMutableRawPointer?, pointer: UnsafeMutableRawPointer?, _: UnsafeMutableRawPointer?) in
-                 if let pointer = pointer,
+                 if let pointer,
                     let expandedSQL = sqlite3_expanded_sql(OpaquePointer(pointer)) {
                      unsafeBitCast(context, to: Trace.self)(expandedSQL)
                      sqlite3_free(expandedSQL)
@@ -507,7 +507,7 @@ public final class Connection {
     ///   `.Insert`, `.Update`, or `.Delete`), database name, table name, and
     ///   rowid.
     public func updateHook(_ callback: ((_ operation: Operation, _ db: String, _ table: String, _ rowid: Int64) -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_update_hook(handle, nil, nil)
             updateHook = nil
             return
@@ -535,7 +535,7 @@ public final class Connection {
     ///   committed. If this callback throws, the transaction will be rolled
     ///   back.
     public func commitHook(_ callback: (() throws -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_commit_hook(handle, nil, nil)
             commitHook = nil
             return
@@ -562,7 +562,7 @@ public final class Connection {
     /// - Parameter callback: A callback invoked when a transaction is rolled
     ///   back.
     public func rollbackHook(_ callback: (() -> Void)?) {
-        guard let callback = callback else {
+        guard let callback else {
             sqlite3_rollback_hook(handle, nil, nil)
             rollbackHook = nil
             return
@@ -650,7 +650,7 @@ public final class Connection {
         try check(sqlite3_create_collation_v2(handle, collation, SQLITE_UTF8,
             unsafeBitCast(box, to: UnsafeMutableRawPointer.self), { (callback: UnsafeMutableRawPointer?, _,
                                                                      lhs: UnsafeRawPointer?, _, rhs: UnsafeRawPointer?) in /* xCompare */
-            if let lhs = lhs, let rhs = rhs {
+            if let lhs, let rhs {
                 return unsafeBitCast(callback, to: Collation.self)(lhs, rhs)
             } else {
                 fatalError("sqlite3_create_collation_v2 callback called with NULL pointer")
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 9fe47ada..ee59e5d1 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -51,13 +51,13 @@ extension Result: CustomStringConvertible {
     public var description: String {
         switch self {
         case let .error(message, errorCode, statement):
-            if let statement = statement {
+            if let statement {
                 return "\(message) (\(statement)) (code: \(errorCode))"
             } else {
                 return "\(message) (code: \(errorCode))"
             }
         case let .extendedError(message, extendedCode, statement):
-            if let statement = statement {
+            if let statement {
                 return "\(message) (\(statement)) (extended code: \(extendedCode))"
             } else {
                 return "\(message) (extended code: \(extendedCode))"
diff --git a/Sources/SQLite/Extensions/FTS4.swift b/Sources/SQLite/Extensions/FTS4.swift
index 8e91e453..c02cfdc1 100644
--- a/Sources/SQLite/Extensions/FTS4.swift
+++ b/Sources/SQLite/Extensions/FTS4.swift
@@ -98,7 +98,7 @@ public struct Tokenizer {
                                  separators: Set<Character> = []) -> Tokenizer {
         var arguments = [String]()
 
-        if let removeDiacritics = removeDiacritics {
+        if let removeDiacritics {
             arguments.append("remove_diacritics=\(removeDiacritics ? 1 : 0)".quote())
         }
 
@@ -208,13 +208,13 @@ open class FTSConfig {
     func options() -> Options {
         var options = Options()
         options.append(formatColumnDefinitions())
-        if let tokenizer = tokenizer {
+        if let tokenizer {
             options.append("tokenize", value: Expression<Void>(literal: tokenizer.description))
         }
         options.appendCommaSeparated("prefix", values: prefixes.sorted().map { String($0) })
         if isContentless {
             options.append("content", value: "")
-        } else if let externalContentSchema = externalContentSchema {
+        } else if let externalContentSchema {
             options.append("content", value: externalContentSchema.tableName())
         }
         return options
@@ -306,19 +306,19 @@ open class FTS4Config: FTSConfig {
         for (column, _) in (columnDefinitions.filter { $0.options.contains(.unindexed) }) {
             options.append("notindexed", value: column)
         }
-        if let languageId = languageId {
+        if let languageId {
             options.append("languageid", value: languageId)
         }
-        if let compressFunction = compressFunction {
+        if let compressFunction {
             options.append("compress", value: compressFunction)
         }
-        if let uncompressFunction = uncompressFunction {
+        if let uncompressFunction {
             options.append("uncompress", value: uncompressFunction)
         }
-        if let matchInfo = matchInfo {
+        if let matchInfo {
             options.append("matchinfo", value: matchInfo.rawValue)
         }
-        if let order = order {
+        if let order {
             options.append("order", value: order.rawValue)
         }
         return options
diff --git a/Sources/SQLite/Extensions/FTS5.swift b/Sources/SQLite/Extensions/FTS5.swift
index 2e4f65fb..3e84e171 100644
--- a/Sources/SQLite/Extensions/FTS5.swift
+++ b/Sources/SQLite/Extensions/FTS5.swift
@@ -69,13 +69,13 @@ open class FTS5Config: FTSConfig {
 
     override func options() -> Options {
         var options = super.options()
-        if let contentRowId = contentRowId {
+        if let contentRowId {
             options.append("content_rowid", value: contentRowId)
         }
-        if let columnSize = columnSize {
+        if let columnSize {
             options.append("columnsize", value: Expression<Int>(value: columnSize))
         }
-        if let detail = detail {
+        if let detail {
             options.append("detail", value: detail.rawValue)
         }
         return options
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 79d057d6..adfadc8a 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -109,7 +109,7 @@ extension String {
 }
 
 func transcode(_ literal: Binding?) -> String {
-    guard let literal = literal else { return "NULL" }
+    guard let literal else { return "NULL" }
 
     switch literal {
     case let blob as Blob:
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index b314e5c0..2d38e1fb 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -170,7 +170,7 @@ public enum LiteralValue: Equatable, CustomStringConvertible {
     // swiftlint:enable identifier_name
 
     init(_ string: String?) {
-        guard let string = string else {
+        guard let string else {
             self = .NULL
             return
         }
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 0c02ba9f..58a9b1bd 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -39,10 +39,10 @@ public class SchemaReader {
                                   type: ObjectDefinition.ObjectType? = nil,
                                   temp: Bool = false) throws -> [ObjectDefinition] {
         var query: QueryType = SchemaTable.get(for: connection, temp: temp)
-        if let name = name {
+        if let name {
             query = query.where(SchemaTable.nameColumn == name)
         }
-        if let type = type {
+        if let type {
             query = query.where(SchemaTable.typeColumn == type.rawValue)
         }
         return try connection.prepare(query).map { row -> ObjectDefinition in
diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index b96bc64e..e7db03fb 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -215,7 +215,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Int?, forKey key: SQLiteEncoder.SQLiteKeyedEncodingContainer<Key>.Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Int?>(key.stringValue) <- nil)
@@ -223,7 +223,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Bool?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Bool?>(key.stringValue) <- nil)
@@ -231,7 +231,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Float?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
@@ -239,7 +239,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: Double?, forKey key: Key) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<Double?>(key.stringValue) <- nil)
@@ -247,7 +247,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent(_ value: String?, forKey key: MyKey) throws {
-            if let value = value {
+            if let value {
                 try encode(value, forKey: key)
             } else if forcingNilValueSetters {
                 encoder.setters.append(Expression<String?>(key.stringValue) <- nil)
@@ -270,7 +270,7 @@ private class SQLiteEncoder: Encoder {
         }
 
         func encodeIfPresent<T>(_ value: T?, forKey key: Key) throws where T: Swift.Encodable {
-            guard let value = value else {
+            guard let value else {
                 guard forcingNilValueSetters else {
                     return
                 }
diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index dc6a1044..4429bb5f 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -100,7 +100,7 @@ extension ExpressionType where UnderlyingType == Double {
     ///
     /// - Returns: A copy of the expression wrapped with the `round` function.
     public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
-        guard let precision = precision else {
+        guard let precision else {
             return Function.round.wrap([self])
         }
         return Function.round.wrap([self, Int(precision)])
@@ -120,7 +120,7 @@ extension ExpressionType where UnderlyingType == Double? {
     ///
     /// - Returns: A copy of the expression wrapped with the `round` function.
     public func round(_ precision: Int? = nil) -> Expression<UnderlyingType> {
-        guard let precision = precision else {
+        guard let precision else {
             return Function.round.wrap(self)
         }
         return Function.round.wrap([self, Int(precision)])
@@ -250,7 +250,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return "LIKE".infix(self, pattern)
         }
         return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
@@ -274,7 +274,7 @@ extension ExpressionType where UnderlyingType == String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> =  Function.like.infix(self, pattern, wrap: false)
@@ -349,7 +349,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `ltrim` function.
     public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.ltrim.wrap(self)
         }
         return Function.ltrim.wrap([self, String(characters)])
@@ -367,7 +367,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `rtrim` function.
     public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.rtrim.wrap(self)
         }
         return Function.rtrim.wrap([self, String(characters)])
@@ -385,7 +385,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     /// - Returns: A copy of the expression wrapped with the `trim` function.
     public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.trim.wrap([self])
         }
         return Function.trim.wrap([self, String(characters)])
@@ -409,7 +409,7 @@ extension ExpressionType where UnderlyingType == String {
     }
 
     public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
-        guard let length = length else {
+        guard let length else {
             return Function.substr.wrap([self, location])
         }
         return Function.substr.wrap([self, location, length])
@@ -475,7 +475,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: String, escape character: Character? = nil) -> Expression<Bool?> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         return Expression("(\(template) LIKE ? ESCAPE ?)", bindings + [pattern, String(character)])
@@ -499,7 +499,7 @@ extension ExpressionType where UnderlyingType == String? {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool?> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
@@ -574,7 +574,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `ltrim` function.
     public func ltrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.ltrim.wrap(self)
         }
         return Function.ltrim.wrap([self, String(characters)])
@@ -592,7 +592,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `rtrim` function.
     public func rtrim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.rtrim.wrap(self)
         }
         return Function.rtrim.wrap([self, String(characters)])
@@ -610,7 +610,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `trim` function.
     public func trim(_ characters: Set<Character>? = nil) -> Expression<UnderlyingType> {
-        guard let characters = characters else {
+        guard let characters else {
             return Function.trim.wrap(self)
         }
         return Function.trim.wrap([self, String(characters)])
@@ -649,7 +649,7 @@ extension ExpressionType where UnderlyingType == String? {
     ///
     /// - Returns: A copy of the expression wrapped with the `substr` function.
     public func substring(_ location: Int, length: Int? = nil) -> Expression<UnderlyingType> {
-        guard let length = length else {
+        guard let length else {
             return Function.substr.wrap([self, location])
         }
         return Function.substr.wrap([self, location, length])
@@ -726,7 +726,7 @@ extension String {
     /// - Returns: A copy of the expression appended with a `LIKE` query against
     ///   the given pattern.
     public func like(_ pattern: Expression<String>, escape character: Character? = nil) -> Expression<Bool> {
-        guard let character = character else {
+        guard let character else {
             return Function.like.infix(self, pattern)
         }
         let like: Expression<Bool> = Function.like.infix(self, pattern, wrap: false)
diff --git a/Sources/SQLite/Typed/Operators.swift b/Sources/SQLite/Typed/Operators.swift
index 5ffbbceb..1c611cbc 100644
--- a/Sources/SQLite/Typed/Operators.swift
+++ b/Sources/SQLite/Typed/Operators.swift
@@ -367,14 +367,14 @@ public func ==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V
     Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     Operator.eq.infix(lhs, rhs)
 }
 public func ==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return Operator.eq.infix(lhs, rhs)
 }
 
@@ -394,14 +394,14 @@ public func ===<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where
     "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS".infix(lhs, Expression<V?>(value: nil)) }
     return "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     "IS".infix(lhs, rhs)
 }
 public func ===<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS".infix(Expression<V?>(value: nil), rhs) }
     return "IS".infix(lhs, rhs)
 }
 
@@ -421,14 +421,14 @@ public func !=<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where V
     Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     Operator.neq.infix(lhs, rhs)
 }
 public func !=<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return Operator.neq.infix(lhs, rhs)
 }
 
@@ -448,14 +448,14 @@ public func !==<V: Value>(lhs: Expression<V>, rhs: V) -> Expression<Bool> where
     "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: Expression<V?>, rhs: V?) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let rhs = rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
+    guard let rhs else { return "IS NOT".infix(lhs, Expression<V?>(value: nil)) }
     return "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V, rhs: Expression<V>) -> Expression<Bool> where V.Datatype: Equatable {
     "IS NOT".infix(lhs, rhs)
 }
 public func !==<V: Value>(lhs: V?, rhs: Expression<V?>) -> Expression<Bool?> where V.Datatype: Equatable {
-    guard let lhs = lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
+    guard let lhs else { return "IS NOT".infix(Expression<V?>(value: nil), rhs) }
     return "IS NOT".infix(lhs, rhs)
 }
 

From 6aab9fcdc27c97f04b4153589908ec871b17ba55 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Mon, 22 May 2023 00:56:52 +0200
Subject: [PATCH 14/95] Re-enable watch tests

---
 run-tests.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index c54300ee..49330c12 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,9 +8,9 @@ if [ -n "$BUILD_SCHEME" ]; then
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast --platforms=ios,osx,tvos
+      pod lib lint --no-subspecs --fail-fast
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=ios,osx,tvos
+      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From ff93ebc831c01daa0117be6c19e0960347e001a6 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Sat, 27 May 2023 23:09:08 +0200
Subject: [PATCH 15/95] Handle FK definitions w/o key references

Closes #1199
---
 SQLite.xcodeproj/project.pbxproj              |  8 ++++
 Sources/SQLite/Schema/SchemaDefinitions.swift |  4 +-
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 .../Schema/SchemaReaderTests.swift            | 38 +++++++++++++++++++
 4 files changed, 49 insertions(+), 3 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..e6bb4fc9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,9 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -340,6 +343,7 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -483,6 +487,7 @@
 		19A17B56FBA20E7245BC8AC0 /* Schema */ = {
 			isa = PBXGroup;
 			children = (
+				DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */,
 				19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */,
 				19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */,
 				19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */,
@@ -998,6 +1003,7 @@
 				19A17FACE8E4D54A50BA934E /* FTS5Tests.swift in Sources */,
 				19A177909023B7B940C5805E /* FTSIntegrationTests.swift in Sources */,
 				19A17E1DD976D5CE80018749 /* FTS4Tests.swift in Sources */,
+				DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A17411403D60640467209E /* ExpressionTests.swift in Sources */,
 				19A17CA4D7B63D845428A9C5 /* StatementTests.swift in Sources */,
 				19A17885B646CB0201BE4BD5 /* QueryTests.swift in Sources */,
@@ -1119,6 +1125,7 @@
 				19A178DA2BB5970778CCAF13 /* FTS5Tests.swift in Sources */,
 				19A1755C49154C87304C9146 /* FTSIntegrationTests.swift in Sources */,
 				19A17444861E1443143DEB44 /* FTS4Tests.swift in Sources */,
+				DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A17DD33C2E43DD6EE05A60 /* ExpressionTests.swift in Sources */,
 				19A17D6EC40BC35A5DC81BA8 /* StatementTests.swift in Sources */,
 				19A17E3F47DA087E2B76D087 /* QueryTests.swift in Sources */,
@@ -1199,6 +1206,7 @@
 				19A1776BD5127DFDF847FF1F /* FTS5Tests.swift in Sources */,
 				19A173088B85A7E18E8582A7 /* FTSIntegrationTests.swift in Sources */,
 				19A178767223229E61C5066F /* FTS4Tests.swift in Sources */,
+				DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */,
 				19A1781CBA8968ABD3E00877 /* ExpressionTests.swift in Sources */,
 				19A17923494236793893BF72 /* StatementTests.swift in Sources */,
 				19A17A52BF29D27C9AA229E7 /* QueryTests.swift in Sources */,
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 2d38e1fb..9f4c126f 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -107,7 +107,7 @@ public struct ColumnDefinition: Equatable {
     public struct ForeignKey: Equatable {
         let table: String
         let column: String
-        let primaryKey: String
+        let primaryKey: String?
         let onUpdate: String?
         let onDelete: String?
     }
@@ -365,7 +365,7 @@ extension ColumnDefinition.ForeignKey {
         ([
             "REFERENCES",
             table.quote(),
-            "(\(primaryKey.quote()))",
+            primaryKey.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 58a9b1bd..eb31888b 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -187,7 +187,7 @@ private enum ForeignKeyListTable {
     static let seqColumn = Expression<Int64>("seq")
     static let tableColumn = Expression<String>("table")
     static let fromColumn = Expression<String>("from")
-    static let toColumn = Expression<String>("to")
+    static let toColumn = Expression<String?>("to") // when null, use primary key
     static let onUpdateColumn = Expression<String>("on_update")
     static let onDeleteColumn = Expression<String>("on_delete")
     static let matchColumn = Expression<String>("match")
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..e90578e7 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -135,6 +135,44 @@ class SchemaReaderTests: SQLiteTestCase {
         ])
     }
 
+    func test_foreignKeys_references_column() throws {
+        let sql = """
+          CREATE TABLE artist(
+            artistid    INTEGER PRIMARY KEY,
+            artistname  TEXT
+          );
+          CREATE TABLE track(
+            trackid     INTEGER,
+            trackname   TEXT,
+            trackartist INTEGER REFERENCES artist(artistid)
+          );
+          """
+        try db.execute(sql)
+        let trackColumns = try db.schema.foreignKeys(table: "track")
+        XCTAssertEqual(trackColumns.map { $0.toSQL() }.joined(separator: "\n"), """
+            REFERENCES "artist" ("artistid")
+            """)
+    }
+
+    func test_foreignKeys_references_null_column() throws {
+        let sql = """
+          CREATE TABLE artist(
+            artistid    INTEGER PRIMARY KEY,
+            artistname  TEXT
+          );
+          CREATE TABLE track(
+            trackid     INTEGER,
+            trackname   TEXT,
+            trackartist INTEGER REFERENCES artist
+          );
+          """
+        try db.execute(sql)
+        let trackColumns = try db.schema.foreignKeys(table: "track")
+        XCTAssertEqual(trackColumns.map { $0.toSQL() }.joined(separator: "\n"), """
+            REFERENCES "artist"
+            """)
+    }
+
     func test_tableDefinitions() throws {
         let tables = try schemaReader.tableDefinitions()
         XCTAssertEqual(tables.count, 1)

From 7997c85cbf6c0a1f8caf300b483f04028548e40e Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Wed, 7 Jun 2023 15:45:35 +0200
Subject: [PATCH 16/95] SchemaReader: return the correct column definition for
 a composite primary key

The `PRAGMA` `table_info` that is used to return the column definitions,
returns one row for each defined column. The `pk` column contains:

> ... either zero for columns that are not part of the primary key, or the 1-based index of the column within the primary key).

See https://www.sqlite.org/pragma.html#pragma_table_info

Checking whether the `pk` column equals 1 only detects a single primary
key and ignores other columns that are part of a composite primary key.
---
 Sources/SQLite/Schema/SchemaReader.swift      |  2 +-
 .../Schema/SchemaReaderTests.swift            | 37 +++++++++++++++++++
 2 files changed, 38 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 58a9b1bd..2be15f29 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -25,7 +25,7 @@ public class SchemaReader {
             .map { (row: Row) -> ColumnDefinition in
                 ColumnDefinition(
                     name: row[TableInfoTable.nameColumn],
-                    primaryKey: row[TableInfoTable.primaryKeyColumn] == 1 ?
+                    primaryKey: (row[TableInfoTable.primaryKeyColumn] ?? 0) > 0 ?
                         try parsePrimaryKey(column: row[TableInfoTable.nameColumn]) : nil,
                     type: ColumnDefinition.Affinity(row[TableInfoTable.typeColumn]),
                     nullable: row[TableInfoTable.notNullColumn] == 0,
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..49871c10 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -90,6 +90,43 @@ class SchemaReaderTests: SQLiteTestCase {
         )
     }
 
+    func test_columnDefinitions_composite_primary_keys() throws {
+        try db.run("""
+        CREATE TABLE t (
+          col1 INTEGER,
+          col2 INTEGER,
+          col3 INTEGER,
+          PRIMARY KEY (col1, col2)
+        );
+        """)
+
+        XCTAssertEqual(
+            try schemaReader.columnDefinitions(table: "t"), [
+            ColumnDefinition(
+                    name: "col1",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil),
+            ColumnDefinition(
+                    name: "col2",
+                    primaryKey: .init(autoIncrement: false),
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil),
+            ColumnDefinition(
+                    name: "col3",
+                    primaryKey: nil,
+                    type: .INTEGER,
+                    nullable: true,
+                    defaultValue: .NULL,
+                    references: nil)
+            ]
+        )
+    }
+
     func test_indexDefinitions_no_index() throws {
         let indexes = try schemaReader.indexDefinitions(table: "users")
         XCTAssertTrue(indexes.isEmpty)

From 656ca871e357df8ef45b7344c777102cdbf7dfc6 Mon Sep 17 00:00:00 2001
From: Stefan Saasen <stefan@ohoj-software.com>
Date: Tue, 30 May 2023 16:20:29 +0200
Subject: [PATCH 17/95] Fix column affinity parsing to match how SQLite
 determines affinity

See https://www.sqlite.org/datatype3.html#determination_of_column_affinity
for how SQLite determines column affinity.
---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 14 +++-
 .../Schema/SchemaDefinitionsTests.swift       | 64 ++++++++++++++++++-
 .../Schema/SchemaReaderTests.swift            |  4 +-
 3 files changed, 77 insertions(+), 5 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 2d38e1fb..3c6b0523 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -57,7 +57,19 @@ public struct ColumnDefinition: Equatable {
         }
 
         init(_ string: String) {
-            self = Affinity.allCases.first { $0.rawValue.lowercased() == string.lowercased() } ?? .TEXT
+            let test = string.uppercased()
+            // https://sqlite.org/datatype3.html#determination_of_column_affinity
+            if test.contains("INT") { // Rule 1
+                self = .INTEGER
+            } else if ["CHAR", "CLOB", "TEXT"].first(where: {test.contains($0)}) != nil { // Rule 2
+                self = .TEXT
+            } else if string.contains("BLOB") { // Rule 3
+                self = .BLOB
+            } else if ["REAL", "FLOA", "DOUB"].first(where: {test.contains($0)}) != nil { // Rule 4
+                self = .REAL
+            } else { // Rule 5
+                self = .NUMERIC
+            }
         }
     }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
index ef97b981..8b7e27e3 100644
--- a/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaDefinitionsTests.swift
@@ -71,8 +71,68 @@ class AffinityTests: XCTestCase {
         XCTAssertEqual(ColumnDefinition.Affinity("NUMERIC"), .NUMERIC)
     }
 
-    func test_returns_TEXT_for_unknown_type() {
-        XCTAssertEqual(ColumnDefinition.Affinity("baz"), .TEXT)
+    // [Determination Of Column Affinity](https://sqlite.org/datatype3.html#determination_of_column_affinity)
+    // Rule 1
+    func testIntegerAffinity() {
+        let declared = [
+            "INT",
+            "INTEGER",
+            "TINYINT",
+            "SMALLINT",
+            "MEDIUMINT",
+            "BIGINT",
+            "UNSIGNED BIG INT",
+            "INT2",
+            "INT8"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .INTEGER}))
+    }
+
+    // Rule 2
+    func testTextAffinity() {
+        let declared = [
+            "CHARACTER(20)",
+            "VARCHAR(255)",
+            "VARYING CHARACTER(255)",
+            "NCHAR(55)",
+            "NATIVE CHARACTER(70)",
+            "NVARCHAR(100)",
+            "TEXT",
+            "CLOB"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .TEXT}))
+    }
+
+    // Rule 3
+    func testBlobAffinity() {
+        XCTAssertEqual(ColumnDefinition.Affinity("BLOB"), .BLOB)
+    }
+
+    // Rule 4
+    func testRealAffinity() {
+        let declared = [
+            "REAL",
+            "DOUBLE",
+            "DOUBLE PRECISION",
+            "FLOAT"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .REAL}))
+    }
+
+    // Rule 5
+    func testNumericAffinity() {
+        let declared = [
+            "NUMERIC",
+            "DECIMAL(10,5)",
+            "BOOLEAN",
+            "DATE",
+            "DATETIME"
+        ]
+        XCTAssertTrue(declared.allSatisfy({ColumnDefinition.Affinity($0) == .NUMERIC}))
+    }
+
+    func test_returns_NUMERIC_for_unknown_type() {
+        XCTAssertEqual(ColumnDefinition.Affinity("baz"), .NUMERIC)
     }
 }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index dd5ae103..b03045de 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -40,7 +40,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              references: nil),
             ColumnDefinition(name: "admin",
                              primaryKey: nil,
-                             type: .TEXT,
+                             type: .NUMERIC,
                              nullable: false,
                              defaultValue: .numericLiteral("0"),
                              references: nil),
@@ -51,7 +51,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              references: .init(table: "users", column: "manager_id", primaryKey: "id", onUpdate: nil, onDelete: nil)),
             ColumnDefinition(name: "created_at",
                              primaryKey: nil,
-                             type: .TEXT,
+                             type: .NUMERIC,
                              nullable: true,
                              defaultValue: .NULL,
                              references: nil)

From 3023a1f63336f70c1470bdef59ac2dfded258206 Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Mon, 16 Oct 2023 07:33:54 -0500
Subject: [PATCH 18/95] Add optional support for decoding

---
 Sources/SQLite/Typed/Coding.swift | 48 +++++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index e7db03fb..8c7755c3 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -471,6 +471,54 @@ private class SQLiteDecoder: Decoder {
             }
         }
 
+        func decodeIfPresent(_ type: Bool.Type, forKey key: Key) throws -> Bool? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Int.Type, forKey key: Key) throws -> Int? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Int64.Type, forKey key: Key) throws -> Int64? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: Float.Type, forKey key: Key) throws -> Float? {
+            try? Float(row.get(Expression<Double>(key.stringValue)))
+        }
+
+        func decodeIfPresent(_ type: Double.Type, forKey key: Key) throws -> Double? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decodeIfPresent(_ type: String.Type, forKey key: Key) throws -> String? {
+            try? row.get(Expression(key.stringValue))
+        }
+
+        func decode<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
+            switch type {
+            case is Data.Type:
+                let data = try row.get(Expression<Data>(key.stringValue))
+                return data as? T
+            case is Date.Type:
+                let date = try row.get(Expression<Date>(key.stringValue))
+                return date as? T
+            case is UUID.Type:
+                let uuid = try row.get(Expression<UUID>(key.stringValue))
+                return uuid as? T
+            default:
+                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                    throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
+                                                                                 debugDescription: "an unsupported type was found"))
+                }
+                guard let data = JSONString.data(using: .utf8) else {
+                    throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,
+                                                                            debugDescription: "invalid utf8 data found"))
+                }
+                return try JSONDecoder().decode(type, from: data)
+            }
+        }
+
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws
         -> KeyedDecodingContainer<NestedKey> where NestedKey: CodingKey {
             throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,

From e7c259226373e9469d9166b842e5395dae302b6e Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Thu, 19 Oct 2023 07:13:41 -0500
Subject: [PATCH 19/95] Fix copypasta

---
 Sources/SQLite/Typed/Coding.swift | 19 +++++++++++--------
 1 file changed, 11 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 8c7755c3..f62f5b6b 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -495,19 +495,22 @@ private class SQLiteDecoder: Decoder {
             try? row.get(Expression(key.stringValue))
         }
 
-        func decode<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
+        func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
             switch type {
             case is Data.Type:
-                let data = try row.get(Expression<Data>(key.stringValue))
-                return data as? T
+                if let data = try? row.get(Expression<Data>(key.stringValue)) {
+                    return data as? T
+                }
             case is Date.Type:
-                let date = try row.get(Expression<Date>(key.stringValue))
-                return date as? T
+                if let date = try? row.get(Expression<Date>(key.stringValue)) {
+                    return date as? T
+                }
             case is UUID.Type:
-                let uuid = try row.get(Expression<UUID>(key.stringValue))
-                return uuid as? T
+                if let uuid = try? row.get(Expression<UUID>(key.stringValue)) {
+                    return uuid as? T
+                }
             default:
-                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
                     throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
                                                                                  debugDescription: "an unsupported type was found"))
                 }

From 128246f30a396ef864318e3a7ca851e90b3e84a1 Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Thu, 19 Oct 2023 11:04:58 -0500
Subject: [PATCH 20/95] Fix missing return

---
 Sources/SQLite/Typed/Coding.swift | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index f62f5b6b..52612063 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -520,6 +520,8 @@ private class SQLiteDecoder: Decoder {
                 }
                 return try JSONDecoder().decode(type, from: data)
             }
+
+            return nil
         }
 
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws

From 1c6bf76948de5b6ab1ef7a45629788e3c5c70d0a Mon Sep 17 00:00:00 2001
From: Jacob Hearst <jacob.hearst@jamf.com>
Date: Fri, 20 Oct 2023 07:20:58 -0500
Subject: [PATCH 21/95] Fix smoothbrain

---
 Sources/SQLite/Typed/Coding.swift | 14 +++-----------
 1 file changed, 3 insertions(+), 11 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index 52612063..bfddc5ee 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -498,17 +498,11 @@ private class SQLiteDecoder: Decoder {
         func decodeIfPresent<T>(_ type: T.Type, forKey key: Key) throws -> T? where T: Swift.Decodable {
             switch type {
             case is Data.Type:
-                if let data = try? row.get(Expression<Data>(key.stringValue)) {
-                    return data as? T
-                }
+                return try? row.get(Expression<Data>(key.stringValue)) as? T
             case is Date.Type:
-                if let date = try? row.get(Expression<Date>(key.stringValue)) {
-                    return date as? T
-                }
+                return try? row.get(Expression<Date>(key.stringValue)) as? T
             case is UUID.Type:
-                if let uuid = try? row.get(Expression<UUID>(key.stringValue)) {
-                    return uuid as? T
-                }
+                return try? row.get(Expression<UUID>(key.stringValue)) as? T
             default:
                 guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
                     throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
@@ -520,8 +514,6 @@ private class SQLiteDecoder: Decoder {
                 }
                 return try JSONDecoder().decode(type, from: data)
             }
-
-            return nil
         }
 
         func nestedContainer<NestedKey>(keyedBy type: NestedKey.Type, forKey key: Key) throws

From fc96d30b72db986b7f930d951eca14c16e32b9b5 Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Sat, 18 Nov 2023 18:30:28 -0800
Subject: [PATCH 22/95] Implements built-in window functions

---
 SQLite.xcodeproj/project.pbxproj              |  18 +++
 Sources/SQLite/Typed/AggregateFunctions.swift |   6 +-
 Sources/SQLite/Typed/WindowFunctions.swift    | 145 ++++++++++++++++++
 .../Typed/QueryIntegrationTests.swift         |  95 ++++++++++++
 .../Typed/WindowFunctionsTests.swift          |  58 +++++++
 5 files changed, 319 insertions(+), 3 deletions(-)
 create mode 100644 Sources/SQLite/Typed/WindowFunctions.swift
 create mode 100644 Tests/SQLiteTests/Typed/WindowFunctionsTests.swift

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..95ed6806 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -195,6 +195,13 @@
 		49EB68C51F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C61F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
 		49EB68C71F7B3CB400D89D40 /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		64A8EE432B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE442B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE452B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64A8EE462B095FBB00F583F7 /* WindowFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */; };
+		64B8E1702B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
+		64B8E1712B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
+		64B8E1722B09748000545AFB /* WindowFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */; };
 		997DF2AE287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2AF287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
 		997DF2B0287FC06D00F8DF95 /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
@@ -335,6 +342,8 @@
 		3DF7B79B2884C901005DD8CA /* Planning.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = Planning.md; sourceTree = "<group>"; };
 		3DFC0B862886C239001C8FC9 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Package.swift; sourceTree = "<group>"; };
 		49EB68C31F7B3CB400D89D40 /* Coding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coding.swift; sourceTree = "<group>"; };
+		64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFunctions.swift; sourceTree = "<group>"; };
+		64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowFunctionsTests.swift; sourceTree = "<group>"; };
 		997DF2AD287FC06D00F8DF95 /* Query+with.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Query+with.swift"; sourceTree = "<group>"; };
 		A121AC451CA35C79005A31D1 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
@@ -476,6 +485,7 @@
 				19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */,
 				19A1709D5BDD2691BA160012 /* SetterTests.swift */,
 				19A174FE5B47A97937A27276 /* RowTests.swift */,
+				64B8E16F2B09748000545AFB /* WindowFunctionsTests.swift */,
 			);
 			path = Typed;
 			sourceTree = "<group>";
@@ -607,6 +617,7 @@
 			isa = PBXGroup;
 			children = (
 				EE247AFA1C3F06E900AE3E12 /* AggregateFunctions.swift */,
+				64A8EE422B095FBB00F583F7 /* WindowFunctions.swift */,
 				EE247AFB1C3F06E900AE3E12 /* Collation.swift */,
 				EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */,
 				EE247AFD1C3F06E900AE3E12 /* CustomFunctions.swift */,
@@ -960,6 +971,7 @@
 				3DF7B78A28842972005DD8CA /* Connection+Attach.swift in Sources */,
 				03A65E811C6BB2FB0062603F /* CustomFunctions.swift in Sources */,
 				03A65E7A1C6BB2F70062603F /* Statement.swift in Sources */,
+				64A8EE452B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				03A65E741C6BB2DA0062603F /* Helpers.swift in Sources */,
 				03A65E831C6BB2FB0062603F /* Operators.swift in Sources */,
 				03A65E851C6BB2FB0062603F /* Schema.swift in Sources */,
@@ -1014,6 +1026,7 @@
 				19A17746150A815944A6820B /* SelectTests.swift in Sources */,
 				19A1766135CE9786B1878603 /* ValueTests.swift in Sources */,
 				19A177D5C6542E2D572162E5 /* QueryIntegrationTests.swift in Sources */,
+				64B8E1722B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A178DF5A96CFEFF1E271F6 /* AggregateFunctionsTests.swift in Sources */,
 				19A17437659BD7FD787D94A6 /* CustomAggregationTests.swift in Sources */,
 				19A17F907258E524B3CA2FAE /* SetterTests.swift in Sources */,
@@ -1057,6 +1070,7 @@
 				19A17DC282E36C4F41AA440B /* Errors.swift in Sources */,
 				19A173668D948AD4DF1F5352 /* DateAndTimeFunctions.swift in Sources */,
 				19A17DF8D4F13A20F5D2269E /* Result.swift in Sources */,
+				64A8EE462B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				19A17DFE05ED8B1F7C45F7EE /* SchemaChanger.swift in Sources */,
 				19A17D1BEABA610ABF003D67 /* SchemaDefinitions.swift in Sources */,
 				19A17A33EA026C2E2CEBAF36 /* Connection+Schema.swift in Sources */,
@@ -1081,6 +1095,7 @@
 				EE247B151C3F06E900AE3E12 /* Setter.swift in Sources */,
 				3DF7B78828842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B101C3F06E900AE3E12 /* CustomFunctions.swift in Sources */,
+				64A8EE432B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				EE247B091C3F06E900AE3E12 /* FTS4.swift in Sources */,
 				EE247B081C3F06E900AE3E12 /* Value.swift in Sources */,
 				EE247B121C3F06E900AE3E12 /* Operators.swift in Sources */,
@@ -1135,6 +1150,7 @@
 				19A17F7977364EC8CD33C3C3 /* SelectTests.swift in Sources */,
 				19A17FD22EF43DF428DD93BA /* ValueTests.swift in Sources */,
 				19A177AA5922527BBDC77CF9 /* QueryIntegrationTests.swift in Sources */,
+				64B8E1702B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A179786A6826D58A70F8BC /* AggregateFunctionsTests.swift in Sources */,
 				19A1793972BDDDB027C113BB /* CustomAggregationTests.swift in Sources */,
 				19A1773155AC2BF2CA86A473 /* SetterTests.swift in Sources */,
@@ -1161,6 +1177,7 @@
 				3DF7B78928842972005DD8CA /* Connection+Attach.swift in Sources */,
 				EE247B701C3F3FEC00AE3E12 /* CustomFunctions.swift in Sources */,
 				EE247B691C3F3FEC00AE3E12 /* Statement.swift in Sources */,
+				64A8EE442B095FBB00F583F7 /* WindowFunctions.swift in Sources */,
 				EE247B641C3F3FDB00AE3E12 /* Helpers.swift in Sources */,
 				EE247B721C3F3FEC00AE3E12 /* Operators.swift in Sources */,
 				EE247B741C3F3FEC00AE3E12 /* Schema.swift in Sources */,
@@ -1215,6 +1232,7 @@
 				19A17DE1FCDB5695702AD24D /* SelectTests.swift in Sources */,
 				19A1726002D24C14F876C8FE /* ValueTests.swift in Sources */,
 				19A173389E53CB24DFA8CEDD /* QueryIntegrationTests.swift in Sources */,
+				64B8E1712B09748000545AFB /* WindowFunctionsTests.swift in Sources */,
 				19A170C56745F9D722A73D77 /* AggregateFunctionsTests.swift in Sources */,
 				19A1772EBE65173EDFB1AFCA /* CustomAggregationTests.swift in Sources */,
 				19A17E0ABA6C415F014CD51C /* SetterTests.swift in Sources */,
diff --git a/Sources/SQLite/Typed/AggregateFunctions.swift b/Sources/SQLite/Typed/AggregateFunctions.swift
index bf4fb8fc..17fc4a20 100644
--- a/Sources/SQLite/Typed/AggregateFunctions.swift
+++ b/Sources/SQLite/Typed/AggregateFunctions.swift
@@ -166,7 +166,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.average
     ///     // avg("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `avg` aggregate
     ///   function.
     public var average: Expression<Double?> {
         Function.avg.wrap(self)
@@ -179,7 +179,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.sum
     ///     // sum("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `sum` aggregate
     ///   function.
     public var sum: Expression<UnderlyingType?> {
         Function.sum.wrap(self)
@@ -192,7 +192,7 @@ extension ExpressionType where UnderlyingType: Value, UnderlyingType.Datatype: N
     ///     salary.total
     ///     // total("salary")
     ///
-    /// - Returns: A copy of the expression wrapped with the `min` aggregate
+    /// - Returns: A copy of the expression wrapped with the `total` aggregate
     ///   function.
     public var total: Expression<Double> {
         Function.total.wrap(self)
diff --git a/Sources/SQLite/Typed/WindowFunctions.swift b/Sources/SQLite/Typed/WindowFunctions.swift
new file mode 100644
index 00000000..e71e1ada
--- /dev/null
+++ b/Sources/SQLite/Typed/WindowFunctions.swift
@@ -0,0 +1,145 @@
+import Foundation
+
+// see https://www.sqlite.org/windowfunctions.html#builtins
+private enum WindowFunction: String {
+    // swiftlint:disable identifier_name
+    case ntile
+    case row_number
+    case rank
+    case dense_rank
+    case percent_rank
+    case cume_dist
+    case lag
+    case lead
+    case first_value
+    case last_value
+    case nth_value
+    // swiftlint:enable identifier_name
+
+    func wrap<T>(_ value: Int? = nil) -> Expression<T> {
+        if let value {
+            return self.rawValue.wrap(Expression(value: value))
+        }
+        return Expression(literal: "\(rawValue)()")
+    }
+
+    func over<T>(value: Int? = nil, _ orderBy: Expressible) -> Expression<T> {
+        return Expression<T>(" ".join([
+            self.wrap(value),
+            Expression<T>("OVER (ORDER BY \(orderBy.expression.template))", orderBy.expression.bindings)
+        ]).expression)
+    }
+
+    func over<T>(valueExpr: Expressible, _ orderBy: Expressible) -> Expression<T> {
+        return Expression<T>(" ".join([
+            self.rawValue.wrap(valueExpr),
+            Expression<T>("OVER (ORDER BY \(orderBy.expression.template))", orderBy.expression.bindings)
+        ]).expression)
+    }
+}
+
+extension ExpressionType where UnderlyingType: Value {
+    /// Builds a copy of the expression with `lag(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `lag(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    public func lag(offset: Int = 0, default: Expressible? = nil, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        if let defaultExpression = `default` {
+            return Expression(
+                "lag(\(template), \(offset), \(defaultExpression.asSQL())) OVER (ORDER BY \(orderBy.expression.template))",
+                bindings + orderBy.expression.bindings
+            )
+
+        }
+        return Expression("lag(\(template), \(offset)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+
+    /// Builds a copy of the expression with `lead(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `lead(self, offset, default) OVER (ORDER BY {orderBy})` window function
+    public func lead(offset: Int = 0, default: Expressible? = nil, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        if let defaultExpression = `default` {
+            return Expression(
+                "lead(\(template), \(offset), \(defaultExpression.asSQL())) OVER (ORDER BY \(orderBy.expression.template))",
+                bindings + orderBy.expression.bindings)
+
+        }
+        return Expression("lead(\(template), \(offset)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+
+    /// Builds a copy of the expression with `first_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `first_value(self) OVER (ORDER BY {orderBy})` window function
+    public func firstValue(_ orderBy: Expressible) -> Expression<UnderlyingType> {
+        WindowFunction.first_value.over(valueExpr: self, orderBy)
+    }
+
+    /// Builds a copy of the expression with `last_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `last_value(self) OVER (ORDER BY {orderBy})` window function
+    public func lastValue(_ orderBy: Expressible) -> Expression<UnderlyingType> {
+        WindowFunction.last_value.over(valueExpr: self, orderBy)
+    }
+
+    /// Builds a copy of the expression with `nth_value(self) OVER (ORDER BY {orderBy})` window function
+    ///
+    /// - Parameter index: Row N of the window frame to return
+    /// - Parameter orderBy: Expression to evaluate window order
+    /// - Returns: An expression returning `nth_value(self) OVER (ORDER BY {orderBy})` window function
+    public func value(_ index: Int, _ orderBy: Expressible) -> Expression<UnderlyingType> {
+        Expression("nth_value(\(template), \(index)) OVER (ORDER BY \(orderBy.expression.template))", bindings + orderBy.expression.bindings)
+    }
+}
+
+/// Builds an expression representing `ntile(size) OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning  `ntile(size) OVER (ORDER BY {orderBy})`
+public func ntile(_ size: Int, _ orderBy: Expressible) -> Expression<Int> {
+//    Expression.ntile(size, orderBy)
+
+        WindowFunction.ntile.over(value: size, orderBy)
+}
+
+/// Builds an expression representing `row_count() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `row_count() OVER (ORDER BY {orderBy})`
+public func rowNumber(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.row_number.over(orderBy)
+}
+
+/// Builds an expression representing `rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `rank() OVER (ORDER BY {orderBy})`
+public func rank(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.rank.over(orderBy)
+}
+
+/// Builds an expression representing `dense_rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `dense_rank() OVER ('over')`
+public func denseRank(_ orderBy: Expressible) -> Expression<Int> {
+    WindowFunction.dense_rank.over(orderBy)
+}
+
+/// Builds an expression representing `percent_rank() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `percent_rank() OVER (ORDER BY {orderBy})`
+public func percentRank(_ orderBy: Expressible) -> Expression<Double> {
+    WindowFunction.percent_rank.over(orderBy)
+}
+
+/// Builds an expression representing `cume_dist() OVER (ORDER BY {orderBy})`
+///
+/// - Parameter orderBy: Expression to evaluate window order
+/// - Returns: An expression returning `cume_dist() OVER (ORDER BY {orderBy})`
+public func cumeDist(_ orderBy: Expressible) -> Expression<Double> {
+    WindowFunction.cume_dist.over(orderBy)
+}
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index d8d31a79..aa45cafe 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -322,6 +322,101 @@ class QueryIntegrationTests: SQLiteTestCase {
         XCTAssertNotNil(row[name])
         XCTAssertNotNil(row[email])
     }
+
+    func test_select_ntile_function() throws {
+        let users = Table("users")
+
+        try insertUser("Joey")
+        try insertUser("Timmy")
+        try insertUser("Jimmy")
+        try insertUser("Billy")
+
+        let bucket = ntile(1, id.asc)
+        try db.prepare(users.select(id, bucket)).forEach {
+            XCTAssertEqual($0[bucket], 1) // only 1 window
+        }
+    }
+
+    func test_select_cume_dist_function() throws {
+        let users = Table("users")
+
+        try insertUser("Joey")
+        try insertUser("Timmy")
+        try insertUser("Jimmy")
+        try insertUser("Billy")
+
+        let cumeDist = cumeDist(email)
+        let results = try db.prepare(users.select(id, cumeDist)).map {
+            $0[cumeDist]
+        }
+        XCTAssertEqual([0.25, 0.5, 0.75, 1], results)
+    }
+
+    func test_select_window_row_number() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let rowNumber = rowNumber(email.asc)
+        var expectedRowNum = 1
+        try db.prepare(users.select(id, rowNumber)).forEach {
+            // should retrieve row numbers in order of INSERT above
+            XCTAssertEqual($0[rowNumber], expectedRowNum)
+            expectedRowNum += 1
+        }
+    }
+
+    func test_select_window_ranking() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let percentRank = percentRank(email)
+        let actualPercentRank: [Int] = try db.prepare(users.select(id, percentRank)).map {
+            Int($0[percentRank] * 100)
+        }
+        XCTAssertEqual([0, 33, 66, 100], actualPercentRank)
+
+        let rank = rank(email)
+        let actualRank: [Int] = try db.prepare(users.select(id, rank)).map {
+            $0[rank]
+        }
+        XCTAssertEqual([1, 2, 3, 4], actualRank)
+
+        let denseRank = denseRank(email)
+        let actualDenseRank: [Int] = try db.prepare(users.select(id, denseRank)).map {
+            $0[denseRank]
+        }
+        XCTAssertEqual([1, 2, 3, 4], actualDenseRank)
+    }
+
+    func test_select_window_values() throws {
+        let users = Table("users")
+
+        try insertUser("Billy")
+        try insertUser("Jimmy")
+        try insertUser("Joey")
+        try insertUser("Timmy")
+
+        let firstValue = email.firstValue(email.desc)
+        try db.prepare(users.select(id, firstValue)).forEach {
+            XCTAssertEqual($0[firstValue], "Timmy@example.com") // should grab last email alphabetically
+        }
+
+        let lastValue = email.lastValue(email.asc)
+        var row = try db.pluck(users.select(id, lastValue))!
+        XCTAssertEqual(row[lastValue], "Billy@example.com")
+
+        let nthValue = email.value(1, email.asc)
+        row = try db.pluck(users.select(id, nthValue))!
+        XCTAssertEqual(row[nthValue], "Billy@example.com")
+    }
 }
 
 extension Connection {
diff --git a/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
new file mode 100644
index 00000000..6ded152b
--- /dev/null
+++ b/Tests/SQLiteTests/Typed/WindowFunctionsTests.swift
@@ -0,0 +1,58 @@
+import XCTest
+import SQLite
+
+class WindowFunctionsTests: XCTestCase {
+
+    func test_ntile_wrapsExpressionWithOverClause() {
+        assertSQL("ntile(1) OVER (ORDER BY \"int\" DESC)", ntile(1, int.desc))
+        assertSQL("ntile(20) OVER (ORDER BY \"intOptional\" ASC)", ntile(20, intOptional.asc))
+        assertSQL("ntile(20) OVER (ORDER BY \"double\" ASC)", ntile(20, double.asc))
+        assertSQL("ntile(1) OVER (ORDER BY \"doubleOptional\" ASC)", ntile(1, doubleOptional.asc))
+        assertSQL("ntile(1) OVER (ORDER BY \"int\" DESC)", ntile(1, int.desc))
+    }
+
+    func test_row_number_wrapsExpressionWithOverClause() {
+        assertSQL("row_number() OVER (ORDER BY \"int\" DESC)", rowNumber(int.desc))
+    }
+
+    func test_rank_wrapsExpressionWithOverClause() {
+        assertSQL("rank() OVER (ORDER BY \"int\" DESC)", rank(int.desc))
+    }
+
+    func test_dense_rank_wrapsExpressionWithOverClause() {
+        assertSQL("dense_rank() OVER (ORDER BY \"int\" DESC)", denseRank(int.desc))
+    }
+
+    func test_percent_rank_wrapsExpressionWithOverClause() {
+        assertSQL("percent_rank() OVER (ORDER BY \"int\" DESC)", percentRank(int.desc))
+    }
+
+    func test_cume_dist_wrapsExpressionWithOverClause() {
+        assertSQL("cume_dist() OVER (ORDER BY \"int\" DESC)", cumeDist(int.desc))
+    }
+
+    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))
+    }
+
+    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))
+    }
+
+    func test_firstValue_wrapsExpressionWithOverClause() {
+        assertSQL("first_value(\"int\") OVER (ORDER BY \"int\" DESC)", int.firstValue(int.desc))
+        assertSQL("first_value(\"double\") OVER (ORDER BY \"int\" DESC)", double.firstValue(int.desc))
+    }
+
+    func test_lastValue_wrapsExpressionWithOverClause() {
+        assertSQL("last_value(\"int\") OVER (ORDER BY \"int\" DESC)", int.lastValue(int.desc))
+    }
+
+    func test_nth_value_wrapsExpressionWithOverClause() {
+        assertSQL("nth_value(\"int\", 3) OVER (ORDER BY \"int\" DESC)", int.value(3, int.desc))
+    }
+}

From 2fc62a96f03112f77604285cd0ad24b60558c22a Mon Sep 17 00:00:00 2001
From: Geoff MacDonald <geoffmacd@gmail.com>
Date: Sat, 18 Nov 2023 18:42:41 -0800
Subject: [PATCH 23/95] update docs

---
 Documentation/Index.md | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index 5d72aa4b..a03c26a3 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -64,6 +64,7 @@
   - [Other Operators](#other-operators)
   - [Core SQLite Functions](#core-sqlite-functions)
   - [Aggregate SQLite Functions](#aggregate-sqlite-functions)
+  - [Window SQLite Functions](#window-sqlite-functions)
   - [Date and Time Functions](#date-and-time-functions)
   - [Custom SQL Functions](#custom-sql-functions)
   - [Custom Collations](#custom-collations)
@@ -1871,6 +1872,11 @@ Most of SQLite’s
 [aggregate functions](https://www.sqlite.org/lang_aggfunc.html) have been
 surfaced in and type-audited for SQLite.swift.
 
+## Window SQLite Functions
+
+Most of SQLite's [window functions](https://www.sqlite.org/windowfunctions.html) have been
+surfaced in and type-audited for SQLite.swift. Currently only `OVER (ORDER BY ...)` windowing is possible. 
+
 ## Date and Time functions
 
 SQLite's [date and time](https://www.sqlite.org/lang_datefunc.html)

From dedef242d9253e5d6169dfe91d9e67ba4597291b Mon Sep 17 00:00:00 2001
From: ivan <ivanlares1@gmail.com>
Date: Thu, 4 Jan 2024 11:22:10 -0800
Subject: [PATCH 24/95] Add privacy manifest

---
 SQLite.xcodeproj/project.pbxproj     | 16 ++++++++++++++++
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 ++++++++++++++
 2 files changed, 30 insertions(+)
 create mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..8eb1df66 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,13 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
+		EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -340,6 +347,7 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -546,6 +554,7 @@
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
+				EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
 				EE247AF91C3F06E900AE3E12 /* Typed */,
@@ -892,6 +901,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -899,6 +909,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79828846FED005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -907,6 +918,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -914,6 +926,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -921,6 +934,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79628846FCC005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -929,6 +943,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -936,6 +951,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79928847055005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..987771fa
--- /dev/null
+++ b/Sources/SQLite/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyTracking</key>
+	<false/>
+</dict>
+</plist>

From e7c4212a213f8dec5d68ec6e7acadeed8f4fb912 Mon Sep 17 00:00:00 2001
From: Anthony Miller <anthonymdev@gmail.com>
Date: Wed, 24 Jan 2024 15:18:34 -0800
Subject: [PATCH 25/95] Add visionOS support to Package.swift

---
 Package.swift | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/Package.swift b/Package.swift
index 70bde7ef..238661ae 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,4 +1,4 @@
-// swift-tools-version:5.7
+// swift-tools-version:5.9
 import PackageDescription
 
 let package = Package(
@@ -7,7 +7,8 @@ let package = Package(
         .iOS(.v11),
         .macOS(.v10_13),
         .watchOS(.v4),
-        .tvOS(.v11)
+        .tvOS(.v11),
+        .visionOS(.v1)
     ],
     products: [
         .library(

From 240f0802792cce82ebd0c27300cd1748b322e200 Mon Sep 17 00:00:00 2001
From: Anthony Miller <anthonymdev@gmail.com>
Date: Wed, 24 Jan 2024 15:18:43 -0800
Subject: [PATCH 26/95] Create visionOS target and test plan

---
 SQLite.xcodeproj/project.pbxproj              | 344 +++++++++++++++++-
 .../xcschemes/SQLite visionOS.xcscheme        |  71 ++++
 Tests/SQLite visionOS.xctestplan              |  24 ++
 3 files changed, 435 insertions(+), 4 deletions(-)
 create mode 100644 SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
 create mode 100644 Tests/SQLite visionOS.xctestplan

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index def32855..857c6fd9 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,6 +211,76 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */; };
+		DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
+		DEB306BE2B61CEF500F9D46B /* RTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF61C3F06E900AE3E12 /* RTree.swift */; };
+		DEB306BF2B61CEF500F9D46B /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
+		DEB306C02B61CEF500F9D46B /* URIQueryParameter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B790288449BA005DD8CA /* URIQueryParameter.swift */; };
+		DEB306C12B61CEF500F9D46B /* Foundation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF71C3F06E900AE3E12 /* Foundation.swift */; };
+		DEB306C22B61CEF500F9D46B /* Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEF1C3F06E900AE3E12 /* Connection.swift */; };
+		DEB306C32B61CEF500F9D46B /* Expression.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFE1C3F06E900AE3E12 /* Expression.swift */; };
+		DEB306C42B61CEF500F9D46B /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF81C3F06E900AE3E12 /* Helpers.swift */; };
+		DEB306C52B61CEF500F9D46B /* Collation.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFB1C3F06E900AE3E12 /* Collation.swift */; };
+		DEB306C62B61CEF500F9D46B /* Setter.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B021C3F06E900AE3E12 /* Setter.swift */; };
+		DEB306C72B61CEF500F9D46B /* Connection+Attach.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DF7B78728842972005DD8CA /* Connection+Attach.swift */; };
+		DEB306C82B61CEF500F9D46B /* CustomFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFD1C3F06E900AE3E12 /* CustomFunctions.swift */; };
+		DEB306C92B61CEF500F9D46B /* FTS4.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF51C3F06E900AE3E12 /* FTS4.swift */; };
+		DEB306CA2B61CEF500F9D46B /* Value.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF31C3F06E900AE3E12 /* Value.swift */; };
+		DEB306CB2B61CEF500F9D46B /* Operators.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFF1C3F06E900AE3E12 /* Operators.swift */; };
+		DEB306CC2B61CEF500F9D46B /* Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B011C3F06E900AE3E12 /* Schema.swift */; };
+		DEB306CD2B61CEF500F9D46B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B001C3F06E900AE3E12 /* Query.swift */; };
+		DEB306CE2B61CEF500F9D46B /* Statement.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AF21C3F06E900AE3E12 /* Statement.swift */; };
+		DEB306CF2B61CEF500F9D46B /* AggregateFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFA1C3F06E900AE3E12 /* AggregateFunctions.swift */; };
+		DEB306D02B61CEF500F9D46B /* FTS5.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1730E4390C775C25677D1 /* FTS5.swift */; };
+		DEB306D12B61CEF500F9D46B /* Cipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178A39ACA9667A62663CC /* Cipher.swift */; };
+		DEB306D22B61CEF500F9D46B /* Backup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02A43A9722738CF100FEC494 /* Backup.swift */; };
+		DEB306D32B61CEF500F9D46B /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1710E73A46D5AC721CDA9 /* Errors.swift */; };
+		DEB306D42B61CEF500F9D46B /* DateAndTimeFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BA55DABB480F9020C8A /* DateAndTimeFunctions.swift */; };
+		DEB306D52B61CEF500F9D46B /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DEB306D62B61CEF500F9D46B /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17E723300E5ED3771DCB5 /* Result.swift */; };
+		DEB306D72B61CEF500F9D46B /* Query+with.swift in Sources */ = {isa = PBXBuildFile; fileRef = 997DF2AD287FC06D00F8DF95 /* Query+with.swift */; };
+		DEB306D82B61CEF500F9D46B /* Connection+Aggregation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A175A9CB446640AE6F2200 /* Connection+Aggregation.swift */; };
+		DEB306D92B61CEF500F9D46B /* SQLiteFeature.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */; };
+		DEB306DA2B61CEF500F9D46B /* SchemaChanger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171B262DDE8718513CFDA /* SchemaChanger.swift */; };
+		DEB306DB2B61CEF500F9D46B /* SchemaDefinitions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17268AE67B746B96AC125 /* SchemaDefinitions.swift */; };
+		DEB306DC2B61CEF500F9D46B /* Connection+Schema.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170A97B51DC5EE365F3C5 /* Connection+Schema.swift */; };
+		DEB306DD2B61CEF500F9D46B /* Connection+Pragmas.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F285B767BFACD96714B /* Connection+Pragmas.swift */; };
+		DEB306DE2B61CEF500F9D46B /* SchemaReader.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB58B21028FB864300F8EEA4 /* SchemaReader.swift */; };
+		DEB306E02B61CEF500F9D46B /* libsqlite3.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = EE9180931C46EA210038162A /* libsqlite3.tbd */; };
+		DEB306EB2B61CF9500F9D46B /* TestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247B161C3F127200AE3E12 /* TestHelpers.swift */; };
+		DEB306EC2B61CF9500F9D46B /* FoundationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1794CC4D7827E997E32A7 /* FoundationTests.swift */; };
+		DEB306ED2B61CF9500F9D46B /* Fixtures.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B93B48B5560E6E51791 /* Fixtures.swift */; };
+		DEB306EE2B61CF9500F9D46B /* SchemaDefinitionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A176174862D0F0139B3987 /* SchemaDefinitionsTests.swift */; };
+		DEB306EF2B61CF9500F9D46B /* SchemaChangerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17FDB0B0CFB8987906FD0 /* SchemaChangerTests.swift */; };
+		DEB306F02B61CF9500F9D46B /* Connection+SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CA1DF7D0F7C9A94C51C /* Connection+SchemaTests.swift */; };
+		DEB306F12B61CF9500F9D46B /* FTS5Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B96EBD42C878E609CDC /* FTS5Tests.swift */; };
+		DEB306F22B61CF9500F9D46B /* FTSIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17BE1CC4AD4036BAB8EE0 /* FTSIntegrationTests.swift */; };
+		DEB306F32B61CF9500F9D46B /* FTS4Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1715904F7B6851FCB5EF6 /* FTS4Tests.swift */; };
+		DEB306F42B61CF9500F9D46B /* ExpressionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170F141BF21946D159083 /* ExpressionTests.swift */; };
+		DEB306F52B61CF9500F9D46B /* StatementTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1756EB81E9F7F45B12A78 /* StatementTests.swift */; };
+		DEB306F62B61CF9500F9D46B /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171ED017645C8B04DF9F2 /* QueryTests.swift */; };
+		DEB306F72B61CF9500F9D46B /* CipherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1787E16C8562C09C076F5 /* CipherTests.swift */; };
+		DEB306F82B61CF9500F9D46B /* BlobTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A2ED4E2640F197F48C /* BlobTests.swift */; };
+		DEB306F92B61CF9500F9D46B /* ConnectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17855BD524FF888265B3C /* ConnectionTests.swift */; };
+		DEB306FA2B61CF9500F9D46B /* CoreFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17B55F2409B7031443495 /* CoreFunctionsTests.swift */; };
+		DEB306FB2B61CF9500F9D46B /* DateAndTimeFunctionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A178AEF32ABC8BF2993FB5 /* DateAndTimeFunctionTests.swift */; };
+		DEB306FC2B61CF9500F9D46B /* CustomFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17F2B5959B212A33C383F /* CustomFunctionsTests.swift */; };
+		DEB306FD2B61CF9500F9D46B /* OperatorsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17475DCA068453F787613 /* OperatorsTests.swift */; };
+		DEB306FE2B61CF9500F9D46B /* ResultTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1745BE8623D8C6808DB3C /* ResultTests.swift */; };
+		DEB306FF2B61CF9500F9D46B /* RTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17162C9861E5C4900455D /* RTreeTests.swift */; };
+		DEB307002B61CF9500F9D46B /* SchemaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A171A7714C6524093255C5 /* SchemaTests.swift */; };
+		DEB307012B61CF9500F9D46B /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17EC0C43015063945D32E /* SelectTests.swift */; };
+		DEB307022B61CF9500F9D46B /* ValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17AE284BB1DF31D1B753E /* ValueTests.swift */; };
+		DEB307032B61CF9500F9D46B /* QueryIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17CAE60446607E99C22A6 /* QueryIntegrationTests.swift */; };
+		DEB307042B61CF9500F9D46B /* AggregateFunctionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17613F00A3F4D4A257A04 /* AggregateFunctionsTests.swift */; };
+		DEB307052B61CF9500F9D46B /* CustomAggregationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A177EF5E2D91BA86DA4480 /* CustomAggregationTests.swift */; };
+		DEB307062B61CF9500F9D46B /* SetterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A1709D5BDD2691BA160012 /* SetterTests.swift */; };
+		DEB307072B61CF9500F9D46B /* RowTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A174FE5B47A97937A27276 /* RowTests.swift */; };
+		DEB307082B61CF9500F9D46B /* Connection+PragmaTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A17C60D3464461057E4D63 /* Connection+PragmaTests.swift */; };
+		DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
+		DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
+		DEB3070D2B61CF9500F9D46B /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -263,6 +333,13 @@
 			remoteGlobalIDString = 03A65E591C6BB0F50062603F;
 			remoteInfo = "SQLite tvOS";
 		};
+		DEB307142B61D07F00F9D46B /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = EE247ACA1C3F04ED00AE3E12 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = DEB306B82B61CEF500F9D46B;
+			remoteInfo = "SQLite visionOS";
+		};
 		EE247ADF1C3F04ED00AE3E12 /* PBXContainerItemProxy */ = {
 			isa = PBXContainerItemProxy;
 			containerPortal = EE247ACA1C3F04ED00AE3E12 /* Project object */;
@@ -340,6 +417,9 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DEB306E52B61CEF500F9D46B /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests visionOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
+		DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = "SQLite visionOS.xctestplan"; path = "Tests/SQLite visionOS.xctestplan"; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -401,6 +481,22 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306DF2B61CEF500F9D46B /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306E02B61CEF500F9D46B /* libsqlite3.tbd in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB3070A2B61CF9500F9D46B /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247ACF1C3F04ED00AE3E12 /* Frameworks */ = {
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
@@ -517,6 +613,7 @@
 				3D3C3CCB26E5568800759140 /* SQLite.playground */,
 				EE247AD51C3F04ED00AE3E12 /* SQLite */,
 				EE247AE11C3F04ED00AE3E12 /* SQLiteTests */,
+				DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */,
 				EE247B8A1C3F81D000AE3E12 /* Metadata */,
 				EE247AD41C3F04ED00AE3E12 /* Products */,
 				3D67B3E41DB2469200A4F4C6 /* Frameworks */,
@@ -535,6 +632,8 @@
 				03A65E5A1C6BB0F50062603F /* SQLite.framework */,
 				03A65E631C6BB0F60062603F /* SQLiteTests tvOS.xctest */,
 				A121AC451CA35C79005A31D1 /* SQLite.framework */,
+				DEB306E52B61CEF500F9D46B /* SQLite.framework */,
+				DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */,
 			);
 			name = Products;
 			sourceTree = "<group>";
@@ -679,6 +778,14 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306B92B61CEF500F9D46B /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247AD01C3F04ED00AE3E12 /* Headers */ = {
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
@@ -752,6 +859,42 @@
 			productReference = A121AC451CA35C79005A31D1 /* SQLite.framework */;
 			productType = "com.apple.product-type.framework";
 		};
+		DEB306B82B61CEF500F9D46B /* SQLite visionOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = DEB306E22B61CEF500F9D46B /* Build configuration list for PBXNativeTarget "SQLite visionOS" */;
+			buildPhases = (
+				DEB306B92B61CEF500F9D46B /* Headers */,
+				DEB306BB2B61CEF500F9D46B /* Sources */,
+				DEB306DF2B61CEF500F9D46B /* Frameworks */,
+				DEB306E12B61CEF500F9D46B /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = "SQLite visionOS";
+			productName = SQLite;
+			productReference = DEB306E52B61CEF500F9D46B /* SQLite.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		DEB306E72B61CF9500F9D46B /* SQLiteTests visionOS */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = DEB3070E2B61CF9500F9D46B /* Build configuration list for PBXNativeTarget "SQLiteTests visionOS" */;
+			buildPhases = (
+				DEB306EA2B61CF9500F9D46B /* Sources */,
+				DEB3070A2B61CF9500F9D46B /* Frameworks */,
+				DEB3070C2B61CF9500F9D46B /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				DEB307152B61D07F00F9D46B /* PBXTargetDependency */,
+			);
+			name = "SQLiteTests visionOS";
+			productName = "SQLite tvOSTests";
+			productReference = DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
 		EE247AD21C3F04ED00AE3E12 /* SQLite iOS */ = {
 			isa = PBXNativeTarget;
 			buildConfigurationList = EE247AE71C3F04ED00AE3E12 /* Build configuration list for PBXNativeTarget "SQLite iOS" */;
@@ -883,6 +1026,8 @@
 				03A65E591C6BB0F50062603F /* SQLite tvOS */,
 				03A65E621C6BB0F60062603F /* SQLiteTests tvOS */,
 				A121AC441CA35C79005A31D1 /* SQLite watchOS */,
+				DEB306B82B61CEF500F9D46B /* SQLite visionOS */,
+				DEB306E72B61CF9500F9D46B /* SQLiteTests visionOS */,
 			);
 		};
 /* End PBXProject section */
@@ -910,6 +1055,21 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306E12B61CEF500F9D46B /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB3070C2B61CF9500F9D46B /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB3070D2B61CF9500F9D46B /* Resources in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247AD11C3F04ED00AE3E12 /* Resources */ = {
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -1064,6 +1224,86 @@
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
+		DEB306BB2B61CEF500F9D46B /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */,
+				DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */,
+				DEB306BE2B61CEF500F9D46B /* RTree.swift in Sources */,
+				DEB306BF2B61CEF500F9D46B /* Blob.swift in Sources */,
+				DEB306C02B61CEF500F9D46B /* URIQueryParameter.swift in Sources */,
+				DEB306C12B61CEF500F9D46B /* Foundation.swift in Sources */,
+				DEB306C22B61CEF500F9D46B /* Connection.swift in Sources */,
+				DEB306C32B61CEF500F9D46B /* Expression.swift in Sources */,
+				DEB306C42B61CEF500F9D46B /* Helpers.swift in Sources */,
+				DEB306C52B61CEF500F9D46B /* Collation.swift in Sources */,
+				DEB306C62B61CEF500F9D46B /* Setter.swift in Sources */,
+				DEB306C72B61CEF500F9D46B /* Connection+Attach.swift in Sources */,
+				DEB306C82B61CEF500F9D46B /* CustomFunctions.swift in Sources */,
+				DEB306C92B61CEF500F9D46B /* FTS4.swift in Sources */,
+				DEB306CA2B61CEF500F9D46B /* Value.swift in Sources */,
+				DEB306CB2B61CEF500F9D46B /* Operators.swift in Sources */,
+				DEB306CC2B61CEF500F9D46B /* Schema.swift in Sources */,
+				DEB306CD2B61CEF500F9D46B /* Query.swift in Sources */,
+				DEB306CE2B61CEF500F9D46B /* Statement.swift in Sources */,
+				DEB306CF2B61CEF500F9D46B /* AggregateFunctions.swift in Sources */,
+				DEB306D02B61CEF500F9D46B /* FTS5.swift in Sources */,
+				DEB306D12B61CEF500F9D46B /* Cipher.swift in Sources */,
+				DEB306D22B61CEF500F9D46B /* Backup.swift in Sources */,
+				DEB306D32B61CEF500F9D46B /* Errors.swift in Sources */,
+				DEB306D42B61CEF500F9D46B /* DateAndTimeFunctions.swift in Sources */,
+				DEB306D52B61CEF500F9D46B /* SQLiteVersion.swift in Sources */,
+				DEB306D62B61CEF500F9D46B /* Result.swift in Sources */,
+				DEB306D72B61CEF500F9D46B /* Query+with.swift in Sources */,
+				DEB306D82B61CEF500F9D46B /* Connection+Aggregation.swift in Sources */,
+				DEB306D92B61CEF500F9D46B /* SQLiteFeature.swift in Sources */,
+				DEB306DA2B61CEF500F9D46B /* SchemaChanger.swift in Sources */,
+				DEB306DB2B61CEF500F9D46B /* SchemaDefinitions.swift in Sources */,
+				DEB306DC2B61CEF500F9D46B /* Connection+Schema.swift in Sources */,
+				DEB306DD2B61CEF500F9D46B /* Connection+Pragmas.swift in Sources */,
+				DEB306DE2B61CEF500F9D46B /* SchemaReader.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DEB306EA2B61CF9500F9D46B /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DEB306EB2B61CF9500F9D46B /* TestHelpers.swift in Sources */,
+				DEB306EC2B61CF9500F9D46B /* FoundationTests.swift in Sources */,
+				DEB306ED2B61CF9500F9D46B /* Fixtures.swift in Sources */,
+				DEB306EE2B61CF9500F9D46B /* SchemaDefinitionsTests.swift in Sources */,
+				DEB306EF2B61CF9500F9D46B /* SchemaChangerTests.swift in Sources */,
+				DEB306F02B61CF9500F9D46B /* Connection+SchemaTests.swift in Sources */,
+				DEB306F12B61CF9500F9D46B /* FTS5Tests.swift in Sources */,
+				DEB306F22B61CF9500F9D46B /* FTSIntegrationTests.swift in Sources */,
+				DEB306F32B61CF9500F9D46B /* FTS4Tests.swift in Sources */,
+				DEB306F42B61CF9500F9D46B /* ExpressionTests.swift in Sources */,
+				DEB306F52B61CF9500F9D46B /* StatementTests.swift in Sources */,
+				DEB306F62B61CF9500F9D46B /* QueryTests.swift in Sources */,
+				DEB306F72B61CF9500F9D46B /* CipherTests.swift in Sources */,
+				DEB306F82B61CF9500F9D46B /* BlobTests.swift in Sources */,
+				DEB306F92B61CF9500F9D46B /* ConnectionTests.swift in Sources */,
+				DEB306FA2B61CF9500F9D46B /* CoreFunctionsTests.swift in Sources */,
+				DEB306FB2B61CF9500F9D46B /* DateAndTimeFunctionTests.swift in Sources */,
+				DEB306FC2B61CF9500F9D46B /* CustomFunctionsTests.swift in Sources */,
+				DEB306FD2B61CF9500F9D46B /* OperatorsTests.swift in Sources */,
+				DEB306FE2B61CF9500F9D46B /* ResultTests.swift in Sources */,
+				DEB306FF2B61CF9500F9D46B /* RTreeTests.swift in Sources */,
+				DEB307002B61CF9500F9D46B /* SchemaTests.swift in Sources */,
+				DEB307012B61CF9500F9D46B /* SelectTests.swift in Sources */,
+				DEB307022B61CF9500F9D46B /* ValueTests.swift in Sources */,
+				DEB307032B61CF9500F9D46B /* QueryIntegrationTests.swift in Sources */,
+				DEB307042B61CF9500F9D46B /* AggregateFunctionsTests.swift in Sources */,
+				DEB307052B61CF9500F9D46B /* CustomAggregationTests.swift in Sources */,
+				DEB307062B61CF9500F9D46B /* SetterTests.swift in Sources */,
+				DEB307072B61CF9500F9D46B /* RowTests.swift in Sources */,
+				DEB307082B61CF9500F9D46B /* Connection+PragmaTests.swift in Sources */,
+				DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
 		EE247ACE1C3F04ED00AE3E12 /* Sources */ = {
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
@@ -1232,6 +1472,11 @@
 			target = 03A65E591C6BB0F50062603F /* SQLite tvOS */;
 			targetProxy = 03A65E651C6BB0F60062603F /* PBXContainerItemProxy */;
 		};
+		DEB307152B61D07F00F9D46B /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = DEB306B82B61CEF500F9D46B /* SQLite visionOS */;
+			targetProxy = DEB307142B61D07F00F9D46B /* PBXContainerItemProxy */;
+		};
 		EE247AE01C3F04ED00AE3E12 /* PBXTargetDependency */ = {
 			isa = PBXTargetDependency;
 			target = EE247AD21C3F04ED00AE3E12 /* SQLite iOS */;
@@ -1365,6 +1610,83 @@
 			};
 			name = Release;
 		};
+		DEB306E32B61CEF500F9D46B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				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.14.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
+				PRODUCT_NAME = SQLite;
+				SDKROOT = xros;
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Debug;
+		};
+		DEB306E42B61CEF500F9D46B /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				CLANG_ENABLE_MODULES = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				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.14.0;
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
+				PRODUCT_NAME = SQLite;
+				SDKROOT = xros;
+				SKIP_INSTALL = YES;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Release;
+		};
+		DEB3070F2B61CF9500F9D46B /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = xros;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Debug;
+		};
+		DEB307102B61CF9500F9D46B /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
+				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = xros;
+				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
+				TARGETED_DEVICE_FAMILY = 7;
+			};
+			name = Release;
+		};
 		EE247AE51C3F04ED00AE3E12 /* Debug */ = {
 			isa = XCBuildConfiguration;
 			buildSettings = {
@@ -1502,7 +1824,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1526,7 +1847,6 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1541,7 +1861,6 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1554,7 +1873,6 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1674,6 +1992,24 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
+		DEB306E22B61CEF500F9D46B /* Build configuration list for PBXNativeTarget "SQLite visionOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DEB306E32B61CEF500F9D46B /* Debug */,
+				DEB306E42B61CEF500F9D46B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		DEB3070E2B61CF9500F9D46B /* Build configuration list for PBXNativeTarget "SQLiteTests visionOS" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DEB3070F2B61CF9500F9D46B /* Debug */,
+				DEB307102B61CF9500F9D46B /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
 		EE247ACD1C3F04ED00AE3E12 /* Build configuration list for PBXProject "SQLite" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
diff --git a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
new file mode 100644
index 00000000..58783573
--- /dev/null
+++ b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1520"
+   version = "1.7">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
+               BuildableName = "SQLite visionOS.framework"
+               BlueprintName = "SQLite visionOS"
+               ReferencedContainer = "container:SQLite.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <TestPlans>
+         <TestPlanReference
+            reference = "container:Tests/SQLite visionOS.xctestplan"
+            default = "YES">
+         </TestPlanReference>
+      </TestPlans>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
+            BuildableName = "SQLite visionOS.framework"
+            BlueprintName = "SQLite visionOS"
+            ReferencedContainer = "container:SQLite.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/Tests/SQLite visionOS.xctestplan b/Tests/SQLite visionOS.xctestplan
new file mode 100644
index 00000000..9b4e90ed
--- /dev/null
+++ b/Tests/SQLite visionOS.xctestplan	
@@ -0,0 +1,24 @@
+{
+  "configurations" : [
+    {
+      "id" : "72B91FD4-441C-4C06-9E92-CAEDCB7325AB",
+      "name" : "Configuration 1",
+      "options" : {
+
+      }
+    }
+  ],
+  "defaultOptions" : {
+
+  },
+  "testTargets" : [
+    {
+      "target" : {
+        "containerPath" : "container:SQLite.xcodeproj",
+        "identifier" : "DEB306E72B61CF9500F9D46B",
+        "name" : "SQLiteTests visionOS"
+      }
+    }
+  ],
+  "version" : 1
+}

From 9043277c83fcddf18d325192616ec5214c3a50df Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 18:54:57 +0100
Subject: [PATCH 27/95] Using macOS 14 to build

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9cc2f56e..d4fc87d8 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,7 +5,7 @@ env:
   IOS_VERSION: "16.2"
 jobs:
   build:
-    runs-on: macos-13
+    runs-on: macos-14
     steps:
     - uses: actions/checkout@v2
     - name: "Select Xcode"

From 623ac53da9a23e6e8d53acdcc07291b8496580c5 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 18:56:12 +0100
Subject: [PATCH 28/95] Xcode 15?

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index d4fc87d8..ec25e7bf 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
       # https://github.com/CocoaPods/CocoaPods/issues/11839
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_14.2.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"

From 0c68b99da0e8378a0d43c7ad6911b98e437dd8c1 Mon Sep 17 00:00:00 2001
From: Nathan Fallet <contact@nathanfallet.me>
Date: Tue, 6 Feb 2024 19:00:02 +0100
Subject: [PATCH 29/95] Testing using iOS 17

---
 .github/workflows/build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index ec25e7bf..7be89270 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,8 +1,8 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: "iPhone 14"
-  IOS_VERSION: "16.2"
+  IOS_SIMULATOR: "iPhone 15"
+  IOS_VERSION: "17.2"
 jobs:
   build:
     runs-on: macos-14

From 03724bf3d28a16ac0197027d47956b8039c42e69 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Elfred=20Pag=C3=A1n?= <elfred@elfredpagan.com>
Date: Tue, 20 Feb 2024 20:06:37 -0600
Subject: [PATCH 30/95] make fromDatatypeValue throw

When using custom types, sometimes decoding can fail, say due to
changes in the type structure. In this case decoding would fail
and the only way to handle it is forcing a crash.

This change allows you to use `try row.get()` instead. Givng you
the chance to handle the mismatch.
---
 Sources/SQLite/Core/Value.swift        |  2 +-
 Sources/SQLite/Helpers.swift           |  4 ++--
 Sources/SQLite/Typed/Query.swift       | 12 +++++------
 Tests/SQLiteTests/Typed/RowTests.swift | 30 ++++++++++++++++++++++++++
 4 files changed, 39 insertions(+), 9 deletions(-)

diff --git a/Sources/SQLite/Core/Value.swift b/Sources/SQLite/Core/Value.swift
index 9c463f0c..249a7728 100644
--- a/Sources/SQLite/Core/Value.swift
+++ b/Sources/SQLite/Core/Value.swift
@@ -39,7 +39,7 @@ public protocol Value: Expressible { // extensions cannot have inheritance claus
 
     static var declaredDatatype: String { get }
 
-    static func fromDatatypeValue(_ datatypeValue: Datatype) -> ValueType
+    static func fromDatatypeValue(_ datatypeValue: Datatype) throws -> ValueType
 
     var datatypeValue: Datatype { get }
 
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index adfadc8a..c27ccf05 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -121,9 +121,9 @@ func transcode(_ literal: Binding?) -> String {
     }
 }
 
-// swiftlint:disable force_cast
+// swiftlint:disable force_cast force_try
 func value<A: Value>(_ binding: Binding) -> A {
-    A.fromDatatypeValue(binding as! A.Datatype) as! A
+    try! A.fromDatatypeValue(binding as! A.Datatype) as! A
 }
 
 func value<A: Value>(_ binding: Binding?) -> A {
diff --git a/Sources/SQLite/Typed/Query.swift b/Sources/SQLite/Typed/Query.swift
index c59b728f..2a83665c 100644
--- a/Sources/SQLite/Typed/Query.swift
+++ b/Sources/SQLite/Typed/Query.swift
@@ -1097,7 +1097,7 @@ extension Connection {
     public func scalar<V: Value>(_ query: ScalarQuery<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
-        return V.fromDatatypeValue(value)
+        return try V.fromDatatypeValue(value)
     }
 
     public func scalar<V: Value>(_ query: Select<V>) throws -> V {
@@ -1108,7 +1108,7 @@ extension Connection {
     public func scalar<V: Value>(_ query: Select<V?>) throws -> V.ValueType? {
         let expression = query.expression
         guard let value = try scalar(expression.template, expression.bindings) as? V.Datatype else { return nil }
-        return V.fromDatatypeValue(value)
+        return try V.fromDatatypeValue(value)
     }
 
     public func pluck(_ query: QueryType) throws -> Row? {
@@ -1200,9 +1200,9 @@ public struct Row {
     }
 
     public func get<V: Value>(_ column: Expression<V?>) throws -> V? {
-        func valueAtIndex(_ idx: Int) -> V? {
+        func valueAtIndex(_ idx: Int) throws -> V? {
             guard let value = values[idx] as? V.Datatype else { return nil }
-            return V.fromDatatypeValue(value) as? V
+            return try V.fromDatatypeValue(value) as? V
         }
 
         guard let idx = columnNames[column.template] else {
@@ -1224,10 +1224,10 @@ public struct Row {
                     similar: columnNames.keys.filter(similar).sorted()
                 )
             }
-            return valueAtIndex(columnNames[firstIndex].value)
+            return try valueAtIndex(columnNames[firstIndex].value)
         }
 
-        return valueAtIndex(idx)
+        return try valueAtIndex(idx)
     }
 
     public subscript<T: Value>(column: Expression<T>) -> T {
diff --git a/Tests/SQLiteTests/Typed/RowTests.swift b/Tests/SQLiteTests/Typed/RowTests.swift
index 506f6b10..14fa373b 100644
--- a/Tests/SQLiteTests/Typed/RowTests.swift
+++ b/Tests/SQLiteTests/Typed/RowTests.swift
@@ -85,4 +85,34 @@ class RowTests: XCTestCase {
             }
         }
     }
+
+    public func test_get_datatype_throws() {
+        // swiftlint:disable nesting
+        struct MyType: Value {
+            enum MyError: Error {
+                case failed
+            }
+
+            public static var declaredDatatype: String {
+                Blob.declaredDatatype
+            }
+
+            public static func fromDatatypeValue(_ dataValue: Blob) throws -> Data {
+                throw MyError.failed
+            }
+
+            public var datatypeValue: Blob {
+                return Blob(bytes: [])
+            }
+        }
+
+        let row = Row(["\"foo\"": 0], [Blob(bytes: [])])
+        XCTAssertThrowsError(try row.get(Expression<MyType>("foo"))) { error in
+            if case MyType.MyError.failed = error {
+                XCTAssertTrue(true)
+            } else {
+                XCTFail("unexpected error: \(error)")
+            }
+        }
+    }
 }

From 5bfaddf455a152a276e534b24c98ff08c67d3f63 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 13:50:42 +0100
Subject: [PATCH 31/95] fix: json tests (due to a swift upgrade)

---
 Tests/SQLiteTests/TestHelpers.swift      | 10 ++++++++++
 Tests/SQLiteTests/Typed/QueryTests.swift | 15 ++++++++++++---
 2 files changed, 22 insertions(+), 3 deletions(-)

diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 65cff8bb..e2da5927 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -105,6 +105,16 @@ func assertSQL(_ expression1: @autoclosure () -> String, _ expression2: @autoclo
     XCTAssertEqual(expression1(), expression2().asSQL(), file: file, line: line)
 }
 
+func extractAndReplace(_ value: String, regex: String, with replacement: String) -> (String, String) {
+    // We cannot use `Regex` because it is not available before iOS 16 :(
+    let regex = try! NSRegularExpression(pattern: regex)
+    let valueRange = NSRange(location: 0, length: value.utf16.count)
+    let match = regex.firstMatch(in: value, options: [], range: valueRange)!.range
+    let range = Range(match, in: value)!
+    let extractedValue = String(value[range])
+    return (value.replacingCharacters(in: range, with: replacement), extractedValue)
+}
+
 let table = Table("table")
 let qualifiedTable = Table("table", database: "main")
 let virtualTable = VirtualTable("virtual_table")
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index 82a33808..f698c621 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -364,13 +364,22 @@ class QueryTests: XCTestCase {
         let insert = try emails.insert(value)
         let encodedJSON = try JSONEncoder().encode(value1)
         let encodedJSONString = String(data: encodedJSON, encoding: .utf8)!
-        assertSQL(
+
+        let expectedSQL =
             """
             INSERT INTO \"emails\" (\"int\", \"string\", \"bool\", \"float\", \"double\", \"date\", \"uuid\", \"optional\",
              \"sub\") VALUES (1, '2', 1, 3.0, 4.0, '1970-01-01T00:00:00.000', 'E621E1F8-C36C-495A-93FC-0C247A3E6E5F',
              'optional', '\(encodedJSONString)')
-            """.replacingOccurrences(of: "\n", with: ""),
-            insert
+            """.replacingOccurrences(of: "\n", with: "")
+
+        // As JSON serialization gives a different result each time, we extract JSON and compare it by deserializing it
+        // and keep comparing the query but with the json replaced by the `JSON` string
+        let (expectedQuery, expectedJSON) = extractAndReplace(expectedSQL, regex: "\\{.*\\}", with: "JSON")
+        let (actualQuery, actualJSON) = extractAndReplace(insert.asSQL(), regex: "\\{.*\\}", with: "JSON")
+        XCTAssertEqual(expectedQuery, actualQuery)
+        XCTAssertEqual(
+            try JSONDecoder().decode(TestCodable.self, from: expectedJSON.data(using: .utf8)!),
+            try JSONDecoder().decode(TestCodable.self, from: actualJSON.data(using: .utf8)!)
         )
     }
     #endif

From 485b677b8991287bf9b91de2b7d18f3eff21c38e Mon Sep 17 00:00:00 2001
From: Nathan Fallet <nathan.fallet@gmail.com>
Date: Fri, 23 Feb 2024 14:26:22 +0100
Subject: [PATCH 32/95] Revert "Add privacy manifest"

---
 SQLite.xcodeproj/project.pbxproj     | 16 ----------------
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 --------------
 2 files changed, 30 deletions(-)
 delete mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 8eb1df66..def32855 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -211,13 +211,6 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
-		EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
-		EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -347,7 +340,6 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
-		EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -554,7 +546,6 @@
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
-				EAE1E1532B473B0C0048B157 /* PrivacyInfo.xcprivacy */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
 				EE247AF91C3F06E900AE3E12 /* Typed */,
@@ -901,7 +892,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1582B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -909,7 +899,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1592B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79828846FED005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -918,7 +907,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E15A2B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -926,7 +914,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1542B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -934,7 +921,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1552B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79628846FCC005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -943,7 +929,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1562B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -951,7 +936,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				EAE1E1572B473B0C0048B157 /* PrivacyInfo.xcprivacy in Resources */,
 				3DF7B79928847055005DD8CA /* Resources in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
deleted file mode 100644
index 987771fa..00000000
--- a/Sources/SQLite/PrivacyInfo.xcprivacy
+++ /dev/null
@@ -1,14 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
-	<key>NSPrivacyTrackingDomains</key>
-	<array/>
-	<key>NSPrivacyCollectedDataTypes</key>
-	<array/>
-	<key>NSPrivacyAccessedAPITypes</key>
-	<array/>
-	<key>NSPrivacyTracking</key>
-	<false/>
-</dict>
-</plist>

From fec58b90b7a340377d16dfcce89027a6b6f81e26 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 14:47:00 +0100
Subject: [PATCH 33/95] fix: ios deployment target

---
 SQLite.swift.podspec                                          | 2 +-
 SQLite.xcodeproj/project.pbxproj                              | 4 ++--
 .../xcshareddata/xcschemes/SQLite visionOS.xcscheme           | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4edddd67..b8aaaa8f 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,7 +18,7 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '11.0'
+  ios_deployment_target = '17.0'
   tvos_deployment_target = '11.0'
   osx_deployment_target = '10.13'
   watchos_deployment_target = '4.0'
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 857c6fd9..d23b6d64 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1736,7 +1736,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1795,7 +1795,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 17.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
diff --git a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme
index 58783573..c1536b66 100644
--- a/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
+++ b/SQLite.xcodeproj/xcshareddata/xcschemes/SQLite visionOS.xcscheme	
@@ -15,7 +15,7 @@
             <BuildableReference
                BuildableIdentifier = "primary"
                BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
-               BuildableName = "SQLite visionOS.framework"
+               BuildableName = "SQLite.framework"
                BlueprintName = "SQLite visionOS"
                ReferencedContainer = "container:SQLite.xcodeproj">
             </BuildableReference>
@@ -55,7 +55,7 @@
          <BuildableReference
             BuildableIdentifier = "primary"
             BlueprintIdentifier = "DEB306B82B61CEF500F9D46B"
-            BuildableName = "SQLite visionOS.framework"
+            BuildableName = "SQLite.framework"
             BlueprintName = "SQLite visionOS"
             ReferencedContainer = "container:SQLite.xcodeproj">
          </BuildableReference>

From ce125ce64c03e9fcd142f18aca254637c223be8c Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 23 Feb 2024 15:37:04 +0100
Subject: [PATCH 34/95] fix: deployment targets?

---
 SQLite.swift.podspec | 21 ++++++++++++++++++---
 1 file changed, 18 insertions(+), 3 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index b8aaaa8f..2a75f169 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -19,9 +19,9 @@ Pod::Spec.new do |s|
   s.swift_versions = ['5']
 
   ios_deployment_target = '17.0'
-  tvos_deployment_target = '11.0'
-  osx_deployment_target = '10.13'
-  watchos_deployment_target = '4.0'
+  tvos_deployment_target = '17.0'
+  osx_deployment_target = '14.0'
+  watchos_deployment_target = '10.0'
 
   s.ios.deployment_target = ios_deployment_target
   s.tvos.deployment_target = tvos_deployment_target
@@ -32,6 +32,11 @@ Pod::Spec.new do |s|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
     ss.library = '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.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -51,6 +56,11 @@ Pod::Spec.new do |s|
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_SWIFT_STANDALONE=1'
     }
     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.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'
@@ -68,6 +78,11 @@ Pod::Spec.new do |s|
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1'
     }
     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.test_spec 'tests' do |test_spec|
       test_spec.resources = 'Tests/SQLiteTests/Resources/*'

From e7813517c80c5bd79d4e7029ec8a89121f5afe60 Mon Sep 17 00:00:00 2001
From: ivan <ivanlares1@gmail.com>
Date: Fri, 23 Feb 2024 12:51:28 -0800
Subject: [PATCH 35/95] Add privacy manifest

- fixes build issue https://github.com/stephencelis/SQLite.swift/actions/runs/8019552944/job/21907598856
---
 SQLite.xcodeproj/project.pbxproj     | 10 ++++++++++
 Sources/SQLite/PrivacyInfo.xcprivacy | 14 ++++++++++++++
 2 files changed, 24 insertions(+)
 create mode 100644 Sources/SQLite/PrivacyInfo.xcprivacy

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 37c8a802..0b246a00 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -221,6 +221,10 @@
 		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
+		EAE5A03A2B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EE247AD71C3F04ED00AE3E12 /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		EE247ADE1C3F04ED00AE3E12 /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EE247AD31C3F04ED00AE3E12 /* SQLite.framework */; };
 		EE247B031C3F06E900AE3E12 /* Blob.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AEE1C3F06E900AE3E12 /* Blob.swift */; };
@@ -353,6 +357,7 @@
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
 		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
+		EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
 		EE247AD81C3F04ED00AE3E12 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@@ -560,6 +565,7 @@
 				EE247AD61C3F04ED00AE3E12 /* SQLite.h */,
 				EE247AF71C3F06E900AE3E12 /* Foundation.swift */,
 				EE247AF81C3F06E900AE3E12 /* Helpers.swift */,
+				EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */,
 				EE247AD81C3F04ED00AE3E12 /* Info.plist */,
 				EE247AED1C3F06E900AE3E12 /* Core */,
 				EE247AF41C3F06E900AE3E12 /* Extensions */,
@@ -908,6 +914,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -923,6 +930,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A03A2B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -930,6 +938,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -945,6 +954,7 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
diff --git a/Sources/SQLite/PrivacyInfo.xcprivacy b/Sources/SQLite/PrivacyInfo.xcprivacy
new file mode 100644
index 00000000..987771fa
--- /dev/null
+++ b/Sources/SQLite/PrivacyInfo.xcprivacy
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>NSPrivacyTrackingDomains</key>
+	<array/>
+	<key>NSPrivacyCollectedDataTypes</key>
+	<array/>
+	<key>NSPrivacyAccessedAPITypes</key>
+	<array/>
+	<key>NSPrivacyTracking</key>
+	<false/>
+</dict>
+</plist>

From e78ae0220e17525a15ac68c697a155eb7a672a8e Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 24 Feb 2024 11:51:41 +0100
Subject: [PATCH 36/95] changelogs

---
 CHANGELOG.md            | 33 ++++++++++++++++++++++++++++++--
 Documentation/Index.md  | 42 ++++++++++++++++++++++++++++++-----------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 66 insertions(+), 17 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 970b4ca1..4c5c1fba 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,20 @@
-0.15.0 (unreleased)
+0.15.0 (24-02-2024), [diff][diff-0.15.0]
 ========================================
 
+* Fix incorrect behavior when preparing `SELECT *` preceded by a `WITH` ([#1179][])
+* Adds support for returning extended error codes ([#1178][])
+* Fix typos ([#1182][])
+* fix Xcode build error ([#1192][])
+* Make the IndexDefinition properties public ([#1196][])
+* Fix GitHub Actions build badge ([#1200][])
+* Run CI on macOS 13 ([#1206][])
+* SchemaReader: return the correct column definition for a composite primary key ([#1217][])
+* Add optional support for decoding ([#1224][])
+* make fromDatatypeValue throw ([#1242][])
+* Implements built-in window functions ([#1228][])
+* Fix column affinity parsing to match how SQLite determines affinity ([#1218][])
+* Handle FK definitions w/o key references ([#1210][])
+* Add privacy manifest ([#1245][])
 * New minimum deployment targets: iOS/tvOS 11.0, watchOS 4.0
 
 0.14.1 (01-11-2022), [diff][diff-0.14.1]
@@ -14,7 +28,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 
 * Support more complex schema changes and queries ([#1073][], [#1146][] [#1148][])
 * Support `ATTACH`/`DETACH` ([#30][], [#1142][])
-* Expose connection flags (via `URIQueryParameter`) to open db ([#1074][]))
+* Expose connection flags (via `URIQueryParameter`) to open db ([#1074][])
 * Support `WITH` clause ([#1139][])
 * Add `Value` conformance for `NSURL` ([#1110][], [#1141][])
 * Add decoding for `UUID` ([#1137][])
@@ -140,6 +154,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.13.3]: https://github.com/stephencelis/SQLite.swift/compare/0.13.2...0.13.3
 [diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
+[diff-0.15.0]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.15.0
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -206,3 +221,17 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1146]: https://github.com/stephencelis/SQLite.swift/pull/1146
 [#1148]: https://github.com/stephencelis/SQLite.swift/pull/1148
 [#1167]: https://github.com/stephencelis/SQLite.swift/pull/1167
+[#1179]: https://github.com/stephencelis/SQLite.swift/pull/1179
+[#1178]: https://github.com/stephencelis/SQLite.swift/pull/1178
+[#1182]: https://github.com/stephencelis/SQLite.swift/pull/1182
+[#1192]: https://github.com/stephencelis/SQLite.swift/pull/1192
+[#1196]: https://github.com/stephencelis/SQLite.swift/pull/1196
+[#1200]: https://github.com/stephencelis/SQLite.swift/pull/1200
+[#1206]: https://github.com/stephencelis/SQLite.swift/pull/1206
+[#1217]: https://github.com/stephencelis/SQLite.swift/pull/1217
+[#1224]: https://github.com/stephencelis/SQLite.swift/pull/1224
+[#1242]: https://github.com/stephencelis/SQLite.swift/pull/1242
+[#1228]: https://github.com/stephencelis/SQLite.swift/pull/1228
+[#1218]: https://github.com/stephencelis/SQLite.swift/pull/1218
+[#1210]: https://github.com/stephencelis/SQLite.swift/pull/1210
+[#1245]: https://github.com/stephencelis/SQLite.swift/pull/1245
diff --git a/Documentation/Index.md b/Documentation/Index.md
index a03c26a3..7f900bc5 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1,21 +1,24 @@
 # SQLite.swift Documentation
 
+- [SQLite.swift Documentation](#sqliteswift-documentation)
   - [Installation](#installation)
     - [Swift Package Manager](#swift-package-manager)
     - [Carthage](#carthage)
     - [CocoaPods](#cocoapods)
+      - [Requiring a specific version of SQLite](#requiring-a-specific-version-of-sqlite)
+      - [Using SQLite.swift with SQLCipher](#using-sqliteswift-with-sqlcipher)
     - [Manual](#manual)
   - [Getting Started](#getting-started)
     - [Connecting to a Database](#connecting-to-a-database)
       - [Read-Write Databases](#read-write-databases)
       - [Read-Only Databases](#read-only-databases)
-      - [In a Shared Group Container](#in-a-shared-group-container)
+      - [In a shared group container](#in-a-shared-group-container)
       - [In-Memory Databases](#in-memory-databases)
       - [URI parameters](#uri-parameters)
       - [Thread-Safety](#thread-safety)
   - [Building Type-Safe SQL](#building-type-safe-sql)
     - [Expressions](#expressions)
-      - [Compound Expressions](#compound-expressions)
+    - [Compound Expressions](#compound-expressions)
     - [Queries](#queries)
   - [Creating a Table](#creating-a-table)
     - [Create Table Options](#create-table-options)
@@ -24,8 +27,11 @@
   - [Inserting Rows](#inserting-rows)
     - [Handling SQLite errors](#handling-sqlite-errors)
     - [Setters](#setters)
+          - [Infix Setters](#infix-setters)
+          - [Postfix Setters](#postfix-setters)
   - [Selecting Rows](#selecting-rows)
     - [Iterating and Accessing Values](#iterating-and-accessing-values)
+      - [Failable iteration](#failable-iteration)
     - [Plucking Rows](#plucking-rows)
     - [Building Complex Queries](#building-complex-queries)
       - [Selecting Columns](#selecting-columns)
@@ -34,6 +40,9 @@
         - [Table Aliasing](#table-aliasing)
       - [Filtering Rows](#filtering-rows)
         - [Filter Operators and Functions](#filter-operators-and-functions)
+          - [Infix Filter Operators](#infix-filter-operators)
+          - [Prefix Filter Operators](#prefix-filter-operators)
+          - [Filtering Functions](#filtering-functions)
       - [Sorting Rows](#sorting-rows)
       - [Limiting and Paging Results](#limiting-and-paging-results)
       - [Recursive and Hierarchical Queries](#recursive-and-hierarchical-queries)
@@ -43,13 +52,14 @@
   - [Deleting Rows](#deleting-rows)
   - [Transactions and Savepoints](#transactions-and-savepoints)
   - [Querying the Schema](#querying-the-schema)
+    - [Indexes and Columns](#indexes-and-columns)
   - [Altering the Schema](#altering-the-schema)
     - [Renaming Tables](#renaming-tables)
     - [Dropping Tables](#dropping-tables)
     - [Adding Columns](#adding-columns)
       - [Added Column Constraints](#added-column-constraints)
-    - [Schema Changer](#schemachanger)
-      - [Adding Columns](#adding-columns)
+    - [SchemaChanger](#schemachanger)
+      - [Adding Columns](#adding-columns-1)
       - [Renaming Columns](#renaming-columns)
       - [Dropping Columns](#dropping-columns)
       - [Renaming/Dropping Tables](#renamingdropping-tables)
@@ -61,17 +71,27 @@
     - [Date-Time Values](#date-time-values)
     - [Binary Data](#binary-data)
   - [Codable Types](#codable-types)
+    - [Inserting Codable Types](#inserting-codable-types)
+    - [Updating Codable Types](#updating-codable-types)
+    - [Retrieving Codable Types](#retrieving-codable-types)
+    - [Restrictions](#restrictions)
   - [Other Operators](#other-operators)
+          - [Other Infix Operators](#other-infix-operators)
+          - [Other Prefix Operators](#other-prefix-operators)
   - [Core SQLite Functions](#core-sqlite-functions)
   - [Aggregate SQLite Functions](#aggregate-sqlite-functions)
   - [Window SQLite Functions](#window-sqlite-functions)
-  - [Date and Time Functions](#date-and-time-functions)
+  - [Date and Time functions](#date-and-time-functions)
   - [Custom SQL Functions](#custom-sql-functions)
+  - [Custom Aggregations](#custom-aggregations)
   - [Custom Collations](#custom-collations)
   - [Full-text Search](#full-text-search)
+    - [FTS5](#fts5)
   - [Executing Arbitrary SQL](#executing-arbitrary-sql)
+  - [Online Database Backup](#online-database-backup)
   - [Attaching and detaching databases](#attaching-and-detaching-databases)
   - [Logging](#logging)
+  - [Vacuum](#vacuum)
 
 [↩]: #sqliteswift-documentation
 
@@ -88,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
   ]
   ```
 
@@ -109,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.1
+    github "stephencelis/SQLite.swift" ~> 0.15.0
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -139,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.1'
+        pod 'SQLite.swift', '~> 0.15.0'
     end
     ```
 
@@ -153,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.0'
 end
 ```
 
@@ -163,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.14.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.0'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -179,7 +199,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.14.1'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.0'
 end
 ```
 
diff --git a/README.md b/README.md
index e6e5a894..117edb5a 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.14.1")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.14.1
+    github "stephencelis/SQLite.swift" ~> 0.15.0
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4edddd67..b691aeab 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.14.1"
+  s.version          = "0.15.0"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 3c446ae6..c82b3b9e 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.14.1")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 20ebe9b334ff557c1166e26ca6c89e94a77a7f2d Mon Sep 17 00:00:00 2001
From: Chris Stockbridge <chris.stockbridge@vantaca.com>
Date: Mon, 4 Mar 2024 20:07:22 -0500
Subject: [PATCH 37/95] Adding failing test that includes an optional struct

This test passes with release 0.14.1, but fails starting with 0.15
---
 .../Typed/QueryIntegrationTests.swift         | 27 +++++++++++++++++++
 1 file changed, 27 insertions(+)

diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index aa45cafe..7ac72d0c 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -152,6 +152,33 @@ class QueryIntegrationTests: SQLiteTestCase {
          XCTAssertEqual(values.count, 2)
     }
 
+    func test_insert_custom_encodable_type() throws {
+        struct TestTypeWithOptionalArray: Codable {
+            var myInt: Int
+            var myString: String
+            var myOptionalArray: [Int]?
+        }
+
+        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"))
+        })
+
+        let customType = TestTypeWithOptionalArray(myInt: 13, myString: "foo", myOptionalArray: [1, 2, 3])
+        try db.run(table.insert(customType))
+        let rows = try db.prepare(table)
+        let values: [TestTypeWithOptionalArray] = try rows.map({ try $0.decode() })
+        XCTAssertEqual(values.count, 1, "return one optional custom type")
+
+        let customTypeWithNil = TestTypeWithOptionalArray(myInt: 123, myString: "String", myOptionalArray: nil)
+        try db.run(table.insert(customTypeWithNil))
+        let rowsNil = try db.prepare(table)
+        let valuesNil: [TestTypeWithOptionalArray] = try rowsNil.map({ try $0.decode() })
+        XCTAssertEqual(valuesNil.count, 2, "return two custom objects, including one that contains a nil optional")
+    }
+
     func test_upsert() throws {
         try XCTSkipUnless(db.satisfiesMinimumVersion(minor: 24))
         let fetchAge = { () throws -> Int? in

From bad16cb5d1a09e93bbdb40f50df5f222920a10e3 Mon Sep 17 00:00:00 2001
From: Chris Stockbridge <chris.stockbridge@vantaca.com>
Date: Tue, 5 Mar 2024 11:11:50 -0500
Subject: [PATCH 38/95] Bugfix: returning nil when decoding an optional should
 not throw an error

---
 Sources/SQLite/Typed/Coding.swift | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/Sources/SQLite/Typed/Coding.swift b/Sources/SQLite/Typed/Coding.swift
index bfddc5ee..d0061851 100644
--- a/Sources/SQLite/Typed/Coding.swift
+++ b/Sources/SQLite/Typed/Coding.swift
@@ -504,9 +504,8 @@ private class SQLiteDecoder: Decoder {
             case is UUID.Type:
                 return try? row.get(Expression<UUID>(key.stringValue)) as? T
             default:
-                guard let JSONString = try? row.get(Expression<String?>(key.stringValue)) else {
-                    throw DecodingError.typeMismatch(type, DecodingError.Context(codingPath: codingPath,
-                                                                                 debugDescription: "an unsupported type was found"))
+                guard let JSONString = try row.get(Expression<String?>(key.stringValue)) else {
+                    return nil
                 }
                 guard let data = JSONString.data(using: .utf8) else {
                     throw DecodingError.dataCorrupted(DecodingError.Context(codingPath: codingPath,

From cda8a1274a5c426c043bea6d381fddab630dad95 Mon Sep 17 00:00:00 2001
From: Steven <tongwanglin@gmail.com>
Date: Sun, 17 Mar 2024 01:10:17 +0800
Subject: [PATCH 39/95] Update CoreFunctions.swift fix typo

fix lower to upper
---
 Sources/SQLite/Typed/CoreFunctions.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Typed/CoreFunctions.swift b/Sources/SQLite/Typed/CoreFunctions.swift
index 4429bb5f..c4359d8b 100644
--- a/Sources/SQLite/Typed/CoreFunctions.swift
+++ b/Sources/SQLite/Typed/CoreFunctions.swift
@@ -224,7 +224,7 @@ extension ExpressionType where UnderlyingType == String {
     ///
     ///     let name = Expression<String>("name")
     ///     name.uppercaseString
-    ///     // lower("name")
+    ///     // upper("name")
     ///
     /// - Returns: A copy of the expression wrapped with the `upper` function.
     public var uppercaseString: Expression<UnderlyingType> {

From 6d821f83499500cb7bb7f258655fe83ac6f890ed Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Sat, 13 Apr 2024 08:23:49 +0530
Subject: [PATCH 40/95] Add dependency on custom cocoapods fork

---
 Gemfile      |   1 +
 Gemfile.lock | 116 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 117 insertions(+)
 create mode 100644 Gemfile
 create mode 100644 Gemfile.lock

diff --git a/Gemfile b/Gemfile
new file mode 100644
index 00000000..2f97c391
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1 @@
+gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 00000000..75374a30
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,116 @@
+GIT
+  remote: https://github.com/SagarSDagdu/CocoaPods.git
+  revision: d96f491f79abd2804d1359c5228cce404dd365b7
+  tag: 1.15.2.1-sagard
+  specs:
+    cocoapods (1.15.2.1.pre.sagard)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      cocoapods-core (= 1.15.2)
+      cocoapods-deintegrate (>= 1.0.3, < 2.0)
+      cocoapods-downloader (>= 2.1, < 3.0)
+      cocoapods-plugins (>= 1.0.0, < 2.0)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (>= 2.3.0, < 3.0)
+      xcodeproj (>= 1.23.0, < 2.0)
+
+GEM
+  specs:
+    CFPropertyList (3.0.7)
+      base64
+      nkf
+      rexml
+    activesupport (7.1.3.2)
+      base64
+      bigdecimal
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      connection_pool (>= 2.2.5)
+      drb
+      i18n (>= 1.6, < 2)
+      minitest (>= 5.1)
+      mutex_m
+      tzinfo (~> 2.0)
+    addressable (2.8.6)
+      public_suffix (>= 2.0.2, < 6.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.5)
+    claide (1.1.0)
+    cocoapods-core (1.15.2)
+      activesupport (>= 5.0, < 8)
+      addressable (~> 2.8)
+      algoliasearch (~> 1.0)
+      concurrent-ruby (~> 1.1)
+      fuzzy_match (~> 2.0.4)
+      nap (~> 1.0)
+      netrc (~> 0.11)
+      public_suffix (~> 4.0)
+      typhoeus (~> 1.0)
+    cocoapods-deintegrate (1.0.5)
+    cocoapods-downloader (2.1)
+    cocoapods-plugins (1.0.0)
+      nap
+    cocoapods-search (1.0.1)
+    cocoapods-trunk (1.6.0)
+      nap (>= 0.8, < 2.0)
+      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.0)
+      ruby2_keywords
+    escape (0.0.4)
+    ethon (0.16.0)
+      ffi (>= 1.15.0)
+    ffi (1.16.3)
+    fourflusher (2.3.1)
+    fuzzy_match (2.0.4)
+    gh_inspector (1.1.3)
+    httpclient (2.8.3)
+    i18n (1.14.4)
+      concurrent-ruby (~> 1.0)
+    json (2.7.1)
+    minitest (5.22.0)
+    molinillo (0.8.0)
+    mutex_m (0.2.0)
+    nanaimo (0.3.0)
+    nap (1.1.0)
+    netrc (0.11.0)
+    nkf (0.1.3)
+    public_suffix (4.0.7)
+    rexml (3.2.6)
+    ruby-macho (2.5.1)
+    ruby2_keywords (0.0.5)
+    typhoeus (1.4.1)
+      ethon (>= 0.9.0)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    xcodeproj (1.24.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)
+
+PLATFORMS
+  arm64-darwin-23
+  ruby
+
+DEPENDENCIES
+  cocoapods!
+
+BUNDLED WITH
+   2.5.4

From 6491e381d7f827ced234e6aa7edca0a92f25eff6 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Sat, 13 Apr 2024 08:24:03 +0530
Subject: [PATCH 41/95] Update deployment targets for Xcode 15

---
 SQLite.swift.podspec             |  4 ++--
 SQLite.xcodeproj/project.pbxproj | 24 ++++++++++++------------
 2 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index b691aeab..4a7a390c 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -18,8 +18,8 @@ Pod::Spec.new do |s|
   s.default_subspec  = 'standard'
   s.swift_versions = ['5']
 
-  ios_deployment_target = '11.0'
-  tvos_deployment_target = '11.0'
+  ios_deployment_target = '12.0'
+  tvos_deployment_target = '12.0'
   osx_deployment_target = '10.13'
   watchos_deployment_target = '4.0'
 
diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 0b246a00..c5215807 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -1299,7 +1299,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1321,7 +1321,7 @@
 				SDKROOT = appletvos;
 				SKIP_INSTALL = YES;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1335,7 +1335,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Debug;
 		};
@@ -1349,7 +1349,7 @@
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SDKROOT = appletvos;
 				SWIFT_TREAT_WARNINGS_AS_ERRORS = YES;
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 			};
 			name = Release;
 		};
@@ -1450,7 +1450,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
@@ -1459,7 +1459,7 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
 				WATCHOS_DEPLOYMENT_TARGET = 4.0;
@@ -1509,7 +1509,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.0;
 				MACOSX_DEPLOYMENT_TARGET = 10.13;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				PRODUCT_NAME = "";
@@ -1517,7 +1517,7 @@
 				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
 				SWIFT_VERSION = 5.0;
 				TARGETED_DEVICE_FAMILY = "1,2,3";
-				TVOS_DEPLOYMENT_TARGET = 11.0;
+				TVOS_DEPLOYMENT_TARGET = 12.0;
 				VALIDATE_PRODUCT = YES;
 				VERSIONING_SYSTEM = "apple-generic";
 				VERSION_INFO_PREFIX = "";
@@ -1538,7 +1538,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1562,7 +1562,7 @@
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = "$(SRCROOT)/Sources/SQLite/Info.plist";
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				MARKETING_VERSION = 0.14.0;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
@@ -1577,7 +1577,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -1590,7 +1590,7 @@
 			buildSettings = {
 				GCC_TREAT_WARNINGS_AS_ERRORS = YES;
 				INFOPLIST_FILE = Tests/SQLiteTests/Info.plist;
-				IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLiteTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";

From c4ec236be4b089984c8239869309bd4c637642b6 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:17:15 +0200
Subject: [PATCH 42/95] using temp cocoapods (fix xcode 15 build, to be
 reverted later)

---
 run-tests.sh | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index 49330c12..1bf8de96 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,10 +7,11 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
+    bundle install
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
-      pod lib lint --no-subspecs --fail-fast
+      bundle exec pod lib lint --no-subspecs --fail-fast
     else
-      pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
+      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From a6e7697bac187dd6ee5ee8026e7fca91235e5b73 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:31:08 +0200
Subject: [PATCH 43/95] fixing bundle versions?

---
 Gemfile.lock | 2 +-
 run-tests.sh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 75374a30..159cbf9c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -82,7 +82,7 @@ GEM
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
     json (2.7.1)
-    minitest (5.22.0)
+    minitest (5.22.3)
     molinillo (0.8.0)
     mutex_m (0.2.0)
     nanaimo (0.3.0)
diff --git a/run-tests.sh b/run-tests.sh
index 1bf8de96..13975f0e 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,7 +7,7 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
-    bundle install
+    bundle install --deployment
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
       bundle exec pod lib lint --no-subspecs --fail-fast
     else

From 9bed2450cbd38352a5a3316b2817bec8baf4f5d3 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 15:38:17 +0200
Subject: [PATCH 44/95] adding source for gems as otherwise it does not resolve
 correctly in CI

---
 Gemfile      |  2 ++
 Gemfile.lock | 11 +++++------
 run-tests.sh |  2 +-
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Gemfile b/Gemfile
index 2f97c391..eb6036cd 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1 +1,3 @@
+source "https://rubygems.org"
+
 gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
diff --git a/Gemfile.lock b/Gemfile.lock
index 159cbf9c..a58783ea 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -23,6 +23,7 @@ GIT
       xcodeproj (>= 1.23.0, < 2.0)
 
 GEM
+  remote: https://rubygems.org/
   specs:
     CFPropertyList (3.0.7)
       base64
@@ -45,7 +46,7 @@ GEM
       json (>= 1.5.1)
     atomos (0.1.3)
     base64 (0.2.0)
-    bigdecimal (3.1.5)
+    bigdecimal (3.1.7)
     claide (1.1.0)
     cocoapods-core (1.15.2)
       activesupport (>= 5.0, < 8)
@@ -69,8 +70,7 @@ GEM
     colored2 (3.1.2)
     concurrent-ruby (1.2.3)
     connection_pool (2.4.1)
-    drb (2.2.0)
-      ruby2_keywords
+    drb (2.2.1)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
@@ -81,18 +81,17 @@ GEM
     httpclient (2.8.3)
     i18n (1.14.4)
       concurrent-ruby (~> 1.0)
-    json (2.7.1)
+    json (2.7.2)
     minitest (5.22.3)
     molinillo (0.8.0)
     mutex_m (0.2.0)
     nanaimo (0.3.0)
     nap (1.1.0)
     netrc (0.11.0)
-    nkf (0.1.3)
+    nkf (0.2.0)
     public_suffix (4.0.7)
     rexml (3.2.6)
     ruby-macho (2.5.1)
-    ruby2_keywords (0.0.5)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
     tzinfo (2.0.6)
diff --git a/run-tests.sh b/run-tests.sh
index 13975f0e..1bf8de96 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -7,7 +7,7 @@ if [ -n "$BUILD_SCHEME" ]; then
         make test BUILD_SCHEME="$BUILD_SCHEME"
     fi
 elif [ -n "$VALIDATOR_SUBSPEC" ]; then
-    bundle install --deployment
+    bundle install
     if [ "$VALIDATOR_SUBSPEC" == "none" ]; then
       bundle exec pod lib lint --no-subspecs --fail-fast
     else

From 6be8ca943138e3b3d77bb14b74c1b498d75b0cf4 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 16:30:09 +0200
Subject: [PATCH 45/95] fix carthage location?

---
 Tests/Carthage/Makefile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Tests/Carthage/Makefile b/Tests/Carthage/Makefile
index b44d85b9..fe708ab0 100644
--- a/Tests/Carthage/Makefile
+++ b/Tests/Carthage/Makefile
@@ -1,4 +1,4 @@
-CARTHAGE := /usr/local/bin/carthage
+CARTHAGE := $(shell which carthage)
 CARTHAGE_PLATFORM := iOS
 CARTHAGE_CONFIGURATION := Release
 CARTHAGE_DIR := Carthage

From a2a550e6266e80ebe4d896d1360cff4ef9ad202a Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 17:18:49 +0200
Subject: [PATCH 46/95] bump xcode version + carthage visionOS tests

---
 .github/workflows/build.yml | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 7be89270..9fbba1c5 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -13,7 +13,7 @@ jobs:
       # https://github.com/CocoaPods/CocoaPods/issues/11839
       run: |
         xcode-select -p
-        sudo xcode-select -s /Applications/Xcode_15.0.1.app/Contents/Developer
+        sudo xcode-select -s /Applications/Xcode_15.3.app/Contents/Developer
     - name: "Lint"
       run: make lint
     - name: "Run tests (PACKAGE_MANAGER_COMMAND: test)"
@@ -64,6 +64,10 @@ jobs:
       env:
         CARTHAGE_PLATFORM: tvOS
       run: ./run-tests.sh
+    - name: "Run tests (CARTHAGE_PLATFORM: visionOS)"
+      env:
+        CARTHAGE_PLATFORM: visionOS
+      run: ./run-tests.sh
   build-linux:
     runs-on: ubuntu-latest
     steps:

From f0af5e0a2b6917ae38e3019e3456fd0fa9365864 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Sat, 13 Apr 2024 18:55:58 +0200
Subject: [PATCH 47/95] Changelogs v0.15.1

---
 CHANGELOG.md            | 13 +++++++++++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c5c1fba..79085ce3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+0.15.1 (14-04-2024), [diff][diff-0.15.1]
+========================================
+
+* Update CoreFunctions.swift fix typo ([#1249][])
+* Fix #1247 support nil case when decoding optionals ([#1248][])
+* Change deployment targets for Xcode 15 and add dependency on custom Cocoapods fork ([#1255][])
+* Add VisionOS support ([#1237][])
+
 0.15.0 (24-02-2024), [diff][diff-0.15.0]
 ========================================
 
@@ -155,6 +163,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.14.0]: https://github.com/stephencelis/SQLite.swift/compare/0.13.3...0.14.0
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
 [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
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -235,3 +244,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1218]: https://github.com/stephencelis/SQLite.swift/pull/1218
 [#1210]: https://github.com/stephencelis/SQLite.swift/pull/1210
 [#1245]: https://github.com/stephencelis/SQLite.swift/pull/1245
+[#1249]: https://github.com/stephencelis/SQLite.swift/pull/1249
+[#1248]: https://github.com/stephencelis/SQLite.swift/pull/1248
+[#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
+[#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
diff --git a/Documentation/Index.md b/Documentation/Index.md
index 7f900bc5..edb9dab3 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.0
+    github "stephencelis/SQLite.swift" ~> 0.15.1
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.0'
+        pod 'SQLite.swift', '~> 0.15.1'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.0'
+  pod 'SQLite.swift/standalone', '~> 0.15.1'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.0'
+  pod 'SQLite.swift/standalone', '~> 0.15.1'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,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.0'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.1'
 end
 ```
 
diff --git a/README.md b/README.md
index 117edb5a..51090e5d 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.0")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.0
+    github "stephencelis/SQLite.swift" ~> 0.15.1
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 860fe3d4..035fffb0 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.0"
+  s.version          = "0.15.1"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index c82b3b9e..5a96daff 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.0")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From fc236997c8e1b21be4ecaaa547496857483dcf6b Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 16 Apr 2024 05:40:47 +0200
Subject: [PATCH 48/95] fix: visionos to cocoapods

---
 CHANGELOG.md            |  6 ++++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  7 ++++++-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 21 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 79085ce3..376c19d8 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+0.15.2 (16-04-2024), [diff][diff-0.15.2]
+========================================
+* fix: visionos to cocoapods ([#1260][])
+
 0.15.1 (14-04-2024), [diff][diff-0.15.1]
 ========================================
 
@@ -164,6 +168,7 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [diff-0.14.1]: https://github.com/stephencelis/SQLite.swift/compare/0.14.0...0.14.1
 [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
 
 [#30]: https://github.com/stephencelis/SQLite.swift/issues/30
 [#142]: https://github.com/stephencelis/SQLite.swift/issues/142
@@ -248,3 +253,4 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1248]: https://github.com/stephencelis/SQLite.swift/pull/1248
 [#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
 [#1237]: https://github.com/stephencelis/SQLite.swift/pull/1237
+[#1260]: https://github.com/stephencelis/SQLite.swift/pull/1260
diff --git a/Documentation/Index.md b/Documentation/Index.md
index edb9dab3..adbbff8b 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.1
+    github "stephencelis/SQLite.swift" ~> 0.15.2
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.1'
+        pod 'SQLite.swift', '~> 0.15.2'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.2'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.1'
+  pod 'SQLite.swift/standalone', '~> 0.15.2'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,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.1'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.2'
 end
 ```
 
diff --git a/README.md b/README.md
index 51090e5d..a87ab937 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.1")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.1
+    github "stephencelis/SQLite.swift" ~> 0.15.2
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 035fffb0..efed67c1 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.1"
+  s.version          = "0.15.2"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
@@ -22,11 +22,13 @@ Pod::Spec.new do |s|
   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.subspec 'standard' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
@@ -37,6 +39,7 @@ Pod::Spec.new do |s|
     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/*'
@@ -61,6 +64,7 @@ Pod::Spec.new do |s|
     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/*'
@@ -83,6 +87,7 @@ Pod::Spec.new do |s|
     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/*'
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index 5a96daff..b73ecd83 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.1")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 8f75f4609221c55901cf60b1438b7a8441af145e Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 16 Apr 2024 06:06:25 +0200
Subject: [PATCH 49/95] gonna fix that later

---
 SQLite.swift.podspec | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index efed67c1..88713c9e 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -87,7 +87,7 @@ Pod::Spec.new do |s|
     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.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/*'

From 15fe07e3d5d0555a7f03966114ce5a9390823af7 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Thu, 18 Apr 2024 18:57:43 +0530
Subject: [PATCH 50/95] Update the marketing version to the version we will be
 releasing next

For recent releases, the `Info.plist` shipped inside the `xcframeworks` do not match the release version. Fixing that to reflect the marketing version we will be releasing next.
---
 SQLite.xcodeproj/project.pbxproj | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/SQLite.xcodeproj/project.pbxproj b/SQLite.xcodeproj/project.pbxproj
index 79de22e7..a2d7edbe 100644
--- a/SQLite.xcodeproj/project.pbxproj
+++ b/SQLite.xcodeproj/project.pbxproj
@@ -218,6 +218,9 @@
 		DB7C5DA728D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA828D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
 		DB7C5DA928D7C9B6006395CF /* SQLiteVersion.swift in Sources */ = {isa = PBXBuildFile; fileRef = DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */; };
+		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
+		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		DEB306BA2B61CEF500F9D46B /* SQLite.h in Headers */ = {isa = PBXBuildFile; fileRef = EE247AD61C3F04ED00AE3E12 /* SQLite.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		DEB306BC2B61CEF500F9D46B /* CoreFunctions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EE247AFC1C3F06E900AE3E12 /* CoreFunctions.swift */; };
 		DEB306BD2B61CEF500F9D46B /* Coding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49EB68C31F7B3CB400D89D40 /* Coding.swift */; };
@@ -288,9 +291,6 @@
 		DEB307092B61CF9500F9D46B /* Connection+AttachTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 19A170C08525D3D27CB5F83C /* Connection+AttachTests.swift */; };
 		DEB3070B2B61CF9500F9D46B /* SQLite.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 03A65E5A1C6BB0F50062603F /* SQLite.framework */; };
 		DEB3070D2B61CF9500F9D46B /* Resources in Resources */ = {isa = PBXBuildFile; fileRef = 3DF7B79528846FCC005DD8CA /* Resources */; };
-		DBB93D5A2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
-		DBB93D5B2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
-		DBB93D5C2A22A373009BB96E /* SchemaReaderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */; };
 		EAE5A0372B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EAE5A0382B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
 		EAE5A0392B893C43007C7EA4 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */; };
@@ -433,10 +433,10 @@
 		DB58B21028FB864300F8EEA4 /* SchemaReader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReader.swift; sourceTree = "<group>"; };
 		DB58B21528FC7C4600F8EEA4 /* SQLiteFeature.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteFeature.swift; sourceTree = "<group>"; };
 		DB7C5DA528D7C9B6006395CF /* SQLiteVersion.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SQLiteVersion.swift; sourceTree = "<group>"; };
+		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		DEB306E52B61CEF500F9D46B /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		DEB307112B61CF9500F9D46B /* SQLiteTests visionOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SQLiteTests visionOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
 		DEB307132B61D04500F9D46B /* SQLite visionOS.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; name = "SQLite visionOS.xctestplan"; path = "Tests/SQLite visionOS.xctestplan"; sourceTree = "<group>"; };
-		DBB93D592A22A373009BB96E /* SchemaReaderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SchemaReaderTests.swift; sourceTree = "<group>"; };
 		EAE5A0362B893C43007C7EA4 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
 		EE247AD31C3F04ED00AE3E12 /* SQLite.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SQLite.framework; sourceTree = BUILT_PRODUCTS_DIR; };
 		EE247AD61C3F04ED00AE3E12 /* SQLite.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SQLite.h; sourceTree = "<group>"; };
@@ -1539,6 +1539,7 @@
 				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;
@@ -1561,6 +1562,7 @@
 				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;
@@ -1612,6 +1614,7 @@
 				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;
@@ -1636,6 +1639,7 @@
 				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;
@@ -1660,7 +1664,7 @@
 				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.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1685,7 +1689,7 @@
 				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.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SDKROOT = xros;
@@ -1862,7 +1866,7 @@
 				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.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1886,7 +1890,7 @@
 				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.14.0;
+				MARKETING_VERSION = 0.15.3;
 				PRODUCT_BUNDLE_IDENTIFIER = com.stephencelis.SQLite;
 				PRODUCT_NAME = SQLite;
 				SKIP_INSTALL = YES;
@@ -1936,6 +1940,7 @@
 				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;
@@ -1961,6 +1966,7 @@
 				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;

From 5d3db22d9b1e979a3436d199f10bc7dddd4d3a39 Mon Sep 17 00:00:00 2001
From: Sagar Dagdu <shags032@gmail.com>
Date: Thu, 18 Apr 2024 21:26:22 +0530
Subject: [PATCH 51/95] Update `podspec` to include privacy manifest

Update `podspec` to include privacy manifest
---
 SQLite.swift.podspec | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 88713c9e..e6aa2c6a 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -34,6 +34,7 @@ Pod::Spec.new do |s|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
     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
@@ -53,6 +54,7 @@ Pod::Spec.new do |s|
   s.subspec 'standalone' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
     ss.exclude_files = 'Sources/**/Cipher.swift'
+    ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
 
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_STANDALONE',
@@ -77,6 +79,8 @@ Pod::Spec.new do |s|
 
   s.subspec 'SQLCipher' do |ss|
     ss.source_files = 'Sources/SQLite/**/*.{c,h,m,swift}'
+    ss.resource_bundle = { 'SQLite.swift' => 'Sources/SQLite/PrivacyInfo.xcprivacy' }
+
     ss.xcconfig = {
       'OTHER_SWIFT_FLAGS' => '$(inherited) -DSQLITE_SWIFT_SQLCIPHER',
       'GCC_PREPROCESSOR_DEFINITIONS' => '$(inherited) SQLITE_HAS_CODEC=1 SQLITE_SWIFT_SQLCIPHER=1'

From a95fc6df17d108bd99210db5e8a9bac90fe984b8 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Fri, 19 Apr 2024 02:50:43 +0200
Subject: [PATCH 52/95] v0.15.3 (oups)

---
 CHANGELOG.md            |  5 +++++
 Documentation/Index.md  | 12 ++++++------
 README.md               |  4 ++--
 SQLite.swift.podspec    |  2 +-
 Tests/SPM/Package.swift |  2 +-
 5 files changed, 15 insertions(+), 10 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 376c19d8..c96724fd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,7 @@
+0.15.3 (19-04-2024), [diff][diff-0.15.3]
+========================================
+* Update `podspec` to include privacy manifest ([#1265][])
+
 0.15.2 (16-04-2024), [diff][diff-0.15.2]
 ========================================
 * fix: visionos to cocoapods ([#1260][])
@@ -254,3 +258,4 @@ For breaking changes, see [Upgrading.md](Documentation/Upgrading.md).
 [#1255]: https://github.com/stephencelis/SQLite.swift/pull/1255
 [#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
diff --git a/Documentation/Index.md b/Documentation/Index.md
index adbbff8b..fcd2d642 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -108,7 +108,7 @@ process of downloading, compiling, and linking dependencies.
 
   ```swift
   dependencies: [
-    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
+    .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
   ]
   ```
 
@@ -129,7 +129,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.2
+    github "stephencelis/SQLite.swift" ~> 0.15.3
     ```
 
  3. Run `carthage update` and [add the appropriate framework][Carthage Usage].
@@ -159,7 +159,7 @@ install SQLite.swift with Carthage:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.15.2'
+        pod 'SQLite.swift', '~> 0.15.3'
     end
     ```
 
@@ -173,7 +173,7 @@ with the OS you can require the `standalone` subspec:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.2'
+  pod 'SQLite.swift/standalone', '~> 0.15.3'
 end
 ```
 
@@ -183,7 +183,7 @@ dependency to sqlite3 or one of its subspecs:
 
 ```ruby
 target 'YourAppTargetName' do
-  pod 'SQLite.swift/standalone', '~> 0.15.2'
+  pod 'SQLite.swift/standalone', '~> 0.15.3'
   pod 'sqlite3/fts5', '= 3.15.0'  # SQLite 3.15.0 with FTS5 enabled
 end
 ```
@@ -199,7 +199,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.2'
+  pod 'SQLite.swift/SQLCipher', '~> 0.15.3'
 end
 ```
 
diff --git a/README.md b/README.md
index a87ab937..a4d713b6 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,7 @@ Swift code.
 
   ```swift
   dependencies: [
-      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.2")
+      .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
   ]
   ```
 
@@ -152,7 +152,7 @@ install SQLite.swift with Carthage:
  2. Update your Cartfile to include the following:
 
     ```ruby
-    github "stephencelis/SQLite.swift" ~> 0.15.2
+    github "stephencelis/SQLite.swift" ~> 0.15.3
     ```
 
  3. Run `carthage update` and
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index e6aa2c6a..184ce23e 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.2"
+  s.version          = "0.15.3"
   s.summary          = "A type-safe, Swift-language layer over SQLite3."
 
   s.description      = <<-DESC
diff --git a/Tests/SPM/Package.swift b/Tests/SPM/Package.swift
index b73ecd83..6521211a 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.2")
+        // .package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.15.3")
     ],
     targets: [
         .executableTarget(name: "test", dependencies: [.product(name: "SQLite", package: "SQLite.swift")])

From 363141ac6c070799239593cf6dcad63fe33edd17 Mon Sep 17 00:00:00 2001
From: YiKai Deng <dyikai@gmail.com>
Date: Thu, 26 Sep 2024 17:33:15 +0800
Subject: [PATCH 53/95] Add `CustomStringConvertible` for Setter

---
 Documentation/Upgrading.md                    |  4 +-
 Sources/SQLite/Typed/Setter.swift             |  6 ++
 .../Core/Connection+AttachTests.swift         |  4 +-
 .../SQLiteTests/Core/CoreFunctionsTests.swift | 16 ++---
 Tests/SQLiteTests/Core/StatementTests.swift   |  4 +-
 Tests/SQLiteTests/Extensions/FTS4Tests.swift  |  6 +-
 .../Extensions/FTSIntegrationTests.swift      |  2 +-
 .../Schema/SchemaChangerTests.swift           |  2 +-
 .../Schema/SchemaReaderTests.swift            |  4 +-
 Tests/SQLiteTests/TestHelpers.swift           | 32 ++++-----
 .../Typed/CustomFunctionsTests.swift          | 28 ++++----
 Tests/SQLiteTests/Typed/ExpressionTests.swift | 10 +--
 Tests/SQLiteTests/Typed/OperatorsTests.swift  |  2 +-
 .../Typed/QueryIntegrationTests.swift         | 70 +++++++++----------
 Tests/SQLiteTests/Typed/QueryTests.swift      | 22 +++---
 Tests/SQLiteTests/Typed/RowTests.swift        | 22 +++---
 Tests/SQLiteTests/Typed/SelectTests.swift     |  8 +--
 Tests/SQLiteTests/Typed/SetterTests.swift     |  3 +
 .../Typed/WindowFunctionsTests.swift          |  4 +-
 19 files changed, 130 insertions(+), 119 deletions(-)

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/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/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index 940a30ca..f37300ca 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -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/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/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index 5f212505..dbf99d7c 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -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..1129ae08 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -11,7 +11,7 @@ import SQLite3
 @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..2bec6a06 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,
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index d57ebff7..8c033e41 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -163,7 +163,7 @@ 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")
@@ -238,7 +238,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..56415a59 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")!
 
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..6be98ca0 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -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..f018f097 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -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() {

From 4a5eda39a5aa63b4151b54e13ac9cd6ff3390a7e Mon Sep 17 00:00:00 2001
From: Cary Clark <clarkcb@gmail.com>
Date: Fri, 4 Oct 2024 15:54:10 -0700
Subject: [PATCH 54/95] Update oldest supported platform versions

---
 Package.swift | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Package.swift b/Package.swift
index 238661ae..12a2f11c 100644
--- a/Package.swift
+++ b/Package.swift
@@ -4,10 +4,10 @@ import PackageDescription
 let package = Package(
     name: "SQLite.swift",
     platforms: [
-        .iOS(.v11),
+        .iOS(.v12),
         .macOS(.v10_13),
         .watchOS(.v4),
-        .tvOS(.v11),
+        .tvOS(.v12),
         .visionOS(.v1)
     ],
     products: [

From 748dcba3312485151fa91b551a4564238f2b69b9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 18:35:08 +0200
Subject: [PATCH 55/95] Support creating tables in schema changer

---
 Makefile                                      |  2 +-
 Sources/SQLite/Schema/SchemaChanger.swift     | 49 +++++++++++++++++++
 Sources/SQLite/Schema/SchemaDefinitions.swift |  5 +-
 .../Schema/SchemaChangerTests.swift           | 46 +++++++++++++++++
 4 files changed, 100 insertions(+), 2 deletions(-)

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/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index af7b5e27..b6ed7312 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -43,19 +43,31 @@ public class SchemaChanger: CustomStringConvertible {
 
     public enum Operation {
         case addColumn(ColumnDefinition)
+        case addIndex(IndexDefinition)
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
+        case createTable(columns: [ColumnDefinition])
 
         /// 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 unique = definition.unique ? "UNIQUE" : ""
+                let columns = definition.columns.joined(separator: ", ")
+                let `where` = definition.where.map { " WHERE " + $0 } ?? ""
+
+                return "CREATE \(unique) INDEX \(definition.name) ON \(definition.table) (\(columns)) \(`where`)"
             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 .createTable(let columns):
+                return "CREATE TABLE \(table.quote()) (" +
+                    columns.map { $0.toSQL() }.joined(separator: ", ") +
+                ")"
             default: return nil
             }
         }
@@ -108,12 +120,39 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
+    public class CreateTableDefinition {
+        fileprivate var columnDefinitions: [ColumnDefinition] = []
+        fileprivate var indexDefinitions: [IndexDefinition] = []
+
+        let name: String
+
+        init(name: String) {
+            self.name = name
+        }
+
+        public func add(column: ColumnDefinition) {
+            columnDefinitions.append(column)
+        }
+
+        public func add(index: IndexDefinition) {
+            indexDefinitions.append(index)
+        }
+
+        var operations: [Operation] {
+            precondition(!columnDefinitions.isEmpty)
+            return [
+                .createTable(columns: columnDefinitions)
+            ] + indexDefinitions.map { .addIndex($0) }
+        }
+    }
+
     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 +180,15 @@ public class SchemaChanger: CustomStringConvertible {
         }
     }
 
+    public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
+        let createTableDefinition = CreateTableDefinition(name: table)
+        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)
     }
@@ -263,6 +311,7 @@ extension TableDefinition {
     func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
+        case .createTable, .addIndex: fatalError()
         case .addColumn: fatalError("Use 'ALTER TABLE ADD COLUMN (...)'")
         case .dropColumn(let column):
             return TableDefinition(name: name,
diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 80f9e199..60837244 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -270,12 +270,15 @@ 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)
     }
 
     public let table: String
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 2bec6a06..125ef09e 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -154,4 +154,50 @@ 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))
+            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,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "name",
+                             primaryKey: nil,
+                             type: .TEXT,
+                             nullable: false,
+                             defaultValue: .NULL,
+                             references: nil),
+            ColumnDefinition(name: "age",
+                             primaryKey: nil,
+                             type: .INTEGER,
+                             nullable: true,
+                             defaultValue: .NULL,
+                             references: nil)
+        ])
+
+        let indexes = try schema.indexDefinitions(table: "foo")
+        XCTAssertEqual(indexes, [
+            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil)
+        ])
+    }
 }

From 261f98ae268f986ec6e54822d45d9bda263bdd50 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:17:45 +0200
Subject: [PATCH 56/95] Update docs

---
 Documentation/Index.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index fcd2d642..bc62c791 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)
@@ -1583,6 +1584,16 @@ 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
 
 

From e21fde28f0f96cc452151aaa17413b198f3718a9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:18:37 +0200
Subject: [PATCH 57/95] Revert to standard cocoapods

---
 Gemfile      |  2 +-
 Gemfile.lock | 93 ++++++++++++++++++++++++++--------------------------
 2 files changed, 48 insertions(+), 47 deletions(-)

diff --git a/Gemfile b/Gemfile
index eb6036cd..1f2ebb87 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,3 @@
 source "https://rubygems.org"
 
-gem 'cocoapods', :git => 'https://github.com/SagarSDagdu/CocoaPods.git', tag: '1.15.2.1-sagard'
+gem 'cocoapods' 
diff --git a/Gemfile.lock b/Gemfile.lock
index a58783ea..464ef06b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,27 +1,3 @@
-GIT
-  remote: https://github.com/SagarSDagdu/CocoaPods.git
-  revision: d96f491f79abd2804d1359c5228cce404dd365b7
-  tag: 1.15.2.1-sagard
-  specs:
-    cocoapods (1.15.2.1.pre.sagard)
-      addressable (~> 2.8)
-      claide (>= 1.0.2, < 2.0)
-      cocoapods-core (= 1.15.2)
-      cocoapods-deintegrate (>= 1.0.3, < 2.0)
-      cocoapods-downloader (>= 2.1, < 3.0)
-      cocoapods-plugins (>= 1.0.0, < 2.0)
-      cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.6.0, < 2.0)
-      cocoapods-try (>= 1.1.0, < 2.0)
-      colored2 (~> 3.1)
-      escape (~> 0.0.4)
-      fourflusher (>= 2.3.0, < 3.0)
-      gh_inspector (~> 1.0)
-      molinillo (~> 0.8.0)
-      nap (~> 1.0)
-      ruby-macho (>= 2.3.0, < 3.0)
-      xcodeproj (>= 1.23.0, < 2.0)
-
 GEM
   remote: https://rubygems.org/
   specs:
@@ -29,26 +5,47 @@ 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 (1.16.2)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      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)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (>= 2.3.0, < 3.0)
+      xcodeproj (>= 1.27.0, < 2.0)
+    cocoapods-core (1.16.2)
       activesupport (>= 5.0, < 8)
       addressable (~> 2.8)
       algoliasearch (~> 1.0)
@@ -68,48 +65,52 @@ GEM
       netrc (~> 0.11)
     cocoapods-try (1.2.0)
     colored2 (3.1.2)
-    concurrent-ruby (1.2.3)
-    connection_pool (2.4.1)
+    concurrent-ruby (1.3.5)
+    connection_pool (2.5.3)
     drb (2.2.1)
     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)
+    rexml (3.4.1)
     ruby-macho (2.5.1)
+    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
   ruby
 
 DEPENDENCIES
-  cocoapods!
+  cocoapods
 
 BUNDLED WITH
    2.5.4

From 5ef20d1887e4b8763df890c4f1ffc256b3f79b99 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 22:39:15 +0200
Subject: [PATCH 58/95] Don't lint against visionOS

---
 run-tests.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/run-tests.sh b/run-tests.sh
index 1bf8de96..cb68a022 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -11,7 +11,7 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
     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
+      bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
     fi
 elif [ -n "$CARTHAGE_PLATFORM" ]; then
     cd Tests/Carthage && make test CARTHAGE_PLATFORM="$CARTHAGE_PLATFORM"

From 017929080c146c2085b21adcce95ff67b9e82d2e Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:14:13 +0200
Subject: [PATCH 59/95] Disable visionos just for standalone

---
 run-tests.sh | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index cb68a022..94f643e4 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -8,15 +8,21 @@ 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 --platforms=macos,ios,tvos,watchos
-    fi
+    case "$VALIDATOR_SUBSPEC" in
+      none)
+        bundle exec pod lib lint --no-subspecs --fail-fast
+        ;;
+      standalone)
+        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
+        ;;
+      *)
+        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}
+    swift "${PACKAGE_MANAGER_COMMAND}"
 fi

From 023c5039780b1c086f1d4bbea1c6d5a683e3041f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:18:21 +0200
Subject: [PATCH 60/95] Enable word splitting

---
 run-tests.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/run-tests.sh b/run-tests.sh
index 94f643e4..7d5c26d2 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -24,5 +24,5 @@ elif [ -n "$CARTHAGE_PLATFORM" ]; then
 elif [ -n "$SPM" ]; then
     cd Tests/SPM && swift "${SPM}"
 elif [ -n "${PACKAGE_MANAGER_COMMAND}" ]; then
-    swift "${PACKAGE_MANAGER_COMMAND}"
+    swift ${PACKAGE_MANAGER_COMMAND}
 fi

From 673367b772b04a8df0be64e757b2979da8798939 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Tue, 20 May 2025 23:36:41 +0200
Subject: [PATCH 61/95] Public init

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 60837244..897f8557 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
         }

From 5de1420d58a87e082bb3659da9b59a19687cb867 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 14:23:31 +0200
Subject: [PATCH 62/95] Support ifNotExists

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 14 +++++++-----
 .../Schema/SchemaChangerTests.swift           | 22 +++++++++++++++++++
 2 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index b6ed7312..e2e3d5fa 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -47,7 +47,7 @@ public class SchemaChanger: CustomStringConvertible {
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
-        case createTable(columns: [ColumnDefinition])
+        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? {
@@ -64,8 +64,8 @@ public class SchemaChanger: CustomStringConvertible {
                 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 .createTable(let columns):
-                return "CREATE TABLE \(table.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
@@ -125,9 +125,11 @@ public class SchemaChanger: CustomStringConvertible {
         fileprivate var indexDefinitions: [IndexDefinition] = []
 
         let name: String
+        let ifNotExists: Bool
 
-        init(name: String) {
+        init(name: String, ifNotExists: Bool) {
             self.name = name
+            self.ifNotExists = ifNotExists
         }
 
         public func add(column: ColumnDefinition) {
@@ -141,7 +143,7 @@ public class SchemaChanger: CustomStringConvertible {
         var operations: [Operation] {
             precondition(!columnDefinitions.isEmpty)
             return [
-                .createTable(columns: columnDefinitions)
+                .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
             ] + indexDefinitions.map { .addIndex($0) }
         }
     }
@@ -181,7 +183,7 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     public func create(table: String, ifNotExists: Bool = false, block: CreateTableDefinitionBlock) throws {
-        let createTableDefinition = CreateTableDefinition(name: table)
+        let createTableDefinition = CreateTableDefinition(name: table, ifNotExists: ifNotExists)
         block(createTableDefinition)
 
         for operation in createTableDefinition.operations {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 125ef09e..bba16e08 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -200,4 +200,26 @@ class SchemaChangerTests: SQLiteTestCase {
             IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: 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)")
+            }
+        }
+    }
 }

From f2a86841f527f756610797d56b6c49a46c947f41 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 17:24:57 +0200
Subject: [PATCH 63/95] Add columns via Expressions

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 25 ++++++++++++++++
 .../Schema/SchemaChangerTests.swift           | 30 +++++++++++++++++++
 2 files changed, 55 insertions(+)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index e2e3d5fa..d3342032 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -136,6 +136,14 @@ public class SchemaChanger: CustomStringConvertible {
             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)
         }
@@ -146,6 +154,13 @@ public class SchemaChanger: CustomStringConvertible {
                 .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
             ] + indexDefinitions.map { .addIndex($0) }
         }
+
+        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
@@ -331,3 +346,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/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index bba16e08..6b53c9ce 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -201,6 +201,36 @@ class SchemaChangerTests: SQLiteTestCase {
         ])
     }
 
+    func test_create_table_add_column_expression() throws {
+        try schemaChanger.create(table: "foo") { table in
+            table.add(expression: Expression<String>("name"))
+            table.add(expression: Expression<Int>("age"))
+            table.add(expression: 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))

From cdaade15b93a43a5db8038da05da1c30c997ea3d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 18:45:26 +0200
Subject: [PATCH 64/95] Respect ifNotExists for index

---
 Sources/SQLite/Schema/SchemaChanger.swift         | 12 ++++--------
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 14 ++++++++++++++
 2 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index d3342032..13222dab 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -43,7 +43,7 @@ public class SchemaChanger: CustomStringConvertible {
 
     public enum Operation {
         case addColumn(ColumnDefinition)
-        case addIndex(IndexDefinition)
+        case addIndex(IndexDefinition, ifNotExists: Bool)
         case dropColumn(String)
         case renameColumn(String, String)
         case renameTable(String)
@@ -54,12 +54,8 @@ public class SchemaChanger: CustomStringConvertible {
             switch self {
             case .addColumn(let definition):
                 return "ALTER TABLE \(table.quote()) ADD COLUMN \(definition.toSQL())"
-            case .addIndex(let definition):
-                let unique = definition.unique ? "UNIQUE" : ""
-                let columns = definition.columns.joined(separator: ", ")
-                let `where` = definition.where.map { " WHERE " + $0 } ?? ""
-
-                return "CREATE \(unique) INDEX \(definition.name) ON \(definition.table) (\(columns)) \(`where`)"
+            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):
@@ -152,7 +148,7 @@ public class SchemaChanger: CustomStringConvertible {
             precondition(!columnDefinitions.isEmpty)
             return [
                 .createTable(columns: columnDefinitions, ifNotExists: ifNotExists)
-            ] + indexDefinitions.map { .addIndex($0) }
+            ] + indexDefinitions.map { .addIndex($0, ifNotExists: ifNotExists) }
         }
 
         private func columnName<T>(for expression: Expression<T>) -> String {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 6b53c9ce..02a82267 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -252,4 +252,18 @@ class SchemaChangerTests: SQLiteTestCase {
             }
         }
     }
+
+    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))
+        }
+    }
 }

From a0136544e9627b941008d7c59836b61feaf1f142 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 22:17:39 +0200
Subject: [PATCH 65/95] New visionOS0-compatible pod has been release

---
 run-tests.sh | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/run-tests.sh b/run-tests.sh
index 7d5c26d2..3ffba810 100755
--- a/run-tests.sh
+++ b/run-tests.sh
@@ -12,9 +12,6 @@ elif [ -n "$VALIDATOR_SUBSPEC" ]; then
       none)
         bundle exec pod lib lint --no-subspecs --fail-fast
         ;;
-      standalone)
-        bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast --platforms=macos,ios,tvos,watchos
-        ;;
       *)
         bundle exec pod lib lint --subspec="${VALIDATOR_SUBSPEC}" --fail-fast
         ;;

From 77b493e661cff1498892dbaa48aa2205b673ce46 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 22:42:20 +0200
Subject: [PATCH 66/95] Use inheritance

---
 SQLite.swift.podspec | 43 +++++--------------------------------------
 1 file changed, 5 insertions(+), 38 deletions(-)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 184ce23e..4cbd91bb 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -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,18 +47,9 @@ 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
 
@@ -87,18 +63,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

From 7309b4337a0649d53d8ebd703f582117c1dc9e91 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:04:30 +0200
Subject: [PATCH 67/95] Use newer Xcode

---
 .github/workflows/build.yml | 8 +-------
 1 file changed, 1 insertion(+), 7 deletions(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 9fbba1c5..5b6b74a6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -5,15 +5,9 @@ env:
   IOS_VERSION: "17.2"
 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)"

From 4166f86e844f5d1e87e25264eb9a5194dbaf3b08 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:10:36 +0200
Subject: [PATCH 68/95] iOS 17.5

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 5b6b74a6..a0274321 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 15"
-  IOS_VERSION: "17.2"
+  IOS_VERSION: "17.5"
 jobs:
   build:
     runs-on: macos-15

From f312ab4d12b344f7e541df5cb0363070a0f964ba Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Wed, 21 May 2025 23:29:27 +0200
Subject: [PATCH 69/95] Update bundler

---
 Gemfile.lock | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Gemfile.lock b/Gemfile.lock
index 464ef06b..9f71603b 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -67,7 +67,7 @@ GEM
     colored2 (3.1.2)
     concurrent-ruby (1.3.5)
     connection_pool (2.5.3)
-    drb (2.2.1)
+    drb (2.2.3)
     escape (0.0.4)
     ethon (0.16.0)
       ffi (>= 1.15.0)
@@ -113,4 +113,4 @@ DEPENDENCIES
   cocoapods
 
 BUNDLED WITH
-   2.5.4
+   2.6.9

From 9dc478c9f1f6eaae43f1aa4286e3bf574bb5976d Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 10:02:48 +0200
Subject: [PATCH 70/95] SchemaReader: parse and create unique constraints

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 27 ++++++++--
 Sources/SQLite/Schema/SchemaReader.swift      | 52 ++++++++++++++-----
 .../Schema/SchemaChangerTests.swift           |  9 ++--
 .../Schema/SchemaReaderTests.swift            | 32 ++++++++++--
 4 files changed, 98 insertions(+), 22 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index 897f8557..bb4ce82b 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -128,6 +128,7 @@ public struct ColumnDefinition: Equatable {
     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 +136,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 +247,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))
@@ -278,7 +283,8 @@ public struct IndexDefinition: Equatable {
                   unique: unique,
                   columns: columns,
                   where: indexSQL.flatMap(wherePart),
-                  orders: (orders?.isEmpty ?? false) ? nil : orders)
+                  orders: (orders?.isEmpty ?? false) ? nil : orders,
+                  origin: origin)
     }
 
     public let table: String
@@ -287,6 +293,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)
@@ -300,6 +313,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)
@@ -348,6 +368,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: " ")
diff --git a/Sources/SQLite/Schema/SchemaReader.swift b/Sources/SQLite/Schema/SchemaReader.swift
index 1989ad6f..a67fb94f 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -21,7 +21,7 @@ public class SchemaReader {
         let foreignKeys: [String: [ColumnDefinition.ForeignKey]] =
             Dictionary(grouping: try foreignKeys(table: table), by: { $0.column })
 
-        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])
                 )
             }
     }
@@ -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/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 02a82267..724c2d0c 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -158,7 +158,7 @@ class SchemaChangerTests: SQLiteTestCase {
     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))
+            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,
@@ -179,25 +179,28 @@ class SchemaChangerTests: SQLiteTestCase {
                              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")
+        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)
+            IndexDefinition(table: "foo", name: "nameIndex", unique: true, columns: ["name"], where: nil, orders: nil, origin: .createIndex)
         ])
     }
 
diff --git a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
index 8c033e41..01047c9c 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)),
             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
             )
         ])
     }

From 374d99da2107656adf12670feea4f167853321d9 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 10:53:36 +0200
Subject: [PATCH 71/95] Use cocoapods with watchOS fix

---
 Gemfile      |  2 +-
 Gemfile.lock | 46 ++++++++++++++++++++++++++--------------------
 2 files changed, 27 insertions(+), 21 deletions(-)

diff --git a/Gemfile b/Gemfile
index 1f2ebb87..2bb803ca 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,3 @@
 source "https://rubygems.org"
 
-gem 'cocoapods' 
+gem 'cocoapods', :git => 'https://github.com/jberkel/CocoaPods.git', branch: 'watchos-fourflusher'
diff --git a/Gemfile.lock b/Gemfile.lock
index 9f71603b..20ed2aad 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,3 +1,27 @@
+GIT
+  remote: https://github.com/jberkel/CocoaPods.git
+  revision: 899f273f298ea20de2378687ea55331004b39371
+  branch: watchos-fourflusher
+  specs:
+    cocoapods (1.16.2)
+      addressable (~> 2.8)
+      claide (>= 1.0.2, < 2.0)
+      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)
+      cocoapods-search (>= 1.0.0, < 2.0)
+      cocoapods-trunk (>= 1.6.0, < 2.0)
+      cocoapods-try (>= 1.1.0, < 2.0)
+      colored2 (~> 3.1)
+      escape (~> 0.0.4)
+      fourflusher (>= 2.3.0, < 3.0)
+      gh_inspector (~> 1.0)
+      molinillo (~> 0.8.0)
+      nap (~> 1.0)
+      ruby-macho (~> 4.1.0)
+      xcodeproj (>= 1.27.0, < 2.0)
+
 GEM
   remote: https://rubygems.org/
   specs:
@@ -27,24 +51,6 @@ GEM
     benchmark (0.4.0)
     bigdecimal (3.1.9)
     claide (1.1.0)
-    cocoapods (1.16.2)
-      addressable (~> 2.8)
-      claide (>= 1.0.2, < 2.0)
-      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)
-      cocoapods-search (>= 1.0.0, < 2.0)
-      cocoapods-trunk (>= 1.6.0, < 2.0)
-      cocoapods-try (>= 1.1.0, < 2.0)
-      colored2 (~> 3.1)
-      escape (~> 0.0.4)
-      fourflusher (>= 2.3.0, < 3.0)
-      gh_inspector (~> 1.0)
-      molinillo (~> 0.8.0)
-      nap (~> 1.0)
-      ruby-macho (>= 2.3.0, < 3.0)
-      xcodeproj (>= 1.27.0, < 2.0)
     cocoapods-core (1.16.2)
       activesupport (>= 5.0, < 8)
       addressable (~> 2.8)
@@ -91,7 +97,7 @@ GEM
     nkf (0.2.0)
     public_suffix (4.0.7)
     rexml (3.4.1)
-    ruby-macho (2.5.1)
+    ruby-macho (4.1.0)
     securerandom (0.4.1)
     typhoeus (1.4.1)
       ethon (>= 0.9.0)
@@ -110,7 +116,7 @@ PLATFORMS
   ruby
 
 DEPENDENCIES
-  cocoapods
+  cocoapods!
 
 BUNDLED WITH
    2.6.9

From 108431244de611050f25c20efd02bcfe68687b28 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 11:52:52 +0200
Subject: [PATCH 72/95] Disable visionOS for SQLCipher

---
 SQLite.swift.podspec | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index 4cbd91bb..d0ccb136 100644
--- a/SQLite.swift.podspec
+++ b/SQLite.swift.podspec
@@ -54,6 +54,13 @@ Pod::Spec.new do |s|
   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' }
 

From 718c7988780f803ede87c03ef2cdce58ea762656 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 12:21:19 +0200
Subject: [PATCH 73/95] Add comment

---
 Gemfile      | 1 +
 Gemfile.lock | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/Gemfile b/Gemfile
index 2bb803ca..2770e85d 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,3 +1,4 @@
 source "https://rubygems.org"
 
+# 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 20ed2aad..7dac2c18 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,6 +1,6 @@
 GIT
   remote: https://github.com/jberkel/CocoaPods.git
-  revision: 899f273f298ea20de2378687ea55331004b39371
+  revision: 32a90c184bc5dc9ec8b7b9b8ad08e98b7253dec2
   branch: watchos-fourflusher
   specs:
     cocoapods (1.16.2)

From c812caf9c432bc196f87648b56551dff2c0017e3 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 13:30:36 +0200
Subject: [PATCH 74/95] Make ForeignKey public, rename fields for clarity

---
 Sources/SQLite/Schema/SchemaDefinitions.swift | 23 ++++++++++---
 Sources/SQLite/Schema/SchemaReader.swift      |  8 ++---
 .../Schema/SchemaChangerTests.swift           | 34 +++++++++++++++++++
 .../Schema/SchemaDefinitionsTests.swift       |  8 ++---
 .../Schema/SchemaReaderTests.swift            |  4 +--
 5 files changed, 62 insertions(+), 15 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaDefinitions.swift b/Sources/SQLite/Schema/SchemaDefinitions.swift
index bb4ce82b..d7803999 100644
--- a/Sources/SQLite/Schema/SchemaDefinitions.swift
+++ b/Sources/SQLite/Schema/SchemaDefinitions.swift
@@ -117,11 +117,24 @@ 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
@@ -400,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 a67fb94f..995cd4d7 100644
--- a/Sources/SQLite/Schema/SchemaReader.swift
+++ b/Sources/SQLite/Schema/SchemaReader.swift
@@ -19,7 +19,7 @@ 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 })
 
         let columnDefinitions = try connection.prepareRowIterator("PRAGMA table_info(\(table.quote()))")
             .map { (row: Row) -> ColumnDefinition in
@@ -111,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
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 724c2d0c..b08344fc 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -269,4 +269,38 @@ class SchemaChangerTests: SQLiteTestCase {
             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)
+    }
 }
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 01047c9c..8963070f 100644
--- a/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaReaderTests.swift
@@ -54,7 +54,7 @@ class SchemaReaderTests: SQLiteTestCase {
                              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,
@@ -194,7 +194,7 @@ class SchemaReaderTests: SQLiteTestCase {
 
         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)
         ])
     }
 

From 1c440766b22019190a9c78731f0c69b78f5d177b Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:06:41 +0200
Subject: [PATCH 75/95] Add drop(index:)

---
 Sources/SQLite/Schema/SchemaChanger.swift     | 10 ++++-
 .../Schema/SchemaChangerTests.swift           | 38 +++++++++++++++++++
 2 files changed, 47 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 13222dab..7ba458c1 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -45,6 +45,7 @@ public class SchemaChanger: CustomStringConvertible {
         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)
@@ -60,6 +61,8 @@ public class SchemaChanger: CustomStringConvertible {
                 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: ", ") +
@@ -111,6 +114,10 @@ public class SchemaChanger: CustomStringConvertible {
             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))
         }
@@ -324,8 +331,9 @@ extension TableDefinition {
     func apply(_ operation: SchemaChanger.Operation?) -> TableDefinition {
         switch operation {
         case .none: return self
-        case .createTable, .addIndex: fatalError()
+        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 },
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index b08344fc..75331a97 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -124,6 +124,44 @@ class SchemaChangerTests: SQLiteTestCase {
         }
     }
 
+    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

From 08bed022dc12289d64278cc5ad2e9a63868fb12f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:23:02 +0200
Subject: [PATCH 76/95] Implement add(index:)

---
 Sources/SQLite/Schema/SchemaChanger.swift     |  4 +++
 .../Schema/SchemaChangerTests.swift           | 34 +++++++++++++++++++
 2 files changed, 38 insertions(+)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 7ba458c1..3badb8d8 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -110,6 +110,10 @@ 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))
         }
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index 75331a97..dafdea54 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -124,6 +124,40 @@ 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)

From 3def7d60f70b109e0f74f6d42bac61d631de56dd Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:45:33 +0200
Subject: [PATCH 77/95] Make name public, expose plain SQL run

---
 Sources/SQLite/Schema/SchemaChanger.swift | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 3badb8d8..3e146df7 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -100,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
@@ -223,6 +223,11 @@ 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.
+    public func run(sql: String) throws {
+        try connection.run(sql)
+    }
+
     private func run(table: String, operation: Operation) throws {
         try operation.validate()
 

From 25bd06392f65d7ca75ffc481471de05905ec1c79 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 22 May 2025 17:53:46 +0200
Subject: [PATCH 78/95] Better API, test

---
 Sources/SQLite/Schema/SchemaChanger.swift         | 5 +++--
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 5 +++++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/Sources/SQLite/Schema/SchemaChanger.swift b/Sources/SQLite/Schema/SchemaChanger.swift
index 3e146df7..6fae532a 100644
--- a/Sources/SQLite/Schema/SchemaChanger.swift
+++ b/Sources/SQLite/Schema/SchemaChanger.swift
@@ -224,8 +224,9 @@ public class SchemaChanger: CustomStringConvertible {
     }
 
     // Runs arbitrary SQL. Should only be used if no predefined operations exist.
-    public func run(sql: String) throws {
-        try connection.run(sql)
+    @discardableResult
+    public func run(_ sql: String, _ bindings: Binding?...) throws -> Statement {
+        return try connection.run(sql, bindings)
     }
 
     private func run(table: String, operation: Operation) throws {
diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index dafdea54..d942fba4 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -375,4 +375,9 @@ class SchemaChangerTests: SQLiteTestCase {
                              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)
+    }
 }

From 8938697549dccc21803c5cbef9f2e5fec7ee274a Mon Sep 17 00:00:00 2001
From: ha100 <thastik@gmail.com>
Date: Sun, 8 Jun 2025 05:38:36 +0200
Subject: [PATCH 79/95] change CSQLite dep to SwiftToolchainCSQLite to allow
 swift sdk cross compilation

---
 Package.swift                                 | 69 +++++++++++--------
 Sources/SQLite/Core/Backup.swift              |  2 +-
 .../SQLite/Core/Connection+Aggregation.swift  |  2 +-
 Sources/SQLite/Core/Connection+Attach.swift   |  2 +-
 Sources/SQLite/Core/Connection.swift          |  2 +-
 Sources/SQLite/Core/Result.swift              |  2 +-
 Sources/SQLite/Core/Statement.swift           |  2 +-
 Sources/SQLite/Helpers.swift                  |  2 +-
 .../Core/Connection+AttachTests.swift         |  2 +-
 .../Core/Connection+PragmaTests.swift         |  2 +-
 Tests/SQLiteTests/Core/ConnectionTests.swift  |  2 +-
 Tests/SQLiteTests/Core/ResultTests.swift      |  2 +-
 Tests/SQLiteTests/Core/StatementTests.swift   |  2 +-
 .../Extensions/FTSIntegrationTests.swift      |  2 +-
 .../Typed/CustomAggregationTests.swift        |  2 +-
 .../Typed/QueryIntegrationTests.swift         |  2 +-
 Tests/SQLiteTests/Typed/QueryTests.swift      |  2 +-
 17 files changed, 56 insertions(+), 45 deletions(-)

diff --git a/Package.swift b/Package.swift
index 238661ae..690d41e2 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,6 +1,38 @@
 // 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")
+        ],
+        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: [
@@ -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/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 0eebbdd5..5e741ecd 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -29,7 +29,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index 4eea76c3..bfe253a7 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 8a25e51d..32461468 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index f2c3b781..188ff80b 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -29,7 +29,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index ee59e5d1..2659ad21 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -3,7 +3,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 6cb2e5d3..458e3d9c 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -27,7 +27,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index c27ccf05..00493f21 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -27,7 +27,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index f37300ca..68618596 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index 2bcdb6af..bd29bd98 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 6a1d94ae..61083453 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -8,7 +8,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index d3c8bb1f..b6a373f7 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -7,7 +7,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index dbf99d7c..ceaa8813 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -6,7 +6,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index 1129ae08..8a34e93b 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
index 71cbba9c..8b7ec09a 100644
--- a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
@@ -8,7 +8,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 6be98ca0..899bf354 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index f018f097..7fc3dcf5 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -4,7 +4,7 @@ import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
 #elseif os(Linux)
-import CSQLite
+import SwiftToolchainCSQLite
 #else
 import SQLite3
 #endif

From fc774a2585965a90f60aa034001e6d69aee42382 Mon Sep 17 00:00:00 2001
From: ha100 <thastik@gmail.com>
Date: Sun, 8 Jun 2025 07:08:47 +0200
Subject: [PATCH 80/95] fix macOS build via platform condition

---
 Package.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Package.swift b/Package.swift
index 690d41e2..48085178 100644
--- a/Package.swift
+++ b/Package.swift
@@ -9,7 +9,7 @@ let targets: [Target] = [
     .target(
         name: "SQLite",
         dependencies: [
-            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite")
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows]))
         ],
         exclude: [
             "Info.plist"

From f3cb9105a80d566575c02bb77ac50e74e5895ccc Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:15:34 +0200
Subject: [PATCH 81/95] Fully qualify Expression

---
 Tests/SQLiteTests/Schema/SchemaChangerTests.swift | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
index d942fba4..f5a6de42 100644
--- a/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
+++ b/Tests/SQLiteTests/Schema/SchemaChangerTests.swift
@@ -278,9 +278,9 @@ class SchemaChangerTests: SQLiteTestCase {
 
     func test_create_table_add_column_expression() throws {
         try schemaChanger.create(table: "foo") { table in
-            table.add(expression: Expression<String>("name"))
-            table.add(expression: Expression<Int>("age"))
-            table.add(expression: Expression<Double?>("salary"))
+            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")

From a7d7e8c0b7529f851fd8927071003095f1e87522 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:27:28 +0200
Subject: [PATCH 82/95] 17.5 no longer available

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index a0274321..33c4dba6 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,7 +2,7 @@ name: Build and test
 on: [push, pull_request]
 env:
   IOS_SIMULATOR: "iPhone 15"
-  IOS_VERSION: "17.5"
+  IOS_VERSION: "18.0"
 jobs:
   build:
     runs-on: macos-15

From 716394d091618047dfe86b7da5106fb21ede412f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 12:34:42 +0200
Subject: [PATCH 83/95] iPhone 16

---
 .github/workflows/build.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 33c4dba6..37f75476 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,7 +1,7 @@
 name: Build and test
 on: [push, pull_request]
 env:
-  IOS_SIMULATOR: "iPhone 15"
+  IOS_SIMULATOR: "iPhone 16"
   IOS_VERSION: "18.0"
 jobs:
   build:

From 9831a45edb84d492cd5b68ba6f8d53300808499f Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Thu, 12 Jun 2025 13:17:56 +0200
Subject: [PATCH 84/95] Update Index.md

---
 Documentation/Index.md | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index bc62c791..bd2a7a77 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -1592,7 +1592,8 @@ 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
 

From e3a916637162ece77b0ffd89b9e758669adc62f0 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:49:43 +0200
Subject: [PATCH 85/95] Update CHANGELOG for release

---
 CHANGELOG.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)

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

From 02e055c99c4fa19604a95fea4e82fd085391fdf0 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:51:53 +0200
Subject: [PATCH 86/95] Update README

---
 README.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index a4d713b6..3e782f86 100644
--- a/README.md
+++ b/README.md
@@ -128,7 +128,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 +152,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 +183,7 @@ SQLite.swift with CocoaPods:
     use_frameworks!
 
     target 'YourAppTargetName' do
-        pod 'SQLite.swift', '~> 0.14.0'
+        pod 'SQLite.swift', '~> 0.15.0'
     end
     ```
 

From bbc5212c2ae9905d17ae581927c06e2cbad61ff7 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 22:53:36 +0200
Subject: [PATCH 87/95] Project looks dead

---
 README.md | 1 -
 1 file changed, 1 deletion(-)

diff --git a/README.md b/README.md
index 3e782f86..db80fd3a 100644
--- a/README.md
+++ b/README.md
@@ -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

From 4e289bed6f1ed6737ffa963570beae3359a849e8 Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 23:00:44 +0200
Subject: [PATCH 88/95] No longer maintained

---
 README.md | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/README.md b/README.md
index db80fd3a..0e83504f 100644
--- a/README.md
+++ b/README.md
@@ -226,8 +226,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 +233,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

From 0f4f2c85c5beddeb70f02e1db36785b5f6e2dccf Mon Sep 17 00:00:00 2001
From: Jan Berkel <jan@berkel.fr>
Date: Fri, 13 Jun 2025 23:12:38 +0200
Subject: [PATCH 89/95] Bump version to 0.15.4

---
 Documentation/Index.md           | 12 ++++++------
 SQLite.swift.podspec             |  2 +-
 SQLite.xcodeproj/project.pbxproj | 12 ++----------
 Tests/SPM/Package.swift          |  2 +-
 4 files changed, 10 insertions(+), 18 deletions(-)

diff --git a/Documentation/Index.md b/Documentation/Index.md
index bd2a7a77..0e8261f2 100644
--- a/Documentation/Index.md
+++ b/Documentation/Index.md
@@ -109,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")
   ]
   ```
 
@@ -130,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].
@@ -160,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
     ```
 
@@ -174,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
 ```
 
@@ -184,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
 ```
@@ -200,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
 ```
 
diff --git a/SQLite.swift.podspec b/SQLite.swift.podspec
index d0ccb136..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
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/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")])

From 496a086e5a4d03fb6b0338f63b600dc153be6fa3 Mon Sep 17 00:00:00 2001
From: Alsey Coleman Miller <alseycmiller@gmail.com>
Date: Mon, 14 Jul 2025 15:14:14 -0400
Subject: [PATCH 90/95] Build `SwiftToolchainCSQLite` for Android

---
 Package.swift | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Package.swift b/Package.swift
index cf3a0b60..56925d18 100644
--- a/Package.swift
+++ b/Package.swift
@@ -9,7 +9,7 @@ let targets: [Target] = [
     .target(
         name: "SQLite",
         dependencies: [
-            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows]))
+            .product(name: "SwiftToolchainCSQLite", package: "swift-toolchain-sqlite", condition: .when(platforms: [.linux, .windows, .android]))
         ],
         exclude: [
             "Info.plist"

From e8cfef658cbdbdd297ed7f549d197ec481b6b7a9 Mon Sep 17 00:00:00 2001
From: Alsey Coleman Miller <alseycmiller@gmail.com>
Date: Mon, 14 Jul 2025 15:14:34 -0400
Subject: [PATCH 91/95] Fix Android build

---
 Sources/SQLite/Core/Backup.swift                       | 2 +-
 Sources/SQLite/Core/Connection+Aggregation.swift       | 2 +-
 Sources/SQLite/Core/Connection+Attach.swift            | 2 +-
 Sources/SQLite/Core/Connection.swift                   | 2 +-
 Sources/SQLite/Core/Result.swift                       | 2 +-
 Sources/SQLite/Core/Statement.swift                    | 2 +-
 Sources/SQLite/Helpers.swift                           | 2 +-
 Tests/SQLiteTests/Core/Connection+AttachTests.swift    | 2 +-
 Tests/SQLiteTests/Core/Connection+PragmaTests.swift    | 2 +-
 Tests/SQLiteTests/Core/ConnectionTests.swift           | 2 +-
 Tests/SQLiteTests/Core/ResultTests.swift               | 2 +-
 Tests/SQLiteTests/Core/StatementTests.swift            | 2 +-
 Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift | 2 +-
 Tests/SQLiteTests/Typed/CustomAggregationTests.swift   | 2 +-
 Tests/SQLiteTests/Typed/QueryIntegrationTests.swift    | 2 +-
 Tests/SQLiteTests/Typed/QueryTests.swift               | 2 +-
 16 files changed, 16 insertions(+), 16 deletions(-)

diff --git a/Sources/SQLite/Core/Backup.swift b/Sources/SQLite/Core/Backup.swift
index 5e741ecd..023acc08 100644
--- a/Sources/SQLite/Core/Backup.swift
+++ b/Sources/SQLite/Core/Backup.swift
@@ -28,7 +28,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection+Aggregation.swift b/Sources/SQLite/Core/Connection+Aggregation.swift
index bfe253a7..a1abb74a 100644
--- a/Sources/SQLite/Core/Connection+Aggregation.swift
+++ b/Sources/SQLite/Core/Connection+Aggregation.swift
@@ -3,7 +3,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection+Attach.swift b/Sources/SQLite/Core/Connection+Attach.swift
index 32461468..0c674ee6 100644
--- a/Sources/SQLite/Core/Connection+Attach.swift
+++ b/Sources/SQLite/Core/Connection+Attach.swift
@@ -3,7 +3,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Connection.swift b/Sources/SQLite/Core/Connection.swift
index 188ff80b..57521f83 100644
--- a/Sources/SQLite/Core/Connection.swift
+++ b/Sources/SQLite/Core/Connection.swift
@@ -28,7 +28,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Result.swift b/Sources/SQLite/Core/Result.swift
index 2659ad21..9a72e4c5 100644
--- a/Sources/SQLite/Core/Result.swift
+++ b/Sources/SQLite/Core/Result.swift
@@ -2,7 +2,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Core/Statement.swift b/Sources/SQLite/Core/Statement.swift
index 458e3d9c..82d535b9 100644
--- a/Sources/SQLite/Core/Statement.swift
+++ b/Sources/SQLite/Core/Statement.swift
@@ -26,7 +26,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Sources/SQLite/Helpers.swift b/Sources/SQLite/Helpers.swift
index 00493f21..e3c84589 100644
--- a/Sources/SQLite/Helpers.swift
+++ b/Sources/SQLite/Helpers.swift
@@ -26,7 +26,7 @@
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/Connection+AttachTests.swift b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
index 68618596..0e185da5 100644
--- a/Tests/SQLiteTests/Core/Connection+AttachTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+AttachTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
index bd29bd98..d1d4ab04 100644
--- a/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
+++ b/Tests/SQLiteTests/Core/Connection+PragmaTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/ConnectionTests.swift b/Tests/SQLiteTests/Core/ConnectionTests.swift
index 61083453..da50974a 100644
--- a/Tests/SQLiteTests/Core/ConnectionTests.swift
+++ b/Tests/SQLiteTests/Core/ConnectionTests.swift
@@ -7,7 +7,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/ResultTests.swift b/Tests/SQLiteTests/Core/ResultTests.swift
index b6a373f7..03415f3a 100644
--- a/Tests/SQLiteTests/Core/ResultTests.swift
+++ b/Tests/SQLiteTests/Core/ResultTests.swift
@@ -6,7 +6,7 @@ import Foundation
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Core/StatementTests.swift b/Tests/SQLiteTests/Core/StatementTests.swift
index ceaa8813..3c90d941 100644
--- a/Tests/SQLiteTests/Core/StatementTests.swift
+++ b/Tests/SQLiteTests/Core/StatementTests.swift
@@ -5,7 +5,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
index 8a34e93b..b3b9e617 100644
--- a/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
+++ b/Tests/SQLiteTests/Extensions/FTSIntegrationTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
index 8b7ec09a..73a0767f 100644
--- a/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
+++ b/Tests/SQLiteTests/Typed/CustomAggregationTests.swift
@@ -7,7 +7,7 @@ import Dispatch
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
index 899bf354..cde5a0c3 100644
--- a/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryIntegrationTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3
diff --git a/Tests/SQLiteTests/Typed/QueryTests.swift b/Tests/SQLiteTests/Typed/QueryTests.swift
index 7fc3dcf5..257b5245 100644
--- a/Tests/SQLiteTests/Typed/QueryTests.swift
+++ b/Tests/SQLiteTests/Typed/QueryTests.swift
@@ -3,7 +3,7 @@ import XCTest
 import sqlite3
 #elseif SQLITE_SWIFT_SQLCIPHER
 import SQLCipher
-#elseif os(Linux)
+#elseif canImport(SwiftToolchainCSQLite)
 import SwiftToolchainCSQLite
 #else
 import SQLite3

From 91a30a3535e74767372ef605751569299696d8d1 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Tue, 15 Jul 2025 13:54:04 +0200
Subject: [PATCH 92/95] add android ci

---
 .github/workflows/build.yml | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 37f75476..f82a9c59 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -76,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

From 22d30c885f6ac1161ff5e88e32f054b4672963d2 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Wed, 16 Jul 2025 15:17:33 +0200
Subject: [PATCH 93/95] add SQLite. prefix for Expression in README

---
 README.md | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index 0e83504f..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.
 

From fee88af870ccf4f574607b1dc8f2835cb2aa0b36 Mon Sep 17 00:00:00 2001
From: hawk0620 <chenhao@weshot.cc>
Date: Thu, 17 Jul 2025 14:11:06 +0800
Subject: [PATCH 94/95] create index support array as parameters

---
 Sources/SQLite/Typed/Schema.swift | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/Sources/SQLite/Typed/Schema.swift b/Sources/SQLite/Typed/Schema.swift
index e162cf34..02f5b102 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -135,8 +135,8 @@ 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"),
@@ -146,12 +146,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()

From 98d83a223db0d1a51c2d575f108c66b1b770b725 Mon Sep 17 00:00:00 2001
From: NathanFallet <contact@nathanfallet.me>
Date: Thu, 17 Jul 2025 13:42:49 +0200
Subject: [PATCH 95/95] fix linting

---
 Sources/SQLite/Typed/Query.swift    |  2 +-
 Sources/SQLite/Typed/Schema.swift   |  8 ++++----
 Tests/SQLiteTests/TestHelpers.swift | 10 ----------
 3 files changed, 5 insertions(+), 15 deletions(-)

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 02f5b102..919042ab 100644
--- a/Sources/SQLite/Typed/Schema.swift
+++ b/Sources/SQLite/Typed/Schema.swift
@@ -135,7 +135,7 @@ extension Table {
     }
 
     // MARK: - CREATE INDEX
-    
+
     public func createIndex(_ columns: [Expressible], unique: Bool = false, ifNotExists: Bool = false) -> String {
         let clauses: [Expressible?] = [
             create("INDEX", indexName(columns), unique ? .unique : nil, ifNotExists),
@@ -146,17 +146,17 @@ 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 {
         drop("INDEX", indexName(columns), ifExists)
     }
-    
+
     public func dropIndex(_ columns: Expressible..., ifExists: Bool = false) -> String {
         dropIndex(Array(columns), ifExists: ifExists)
     }
diff --git a/Tests/SQLiteTests/TestHelpers.swift b/Tests/SQLiteTests/TestHelpers.swift
index 56415a59..e62b2a23 100644
--- a/Tests/SQLiteTests/TestHelpers.swift
+++ b/Tests/SQLiteTests/TestHelpers.swift
@@ -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
-    }
 }
