Repository navigation
🚀 SQL 2.0.0
SwifQL is now SQL 🚀
The project moved to the SwiftStream organization and the package, product, module, and query root now share one name: SQL.
Install 2.0.0 with:
.package(
url: "https://github.com/SwiftStream/SQL",
from: "2.0.0"
)and use product SQL:
.product(name: "SQL", package: "SQL")Breaking change: SwifQL → SQL
was
import SwifQL
let query = SwifQL
.select(\User.$id) // or Path.Column("id")
.from(User.table) // or Path.Table("users")became
import SQL
let query = SQL // <-- SwifQL replaced with SQL
.select(\User.$id)
.from(User.table)There is no compatibility module named SwifQL. After import SQL, retained old SwifQL* symbols may still be available as deprecated/renamed bridges where provided.
Declarative SQL
You can keep the direct fluent style:
let query = SQL
.select(\User.$id, \User.$email)
.from(User.table)
.where(\User.$email == "john@example.com")
.limit(10)Result Builder DSL
Brand new gorgeous way to write your queries
let query = SQL {
Select {
\User.$id
\User.$email
}
// or Select(\User.$id, \User.$email)
From {
User.table
}
// or From(User.table)
Where {
\User.$email == "john@example.com"
}
// you still can use
// Where(\User.$email == "john@example.com")
// for simple predicates
Limit(10)
}The classic declarative and result-builder approaches use the same composable parts, preparation, and binding pipeline.
Select { ... } can be easily replaced with declarative Select(...) form, same for From, Where and other parts.
The biggest advantage of a result-builder DSL, aside from its elegance, is that you can use if/else/for and other conditional constructs.
Reusable queries
SQLQuery lets a reusable value hide conditional SQL while the call site stays small:
struct UserQuery: SQLQuery {
let active: Bool
let email: String?
let roles: [String]?
var query: Query {
Select {
\User.$id
\User.$email
}
From {
User.table
}
Where {
\User.$active == active
if let email {
\User.$email == email
}
if let roles {
Or {
for role in roles {
\User.$role == role
}
}
}
}
}
}Outside the type, it is just a normal composable value:
let users = UserQuery(
active: true,
email: email,
roles: roles
)
let prepared = users.prepare(.psql)Because SQLQuery is SQLable, UserQuery(...) can also be nested directly in FROM/JOIN/subquery positions without manually unwrapping .query.
Declarative table DDL
let createUsers = CreateTable("users") {
NewColumn("id", .uuid).primaryKey()
NewColumn("email", .text).unique().notNull()
}
createUsers.prepare(.psql).plainwill give:
CREATE TABLE "users" ("id" uuid PRIMARY KEY, "email" text UNIQUE NOT NULL)SQL only builds the statement. Migration history, transaction policy, and execution stay in your application or database layer.
More SQL
The 2.0 line significantly expands the SQL surface, including analytics, JSON and nested values, joins and set operations, PIVOT/UNPIVOT, MERGE, COPY, DML/RETURNING, DDL, sequences, macros, and table/file functions.
PostgreSQL, MySQL, and DuckDB use the same preparation model:
query.prepare(.psql)
query.prepare(.mysql)
query.prepare(.duck)Shared semantic values
SQL includes database-facing civil and interval values:
let date = PureDate(year: 2026, month: 9, day: 4)!
let time = PureTime(hour: 12, minute: 34, second: 56, nanosecond: 123_456_789)!
let dateTime = DateTime(
year: 2026,
month: 9,
day: 4,
hour: 12,
minute: 34,
second: 56,
nanosecond: 123_456_789
)!
let interval = Interval(months: 2, days: -3, microseconds: 4)
SQL.select(date, time, dateTime, interval).prepare(.psql).plainwill give:
SELECT DATE '2026-09-04', TIME '12:34:56.123456789', TIMESTAMP '2026-09-04 12:34:56.123456789', INTERVAL '2 months -3 days 4 microseconds'Swift 6
SQL 2.0.0 requires Swift 6.3+ and uses Swift 6 language mode.
The query/bind graph is intentionally not hidden behind unchecked Sendable conformances. Build/prepare on the originating isolation and move an application-owned Sendable snapshot across actor boundaries when needed.
Advanced migration notes
If you have extensions that manually concatenate SQLable.parts, use the structural composition helpers so statement/subquery ownership is preserved.
Predefined Fn.Name values are immutable.
was
Fn.Name.coalesce = .custom("my_coalesce")became
let name = Fn.Name.custom("my_coalesce")
let fn = Fn.build(name)Normal calls such as Fn.coalesce(...) stay the same.
Validation
The final candidate passed 737 tests / 63 suites on Swiftly 6.3.3, Xcode 26.6 / Swift 6.3.3, and Xcode 27 / Swift 6.4. Fresh normal-import clients also passed on all three toolchains with the Query shorthand, explicit SQLContent, all 37 direct-root representatives, PostgreSQL/MySQL/DuckDB bind checks, and deprecated SwifQL fluent/builder/unary parity.
For the short v1 → v2 path and full compatibility details see: