diff --git a/.github/workflows/projectboard.yml b/.github/workflows/projectboard.yml
index 0e4e66f6..a0e6d988 100644
--- a/.github/workflows/projectboard.yml
+++ b/.github/workflows/projectboard.yml
@@ -5,27 +5,7 @@ on:
types: [reopened, closed, labeled, unlabeled, assigned, unassigned]
jobs:
- setup_matrix_input:
- runs-on: ubuntu-latest
-
- steps:
- - id: set-matrix
- run: |
- output=$(curl ${{ github.event.issue.url }}/labels | jq '.[] | .name') || output=""
-
- echo '======================'
- echo 'Process incoming data'
- echo '======================'
- json=$(echo $output | sed 's/"\s"/","/g')
- echo $json
- echo "::set-output name=matrix::$(echo $json)"
- outputs:
- issueTags: ${{ steps.set-matrix.outputs.matrix }}
-
- Manage_project_issues:
- needs: setup_matrix_input
- uses: vapor/ci/.github/workflows/issues-to-project-board.yml@main
- with:
- labelsJson: ${{ needs.setup_matrix_input.outputs.issueTags }}
- secrets:
- PROJECT_BOARD_AUTOMATION_PAT: "${{ secrets.PROJECT_BOARD_AUTOMATION_PAT }}"
+ update_project_boards:
+ name: Update project boards
+ uses: vapor/ci/.github/workflows/update-project-boards-for-issue.yml@reusable-workflows
+ secrets: inherit
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index d90262cd..7c152b57 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,47 +1,15 @@
name: test
+concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}
+ cancel-in-progress: true
on:
pull_request: { branches: ['*'] }
push: { branches: ['main'] }
-
env:
- LOG_LEVEL: debug
+ LOG_LEVEL: info
SWIFT_DETERMINISTIC_HASHING: 1
jobs:
- # Baseline test run for code coverage stats
- codecov:
- runs-on: ubuntu-latest
- container: swift:5.7-jammy
- steps:
- - name: Check out package
- uses: actions/checkout@v3
- - name: Run local tests with coverage
- run: swift test --enable-code-coverage
- - name: Submit coverage report to Codecov.io
- uses: vapor/swift-codecov-action@v0.2
- with:
- cc_flags: 'unittests'
- cc_env_vars: 'SWIFT_VERSION,SWIFT_PLATFORM,RUNNER_OS,RUNNER_ARCH,POSTGRES_VERSION'
- cc_fail_ci_if_error: true
- cc_verbose: true
- cc_dry_run: false
-
- # Check for API breakage versus main
- api-breakage:
- if: ${{ github.event_name == 'pull_request' }}
- runs-on: ubuntu-latest
- container: swift:5.8-jammy
- steps:
- - name: Check out package
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
- # https://github.com/actions/checkout/issues/766
- - name: Mark the workspace as safe
- run: git config --global --add safe.directory ${GITHUB_WORKSPACE}
- - name: Check for API breaking changes
- run: swift package diagnose-api-breaking-changes origin/main
-
unit-tests:
uses: vapor/ci/.github/workflows/run-unit-tests.yml@reusable-workflows
with:
@@ -100,11 +68,18 @@ jobs:
MYSQL_HOSTNAME_A: mysql-a
MYSQL_HOSTNAME_B: mysql-b
steps:
- - name: Install SQLite dependencies
- run: apt-get -q update && apt-get -q install -y libsqlite3-dev
- name: Check out sql-kit
uses: actions/checkout@v3
with: { path: sql-kit }
+ - name: Check out sqlite-kit
+ uses: actions/checkout@v3
+ with: { repository: 'vapor/sqlite-kit', path: sqlite-kit }
+ - name: Check out postgres-kit
+ uses: actions/checkout@v3
+ with: { repository: 'vapor/postgres-kit', path: postgres-kit }
+ - name: Check out mysql-kit
+ uses: actions/checkout@v3
+ with: { repository: 'vapor/mysql-kit', path: mysql-kit }
- name: Check out fluent-sqlite-driver
uses: actions/checkout@v3
with: { repository: 'vapor/fluent-sqlite-driver', path: fluent-sqlite-driver }
@@ -115,28 +90,29 @@ jobs:
uses: actions/checkout@v3
with: { repository: 'vapor/fluent-mysql-driver', path: fluent-mysql-driver }
+ - name: Use sql-kit in sqlite-kit
+ run: swift package --package-path sqlite-kit edit sql-kit --path sql-kit
+ - name: Use sql-kit in postgres-kit
+ run: swift package --package-path postgres-kit edit sql-kit --path sql-kit
+ - name: Use sql-kit in mysql-kit
+ run: swift package --package-path mysql-kit edit sql-kit --path sql-kit
- name: Use sql-kit in fluent-sqlite-driver
run: swift package --package-path fluent-sqlite-driver edit sql-kit --path sql-kit
- name: Use sql-kit in fluent-postgres-driver
run: swift package --package-path fluent-postgres-driver edit sql-kit --path sql-kit
- - name: Use sql-kit in fluent-myql-driver
+ - name: Use sql-kit in fluent-mysql-driver
run: swift package --package-path fluent-mysql-driver edit sql-kit --path sql-kit
+ - name: Run sqlite-kit tests with Thread Sanitizer
+ run: swift test --package-path sqlite-kit --sanitize=thread
+ - name: Run postgres-kit tests with Thread Sanitizer
+ run: swift test --package-path postgres-kit --sanitize=thread
+ - name: Run mysql-kit tests with Thread Sanitizer
+ run: swift test --package-path mysql-kit --sanitize=thread
+
- name: Run fluent-sqlite-driver tests with Thread Sanitizer
- run: swift test --package-path fluent-sqlite-driver
+ run: swift test --package-path fluent-sqlite-driver --sanitize=thread
- name: Run fluent-postgres-driver tests with Thread Sanitizer
run: swift test --package-path fluent-postgres-driver --sanitize=thread
- name: Run fluent-mysql-driver tests with Thread Sanitizer
run: swift test --package-path fluent-mysql-driver --sanitize=thread
-
- test-exports:
- name: Test exports
- runs-on: ubuntu-latest
- container: swift:5.8-jammy
- steps:
- - name: Check out Vapor
- uses: actions/checkout@v3
- with:
- fetch-depth: 0
- - name: Build
- run: swift build -Xswiftc -DBUILDING_DOCC
diff --git a/README.md b/README.md
index 395a17ac..6bc78023 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,54 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Build SQL queries in Swift. Extensible, protocol-based design that supports DQL, DML, and DDL.
-### Major Releases
-
-The table below shows a list of SQLKit major releases alongside their compatible NIO and Swift versions.
-
-|Version|NIO|Swift|SPM|
-|-|-|-|-|
-|3.0|2.0+|5.2+|`from: "3.0.0"`|
-|2.0|1.0+|4.0+|`from: "2.0.0"`|
-|1.0|n/a|4.0+|`from: "1.0.0"`|
+## Using SQLKit
-Use the SPM string to easily include the dependency in your `Package.swift` file.
+Use standard SwiftPM syntax to include SQLKit as a dependency in your `Package.swift` file.
```swift
-.package(url: "https://github.com/vapor/sql-kit.git", from: ...)
+.package(url: "https://github.com/vapor/sql-kit.git", from: "3.0.0")
```
+SQLKit 3.x requires [SwiftNIO](https://github.com/apple/swift-nio) 2.x or later. Previous major versions are no longer supported.
+
### Supported Platforms
-PostgresNIO supports the following platforms:
+SQLKit supports the following platforms:
-- Ubuntu 16.04+
+- Ubuntu 20.04+
- macOS 10.15+
+- iOS 13+
+- tvOS 13+ and watchOS 7+ (experimental)
## Overview
-SQLKit is an API for building and serializing SQL queries in Swift. SQLKit attempts to abstract away SQL dialect inconsistencies where possible allowing you to write queries that can run on multiple database flavors. Where abstraction is not possible, SQLKit provides powerful APIs for custom or dynamic behavior.
+SQLKit is an API for building and serializing SQL queries in Swift. SQLKit attempts to abstract away SQL dialect inconsistencies where possible allowing you to write queries that can run on multiple database flavors. Where abstraction is not possible, SQLKit provides powerful APIs for custom or dynamic behavior.
### Supported Databases
-These database packages are built on SQLKit:
+These database packages are drivers for SQLKit:
- [vapor/postgres-kit](https://github.com/vapor/postgres-kit): PostgreSQL
- [vapor/mysql-kit](https://github.com/vapor/mysql-kit): MySQL and MariaDB
@@ -61,16 +60,16 @@ SQLKit does not deal with creating or managing database connections itself. This
### Database
-Instances of `SQLDatabase` are capable of serializing and executing `SQLExpression`.
+Instances of `SQLDatabase` are capable of serializing and executing `SQLExpression`.
```swift
-let db: SQLDatabase = ...
-db.execute(sql: SQLExpression, onRow: (SQLRow) -> ())
+let db: any SQLDatabase = ...
+db.execute(sql: any SQLExpression, onRow: (any SQLRow) -> ())
```
-`SQLExpression` is a protocol that represents a SQL query string and optional bind values. It can represent an entire SQL query or just a fragment.
+`SQLExpression` is a protocol that represents a SQL query string and optional bind values. It can represent an entire SQL query or just a fragment.
-SQLKit provides `SQLExpression`s for common queries like `SELECT`, `UPDATE`, `INSERT`, `DELETE`, `CREATE TABLE`, and more.
+SQLKit provides `SQLExpression`s for common queries like `SELECT`, `UPDATE`, `INSERT`, `DELETE`, `CREATE TABLE`, and many more.
```swift
var select = SQLSelect()
@@ -79,30 +78,42 @@ select.tables = [...]
select.predicate = ...
```
-`SQLDatabase` can be used to create fluent query builders for most of these query types.
+`SQLDatabase` can be used to create fluent query builders for most of these query types.
```swift
-let planets = try db.select()
- .column("*")
+struct Planet: Codable { var id: Int, name: String }
+
+let db: some SQLDatabase = ...
+try await db.create(table: "planets")
+ .column("id", type: .int, .primaryKey(autoIncrement: true), .notNull)
+ .column("name", type: .string, .notNull)
+ .run()
+try await db.insert(into: "planets")
+ .columns("id", "name")
+ .values(SQLLiteral.default, SQLBind("Earth"))
+ .values(SQLLiteral.default, SQLBind("Mars"))
+ .run()
+let planets = try await db.select()
+ .columns("id", "name")
.from("planets")
- .where("name", .equal, "Earth")
- .all().wait()
+ .all(decoding: Planet.self)
+print(planets) // [Planet(id: 1, name: "Earth"), Planet(id: 2, name: "Mars")]
```
You can execute a query builder by calling `run()`.
### Rows
-For query builders that support returning results, like `select()`, there are additional methods for handling the database output.
+For query builders that support returning results (e.g. any builder conforming to the `SQLQueryFetcher` protocol), there are additional methods for handling the database output:
- `all()`: Returns an array of rows.
- `first()`: Returns an optional row.
- `run(_:)`: Accepts a closure that handles rows as they are returned.
-Each of these methods returns `SQLRow` which has methods for access column values.
+Each of these methods returns `SQLRow`, which has methods for access column values.
```swift
-let row: SQLRow
+let row: any SQLRow
let name = try row.decode(column: "name", as: String.self)
print(name) // String
```
@@ -122,90 +133,90 @@ let planet = try row.decode(model: Planet.self)
Query builders that support returning results have convenience methods for automatically decoding models.
```swift
-let planets = try db.select()
+let planets: [Planet] = try await db.select()
...
- .all(decoding: Planet.self).wait()
+ .all(decoding: Planet.self)
```
## Select
-The `select()` method creates a `SELECT` query builder.
+The `SQLDatabase.select()` method creates a `SELECT` query builder:
```swift
-let planets = try db.select()
+let planets: [any SQLRow] = try await db.select()
.columns("id", "name")
.from("planets")
.where("name", .equal, "Earth")
- .all().wait()
+ .all()
```
-This code would generate the following SQL:
+This code generates the following SQL when used with the PostgresKit driver:
-```sql
-SELECT id, name FROM planets WHERE name = ?
+```PLpgsql
+SELECT "id", "name" FROM "planets" WHERE "name" = $1 -- bindings: ["Earth"]
```
-Notice that `Encodable` values are automatically bound as parameters instead of being serialized directly to the query.
+Notice that `Encodable` values are automatically bound as parameters instead of being serialized directly to the query.
-The select builder has the following methods.
+The select builder includes the following methods (typically with several variations):
-- `columns`
-- `from`
-- `where` (`orWhere`)
-- `limit`
-- `offset`
-- `groupBy`
-- `having` (`orHaving`)
-- `distinct`
-- `for` (`lockingClause`)
-- `join`
+- `columns()` (specify a list of columns and/or expressions to return)
+- `from()` (specify a table to select from)
+- `join()` (specify additional tables and how to relate them to others)
+- `where()` and `orWhere()` (specify conditions that narrow down the possible results)
+- `limit()` and `offset()` (specify a limited and/or offsetted range of results to return)
+- `orderBy()` (specify how to sort results before returning them)
+- `groupBy()` (specify columns and/or expressions for aggregating results)
+- `having()` and `orHaving()` (specify secondary conditions to apply to the results after aggregation)
+- `distinct()` (specify coalescing of duplicate results)
+- `for()` and `lockingClause()` (specify locking behavior for rows that appear in results)
-By default, query components like `where` will be joined by `AND`. Methods prefixed with `or` exist for joining by `OR`.
+Conditional expressions provided to `where()` or `having()` are joined with `AND`. Corresponding `orWhere()` and `orHaving()` methods join conditions with `OR` instead.
```swift
builder.where("name", .equal, "Earth").orWhere("name", .equal, "Mars")
```
-This code would generate the following SQL:
+This code generates the following SQL when used with the MySQL driver:
-```sql
-name = ? OR name = ?
+```mysql
+WHERE `name` = ? OR `name` = ? -- bindings: ["Earth", "Mars"]
```
-`where` also supports creating grouped clauses.
+`where()`, `orWhere()`, `having()`, and `orHaving()` also support creating grouped clauses:
```swift
builder.where("name", .notEqual, SQLLiteral.null).where {
$0.where("name", .equal, SQLBind("Milky Way"))
- .orWhere("name", .equal, SQLBind("Andromeda"))
+ .orWhere("name", .equal, SQLBind("Andromeda"))
}
```
-This code generates the following SQL:
+This code generates the following SQL when used with the SQLite driver:
```sql
-name != NULL AND (name == ? OR name == ?)
+WHERE "name" <> NULL AND ("name" = ?1 OR "name" = ?2) -- bindings: ["Milky Way", "Andromeda"]
```
## Insert
-The `insert(into:)` method creates an `INSERT` query builder.
+The `insert(into:)` method creates an `INSERT` query builder:
```swift
-try db.insert(into: "galaxies")
+try await db.insert(into: "galaxies")
.columns("id", "name")
.values(SQLLiteral.default, SQLBind("Milky Way"))
.values(SQLLiteral.default, SQLBind("Andromeda"))
- .run().wait()
+ .run()
```
-This code would generate the following SQL:
+This code generates the following SQL when used with the PostgreSQL driver:
-```sql
-INSERT INTO galaxies (id, name) VALUES (DEFAULT, ?) (DEFAULT, ?)
+```PLpgsql
+INSERT INTO "galaxies" ("id", "name") VALUES (DEFAULT, $1), (DEFAULT, $2) -- bindings: ["Milky Way", "Andromeda"]
```
-The insert builder also has a method for encoding a `Codable` type as a set of values.
+The insert builder also has a method for encoding a `Codable` type as a set of values:
```swift
struct Galaxy: Codable {
@@ -215,57 +226,62 @@ struct Galaxy: Codable {
try builder.model(Galaxy(name: "Milky Way"))
```
+This code generates the same SQL as would `builder.columns("name").values("Milky Way")`.
+
## Update
-The `update(_:)` method creates an `UPDATE` query builder.
+The `update(_:)` method creates an `UPDATE` query builder:
```swift
-try db.update("planets")
- .where("name", .equal, "Jpuiter")
+try await db.update("planets")
.set("name", to: "Jupiter")
- .run().wait()
+ .where("name", .equal, "Jupiter")
+ .run()
```
-This code generates the following SQL:
+This code generates the following SQL when used with the MySQL driver:
-```sql
-UPDATE planets SET name = ? WHERE name = ?
+```mysql
+UPDATE `planets` SET `name` = ? WHERE `name` = ? -- bindings: ["Jupiter", "Jupiter"]
```
-The update builder supports the same `where` and `orWhere` methods as the select builder.
+The update builder supports the same `where()` and `orWhere()` methods as the select builder, via the `SQLPredicateBuilder` protocol.
## Delete
-The `delete(from:)` method creates a `DELETE` query builder.
+The `delete(from:)` method creates a `DELETE` query builder:
```swift
-try db.delete(from: "planets")
+try await db.delete(from: "planets")
.where("name", .equal, "Jupiter")
- .run().wait()
+ .run()
```
-This code generates the following SQL:
+This code generates the following SQL when used with the SQLite driver:
```sql
-DELETE FROM planets WHERE name = ?
+DELETE FROM "planets" WHERE "name" = ?1 -- bindings: ["Jupiter"]
```
-The delete builder supports the same `where` and `orWhere` methods as the select builder.
+The delete builder is also an `SQLPredicateBuilder`.
## Raw
-The `raw(_:)` method allows for passing custom SQL query strings with support for parameterized binds.
+The `raw(_:)` method allows passing custom SQL query strings, with support for parameterized bindings and correctly-quoted identifiers:
```swift
-let table = "planets"
-let planets = try db.raw("SELECT * FROM \(raw: table) WHERE name = \(bind: planet)")
- .all().wait()
+let planets = try await db.raw("SELECT \(SQLLiteral.all) FROM \(ident: table) WHERE \(ident: name) = \(bind: "planet")")
+ .all()
```
-This code generates the following SQL:
+This code generates the following SQL when used with the PostgreSQL driver:
-```sql
-SELECT * FROM planets WHERE name = ?
+```PLpgsql
+SELECT * FROM "planets" WHERE "name" = $1 -- bindings: ["planet"]
```
-The `\(bind:)` interpolation should be used for any user input to avoid SQL injection.
+The `\(bind:)` interpolation should be used for any user input to avoid SQL injection. The `\(ident:)` interpolation is used to safely specify identifiers such as table and column names.
+
+##### ⚠️ **Important!**⚠️
+
+Always prefer a structured query (i.e. one for which a builder or expression type exists) over raw queries. Consider writing your own `SQLExpression`s, and even your own `SQLQueryBuilder`s, rather than using raw queries, and don't hesitate to [open an issue](https://github.com/vapor/sql-kit/issues/new) to ask for additional feature support.
diff --git a/Sources/SQLKit/Builders/SQLAlterEnumBuilder.swift b/Sources/SQLKit/Builders/SQLAlterEnumBuilder.swift
index 61547404..b0fb6c39 100644
--- a/Sources/SQLKit/Builders/SQLAlterEnumBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLAlterEnumBuilder.swift
@@ -1,43 +1,62 @@
import NIOCore
-extension SQLDatabase {
- public func alter(enum name: String) -> SQLAlterEnumBuilder {
- self.alter(enum: SQLIdentifier(name))
- }
-
- public func alter(enum name: SQLExpression) -> SQLAlterEnumBuilder {
- .init(database: self, name: name)
- }
-}
-
+/// Builds ``SQLAlterEnum`` queries.
public final class SQLAlterEnumBuilder: SQLQueryBuilder {
- public var database: SQLDatabase
+ /// ``SQLAlterEnum`` query being built.
public var alterEnum: SQLAlterEnum
- public var query: SQLExpression {
+
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
+
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
self.alterEnum
}
-
- init(database: SQLDatabase, name: SQLExpression) {
+
+ /// Create a new ``SQLAlterEnumBuilder``.
+ @usableFromInline
+ init(database: any SQLDatabase, name: any SQLExpression) {
self.database = database
self.alterEnum = .init(name: name, value: nil)
}
+ /// Append a new case to the enum type.
+ @inlinable
@discardableResult
public func add(value: String) -> Self {
self.add(value: SQLLiteral.string(value))
}
+ /// Append a new case to the enum type.
+ @inlinable
@discardableResult
- public func add(value: SQLExpression) -> Self {
+ public func add(value: any SQLExpression) -> Self {
self.alterEnum.value = value
return self
}
-
+
+ /// See ``SQLQueryBuilder/run()-2zws8``.
+ @inlinable
public func run() -> EventLoopFuture {
guard self.database.dialect.enumSyntax == .typeName else {
- self.database.logger.warning("Database does not support enum types.")
+ self.database.logger.warning("Database does not support standalone enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
+
+extension SQLDatabase {
+ /// Create a new ``SQLAlterEnumBuilder``.
+ @inlinable
+ public func alter(enum name: String) -> SQLAlterEnumBuilder {
+ self.alter(enum: SQLIdentifier(name))
+ }
+
+ /// Create a new ``SQLAlterEnumBuilder``.
+ @inlinable
+ public func alter(enum name: any SQLExpression) -> SQLAlterEnumBuilder {
+ .init(database: self, name: name)
+ }
+}
diff --git a/Sources/SQLKit/Builders/SQLAlterTableBuilder.swift b/Sources/SQLKit/Builders/SQLAlterTableBuilder.swift
index 515c8bfd..32a4a081 100644
--- a/Sources/SQLKit/Builders/SQLAlterTableBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLAlterTableBuilder.swift
@@ -1,290 +1,158 @@
+/// Builds ``SQLAlterTable`` queries.
public final class SQLAlterTableBuilder: SQLQueryBuilder {
- /// `SQLAlterTable` query being built.
+ /// ``SQLAlterTable`` query being built.
public var alterTable: SQLAlterTable
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.alterTable
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.alterTable
}
- public var columns: [SQLExpression] {
- get { return alterTable.addColumns }
- set { alterTable.addColumns = newValue }
+ /// The set of column alteration expressions.
+ @inlinable
+ public var columns: [any SQLExpression] {
+ get { self.alterTable.addColumns }
+ set { self.alterTable.addColumns = newValue }
}
- /// Creates a new `SQLAlterTableBuilder`.
- public init(_ alterTable: SQLAlterTable, on database: SQLDatabase) {
+ /// Create a new ``SQLAlterTableBuilder``.
+ @inlinable
+ public init(_ alterTable: SQLAlterTable, on database: any SQLDatabase) {
self.alterTable = alterTable
self.database = database
}
- @discardableResult
/// Rename the table.
- /// - Parameter newName: The new name to apply to the table
- /// - Returns: `self` for chaining.
- public func rename(
- to newName: String
- ) -> Self {
- return self.rename(to: SQLIdentifier(newName))
+ @inlinable
+ @discardableResult
+ public func rename(to newName: String) -> Self {
+ self.rename(to: SQLIdentifier(newName))
}
- @discardableResult
/// Rename the table.
- /// - Parameter newName: The new name to apply to the table
- /// - Returns: `self` for chaining.
- public func rename(
- to newName: SQLExpression
- ) -> Self {
+ @inlinable
+ @discardableResult
+ public func rename(to newName: any SQLExpression) -> Self {
self.alterTable.renameTo = newName
return self
}
+ /// Add a new column to the table.
+ @inlinable
@discardableResult
- /// Add a column to the table.
- /// - Parameters:
- /// - column: The name of the new column
- /// - dataType: The new datatype of the new column
- /// - constraints: Constraints for the new column
- /// - Returns: `self` for chaining.
- public func column(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: SQLColumnConstraintAlgorithm...
- ) -> Self {
- return self.addColumn(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: String, type dataType: SQLDataType, _ constraints: SQLColumnConstraintAlgorithm...) -> Self {
+ self.column(column, type: dataType, constraints)
}
+ /// Add a new column to the table.
+ @inlinable
@discardableResult
- /// Add a column to the table.
- /// - Parameters:
- /// - column: The name of the new column
- /// - dataType: The new datatype of the new column
- /// - constraints: Constraints for the new column
- /// - Returns: `self` for chaining.
- public func column(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: [SQLColumnConstraintAlgorithm]
- ) -> Self {
- return self.addColumn(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: String, type dataType: SQLDataType, _ constraints: [SQLColumnConstraintAlgorithm]) -> Self {
+ self.column(SQLIdentifier(column), type: dataType, constraints)
}
+ /// Add a new column to the table.
+ @inlinable
@discardableResult
- /// Add a column to the table.
- /// - Parameters:
- /// - column: The name of the new column
- /// - dataType: The new datatype of the new column
- /// - constraints: Constraints for the new column
- /// - Returns: `self` for chaining.
- public func column(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: SQLExpression...
- ) -> Self {
- return self.addColumn(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: any SQLExpression...) -> Self {
+ self.column(column, type: dataType, constraints)
}
+ /// Add a new column to the table.
+ @inlinable
@discardableResult
- /// Add a column to the table.
- /// - Parameters:
- /// - column: The name of the new column
- /// - dataType: The new datatype of the new column
- /// - constraints: Constraints for the new column
- /// - Returns: `self` for chaining.
- public func column(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: [SQLExpression]
- ) -> Self {
- return self.addColumn(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: [any SQLExpression]) -> Self {
+ self.addColumn(SQLColumnDefinition(column: column, dataType: dataType, constraints: constraints))
}
+ /// Add a new column to the table.
+ @inlinable
@discardableResult
- /// Add a column to the table.
- /// - Parameter columnDefinition: Expression defining the column
- /// - Returns: `self` for chaining.
- public func addColumn(_ columnDefinition: SQLExpression) -> Self {
+ public func addColumn(_ columnDefinition: any SQLExpression) -> Self {
self.alterTable.addColumns.append(columnDefinition)
return self
}
+ /// Change an existing column's type and constraints.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - constraints: Constraints to apply to the altered column
- /// - Returns: `self` for chaining.
- public func modifyColumn(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: SQLColumnConstraintAlgorithm...
- ) -> Self {
- return self.modifyColumn(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func modifyColumn(_ column: String, type dataType: SQLDataType, _ constraints: SQLColumnConstraintAlgorithm...) -> Self {
+ self.modifyColumn(column, type: dataType, constraints)
}
+ /// Change an existing column's type and constraints.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - constraints: Constraints to apply to the altered column
- /// - Returns: `self` for chaining.
- public func modifyColumn(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: [SQLColumnConstraintAlgorithm]
- ) -> Self {
- return self.modifyColumn(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func modifyColumn(_ column: String, type dataType: SQLDataType, _ constraints: [SQLColumnConstraintAlgorithm]) -> Self {
+ self.modifyColumn(SQLIdentifier(column), type: dataType, constraints)
}
+ /// Change an existing column's type and constraints.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - constraints: Constraints to apply to the altered column
- /// - Returns: `self` for chaining.
- public func modifyColumn(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: SQLExpression...
- ) -> Self {
- return self.modifyColumn(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func modifyColumn(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: any SQLExpression...) -> Self {
+ self.modifyColumn(column, type: dataType, constraints)
}
+ /// Change an existing column's type and constraints.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - constraints: Constraints to apply to the altered column
- /// - Returns: `self` for chaining.
- public func modifyColumn(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: [SQLExpression]
- ) -> Self {
- return self.modifyColumn(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func modifyColumn(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: [any SQLExpression]) -> Self {
+ self.modifyColumn(SQLColumnDefinition(column: column, dataType: dataType, constraints: constraints))
}
+ /// Change an existing column's type.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - Returns: `self` for chaining.
- public func update(
- column: String,
- type dataType: SQLDataType
- ) -> Self {
- self.modifyColumn(SQLAlterColumnDefinitionType(
- column: SQLIdentifier(column),
- dataType: dataType
- ))
+ public func update(column: String, type dataType: SQLDataType) -> Self {
+ self.update(column: SQLIdentifier(column), type: dataType)
}
+ /// Change an existing column's type.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameters:
- /// - column: The name of the column to alter
- /// - dataType: The datatype to change the column to
- /// - Returns: `self` for chaining.
- public func update(
- column: SQLExpression,
- type dataType: SQLExpression
- ) -> Self {
- self.modifyColumn(SQLAlterColumnDefinitionType(
- column: column,
- dataType: dataType
- ))
+ public func update(column: any SQLExpression, type dataType: any SQLExpression) -> Self {
+ self.modifyColumn(SQLAlterColumnDefinitionType(column: column, dataType: dataType))
}
+ /// Alter an existing column.
+ @inlinable
@discardableResult
- /// Alter a column in the table.
- /// - Parameter columnDefinition: Expression defining the column changes
- /// - Returns: `self` for chaining.
- public func modifyColumn(_ columnDefinition: SQLExpression) -> Self {
+ public func modifyColumn(_ columnDefinition: any SQLExpression) -> Self {
self.alterTable.modifyColumns.append(columnDefinition)
return self
}
+ /// Drop an existing column from the table
+ @inlinable
@discardableResult
- /// Drop the column from the table
- /// - Parameter column: The name of the column to drop
- /// - Returns: `self` for chaining.
- public func dropColumn(
- _ column: String
- ) -> Self {
- return self.dropColumn(SQLIdentifier(column))
+ public func dropColumn(_ column: String) -> Self {
+ self.dropColumn(SQLIdentifier(column))
}
+ /// Drop an existing column from the table
+ @inlinable
@discardableResult
- /// Drop the column from the table
- /// - Parameter column: The name of the column to drop
- /// - Returns: `self` for chaining.
- public func dropColumn(
- _ column: SQLExpression
- ) -> Self {
+ public func dropColumn(_ column: any SQLExpression) -> Self {
self.alterTable.dropColumns.append(column)
return self
}
-
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLAlterTableBuilder`.
- ///
- /// db.alter(table: "planets")...
- ///
- /// - parameters:
- /// - table: Table to alter.
- /// - returns: `AlterTableBuilder`.
+ /// Create a new ``SQLAlterTableBuilder``.
+ @inlinable
public func alter(table: String) -> SQLAlterTableBuilder {
- return self.alter(table: SQLIdentifier(table))
+ self.alter(table: SQLIdentifier(table))
}
- /// Creates a new `SQLAlterTableBuilder`.
- ///
- /// - parameters:
- /// - table: Table to alter.
- /// - returns: `AlterTableBuilder`.
+ /// Create a new ``SQLAlterTableBuilder``.
+ @inlinable
public func alter(table: SQLIdentifier) -> SQLAlterTableBuilder {
- return .init(.init(name: table), on: self)
+ .init(.init(name: table), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLColumnUpdateBuilder.swift b/Sources/SQLKit/Builders/SQLColumnUpdateBuilder.swift
index 8cb4d4c9..33d18fea 100644
--- a/Sources/SQLKit/Builders/SQLColumnUpdateBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLColumnUpdateBuilder.swift
@@ -2,39 +2,44 @@
///
/// builder.set("name", to: "Earth")
public protocol SQLColumnUpdateBuilder: AnyObject {
- /// List of assignment pairs that have been built.
- var values: [SQLExpression] { get set }
+ /// List of assignment pairs.
+ var values: [any SQLExpression] { get set }
}
extension SQLColumnUpdateBuilder {
- /// Encodes the given `Encodable` value to a sequence of key-value pairs and adds an assignment
+ /// Encodes the given ``Encodable`` value to a sequence of key-value pairs and adds an assignment
/// for each pair.
+ @inlinable
@discardableResult
public func set(model: E) throws -> Self where E: Encodable {
- return try SQLQueryEncoder().encode(model).reduce(self) { $0.set(SQLColumn($1.0), to: $1.1) }
+ try SQLQueryEncoder().encode(model).reduce(self) { $0.set(SQLColumn($1.0), to: $1.1) }
}
/// Add an assignment of the column with the given name to the provided bound value.
+ @inlinable
@discardableResult
- public func set(_ column: String, to bind: Encodable) -> Self {
- return self.set(SQLColumn(column), to: SQLBind(bind))
+ public func set(_ column: String, to bind: any Encodable) -> Self {
+ self.set(SQLColumn(column), to: SQLBind(bind))
}
/// Add an assignment of the column with the given name to the given expression.
+ @inlinable
@discardableResult
- public func set(_ column: String, to value: SQLExpression) -> Self {
- return self.set(SQLColumn(column), to: value)
+ public func set(_ column: String, to value: any SQLExpression) -> Self {
+ self.set(SQLColumn(column), to: value)
}
/// Add an assignment of the given column to the provided bound value.
+ @inlinable
@discardableResult
- public func set(_ column: SQLExpression, to bind: Encodable) -> Self {
- return self.set(column, to: SQLBind(bind))
+ public func set(_ column: any SQLExpression, to bind: any Encodable) -> Self {
+ self.set(column, to: SQLBind(bind))
}
/// Add an assignment of the given column to the given expression.
+ @inlinable
@discardableResult
- public func set(_ column: SQLExpression, to value: SQLExpression) -> Self {
+ public func set(_ column: any SQLExpression, to value: any SQLExpression) -> Self {
self.values.append(SQLBinaryExpression(left: column, op: SQLBinaryOperator.equal, right: value))
return self
}
diff --git a/Sources/SQLKit/Builders/SQLConflictUpdateBuilder.swift b/Sources/SQLKit/Builders/SQLConflictUpdateBuilder.swift
index e3477abf..5d1f2a23 100644
--- a/Sources/SQLKit/Builders/SQLConflictUpdateBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLConflictUpdateBuilder.swift
@@ -1,38 +1,42 @@
/// A builder for specifying column updates and an optional predicate to be applied to
/// rows that caused unique key conflicts during an `INSERT`.
public final class SQLConflictUpdateBuilder: SQLColumnUpdateBuilder, SQLPredicateBuilder {
- // See `SQLColumnUpdateBuilder.values`.
- public var values: [SQLExpression]
+ /// See ``SQLColumnUpdateBuilder/values``.
+ public var values: [any SQLExpression]
- // See `SQLPredicateBuilder.predicate`.
- public var predicate: SQLExpression?
+ /// See ``SQLPredicateBuilder/predicate``.
+ public var predicate: (any SQLExpression)?
/// Create a conflict update builder.
- internal init() {
+ @usableFromInline
+ init() {
self.values = []
self.predicate = nil
}
/// Add an assignment of the column with the given name, using the value the column was
- /// given in the `INSERT` query's `VALUES` list. See `SQLExcludedColumn`.
+ /// given in the `INSERT` query's `VALUES` list. See ``SQLExcludedColumn``.
+ @inlinable
@discardableResult
public func set(excludedValueOf columnName: String) -> Self {
- return self.set(excludedValueOf: SQLColumn(columnName))
+ self.set(excludedValueOf: SQLColumn(columnName))
}
/// Add an assignment of the given column, using the value the column was given in the
- /// `INSERT` query's `VALUES` list. See `SQLExcludedColumn`.
+ /// `INSERT` query's `VALUES` list. See ``SQLExcludedColumn``.
+ @inlinable
@discardableResult
- public func set(excludedValueOf column: SQLExpression) -> Self {
+ public func set(excludedValueOf column: any SQLExpression) -> Self {
self.values.append(SQLColumnAssignment(settingExcludedValueFor: column))
return self
}
- /// Encodes the given `Encodable` value to a sequence of key-value pairs and adds an assignment
+ /// Encodes the given ``Encodable`` value to a sequence of key-value pairs and adds an assignment
/// for each pair which uses the values each column was given in the original `INSERT` query's
- /// `VALUES` list. See `SQLExcludedColumn`.
+ /// `VALUES` list. See ``SQLExcludedColumn``.
+ @inlinable
@discardableResult
public func set(excludedContentOf model: E) throws -> Self where E: Encodable {
- return try SQLQueryEncoder().encode(model).reduce(self) { $0.set(excludedValueOf: $1.0) }
+ try SQLQueryEncoder().encode(model).reduce(self) { $0.set(excludedValueOf: $1.0) }
}
}
diff --git a/Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift b/Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift
index 8d2b6116..719771b0 100644
--- a/Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLCreateEnumBuilder.swift
@@ -1,73 +1,68 @@
import NIOCore
-// MARK: Connection
-
-extension SQLDatabase {
- /// Creates a new `SQLCreateEnumBuilder`.
- ///
- /// db.create(enum: "meal", cases: "breakfast", "lunch", "dinner")...
- ///
- /// - parameters:
- /// - name: Name of ENUM type to create.
- /// - cases: The cases of the ENUM type.
- /// - returns: `SQLCreateEnumBuilder`.
- public func create(enum name: String) -> SQLCreateEnumBuilder {
- return self.create(enum: SQLIdentifier(name))
- }
-
- /// Creates a new `SQLCreateEnumBuilder`.
- ///
- /// db.create(enum: SQLIdentifier("meal"), cases: "breakfast", "lunch", "dinner")...
- ///
- /// - parameters:
- /// - name: Name of ENUM type to create.
- /// - cases: The cases of the ENUM type.
- /// - returns: `SQLCreateEnumBuilder`.
- public func create(enum name: SQLExpression) -> SQLCreateEnumBuilder {
- return .init(name: name, on: self)
- }
-}
-
-/// Builds `SQLCreateEnum` queries.
+/// Builds ``SQLCreateEnum`` queries.
///
-/// db.create(enum: "meal", cases: "breakfast", "lunch", "dinner")
+/// db.create(enum: "meal")
+/// .value("breakfast")
+/// .value("lunch")
+/// .value("dinner")
/// .run()
-///
-/// See `SQLColumnBuilder` and `SQLQueryBuilder` for more information.
public final class SQLCreateEnumBuilder: SQLQueryBuilder {
- /// `CreateType` query being built.
+ /// ``SQLCreateEnum`` query being built.
public var createEnum: SQLCreateEnum
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.createEnum
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.createEnum
}
- /// Creates a new `SQLCreateEnumBuilder`.
- init(name: SQLExpression, on database: SQLDatabase) {
+ /// Create a new ``SQLCreateEnumBuilder``.
+ @usableFromInline
+ init(name: any SQLExpression, on database: any SQLDatabase) {
self.createEnum = .init(name: name, values: [])
self.database = database
}
+ /// Add an enum case to the built type.
+ @inlinable
@discardableResult
public func value(_ value: String) -> Self {
self.value(SQLLiteral.string(value))
}
+ /// Add a enum case to the built type.
+ @inlinable
@discardableResult
- public func value(_ value: SQLExpression) -> Self {
+ public func value(_ value: any SQLExpression) -> Self {
self.createEnum.values.append(value)
return self
}
+ /// See ``SQLQueryBuilder/run()-2sxsg``.
+ @inlinable
public func run() -> EventLoopFuture {
guard self.database.dialect.enumSyntax == .typeName else {
- self.database.logger.warning("Database does not support enum types.")
+ self.database.logger.warning("Database does not support standalone enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
+
+extension SQLDatabase {
+ /// Create a new ``SQLCreateEnumBuilder``.
+ @inlinable
+ public func create(enum name: String) -> SQLCreateEnumBuilder {
+ self.create(enum: SQLIdentifier(name))
+ }
+
+ /// Create a new ``SQLCreateEnumBuilder``.
+ @inlinable
+ public func create(enum name: any SQLExpression) -> SQLCreateEnumBuilder {
+ .init(name: name, on: self)
+ }
+}
diff --git a/Sources/SQLKit/Builders/SQLCreateIndexBuilder.swift b/Sources/SQLKit/Builders/SQLCreateIndexBuilder.swift
index 796a7d52..b110381d 100644
--- a/Sources/SQLKit/Builders/SQLCreateIndexBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLCreateIndexBuilder.swift
@@ -1,79 +1,60 @@
-/// Builds `SQLCreateIndex` queries.
+/// Builds ``SQLCreateIndex`` queries.
///
/// db.create(index: "planet_name_unique").on("planet").column("name").unique().run()
-///
-/// See `SQLCreateIndex`.
public final class SQLCreateIndexBuilder: SQLQueryBuilder {
- /// `AlterTable` query being built.
+ /// ``SQLCreateIndex`` query being built.
public var createIndex: SQLCreateIndex
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.createIndex
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.createIndex
}
/// Adds `UNIQUE` modifier to the index being created.
+ @inlinable
@discardableResult
public func unique() -> Self {
self.createIndex.modifier = SQLColumnConstraintAlgorithm.unique
return self
}
- /// Creates a new `SQLCreateIndexBuilder`.
- ///
- /// db.create(index: "foo").on("planets")...
- ///
- /// - parameters:
- /// - table: Table to create index on.
- /// - returns: `SQLCreateIndexBuilder`.
+ /// Specify a table to operate on.
+ @inlinable
@discardableResult
public func on(_ table: String) -> Self {
- return self.on(SQLIdentifier(table))
+ self.on(SQLIdentifier(table))
}
- /// Creates a new `SQLCreateIndexBuilder`.
- ///
- /// db.create(index: "foo").on("planets")...
- ///
- /// - parameters:
- /// - table: Table to create index on.
- /// - returns: `SQLCreateIndexBuilder`.
+ /// Specify a table to operate on.
+ @inlinable
@discardableResult
- public func on(_ column: SQLExpression) -> Self {
- self.createIndex.table = column
+ public func on(_ table: any SQLExpression) -> Self {
+ self.createIndex.table = table
return self
}
- /// Creates a new `SQLCreateIndexBuilder`.
- ///
- /// db.create(index: "foo").column("name")...
- ///
- /// - parameters:
- /// - column: Column to create index on.
- /// - returns: `SQLCreateIndexBuilder`.
+ /// Specify a column to include in the created index.
+ @inlinable
@discardableResult
public func column(_ column: String) -> Self {
- return self.column(SQLIdentifier(column))
+ self.column(SQLIdentifier(column))
}
- /// Creates a new `SQLCreateIndexBuilder`.
- ///
- /// db.create(index: "foo").column("name")...
- ///
- /// - parameters:
- /// - column: Column to create index on.
- /// - returns: `SQLCreateIndexBuilder`.
+ /// Specify a column to include in the created index.
+ @inlinable
@discardableResult
- public func column(_ column: SQLExpression) -> Self {
+ public func column(_ column: any SQLExpression) -> Self {
self.createIndex.columns.append(column)
return self
}
- /// Creates a new `SQLCreateIndexBuilder`.
- public init(_ createIndex: SQLCreateIndex, on database: SQLDatabase) {
+ /// Create a new `SQLCreateIndexBuilder`.
+ @inlinable
+ public init(_ createIndex: SQLCreateIndex, on database: any SQLDatabase) {
self.createIndex = createIndex
self.database = database
}
@@ -86,25 +67,21 @@ extension SQLDatabase {
///
/// db.create(index: "foo")...
///
- /// - parameters:
- /// - name: Name for this index.
- /// - returns: `SQLCreateIndexBuilder`.
- public func create(
- index name: String
- ) -> SQLCreateIndexBuilder {
- return self.create(index: SQLIdentifier(name))
+ /// - Parameters:
+ /// - name: Name for this index.
+ @inlinable
+ public func create(index name: String) -> SQLCreateIndexBuilder {
+ self.create(index: SQLIdentifier(name))
}
/// Creates a new `SQLCreateIndexBuilder`.
///
/// db.create(index: "foo")...
///
- /// - parameters:
- /// - name: Name for this index.
- /// - returns: `SQLCreateIndexBuilder`.
- public func create(
- index name: SQLExpression
- ) -> SQLCreateIndexBuilder {
- return .init(.init(name: name), on: self)
+ /// - Parameters:
+ /// - name: Name for this index.
+ @inlinable
+ public func create(index name: any SQLExpression) -> SQLCreateIndexBuilder {
+ .init(.init(name: name), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLCreateTableAsSubqueryBuilder.swift b/Sources/SQLKit/Builders/SQLCreateTableAsSubqueryBuilder.swift
index 017cd129..a7e0bfa1 100644
--- a/Sources/SQLKit/Builders/SQLCreateTableAsSubqueryBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLCreateTableAsSubqueryBuilder.swift
@@ -1,13 +1,14 @@
/// A builder used to construct a `SELECT` query for use as part of a `CREATE TABLE` query.
///
/// - Note: There's really nothing for this builder to do besides provide a concrete storage
-/// for the `select` property of `SQLSubqueryClauseBuilder`. All of the interesting methods
+/// for the `select` property of ``SQLSubqueryClauseBuilder``. All of the interesting methods
/// are on the protocol.
public final class SQLCreateTableAsSubqueryBuilder: SQLSubqueryClauseBuilder {
// See `SQLSubqueryClauseBuilder.select`.
public var select: SQLSelect
/// Create a new `SQLCreateTableAsSubqueryBuilder`.
+ @usableFromInline
internal init() {
self.select = .init()
}
diff --git a/Sources/SQLKit/Builders/SQLCreateTableBuilder.swift b/Sources/SQLKit/Builders/SQLCreateTableBuilder.swift
index 64fc24d0..e8373b42 100644
--- a/Sources/SQLKit/Builders/SQLCreateTableBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLCreateTableBuilder.swift
@@ -1,101 +1,87 @@
-/// Builds `SQLCreateTable` queries.
+/// Builds ``SQLCreateTable`` queries.
///
-/// db.create(table: Planet.self).ifNotExists()
-/// .column(for: \Planet.id, .primaryKey)
-/// .column(for: \Planet.galaxyID, .references(\Galaxy.id))
+/// db.create(table: Planet.self).ifNotExists()
+/// .column("id", type: .int, .primaryKey)
+/// .column("galaxy_id", type: .int, .references(Galaxy.schema, "id"))
/// .run()
///
/// See `SQLColumnBuilder` and `SQLQueryBuilder` for more information.
public final class SQLCreateTableBuilder: SQLQueryBuilder {
- /// `CreateTable` query being built.
+ /// ``SQLCreateTable`` query being built.
public var createTable: SQLCreateTable
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.createTable
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.createTable
}
- public var columns: [SQLExpression] {
- get { return createTable.columns }
- set { createTable.columns = newValue }
+ /// The set of column definitions.
+ @inlinable
+ public var columns: [any SQLExpression] {
+ get { self.createTable.columns }
+ set { self.createTable.columns = newValue }
}
- /// Creates a new `SQLCreateTableBuilder`.
- public init(_ createTable: SQLCreateTable, on database: SQLDatabase) {
+ /// Create a new ``SQLCreateTableBuilder``.
+ @inlinable
+ public init(_ createTable: SQLCreateTable, on database: any SQLDatabase) {
self.createTable = createTable
self.database = database
}
+ /// Add a new column by name, type, and constraints.
+ @inlinable
@discardableResult
- public func column(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: SQLColumnConstraintAlgorithm...
- ) -> Self {
- return self.column(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: String, type dataType: SQLDataType, _ constraints: SQLColumnConstraintAlgorithm...) -> Self {
+ self.column(column, type: dataType, constraints)
}
+ /// Add a new column by name, type, and constraints.
+ @inlinable
@discardableResult
- public func column(
- _ column: String,
- type dataType: SQLDataType,
- _ constraints: [SQLColumnConstraintAlgorithm]
- ) -> Self {
- return self.column(SQLColumnDefinition(
- column: SQLIdentifier(column),
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: String, type dataType: SQLDataType, _ constraints: [SQLColumnConstraintAlgorithm]) -> Self {
+ self.column(SQLIdentifier(column), type: dataType, constraints)
}
+ /// Add a new column by name, type, and constraints.
+ @inlinable
@discardableResult
- public func column(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: SQLExpression...
- ) -> Self {
- return self.column(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: any SQLExpression...) -> Self {
+ self.column(column, type: dataType, constraints)
}
+ /// Add a new column by name, type, and constraints.
+ @inlinable
@discardableResult
- public func column(
- _ column: SQLExpression,
- type dataType: SQLExpression,
- _ constraints: [SQLExpression]
- ) -> Self {
- return self.column(SQLColumnDefinition(
- column: column,
- dataType: dataType,
- constraints: constraints
- ))
+ public func column(_ column: any SQLExpression, type dataType: any SQLExpression, _ constraints: [any SQLExpression]) -> Self {
+ self.column(SQLColumnDefinition(column: column, dataType: dataType, constraints: constraints))
}
+ /// Add a new column definition.
+ @inlinable
@discardableResult
- public func column(_ columnDefinition: SQLExpression) -> Self {
+ public func column(_ columnDefinition: any SQLExpression) -> Self {
self.columns.append(columnDefinition)
return self
}
- /// Sugar for `definitions.forEach { builder.column($0) }`
+ /// Add multiple column definitions.
+ @inlinable
@discardableResult
public func column(definitions: [SQLColumnDefinition]) -> SQLCreateTableBuilder {
self.columns.append(contentsOf: definitions)
return self
}
- /// If the "TEMP" or "TEMPORARY" keyword occurs between the "CREATE" and "TABLE" then the new table is created in the temp database.
+ /// Mark the new table as temporary.
+ @inlinable
@discardableResult
public func temporary() -> Self {
- createTable.temporary = true
+ self.createTable.temporary = true
return self
}
@@ -104,20 +90,22 @@ public final class SQLCreateTableBuilder: SQLQueryBuilder {
/// of the same name already exists, the CREATE TABLE command simply has no effect (and no error message is returned). An
/// error is still returned if the table cannot be created because of an existing index, even if the "IF NOT EXISTS" clause is
/// specified.
+ @inlinable
@discardableResult
public func ifNotExists() -> Self {
- createTable.ifNotExists = true
+ self.createTable.ifNotExists = true
return self
}
/// Specify a `SELECT` query to be used to populate the new table.
///
/// If called more than once, each subsequent invocation overwrites the query from the one before.
+ @inlinable
@discardableResult
- public func select(_ closure: (SQLCreateTableAsSubqueryBuilder) -> SQLCreateTableAsSubqueryBuilder) -> Self {
+ public func select(_ closure: (SQLCreateTableAsSubqueryBuilder) throws -> SQLCreateTableAsSubqueryBuilder) rethrows -> Self {
let builder = SQLCreateTableAsSubqueryBuilder()
- _ = closure(builder)
- createTable.asQuery = builder.select
+ _ = try closure(builder)
+ self.createTable.asQuery = builder.select
return self
}
}
@@ -125,122 +113,116 @@ public final class SQLCreateTableBuilder: SQLQueryBuilder {
// MARK: Constraints
extension SQLCreateTableBuilder {
- /// Adds a new `PRIMARY KEY` constraint to the table being built
+ /// Add a `PRIMARY KEY` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a Primary Key.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the primary key.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func primaryKey(_ columns: String..., named constraintName: String? = nil) -> Self {
- return primaryKey(columns, named: constraintName)
+ self.primaryKey(columns, named: constraintName)
}
- /// Adds a new `PRIMARY KEY` constraint to the table being built
+ /// Add a `PRIMARY KEY` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a Primary Key.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the primary key.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func primaryKey(_ columns: [String], named constraintName: String? = nil) -> Self {
- return primaryKey(
- columns.map(SQLIdentifier.init(_:)),
- named: constraintName.map(SQLIdentifier.init(_:))
- )
+ self.primaryKey(columns.map(SQLIdentifier.init(_:)), named: constraintName.map(SQLIdentifier.init(_:)))
}
- /// Adds a new `PRIMARY KEY` constraint to the table being built
+ /// Add a `PRIMARY KEY` constraint to the table.
///
/// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a Primary Key.
- /// - constraintName: An optional name to give the constraint.
+ /// - columns: One or more columns to include in the primary key.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
- public func primaryKey(_ columns: [SQLExpression], named constraintName: SQLExpression? = nil) -> Self {
- createTable.tableConstraints.append(
- SQLConstraint(
- algorithm: SQLTableConstraintAlgorithm.primaryKey(columns: columns),
- name: constraintName
- )
- )
+ public func primaryKey(_ columns: [any SQLExpression], named constraintName: (any SQLExpression)? = nil) -> Self {
+ self.createTable.tableConstraints.append(SQLConstraint(
+ algorithm: SQLTableConstraintAlgorithm.primaryKey(columns: columns),
+ name: constraintName
+ ))
return self
}
- /// Adds a new `UNIQUE` constraint to the table being built
+ /// Add a `UNIQUE` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a UNIQUE constraint.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the unique constraint.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func unique(_ columns: String..., named constraintName: String? = nil) -> Self {
- return unique(columns, named: constraintName)
+ self.unique(columns, named: constraintName)
}
- /// Adds a new `UNIQUE` constraint to the table being built
+ /// Add a `UNIQUE` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a UNIQUE constraint.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the unique constraint.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func unique(_ columns: [String], named constraintName: String? = nil) -> Self {
- return unique(
- columns.map(SQLIdentifier.init(_:)),
- named: constraintName.map(SQLIdentifier.init(_:))
- )
+ self.unique(columns.map(SQLIdentifier.init(_:)), named: constraintName.map(SQLIdentifier.init(_:)))
}
- /// Adds a new `UNIQUE` constraint to the table being built
+ /// Add a `UNIQUE` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to make into a UNIQUE constraint.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the unique constraint.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
- public func unique(_ columns: [SQLExpression], named constraintName: SQLExpression? = nil) -> Self {
- createTable.tableConstraints.append(
- SQLConstraint(
- algorithm: SQLTableConstraintAlgorithm.unique(columns: columns),
- name: constraintName
- )
- )
+ public func unique(_ columns: [any SQLExpression], named constraintName: (any SQLExpression)? = nil) -> Self {
+ self.createTable.tableConstraints.append(SQLConstraint(
+ algorithm: SQLTableConstraintAlgorithm.unique(columns: columns),
+ name: constraintName
+ ))
return self
}
- /// Adds a new `CHECK` constraint to the table being built
+ /// Add a `CHECK` constraint to the table.
///
- /// - parameters:
- /// - expression: A check constraint expression.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - expression: A check constraint expression.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
- public func check(_ expression: SQLExpression, named constraintName: String? = nil) -> Self {
- return self.check(
- expression,
- named: constraintName.map(SQLIdentifier.init(_:))
- )
+ public func check(_ expression: any SQLExpression, named constraintName: String? = nil) -> Self {
+ self.check(expression, named: constraintName.map(SQLIdentifier.init(_:)))
}
- /// Adds a new `CHECK` constraint to the table being built
+ /// Add a `CHECK` constraint to the table.
///
- /// - parameters:
- /// - expression: A check constraint expression.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - expression: A check constraint expression.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
- public func check(_ expression: SQLExpression, named constraintName: SQLExpression? = nil) -> Self {
- createTable.tableConstraints.append(
- SQLConstraint(
- algorithm: SQLTableConstraintAlgorithm.check(expression),
- name: constraintName
- )
- )
+ public func check(_ expression: any SQLExpression, named constraintName: (any SQLExpression)? = nil) -> Self {
+ self.createTable.tableConstraints.append(SQLConstraint(
+ algorithm: SQLTableConstraintAlgorithm.check(expression),
+ name: constraintName
+ ))
return self
}
- /// Adds a new `FOREIGN KEY` constraint to the table being built
+ /// Add a `FOREIGN KEY` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to constrain.
- /// - foreignTable: A table containing a foreign key to be constrained to.
- /// - foreignColumns: One or more columns of the foreign table to be constrained to.
- /// - onDelete: Optional foreign key action to perform on delete.
- /// - onUpdate: Optional foreign key action to perform on update.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the constraint.
+ /// - foreignTable: The table referenced by the constraint.
+ /// - foreignColumns: The foreign table columns corresponding to the constrained columns.
+ /// - onDelete: Optional foreign key action to perform on delete.
+ /// - onUpdate: Optional foreign key action to perform on update.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func foreignKey(
_ columns: [String],
@@ -250,48 +232,40 @@ extension SQLCreateTableBuilder {
onUpdate: SQLForeignKeyAction? = nil,
named constraintName: String? = nil
) -> Self {
- return self.foreignKey(
+ self.foreignKey(
columns.map(SQLIdentifier.init(_:)),
- references: SQLIdentifier(foreignTable),
- foreignColumns.map(SQLIdentifier.init(_:)),
- onDelete: onDelete,
- onUpdate: onUpdate,
+ references: SQLIdentifier(foreignTable), foreignColumns.map(SQLIdentifier.init(_:)),
+ onDelete: onDelete, onUpdate: onUpdate,
named: constraintName.map(SQLIdentifier.init(_:))
)
}
- /// Adds a new `FOREIGN KEY` constraint to the table being built
+ /// Add a `FOREIGN KEY` constraint to the table.
///
- /// - parameters:
- /// - columns: One or more columns of the table currently being built to constrain.
- /// - foreignTable: A table containing a foreign key to be constrained to.
- /// - foreignColumns: One or more columns of the foreign table to be constrained to.
- /// - onDelete: Optional foreign key action to perform on delete.
- /// - onUpdate: Optional foreign key action to perform on update.
- /// - constraintName: An optional name to give the constraint.
+ /// - Parameters:
+ /// - columns: One or more columns to include in the constraint.
+ /// - foreignTable: The table referenced by the constraint.
+ /// - foreignColumns: The foreign table columns corresponding to the constrained columns.
+ /// - onDelete: Optional foreign key action to perform on delete.
+ /// - onUpdate: Optional foreign key action to perform on update.
+ /// - constraintName: An optional name to give the constraint.
+ @inlinable
@discardableResult
public func foreignKey(
- _ columns: [SQLExpression],
- references foreignTable: SQLExpression,
- _ foreignColumns: [SQLExpression],
- onDelete: SQLExpression? = nil,
- onUpdate: SQLExpression? = nil,
- named constraintName: SQLExpression? = nil
+ _ columns: [any SQLExpression],
+ references foreignTable: any SQLExpression,
+ _ foreignColumns: [any SQLExpression],
+ onDelete: (any SQLExpression)? = nil,
+ onUpdate: (any SQLExpression)? = nil,
+ named constraintName: (any SQLExpression)? = nil
) -> Self {
- createTable.tableConstraints.append(
- SQLConstraint(
- algorithm: SQLTableConstraintAlgorithm.foreignKey(
- columns: columns,
- references: SQLForeignKey(
- table: foreignTable,
- columns: foreignColumns,
- onDelete: onDelete,
- onUpdate: onUpdate
- )
- ),
- name: constraintName
- )
- )
+ self.createTable.tableConstraints.append(SQLConstraint(
+ algorithm: SQLTableConstraintAlgorithm.foreignKey(
+ columns: columns,
+ references: SQLForeignKey(table: foreignTable, columns: foreignColumns, onDelete: onDelete, onUpdate: onUpdate)
+ ),
+ name: constraintName
+ ))
return self
}
}
@@ -299,25 +273,14 @@ extension SQLCreateTableBuilder {
// MARK: Connection
extension SQLDatabase {
- /// Creates a new `SQLCreateTableBuilder`.
- ///
- /// db.create(table: "planets")...
- ///
- /// - parameters:
- /// - table: Table to create.
- /// - returns: `CreateTableBuilder`.
+ /// Create a new ``SQLCreateTableBuilder``.
+ @inlinable
public func create(table: String) -> SQLCreateTableBuilder {
- return self.create(table: SQLIdentifier(table))
+ self.create(table: SQLIdentifier(table))
}
- /// Creates a new `SQLCreateTableBuilder`.
- ///
- /// db.create(table: SQLIdentifier("planets"))...
- ///
- /// - parameters:
- /// - table: Table to create.
- /// - returns: `CreateTableBuilder`.
- public func create(table: SQLExpression) -> SQLCreateTableBuilder {
- return .init(.init(name: table), on: self)
+ /// Create a new ``SQLCreateTableBuilder``.
+ public func create(table: any SQLExpression) -> SQLCreateTableBuilder {
+ .init(.init(name: table), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLCreateTriggerBuilder.swift b/Sources/SQLKit/Builders/SQLCreateTriggerBuilder.swift
index ad2adb43..1fef563e 100644
--- a/Sources/SQLKit/Builders/SQLCreateTriggerBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLCreateTriggerBuilder.swift
@@ -1,190 +1,188 @@
-import NIOCore
-
-extension SQLDatabase {
- public func create(trigger: String, table: String, when: SQLTriggerWhen, event: SQLTriggerEvent) -> SQLCreateTriggerBuilder {
- self.create(trigger: SQLIdentifier(trigger), table: SQLIdentifier(table), when: when, event: event)
- }
-
- public func create(trigger: SQLExpression, table: SQLExpression, when: SQLExpression, event: SQLExpression) -> SQLCreateTriggerBuilder {
- .init(trigger: trigger, table: table, when: when, event: event, on: self)
- }
-}
-
+/// Builds ``SQLCreateTrigger`` queries.
public final class SQLCreateTriggerBuilder: SQLQueryBuilder {
+ /// ``SQLCreateTrigger`` query being built.
public var createTrigger: SQLCreateTrigger
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.createTrigger
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.createTrigger
}
- init(trigger: SQLExpression, table: SQLExpression, when: SQLExpression, event: SQLExpression, on database: SQLDatabase) {
- createTrigger = .init(trigger: trigger, table: table, when: when, event: event)
+ /// Create a new ``SQLCreateTriggerBuilder``.
+ @usableFromInline
+ init(trigger: any SQLExpression, table: any SQLExpression, when: any SQLExpression, event: any SQLExpression, on database: any SQLDatabase) {
+ self.createTrigger = .init(trigger: trigger, table: table, when: when, event: event)
self.database = database
}
/// Identifies whether the trigger applies to each row or each statement.
- /// - Parameter value: The option to use.
+ @inlinable
@discardableResult
- public func each(_ value: SQLTriggerEach) -> SQLCreateTriggerBuilder {
- createTrigger.each = value
+ public func each(_ value: SQLTriggerEach) -> Self {
+ self.createTrigger.each = value
return self
}
/// Identifies whether the trigger applies to each row or each statement.
- /// - Parameter value: The appropriate row or statement value for the language.
+ @inlinable
@discardableResult
- public func each(_ value: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.each = value
+ public func each(_ value: any SQLExpression) -> Self {
+ self.createTrigger.each = value
return self
}
/// Specifies this is a constraint trigger.
+ @inlinable
@discardableResult
- public func isConstraint() -> SQLCreateTriggerBuilder {
- createTrigger.isConstraint = true
+ public func isConstraint() -> Self {
+ self.createTrigger.isConstraint = true
return self
}
- /// The columns which the trigger applies to.
- /// - Parameter columns: The names of the columns.
+ /// Specify the columns to which the trigger applies.
+ @inlinable
@discardableResult
- public func columns(_ columns: [String]) -> SQLCreateTriggerBuilder {
- createTrigger.columns = columns.map { SQLRaw($0) }
- return self
+ public func columns(_ columns: [String]) -> Self {
+ self.columns(columns.map(SQLIdentifier.init(_:)))
}
- /// The columns which the trigger applies to.
- /// - Parameter columns: The names of the columns.
+ /// Specify the columns to which the trigger applies.
+ @inlinable
@discardableResult
- public func columns(_ columns: [SQLExpression]) -> SQLCreateTriggerBuilder {
- createTrigger.columns = columns
+ public func columns(_ columns: [any SQLExpression]) -> Self {
+ self.createTrigger.columns = columns
return self
}
- /// The timing of the trigger.
- /// - Parameter value: When the trigger applies.
- /// ### Note ###
- /// Only applicable to constraint triggers.
+ /// Specify the trigger's timing.
+ ///
+ /// - Note: Only applies to constraint triggers.
+ @inlinable
@discardableResult
- public func timing(_ value: SQLTriggerTiming) -> SQLCreateTriggerBuilder {
- createTrigger.timing = value
+ public func timing(_ value: SQLTriggerTiming) -> Self {
+ self.createTrigger.timing = value
return self
}
- /// The timing of the trigger.
- /// - Parameter value: The appropriate option.
- /// ### Note ###
- /// Only applicable to constraint triggers.
+ /// Specify the trigger's timing.
+ ///
+ /// - Note: Only applies to constraint triggers.
+ @inlinable
@discardableResult
- public func timing(_ value: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.timing = value
+ public func timing(_ value: any SQLExpression) -> Self {
+ self.createTrigger.timing = value
return self
}
- /// A Boolean expression that determines whether the trigger function will actually be executed
- /// - Parameter value: The condition
+ /// Specify a conditional expression which determines whether the trigger is actually executed.
+ @available(*, deprecated, message: "Specifying conditions as raw strings is unsafe. Use `SQLBinaryExpression` etc. instead.")
+ @inlinable
@discardableResult
- public func condition(_ value: String) -> SQLCreateTriggerBuilder {
- createTrigger.condition = SQLRaw(value)
- return self
+ public func condition(_ value: String) -> Self {
+ self.condition(SQLRaw(value))
}
- /// A Boolean expression that determines whether the trigger function will actually be executed
- /// - Parameter value: The condition
+ /// Specify a conditional expression which determines whether the trigger is actually executed.
+ @inlinable
@discardableResult
- public func condition(_ value: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.condition = value
+ public func condition(_ value: any SQLExpression) -> Self {
+ self.createTrigger.condition = value
return self
}
- /// The (possibly schema-qualified) name of another table referenced by the constraint
- /// - Parameter value: The name of the table.
- /// ### Note ###
- /// This option is used for foreign-key constraints and is not recommended for general use. This can only be specified for constraint triggers.
+ /// Specify the name of another table referenced by the constraint.
+ ///
+ /// To specify a schema-qualified table, use ``SQLQualifiedTable``.
+ ///
+ /// - Note: This option is used for foreign key constraints and is not recommended for general use. Only applies to constraint triggers.
+ @inlinable
@discardableResult
- public func referencedTable(_ value: String) -> SQLCreateTriggerBuilder {
- createTrigger.referencedTable = SQLIdentifier(value)
- return self
+ public func referencedTable(_ value: String) -> Self {
+ self.referencedTable(SQLIdentifier(value))
}
- /// The (possibly schema-qualified) name of another table referenced by the constraint
- /// - Parameter value: The name of the table.
- /// ### Note ###
- /// This option is used for foreign-key constraints and is not recommended for general use. This can only be specified for constraint triggers.
+ /// Specify the name of another table referenced by the constraint.
+ ///
+ /// To specify a schema-qualified table, use ``SQLQualifiedTable``.
+ ///
+ /// - Note: This option is used for foreign key constraints and is not recommended for general use. Only applies to constraint triggers.
+ @inlinable
@discardableResult
- public func referencedTable(_ value: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.referencedTable = value
+ public func referencedTable(_ value: any SQLExpression) -> Self {
+ self.createTrigger.referencedTable = value
return self
}
- /// The body of the trigger for those dialects that include the body in the trigger itself.
- /// - Parameter statements: The statements for the body of the trigger.
+ /// Specify a body for the trigger.
+ @available(*, deprecated, message: "Specifying SQL statements as raw strings is unsafe. Use `SQLQueryString` or `SQLRaw` explicitly.")
+ @inlinable
@discardableResult
- public func body(_ statements: [String]) -> SQLCreateTriggerBuilder {
- createTrigger.body = statements.map { SQLRaw($0) }
- return self
+ public func body(_ statements: [String]) -> Self {
+ self.body(statements.map { SQLRaw($0) })
}
- /// The body of the trigger for those dialects that include the body in the trigger itself.
- /// - Parameter statements: The statements for the body of the trigger.
+ /// Specify a body for the trigger.
+ @inlinable
@discardableResult
- public func body(_ statements: [SQLExpression]) -> SQLCreateTriggerBuilder {
- createTrigger.body = statements
+ public func body(_ statements: [any SQLExpression]) -> Self {
+ self.createTrigger.body = statements
return self
}
- /// The name of the procedure the trigger will execute for dialects that don't include the body in the trigger itself.
- /// - Parameter name: The name of the procedure.
+ /// Specify a procedure name for the trigger to execute.
+ @inlinable
@discardableResult
- public func procedure(_ name: String) -> SQLCreateTriggerBuilder {
- createTrigger.procedure = SQLIdentifier(name)
- return self
+ public func procedure(_ name: String) -> Self {
+ self.procedure(SQLIdentifier(name))
}
- /// The name of the procedure the trigger will execute for dialects that don't include the body in the trigger itself.
- /// - Parameter name: The name of the procedure.
+ /// Specify a procedure name for the trigger to execute.
+ @inlinable
@discardableResult
- public func procedure(_ name: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.procedure = name
+ public func procedure(_ name: any SQLExpression) -> Self {
+ self.createTrigger.procedure = name
return self
}
- /// Identifies whether this trigger follows or precedes the referenced trigger.
- /// - Parameters:
- /// - precedence: The precedence of this trigger in relation to `otherTriggerName`
- /// - otherTriggerName: The name of the other trigger.
+ /// Specify whether this trigger precedes or follows a referenced trigger.
+ @inlinable
@discardableResult
- public func order(precedence: SQLTriggerOrder, otherTriggerName: String) -> SQLCreateTriggerBuilder {
- createTrigger.order = precedence
- createTrigger.orderTriggerName = SQLIdentifier(otherTriggerName)
- return self
+ public func order(precedence: SQLTriggerOrder, otherTriggerName: String) -> Self {
+ self.order(precedence: precedence, otherTriggerName: SQLIdentifier(otherTriggerName))
}
- /// Identifies whether this trigger follows or precedes the referenced trigger.
- /// - Parameters:
- /// - precedence: The precedence of this trigger in relation to `otherTriggerName`
- /// - otherTriggerName: The name of the other trigger.
+ /// Specify whether this trigger precedes or follows a referenced trigger.
+ @inlinable
@discardableResult
- public func order(precedence: SQLTriggerOrder, otherTriggerName: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.order = otherTriggerName
- createTrigger.orderTriggerName = otherTriggerName
- return self
+ public func order(precedence: SQLTriggerOrder, otherTriggerName: any SQLExpression) -> Self {
+ self.order(precedence: precedence as any SQLExpression, otherTriggerName: otherTriggerName)
}
- /// Identifies whether this trigger follows or precedes the referenced trigger.
- /// - Parameters:
- /// - precedence: The precedence of this trigger in relation to `otherTriggerName`
- /// - otherTriggerName: The name of the other trigger.
+ /// Specify whether this trigger precedes or follows a referenced trigger.
+ @inlinable
@discardableResult
- public func order(precedence: SQLExpression, otherTriggerName: SQLExpression) -> SQLCreateTriggerBuilder {
- createTrigger.order = precedence
- createTrigger.orderTriggerName = otherTriggerName
+ public func order(precedence: any SQLExpression, otherTriggerName: any SQLExpression) -> Self {
+ self.createTrigger.order = precedence
+ self.createTrigger.orderTriggerName = otherTriggerName
return self
}
+}
+
+extension SQLDatabase {
+ /// Create a new ``SQLCreateTriggerBuilder``.
+ @inlinable
+ public func create(trigger: String, table: String, when: SQLTriggerWhen, event: SQLTriggerEvent) -> SQLCreateTriggerBuilder {
+ self.create(trigger: SQLIdentifier(trigger), table: SQLIdentifier(table), when: when, event: event)
+ }
- public func run() -> EventLoopFuture {
- database.execute(sql: self.query) { _ in }
+ /// Create a new ``SQLCreateTriggerBuilder``.
+ @inlinable
+ public func create(trigger: any SQLExpression, table: any SQLExpression, when: any SQLExpression, event: any SQLExpression) -> SQLCreateTriggerBuilder {
+ .init(trigger: trigger, table: table, when: when, event: event, on: self)
}
}
+
diff --git a/Sources/SQLKit/Builders/SQLDeleteBuilder.swift b/Sources/SQLKit/Builders/SQLDeleteBuilder.swift
index 2a04f04a..b3dd2c16 100644
--- a/Sources/SQLKit/Builders/SQLDeleteBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLDeleteBuilder.swift
@@ -1,56 +1,54 @@
-/// Builds `SQLDelete` queries.
+/// Builds ``SQLDelete`` queries.
///
/// db.delete(from: Planet.self)
-/// .where(\.name != "Earth").run()
+/// .where("name", .notEqual, "Earth")
+/// .run()
///
-/// See `SQLQueryBuilder` and `SQLPredicateBuilder` for more information.
+/// See ``SQLPredicateBuilder`` for additional information.
public final class SQLDeleteBuilder: SQLQueryBuilder, SQLPredicateBuilder, SQLReturningBuilder {
- /// `Delete` query being built.
+ /// ``SQLDelete`` query being built.
public var delete: SQLDelete
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.delete
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.delete
}
- public var predicate: SQLExpression? {
- get { return self.delete.predicate }
+ /// See ``SQLPredicateBuilder/predicate``.
+ @inlinable
+ public var predicate: (any SQLExpression)? {
+ get { self.delete.predicate }
set { self.delete.predicate = newValue }
}
+ /// See ``SQLReturningBuilder/returning``.
+ @inlinable
public var returning: SQLReturning? {
- get { return self.delete.returning }
+ get { self.delete.returning }
set { self.delete.returning = newValue }
}
- /// Creates a new `SQLDeleteBuilder`.
- public init(_ delete: SQLDelete, on database: SQLDatabase) {
+ /// Create a new ``SQLDeleteBuilder``.
+ @inlinable
+ public init(_ delete: SQLDelete, on database: any SQLDatabase) {
self.delete = delete
self.database = database
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLDeleteBuilder`.
- ///
- /// db.delete(from: "planets")...
- ///
- /// - parameters:
- /// - table: Table to delete from.
- /// - returns: Newly created `SQLDeleteBuilder`.
+ /// Create a new ``SQLDeleteBuilder``.
+ @inlinable
public func delete(from table: String) -> SQLDeleteBuilder {
- return self.delete(from: SQLIdentifier(table))
+ self.delete(from: SQLIdentifier(table))
}
- /// Creates a new `SQLDeleteBuilder`.
- ///
- /// - parameters:
- /// - table: Table to delete from.
- /// - returns: Newly created `SQLDeleteBuilder`.
- public func delete(from table: SQLExpression) -> SQLDeleteBuilder {
- return .init(.init(table: table), on: self)
+ /// Create a new ``SQLDeleteBuilder``.
+ public func delete(from table: any SQLExpression) -> SQLDeleteBuilder {
+ .init(.init(table: table), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLDropEnumBuilder.swift b/Sources/SQLKit/Builders/SQLDropEnumBuilder.swift
index a951626c..5f093a86 100644
--- a/Sources/SQLKit/Builders/SQLDropEnumBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLDropEnumBuilder.swift
@@ -1,52 +1,29 @@
import NIOCore
-extension SQLDatabase {
- /// Creates a new `SQLDropEnumBuilder`.
- ///
- /// sql.drop(enum: "meal").run()
- ///
- /// - parameters:
- /// - type: Name of type to drop.
- /// - returns: `SQLDropEnumBuilder`.
- public func drop(enum name: String) -> SQLDropEnumBuilder {
- self.drop(enum: SQLIdentifier(name))
- }
-
- /// Creates a new `SQLDropEnumBuilder`.
- ///
- /// sql.drop(enum: "meal").run()
- ///
- /// - parameters:
- /// - type: Name of type to drop.
- /// - returns: `SQLDropEnumBuilder`.
- public func drop(enum name: SQLExpression) -> SQLDropEnumBuilder {
- .init(name: name, on: self)
- }
-}
-
-/// Builds `SQLDropEnumBuilder` queries.
-///
-/// db.drop(type: "meal").run()
-///
-/// See `SQLQueryBuilder` for more information.
+/// Builds ``SQLDropEnum`` queries.
public final class SQLDropEnumBuilder: SQLQueryBuilder {
- /// `DropType` query being built.
+ /// ``SQLDropEnum`` query being built.
public var dropEnum: SQLDropEnum
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.dropEnum
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.dropEnum
}
- /// Creates a new `SQLDropEnumBuilder`.
- init(name: SQLExpression, on database: SQLDatabase) {
+ /// Create a new ``SQLDropEnumBuilder``.
+ @usableFromInline
+ init(name: any SQLExpression, on database: any SQLDatabase) {
self.dropEnum = .init(name: name)
self.database = database
}
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the type does not exist.
+ @inlinable
@discardableResult
public func ifExists() -> Self {
self.dropEnum.ifExists = true
@@ -56,17 +33,34 @@ public final class SQLDropEnumBuilder: SQLQueryBuilder {
/// The optional `CASCADE` clause drops other objects that depend on this type
/// (such as table columns, functions, and operators), and in turn all objects
/// that depend on those objects.
+ @inlinable
@discardableResult
public func cascade() -> Self {
self.dropEnum.cascade = true
return self
}
+ /// See ``SQLQueryBuilder/run()-2sxsg``.
+ @inlinable
public func run() -> EventLoopFuture {
guard self.database.dialect.enumSyntax == .typeName else {
- self.database.logger.warning("Database does not support enum types.")
+ self.database.logger.warning("Database does not support standalone enum types.")
return self.database.eventLoop.makeSucceededFuture(())
}
return self.database.execute(sql: self.query) { _ in }
}
}
+
+extension SQLDatabase {
+ /// Create a new ``SQLDropEnumBuilder``.
+ @inlinable
+ public func drop(enum name: String) -> SQLDropEnumBuilder {
+ self.drop(enum: SQLIdentifier(name))
+ }
+
+ /// Create a new ``SQLDropEnumBuilder``.
+ @inlinable
+ public func drop(enum name: any SQLExpression) -> SQLDropEnumBuilder {
+ .init(name: name, on: self)
+ }
+}
diff --git a/Sources/SQLKit/Builders/SQLDropIndexBuilder.swift b/Sources/SQLKit/Builders/SQLDropIndexBuilder.swift
index 93815a9d..06c98919 100644
--- a/Sources/SQLKit/Builders/SQLDropIndexBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLDropIndexBuilder.swift
@@ -1,95 +1,89 @@
-/// Builds `SQLDropIndex` queries.
-///
-/// db.drop(index: "planet_name_unique").run()
-///
-/// See `SQLDropIndex`.
+/// Builds ``SQLDropIndex`` queries.
public final class SQLDropIndexBuilder: SQLQueryBuilder {
- /// `DropIndex` query being built.
+ /// ``SQLDropIndex`` query being built.
public var dropIndex: SQLDropIndex
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.dropIndex
+ /// See ``SQLQueryBuilder/query``.
+ public var query: any SQLExpression {
+ self.dropIndex
}
- /// Creates a new `SQLDropIndexBuilder`.
- public init(_ dropIndex: SQLDropIndex, on database: SQLDatabase) {
+ /// Create a new ``SQLDropIndexBuilder``.
+ public init(_ dropIndex: SQLDropIndex, on database: any SQLDatabase) {
self.dropIndex = dropIndex
self.database = database
}
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the index does not exist.
+ @inlinable
@discardableResult
public func ifExists() -> Self {
- dropIndex.ifExists = true
+ self.dropIndex.ifExists = true
return self
}
/// Convenience method for specifying an owning object using a `String`. See
/// ``on(_:)-84xo2`` for details.
+ @inlinable
@discardableResult
public func on(_ owningObject: String) -> Self {
- return self.on(SQLIdentifier(owningObject))
+ self.on(SQLIdentifier(owningObject))
}
/// The object (usually a table) which owns the index may be explicitly specified.
/// Some dialects treat indexes as database-level objects in their own right and
/// treat specifying an owner as an error, while others require the owning object
/// in order to perform the drop operation at all. At the time of this writing,
- /// there is no support for specifying this in `SQLDialect`; callers must ensure
+ /// there is no support for specifying this in ``SQLDialect``; callers must ensure
/// that they either specify or omit an owning object as appropriate.
+ @inlinable
@discardableResult
- public func on(_ owningObject: SQLExpression) -> Self {
- dropIndex.owningObject = owningObject
+ public func on(_ owningObject: any SQLExpression) -> Self {
+ self.dropIndex.owningObject = owningObject
return self
}
/// The drop behavior clause specifies if objects that depend on a index
/// should also be dropped or not when the index is dropped, for databases
/// that support this.
+ @inlinable
@discardableResult
public func behavior(_ behavior: SQLDropBehavior) -> Self {
- dropIndex.behavior = behavior
+ self.dropIndex.behavior = behavior
return self
}
/// Adds a `CASCADE` clause to the `DROP INDEX` statement instructing that
/// objects that depend on this index should also be dropped.
+ @inlinable
@discardableResult
public func cascade() -> Self {
- dropIndex.behavior = SQLDropBehavior.cascade
+ self.dropIndex.behavior = SQLDropBehavior.cascade
return self
}
/// Adds a `RESTRICT` clause to the `DROP INDEX` statement instructing that
/// if any objects depend on this index, the drop should be refused.
+ @inlinable
@discardableResult
public func restrict() -> Self {
- dropIndex.behavior = SQLDropBehavior.restrict
+ self.dropIndex.behavior = SQLDropBehavior.restrict
return self
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLDropIndexBuilder`.
- ///
- /// db.drop(index: "foo").run()
- ///
+ /// Create a new ``SQLDropIndexBuilder``.
public func drop(index name: String) -> SQLDropIndexBuilder {
- return self.drop(index: SQLIdentifier(name))
+ self.drop(index: SQLIdentifier(name))
}
- /// Creates a new `SQLDropIndexBuilder`.
- ///
- /// db.drop(index: "foo").run()
- ///
- public func drop(index name: SQLExpression) -> SQLDropIndexBuilder {
- return .init(.init(name: name), on: self)
+ /// Create a new ``SQLDropIndexBuilder``.
+ public func drop(index name: any SQLExpression) -> SQLDropIndexBuilder {
+ .init(.init(name: name), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLDropTableBuilder.swift b/Sources/SQLKit/Builders/SQLDropTableBuilder.swift
index c919dd6e..7d952e39 100644
--- a/Sources/SQLKit/Builders/SQLDropTableBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLDropTableBuilder.swift
@@ -1,84 +1,81 @@
-/// Builds `SQLDropTable` queries.
-///
-/// db.drop(table: Planet.self).run()
-///
-/// See `SQLQueryBuilder` for more information.
+/// Builds ``SQLDropTable`` queries.
public final class SQLDropTableBuilder: SQLQueryBuilder {
- /// `DropTable` query being built.
+ /// ``SQLDropTable`` query being built.
public var dropTable: SQLDropTable
- // See `SQLQueryBuilder.database`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- // See `SQLQueryBuilder.query`.
- public var query: SQLExpression {
- return self.dropTable
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.dropTable
}
- /// Creates a new `SQLDropTableBuilder`.
- public init(_ dropTable: SQLDropTable, on database: SQLDatabase) {
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
+ public init(_ dropTable: SQLDropTable, on database: any SQLDatabase) {
self.dropTable = dropTable
self.database = database
}
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the table does not exist.
+ @inlinable
@discardableResult
public func ifExists() -> Self {
- dropTable.ifExists = true
+ self.dropTable.ifExists = true
return self
}
/// The drop behavior clause specifies if objects that depend on a table
/// should also be dropped or not when the table is dropped, for databases
/// that support this.
+ @inlinable
@discardableResult
public func behavior(_ behavior: SQLDropBehavior) -> Self {
- dropTable.behavior = behavior
+ self.dropTable.behavior = behavior
return self
}
/// Adds a `CASCADE` clause to the `DROP TABLE` statement instructing that
/// objects that depend on this table should also be dropped.
+ @inlinable
@discardableResult
public func cascade() -> Self {
- dropTable.behavior = SQLDropBehavior.cascade
+ self.dropTable.behavior = SQLDropBehavior.cascade
return self
}
/// Adds a `RESTRICT` clause to the `DROP TABLE` statement instructing that
/// if any objects depend on this table, the drop should be refused.
+ @inlinable
@discardableResult
public func restrict() -> Self {
- dropTable.behavior = SQLDropBehavior.restrict
+ self.dropTable.behavior = SQLDropBehavior.restrict
return self
}
- /// If the "TEMPORARY" keyword occurs between "DROP" and "TABLE" then only temporary tables are dropped,
- /// and the drop does not cause an implicit transaction commit.
+ /// If the `TEMPORARY` keyword occurs between `DROP` and `TABLE`, then only
+ /// temporary tables are dropped, and the drop does not cause an implicit transaction commit.
+ @inlinable
@discardableResult
public func temporary() -> Self {
- dropTable.temporary = true
+ self.dropTable.temporary = true
return self
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLDropTable` builder.
- ///
- /// db.drop(table: "planets").run()
- ///
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
public func drop(table: String) -> SQLDropTableBuilder {
- return self.drop(table: SQLIdentifier(table))
+ self.drop(table: SQLIdentifier(table))
}
- /// Creates a new `SQLDropTable` builder.
- ///
- /// db.drop(table: "planets").run()
- ///
- public func drop(table: SQLExpression) -> SQLDropTableBuilder {
- return .init(.init(table: table), on: self)
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
+ public func drop(table: any SQLExpression) -> SQLDropTableBuilder {
+ .init(.init(table: table), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLDropTriggerBuilder.swift b/Sources/SQLKit/Builders/SQLDropTriggerBuilder.swift
index 17833b4a..593c2615 100644
--- a/Sources/SQLKit/Builders/SQLDropTriggerBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLDropTriggerBuilder.swift
@@ -1,61 +1,69 @@
-/// Builds `SQLDropTrigger` query
-///
-/// db.drop()
-///
-/// See `SQLQueryBuilder` for more information.
-extension SQLDatabase {
- public func drop(trigger: String) -> SQLDropTriggerBuilder {
- return self.drop(trigger: SQLIdentifier(trigger))
- }
-
- public func drop(trigger: SQLExpression) -> SQLDropTriggerBuilder {
- return .init(.init(name: trigger), on: self)
- }
-}
-
+/// Builds ``SQLDropTrigger`` queries.
public final class SQLDropTriggerBuilder: SQLQueryBuilder {
- /// `SQLDropTrigger` query being built.
+ /// ``SQLDropTrigger`` query being built.
public var dropTrigger: SQLDropTrigger
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.dropTrigger
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.dropTrigger
}
- public init(_ dropTrigger: SQLDropTrigger, on database: SQLDatabase) {
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
+ public init(_ dropTrigger: SQLDropTrigger, on database: any SQLDatabase) {
self.dropTrigger = dropTrigger
self.database = database
}
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the table does not exist.
+ @inlinable
@discardableResult
public func ifExists() -> Self {
- dropTrigger.ifExists = true
+ self.dropTrigger.ifExists = true
return self
}
/// The optional `CASCADE` clause drops other objects that depend on this type
/// (such as table columns, functions, and operators), and in turn all objects
/// that depend on those objects.
+ @inlinable
@discardableResult
public func cascade() -> Self {
- dropTrigger.cascade = true
+ self.dropTrigger.cascade = true
return self
}
+ /// Specify an associated table that owns the trigger to drop, for dialects that require it.
+ @inlinable
@discardableResult
public func table(_ name: String) -> Self {
- dropTrigger.table = SQLIdentifier(name)
- return self
+ self.table(SQLIdentifier(name))
}
+ /// Specify an associated table that owns the trigger to drop, for dialects that require it.
+ @inlinable
@discardableResult
- public func table(_ name: SQLExpression) -> Self {
- dropTrigger.table = name
+ public func table(_ name: any SQLExpression) -> Self {
+ self.dropTrigger.table = name
return self
}
}
+
+extension SQLDatabase {
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
+ public func drop(trigger: String) -> SQLDropTriggerBuilder {
+ self.drop(trigger: SQLIdentifier(trigger))
+ }
+
+ /// Create a new ``SQLDropTableBuilder``.
+ @inlinable
+ public func drop(trigger: any SQLExpression) -> SQLDropTriggerBuilder {
+ .init(.init(name: trigger), on: self)
+ }
+}
diff --git a/Sources/SQLKit/Builders/SQLInsertBuilder.swift b/Sources/SQLKit/Builders/SQLInsertBuilder.swift
index 61fbb119..474b60db 100644
--- a/Sources/SQLKit/Builders/SQLInsertBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLInsertBuilder.swift
@@ -1,138 +1,180 @@
-/// Builds `SQLInsert` queries.
-///
-/// db.insert(into: "planets")
-/// .value(earth).run()
-///
-/// See `SQLQueryBuilder` for more information.
+/// Builds ``SQLInsert`` queries.
public final class SQLInsertBuilder: SQLQueryBuilder, SQLReturningBuilder {
- /// `Insert` query being built.
+ /// ``SQLInsert`` query being built.
public var insert: SQLInsert
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.insert
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.insert
}
+ /// See ``SQLReturningBuilder/returning``.
+ @inlinable
public var returning: SQLReturning? {
- get { return self.insert.returning }
+ get { self.insert.returning }
set { self.insert.returning = newValue }
}
/// Creates a new `SQLInsertBuilder`.
- public init(_ insert: SQLInsert, on database: SQLDatabase) {
+ @inlinable
+ public init(_ insert: SQLInsert, on database: any SQLDatabase) {
self.insert = insert
self.database = database
}
- /// Adds a single encodable value to be inserted. Equivalent to calling `values(_:)`
- /// with single-element array.
+ /// Adds a single encodable value to be inserted.
///
- /// db.insert(into: Planet.self)
- /// .value(earth).run()
+ /// db.insert(into: Planet.self).model(earth).run()
///
- /// - parameters:
- /// - value: `Encodable` value to insert.
- /// - returns: Self for chaining.
- @discardableResult
- public func model(_ model: E, prefix: String? = nil, keyEncodingStrategy: SQLQueryEncoder.KeyEncodingStrategy = .useDefaultKeys, nilEncodingStrategy: SQLQueryEncoder.NilEncodingStrategy = .default) throws -> Self
- where E: Encodable {
- return try models([model], prefix: prefix, keyEncodingStrategy: keyEncodingStrategy, nilEncodingStrategy: nilEncodingStrategy)
+ /// - Note: The term "model" here does _not_ refer to Fluent's `Model` type.
+ ///
+ /// - Parameters:
+ /// - model: ``Encodable`` model to insert. This can be any encodable type.
+ /// - prefix: An optional prefix to apply to the value's derived column names.
+ /// - keyEncodingStrategy: See ``SQLQueryEncoder/KeyEncodingStrategy-swift.enum``.
+ /// - nilEncodingStrategy: See ``SQLQueryEncoder/NilEncodingStrategy-swift.enum``.
+ @inlinable
+ @discardableResult
+ public func model(
+ _ model: E, // TODO: When we start requiring Swift 5.7+, use `some Encodable` here.
+ prefix: String? = nil,
+ keyEncodingStrategy: SQLQueryEncoder.KeyEncodingStrategy = .useDefaultKeys,
+ nilEncodingStrategy: SQLQueryEncoder.NilEncodingStrategy = .default
+ ) throws -> Self {
+ try models([model], prefix: prefix, keyEncodingStrategy: keyEncodingStrategy, nilEncodingStrategy: nilEncodingStrategy)
}
- @discardableResult
- public func models(_ models: [E], prefix: String? = nil, keyEncodingStrategy: SQLQueryEncoder.KeyEncodingStrategy = .useDefaultKeys, nilEncodingStrategy: SQLQueryEncoder.NilEncodingStrategy = .default) throws -> Self where E: Encodable {
- var encoder = SQLQueryEncoder()
- encoder.keyEncodingStrategy = keyEncodingStrategy
- encoder.nilEncodingStrategy = nilEncodingStrategy
- encoder.prefix = prefix
+ /// Adds an array of encodable values to be inserted.
+ ///
+ /// db.insert(into: Planet.self).models([mercury, venus, earth, mars]).run()
+ ///
+ /// - Note: The term "model" here does _not_ refer to Fluent's `Model` type.
+ ///
+ /// - Parameters:
+ /// - models: ``Encodable`` models to insert.
+ /// - prefix: An optional prefix to apply to the values' derived column names.
+ /// - keyEncodingStrategy: See ``SQLQueryEncoder/KeyEncodingStrategy-swift.enum``.
+ /// - nilEncodingStrategy: See ``SQLQueryEncoder/NilEncodingStrategy-swift.enum``.
+ @discardableResult
+ public func models(
+ _ models: [E], // TODO: When we start requiring Swift 5.7+, use `some Encodable` here.
+ prefix: String? = nil,
+ keyEncodingStrategy: SQLQueryEncoder.KeyEncodingStrategy = .useDefaultKeys,
+ nilEncodingStrategy: SQLQueryEncoder.NilEncodingStrategy = .default
+ ) throws -> Self {
+ let encoder = SQLQueryEncoder(prefix: prefix, keyEncodingStrategy: keyEncodingStrategy, nilEncodingStrategy: nilEncodingStrategy)
for model in models {
let row = try encoder.encode(model)
if self.insert.columns.isEmpty {
- self.insert.columns += row.map { $0.0 }.map { SQLColumn($0, table: nil) }
+ self.columns(row.map(\.0))
} else {
- assert(
- self.insert.columns.count == row.count,
- "Column count (\(self.insert.columns.count)) did not equal value count (\(row.count)): \(model)."
- )
+ assert(self.insert.columns.count == row.count, "Wrong number of columns in model (wanted \(self.insert.columns.count), got \(row.count)): \(model)")
}
- self.insert.values.append(.init(row.map { $0.1 }))
+ self.values(row.map(\.1))
}
-
return self
}
+ /// Specify the set of columns that appear in the list(s) of values.
+ ///
+ /// Overwrites the existing set of columns, if any.
+ @inlinable
@discardableResult
public func columns(_ columns: String...) -> Self {
- self.insert.columns = columns.map(SQLIdentifier.init(_:))
- return self
+ self.columns(columns)
}
+ /// Specify the set of columns that appear in the list(s) of values.
+ ///
+ /// Overwrites the existing set of columns, if any.
+ @inlinable
@discardableResult
public func columns(_ columns: [String]) -> Self {
- self.insert.columns = columns.map(SQLIdentifier.init(_:))
- return self
+ self.columns(columns.map(SQLIdentifier.init(_:)))
}
+ /// Specify the set of columns that appear in the list(s) of values.
+ ///
+ /// Overwrites the existing set of columns, if any.
+ @inlinable
@discardableResult
- public func columns(_ columns: SQLExpression...) -> Self {
- self.insert.columns = columns
- return self
+ public func columns(_ columns: any SQLExpression...) -> Self {
+ self.columns(columns)
}
+ /// Specify the set of columns that appear in the list(s) of values.
+ ///
+ /// Overwrites the existing set of columns, if any.
+ @inlinable
@discardableResult
- public func columns(_ columns: [SQLExpression]) -> Self {
+ public func columns(_ columns: [any SQLExpression]) -> Self {
self.insert.columns = columns
return self
}
+ /// Add a set of values to be inserted as a single row.
+ @inlinable
@discardableResult
@_disfavoredOverload
- public func values(_ values: Encodable...) -> Self {
- let row: [SQLExpression] = values.map(SQLBind.init)
- self.insert.values.append(row)
- return self
+ public func values(_ values: any Encodable...) -> Self { // TODO: When we require Swift 5.7+, use `some Encodable` here.
+ self.values(values)
}
+ /// Add a set of values to be inserted as a single row.
+ @inlinable
@discardableResult
- public func values(_ values: [Encodable]) -> Self {
- let row: [SQLExpression] = values.map(SQLBind.init)
- self.insert.values.append(row)
- return self
+ public func values(_ values: [any Encodable]) -> Self { // TODO: When we require Swift 5.7+, use `some Encodable` here.
+ self.values(values.map(SQLBind.init(_:)))
}
+ /// Add a set of values to be inserted as a single row.
+ @inlinable
@discardableResult
- public func values(_ values: SQLExpression...) -> Self {
- self.insert.values.append(values)
- return self
+ public func values(_ values: any SQLExpression...) -> Self {
+ self.values(values)
}
+ /// Add a set of values to be inserted as a single row.
+ @inlinable
@discardableResult
- public func values(_ values: [SQLExpression]) -> Self {
+ public func values(_ values: [any SQLExpression]) -> Self {
self.insert.values.append(values)
return self
}
+ /// Specify that constraint violations for the key over the given column should cause the conflicting
+ /// row(s) to be ignored.
+ @inlinable
@discardableResult
public func ignoringConflicts(with targetColumn: String) -> Self {
self.ignoringConflicts(with: [targetColumn])
}
+ /// Specify that constraint violations for the key over the given columns should cause the conflicting
+ /// row(s) to be ignored.
+ @inlinable
@discardableResult
public func ignoringConflicts(with targetColumns: [String] = []) -> Self {
- self.insert.conflictStrategy = .init(targets: targetColumns, action: .noAction)
- return self
+ self.ignoringConflicts(with: targetColumns.map(SQLIdentifier.init(_:)))
}
+ /// Specify that constraint violations for the key over the given columns should cause the conflicting
+ /// row(s) to be ignored.
+ @inlinable
@discardableResult
- public func ignoringConflicts(with targetColumns: [SQLExpression]) -> Self {
+ public func ignoringConflicts(with targetColumns: [any SQLExpression]) -> Self {
self.insert.conflictStrategy = .init(targets: targetColumns, action: .noAction)
return self
}
+ /// Specify that constraint violations for the key over the given column should cause the conflicting
+ /// row(s) to be updated as specified instead. See ``SQLConflictUpdateBuilder``.
+ @inlinable
@discardableResult
public func onConflict(
with targetColumn: String,
@@ -141,49 +183,45 @@ public final class SQLInsertBuilder: SQLQueryBuilder, SQLReturningBuilder {
try self.onConflict(with: [targetColumn], do: updatePredicate)
}
+ /// Specify that constraint violations for the key over the given columns should cause the conflicting
+ /// row(s) to be updated as specified instead. See ``SQLConflictUpdateBuilder``.
+ @inlinable
@discardableResult
public func onConflict(
with targetColumns: [String] = [],
`do` updatePredicate: (SQLConflictUpdateBuilder) throws -> SQLConflictUpdateBuilder
) rethrows -> Self {
- try self.onConflict(with: targetColumns.map { SQLColumn($0) }, do: updatePredicate)
+ try self.onConflict(with: targetColumns.map(SQLIdentifier.init(_:)), do: updatePredicate)
}
+ /// Specify that constraint violations for the key over the given column should cause the conflicting
+ /// row(s) to be updated as specified instead. See ``SQLConflictUpdateBuilder``.
+ @inlinable
@discardableResult
public func onConflict(
- with targetColumns: [SQLExpression],
+ with targetColumns: [any SQLExpression],
`do` updatePredicate: (SQLConflictUpdateBuilder) throws -> SQLConflictUpdateBuilder
) rethrows -> Self {
let conflictBuilder = SQLConflictUpdateBuilder()
_ = try updatePredicate(conflictBuilder)
-
- self.insert.conflictStrategy = .init(targets: targetColumns, action: .update(assignments: conflictBuilder.values, predicate: conflictBuilder.predicate))
+ self.insert.conflictStrategy = .init(
+ targets: targetColumns,
+ action: .update(assignments: conflictBuilder.values, predicate: conflictBuilder.predicate)
+ )
return self
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLInsertBuilder`.
- ///
- /// db.insert(into: "planets")...
- ///
- /// - parameters:
- /// - table: Table to insert into.
- /// - returns: Newly created `SQLInsertBuilder`.
+ /// Create a new ``SQLInsertBuilder``.
+ @inlinable
public func insert(into table: String) -> SQLInsertBuilder {
- return self.insert(into: SQLIdentifier(table))
+ self.insert(into: SQLIdentifier(table))
}
- /// Creates a new `SQLInsertBuilder`.
- ///
- /// db.insert(into: "planets")...
- ///
- /// - parameters:
- /// - table: Table to insert into.
- /// - returns: Newly created `SQLInsertBuilder`.
- public func insert(into table: SQLExpression) -> SQLInsertBuilder {
- return .init(.init(table: table), on: self)
+ /// Create a new ``SQLInsertBuilder``.
+ @inlinable
+ public func insert(into table: any SQLExpression) -> SQLInsertBuilder {
+ .init(.init(table: table), on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLJoinBuilder.swift b/Sources/SQLKit/Builders/SQLJoinBuilder.swift
index b5cd503e..ba0fbd75 100644
--- a/Sources/SQLKit/Builders/SQLJoinBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLJoinBuilder.swift
@@ -1,109 +1,102 @@
+/// Common definitions for any query builder which permits specifying joins.
public protocol SQLJoinBuilder: AnyObject {
- var joins: [SQLExpression] { get set }
+ /// The set of joins to be applied to the query.
+ var joins: [any SQLExpression] { get set }
}
-/// Joins
extension SQLJoinBuilder {
- /// Include the given table in the list of those used by the query,
- /// performing an explicit join using the given method and condition(s).
- /// Tables are joined left to right, in the same order as invocations of
- /// `from()` and `join()`. The table specifier is a string assumed to be a
- /// valid SQL identifier. The condition is a strings assumed to be valid
- /// (semi-))arbitrary SQL. The join method is any `SQLJoinMethod`.
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and condition(s). Tables are joined left to right.
///
/// - Parameters:
/// - table: The name of the table to join.
/// - method: The join method to use.
/// - expression: A string containing a join condition.
+ @available(*, deprecated, message: "Specifying conditions as raw strings is unsafe. Use `SQLBinaryExpression` etc. instead.")
+ @inlinable
@discardableResult
public func join(_ table: String, method: SQLJoinMethod = .inner, on expression: String) -> Self {
self.join(SQLIdentifier(table), method: method, on: SQLRaw(expression))
}
- /// Include the given table in the list of those used by the query,
- /// performing an explicit join using the given method and condition(s).
- /// Tables are joined left to right, in the same order as invocations of
- /// `from()` and `join()`. The table specifier, condition, and join method
- /// may be arbitrary expressions.
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and condition(s). Tables are joined left to right.
///
/// - Parameters:
- /// - table: An expression identifying the table to join.
- /// - method: An expression providing the join method to use.
- /// - expression: An expression used as the join condition.
+ /// - table: The name of the table to join.
+ /// - method: The join method to use.
+ /// - expression: A string containing a join condition.
+ @inlinable
+ @discardableResult
+ public func join(_ table: String, method: SQLJoinMethod = .inner, on expression: any SQLExpression) -> Self {
+ self.join(SQLIdentifier(table), method: method, on: expression)
+ }
+
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and condition(s). Tables are joined left to right.
+ ///
+ /// - Parameters:
+ /// - table: The name of the table to join.
+ /// - method: The join method to use.
+ /// - expression: A string containing a join condition.
+ @inlinable
@discardableResult
- public func join(_ table: SQLExpression, method: SQLExpression = SQLJoinMethod.inner, on expression: SQLExpression) -> Self {
+ public func join(_ table: any SQLExpression, method: any SQLExpression = SQLJoinMethod.inner, on expression: any SQLExpression) -> Self {
self.joins.append(SQLJoin(method: method, table: table, expression: expression))
return self
}
- /// Include the given table in the list of those used by the query,
- /// performing an explicit join using the given method and condition(s).
- /// Tables are joined left to right, in the same order as invocations of
- /// `from()` and `join()`. The table specifier and join method may be
- /// arbitrary expressions. The condition is a triplet of inputs representing
- /// a binary expression.
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and condition(s). Tables are joined left to right.
///
/// - Parameters:
- /// - table: An expression identifying the table to join.
- /// - method: An expression providing the join method to use.
- /// - left: The left side of a binary expression used as a join condition.
- /// - op: The operator in a binary expression used as a join condition.
- /// - right: The right side of a binary expression used as a join condition.
+ /// - table: The name of the table to join.
+ /// - method: The join method to use.
+ /// - left: The left side of a ``SQLBinaryExpression``.
+ /// - op: A ``SQLBinaryOperator``.
+ /// - right: The right side of a ``SQLBinaryExpression``.
+ @inlinable
@discardableResult
public func join(
- _ table: SQLExpression,
- method: SQLExpression = SQLJoinMethod.inner,
- on left: SQLExpression,
- _ op: SQLBinaryOperator,
- _ right: SQLExpression
+ _ table: String,
+ method: any SQLExpression = SQLJoinMethod.inner,
+ on left: any SQLExpression, _ op: SQLBinaryOperator, _ right: any SQLExpression
) -> Self {
- self.join(table, method: method, on: SQLBinaryExpression(left: left, op: op, right: right))
+ self.join(SQLIdentifier(table), method: method, on: left, op, right)
}
- /// Include the given table in the list of those used by the query,
- /// performing an explicit join using the given method and condition(s).
- /// Tables are joined left to right, in the same order as invocations of
- /// `from()` and `join()`. The table specifier is a string assumed to be
- /// a valid SQL identifier. The join method may be an arbitrary expression.
- /// The condition is a triplet of inputs representing a binary expression.
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and condition(s). Tables are joined left to right.
///
/// - Parameters:
/// - table: The name of the table to join.
- /// - method: An expression providing the join method to use.
- /// - left: The left side of a binary expression used as a join condition.
- /// - op: The operator in a binary expression used as a join condition.
- /// - right: The right side of a binary expression used as a join condition.
+ /// - method: The join method to use.
+ /// - left: The left side of a ``SQLBinaryExpression``.
+ /// - op: A ``SQLBinaryOperator``.
+ /// - right: The right side of a ``SQLBinaryExpression``.
+ @inlinable
@discardableResult
public func join(
- _ table: String,
- method: SQLExpression = SQLJoinMethod.inner,
- on left: SQLExpression,
- _ op: SQLBinaryOperator,
- _ right: SQLExpression
+ _ table: any SQLExpression,
+ method: any SQLExpression = SQLJoinMethod.inner,
+ on left: any SQLExpression, _ op: SQLBinaryOperator, _ right: any SQLExpression
) -> Self {
- self.join(SQLIdentifier(table), method: method, on: left, op, right)
+ self.join(table, method: method, on: SQLBinaryExpression(left: left, op: op, right: right))
}
- /// Include the given table in the list of those used by the query,
- /// performing an explicit join using the given method and a list of column
- /// names to be used as shorthand join conditions. Tables are joined left to
- /// right, in the same order as invocations of `from()` and `join()`. The
- /// table specifier, column list, and join method may be arbitrary
- /// expressions.
+ /// Include the given table in the list of those used by the query, performing an explicit join using the
+ /// given method and columns. Tables are joined left to right.
///
/// - Parameters:
- /// - table: An expression identifying the table to join.
- /// - method: An expression providing the join method to use.
- /// - column: An expression giving a list of columns to match between
- /// the joined tables.
+ /// - table: The name of the table to join.
+ /// - method: The join method to use.
+ /// - columns: One or more columns to use to perform a `NATURAL JOIN`.
+ @inlinable
@discardableResult
- public func join(_ table: SQLExpression, method: SQLExpression = SQLJoinMethod.inner, using columns: SQLExpression) -> Self {
- // TODO TODO TODO: Figure out a nice way to make `SQLJoin` aware of the
- // `USING()` syntax; this method is hacky and doesn't respect
- // differences between database drivers.
+ public func join(_ table: any SQLExpression, method: any SQLExpression = SQLJoinMethod.inner, using columns: any SQLExpression) -> Self {
+ // TODO: Make ``SQLJoin`` aware of the `USING` syntax; this method is hacky and somewhat driver-specific.
self.joins.append(SQLList([
- method, SQLRaw("JOIN"), table,
- SQLRaw("USING ("), columns, SQLRaw(")")
+ method, SQLRaw("JOIN"), table, SQLRaw("USING"), SQLGroupExpression(columns)
], separator: SQLRaw(" ")))
return self
}
diff --git a/Sources/SQLKit/Builders/SQLPartialResultBuilder.swift b/Sources/SQLKit/Builders/SQLPartialResultBuilder.swift
index e611bce3..e22cadb5 100644
--- a/Sources/SQLKit/Builders/SQLPartialResultBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLPartialResultBuilder.swift
@@ -1,7 +1,7 @@
-
+/// Common definitions for any query builder which permits specifying range and ordering behaviors.
public protocol SQLPartialResultBuilder: AnyObject {
/// Zero or more `ORDER BY` clauses.
- var orderBys: [SQLExpression] { get set }
+ var orderBys: [any SQLExpression] { get set }
/// If set, limits the maximum number of results.
var limit: Int? { get set }
@@ -16,7 +16,7 @@ extension SQLPartialResultBuilder {
/// Adds a `LIMIT` clause to the query. If called more than once, the last call wins.
///
/// - Parameter max: Optional maximum limit. If `nil`, any existing limit is removed.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func limit(_ max: Int?) -> Self {
self.limit = max
@@ -27,6 +27,7 @@ extension SQLPartialResultBuilder {
///
/// - Parameter max: Optional offset. If `nil`, any existing offset is removed.
/// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func offset(_ n: Int?) -> Self {
self.offset = n
@@ -42,10 +43,10 @@ extension SQLPartialResultBuilder {
/// - Parameters:
/// - column: Name of column to sort results by. Appended to any previously added orderings.
/// - direction: The sort direction for the column.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func orderBy(_ column: String, _ direction: SQLDirection = .ascending) -> Self {
- return self.orderBy(SQLColumn(column), direction)
+ self.orderBy(SQLColumn(column), direction)
}
@@ -54,19 +55,19 @@ extension SQLPartialResultBuilder {
/// - Parameters:
/// - expression: Expression to sort results by. Appended to any previously added orderings.
/// - direction: An expression describing the sort direction for the ordering expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orderBy(_ expression: SQLExpression, _ direction: SQLExpression) -> Self {
- return self.orderBy(SQLOrderBy(expression: expression, direction: direction))
+ public func orderBy(_ expression: any SQLExpression, _ direction: any SQLExpression) -> Self {
+ self.orderBy(SQLOrderBy(expression: expression, direction: direction))
}
/// Adds an `ORDER BY` clause to the query using the specified expression.
///
/// - Parameter expression: Expression to sort results by. Appended to any previously added orderings.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orderBy(_ expression: SQLExpression) -> Self {
- orderBys.append(expression)
+ public func orderBy(_ expression: any SQLExpression) -> Self {
+ self.orderBys.append(expression)
return self
}
}
diff --git a/Sources/SQLKit/Builders/SQLPredicateBuilder.swift b/Sources/SQLKit/Builders/SQLPredicateBuilder.swift
index 3620dc4c..746de67e 100644
--- a/Sources/SQLKit/Builders/SQLPredicateBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLPredicateBuilder.swift
@@ -1,15 +1,13 @@
-/// Builds `SQLExpression` predicates, i.e., `WHERE` clauses.
+/// Common definitions for any query builder which permits specifying a primary predicate.
///
/// builder.where("name", .equal, "Earth")
///
-/// Expressions will be added using `AND` logic by default. Use `orWhere` to join via `OR` logic.
-///
-/// builder.where("name", .equal, "Earth").orWhere("name", .equal, "Mars")
-///
-/// See `SQLPredicateGroupBuilder` for building expression groups.
+/// Expressions specified with ``where(_:)`` are considered conjunctive (`AND`).
+/// Expressions specified with ``orWhere(_:)`` are considered inclusively disjunctive (`OR`).
+/// See ``SQLPredicateGroupBuilder`` for details of grouping expressions (i.e. with parenthesis).
public protocol SQLPredicateBuilder: AnyObject {
/// Expression being built.
- var predicate: SQLExpression? { get set }
+ var predicate: (any SQLExpression)? { get set }
}
extension SQLPredicateBuilder {
@@ -20,15 +18,10 @@ extension SQLPredicateBuilder {
/// This method compares two _columns_.
///
/// SELECT * FROM "users" WHERE "firstName" = "lastName"
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func `where`(_ lhs: String, _ op: SQLBinaryOperator, column rhs: String) -> Self {
- return self.where(SQLIdentifier(lhs), op, SQLIdentifier(rhs))
+ self.where(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to column comparison to this builder's `WHERE` clause by `AND`ing.
@@ -38,15 +31,10 @@ extension SQLPredicateBuilder {
/// This method compares two _columns_.
///
/// SELECT * FROM "users" WHERE "firstName" = "lastName"
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, column rhs: SQLIdentifier) -> Self {
- return self.where(SQLColumn(lhs), op, SQLColumn(rhs))
+ self.where(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to encodable comparison to this builder's `WHERE` clause by `AND`ing.
@@ -55,18 +43,11 @@ extension SQLPredicateBuilder {
///
/// The encodable value supplied will be bound to the query as a parameter.
///
- /// SELECT * FROM "planets" WHERE "name" = ? // Earth
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed `Encodable` value.
- /// - Returns: `self` for chaining.
+ /// SELECT * FROM "planets" WHERE "name" = $0 ["Earth"]
+ @inlinable
@discardableResult
- public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self
- where E: Encodable
- {
- return self.where(SQLColumn(lhs), op, SQLBind(rhs))
+ public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self { // TODO: Use `some Encodable` when possible.
+ self.where(SQLColumn(lhs), op, SQLBind(rhs))
}
/// Adds a column to encodable array comparison to this builder's `WHERE` clause by `AND`ing.
@@ -75,91 +56,48 @@ extension SQLPredicateBuilder {
///
/// The encodable values supplied will be bound to the query as parameters.
///
- /// SELECT * FROM "planets" WHERE "name" IN (?, ?) // Earth, Mars
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed array of `Encodable` values.
- /// - Returns: `self` for chaining.
+ /// SELECT * FROM "planets" WHERE "name" IN ($0, $1) ["Earth", "Mars"]
+ @inlinable
@discardableResult
- public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self
- where E: Encodable
- {
- return self.where(SQLColumn(lhs), op, SQLBind.group(rhs))
+ public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self { // TODO: Use `some Encodable` when possible.
+ self.where(SQLColumn(lhs), op, SQLBind.group(rhs))
}
/// Adds a column to expression comparison to this builder' `WHERE` clause by `AND`ing.
- ///
- /// builder.where("name", .equal, SQLLiteral.string("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func `where`(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.where(SQLIdentifier(lhs), op, rhs)
+ public func `where`(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.where(SQLColumn(lhs), op, rhs)
}
/// Adds a column to expression comparison to this builder's `WHERE` clause by `AND`ing.
- ///
- /// builder.where(SQLIdentifier("name"), .equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.where(SQLColumn(lhs), op, rhs)
+ public func `where`(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.where(SQLColumn(lhs), op, rhs)
}
/// Adds an expression to expression comparison to this builder's `WHERE` clause by `AND`ing.
- ///
- /// builder.where(SQLColumn("name"), .equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func `where`(_ lhs: SQLExpression, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.where(lhs, op as SQLExpression, rhs)
+ public func `where`(_ lhs: any SQLExpression, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.where(lhs, op as any SQLExpression, rhs)
}
/// Adds an expression to expression comparison with arbitrary operator expression to this
/// builder's `WHERE` clause by `AND`ing.
- ///
- /// builder.where(SQLColumn("name"), SQLBinaryOperator.equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Operator expression.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func `where`(_ lhs: SQLExpression, _ op: SQLExpression, _ rhs: SQLExpression) -> Self {
- return self.where(SQLBinaryExpression(left: lhs, op: op, right: rhs))
+ public func `where`(_ lhs: any SQLExpression, _ op: any SQLExpression, _ rhs: any SQLExpression) -> Self {
+ self.where(SQLBinaryExpression(left: lhs, op: op, right: rhs))
}
/// Adds an expression to this builder's `WHERE` clause by `AND`ing.
- ///
- /// builder.where(SQLBinaryOperation(SQLColumn("name"), SQLBinaryOperator.notEqual, SQLLiteral.null))
- ///
- /// - Parameter expression: Expression to be added to the predicate.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func `where`(_ expression: SQLExpression) -> Self {
+ public func `where`(_ expression: any SQLExpression) -> Self {
if let existing = self.predicate {
- self.predicate = SQLBinaryExpression(
- left: existing,
- op: SQLBinaryOperator.and,
- right: expression
- )
+ self.predicate = SQLBinaryExpression(left: existing, op: SQLBinaryOperator.and, right: expression)
} else {
self.predicate = expression
}
@@ -170,117 +108,73 @@ extension SQLPredicateBuilder {
extension SQLPredicateBuilder {
/// Adds a column to column comparison to this builder's `WHERE` clause by `OR`ing.
///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ /// builder.where(SQLLiteral.boolean(false)).orWhere("firstName", .equal, column: "lastName")
+ ///
+ /// This method compares two _columns_.
+ ///
+ /// SELECT * FROM "users" WHERE 0 OR "firstName" = "lastName"
+ @inlinable
@discardableResult
public func orWhere(_ lhs: String, _ op: SQLBinaryOperator, column rhs: String) -> Self {
- return self.orWhere(SQLIdentifier(lhs), op, column: SQLIdentifier(rhs))
+ self.orWhere(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to column comparison to this builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column identifier.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, column rhs: SQLIdentifier) -> Self {
- return self.orWhere(SQLColumn(lhs), op, SQLColumn(rhs))
+ self.orWhere(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to encodable comparison to this builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed `Encodable` value.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self
- where E: Encodable
- {
- return self.orWhere(SQLColumn(lhs), op, SQLBind(rhs))
+ public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self {
+ self.orWhere(SQLColumn(lhs), op, SQLBind(rhs))
}
/// Adds a column to encodable array comparison to this builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed array of `Encodable` values.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self
- where E: Encodable
- {
- return self.orWhere(SQLColumn(lhs), op, SQLBind.group(rhs))
+ public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self {
+ self.orWhere(SQLColumn(lhs), op, SQLBind.group(rhs))
}
/// Adds a column to expression comparison to the `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orWhere(SQLIdentifier(lhs), op, rhs)
+ public func orWhere(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orWhere(SQLIdentifier(lhs), op, rhs)
}
/// Adds a column to expression comparison to the `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orWhere(SQLColumn(lhs), op, rhs)
+ public func orWhere(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orWhere(SQLColumn(lhs), op, rhs)
}
/// Adds an expression to expression comparison to this builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: SQLExpression, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orWhere(lhs, op as SQLExpression, rhs)
+ public func orWhere(_ lhs: any SQLExpression, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orWhere(lhs, op as any SQLExpression, rhs)
}
/// Adds an expression to expression comparison with arbitrary operator expression to this
/// builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Operator expression.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ lhs: SQLExpression, _ op: SQLExpression, _ rhs: SQLExpression) -> Self {
- return self.orWhere(SQLBinaryExpression(left: lhs, op: op, right: rhs))
+ public func orWhere(_ lhs: any SQLExpression, _ op: any SQLExpression, _ rhs: any SQLExpression) -> Self {
+ self.orWhere(SQLBinaryExpression(left: lhs, op: op, right: rhs))
}
/// Adds an expression to this builder's `WHERE` clause by `OR`ing.
- ///
- /// - Parameter expression: Expression to be added to the predicate.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orWhere(_ expression: SQLExpression) -> Self {
+ public func orWhere(_ expression: any SQLExpression) -> Self {
if let existing = self.predicate {
- self.predicate = SQLBinaryExpression(
- left: existing,
- op: SQLBinaryOperator.or,
- right: expression
- )
+ self.predicate = SQLBinaryExpression(left: existing, op: SQLBinaryOperator.or, right: expression)
} else {
self.predicate = expression
}
diff --git a/Sources/SQLKit/Builders/SQLPredicateGroupBuilder.swift b/Sources/SQLKit/Builders/SQLPredicateGroupBuilder.swift
index 6d237208..abea203d 100644
--- a/Sources/SQLKit/Builders/SQLPredicateGroupBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLPredicateGroupBuilder.swift
@@ -1,32 +1,28 @@
-/// Nested `SQLPredicateBuilder` for building expression groups.
-///
-/// builder.where(\Planet.type == .smallRocky).where {
-/// $0.where(\Planet.name == "Earth").orWhere(\Planet.name == "Mars")
-/// }
-///
+/// Nested ``SQLPredicateBuilder`` for building expression groups.
public final class SQLPredicateGroupBuilder: SQLPredicateBuilder {
- /// See `SQLPredicateBuilder`.
- public var predicate: SQLExpression?
+ /// See ``SQLPredicateBuilder/predicate``.
+ public var predicate: (any SQLExpression)?
- /// Creates a new `SQLPredicateGroupBuilder`.
- internal init() { }
+ /// Create a new ``SQLPredicateGroupBuilder``.
+ @usableFromInline
+ init() {}
}
extension SQLPredicateBuilder {
- /// Builds a grouped `WHERE` expression.
+ /// Builds a grouped `WHERE` expression by conjunction (`AND`).
///
- /// builder.where(\Planet.type == .smallRocky).where {
- /// $0.where(\Planet.name == "Earth").orWhere(\Planet.name == "Mars")
+ /// builder.where("type", .equal, PlanetType.smallRocky).where {
+ /// $0.where("name", .equal, "Earth").orWhere("name", .equal, "Mars")
/// }
///
/// The above code would result in the following SQL.
///
/// WHERE "type" = "smallRocky" AND ("name" = "Earth" OR "name" = "Mars")
- ///
+ @inlinable
@discardableResult
- public func `where`(group: (SQLPredicateGroupBuilder) -> (SQLPredicateGroupBuilder)) -> Self {
+ public func `where`(group: (SQLPredicateGroupBuilder) throws -> (SQLPredicateGroupBuilder)) rethrows -> Self {
let builder = SQLPredicateGroupBuilder()
- _ = group(builder)
+ _ = try group(builder)
if let sub = builder.predicate {
return self.where(SQLGroupExpression(sub))
} else {
@@ -34,20 +30,20 @@ extension SQLPredicateBuilder {
}
}
- /// Builds a grouped `WHERE` expression.
+ /// Builds a grouped `WHERE` expression by disjunction (`OR`).
///
- /// builder.where(\Planet.name == "Jupiter").orWhere {
- /// $0.where(\Planet.name == "Earth").where(\Planet.type == .smallRocky)
+ /// builder.where("name", .equal, "Jupiter").orWhere {
+ /// $0.where("name", .equal, "Earth").where("type", .equal, PlanetType.smallRocky)
/// }
///
/// The above code would result in the following SQL.
///
/// WHERE "name" = "Jupiter" OR ("name" = "Earth" AND "type" = "smallRocky")
- ///
+ @inlinable
@discardableResult
- public func orWhere(group: (SQLPredicateGroupBuilder) -> (SQLPredicateGroupBuilder)) -> Self {
+ public func orWhere(group: (SQLPredicateGroupBuilder) throws -> (SQLPredicateGroupBuilder)) rethrows -> Self {
let builder = SQLPredicateGroupBuilder()
- _ = group(builder)
+ _ = try group(builder)
if let sub = builder.predicate {
return self.orWhere(SQLGroupExpression(sub))
} else {
diff --git a/Sources/SQLKit/Builders/SQLQueryBuilder.swift b/Sources/SQLKit/Builders/SQLQueryBuilder.swift
index 6a8d4a01..f383155b 100644
--- a/Sources/SQLKit/Builders/SQLQueryBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLQueryBuilder.swift
@@ -1,26 +1,21 @@
-import NIO
+import NIOCore
-/// Builds queries and executes them on a connection.
-///
-/// builder.run()
-///
+/// Base definitions for builders which set up queries and executes them on a connection.
public protocol SQLQueryBuilder: AnyObject {
/// Query being built.
- var query: SQLExpression { get }
+ var query: any SQLExpression { get }
/// Connection to execute query on.
- var database: SQLDatabase { get }
+ var database: any SQLDatabase { get }
+ /// Execute the query on the connection, ignoring any results.
func run() -> EventLoopFuture
}
extension SQLQueryBuilder {
- /// Runs the query.
- ///
- /// builder.run()
- ///
- /// - returns: A future signaling completion.
+ /// Execute the query on the connection, ignoring any results.
+ @inlinable
public func run() -> EventLoopFuture {
- return self.database.execute(sql: self.query) { _ in }
+ self.database.execute(sql: self.query) { _ in }
}
}
diff --git a/Sources/SQLKit/Builders/SQLQueryFetcher.swift b/Sources/SQLKit/Builders/SQLQueryFetcher.swift
index b7117da4..0c24187c 100644
--- a/Sources/SQLKit/Builders/SQLQueryFetcher.swift
+++ b/Sources/SQLKit/Builders/SQLQueryFetcher.swift
@@ -1,39 +1,31 @@
-import NIO
+import NIOCore
-/// A `SQLQueryBuilder` that supports decoding results.
-///
-/// builder.all(decoding: Planet.self)
-///
-public protocol SQLQueryFetcher: SQLQueryBuilder { }
+/// Common definitions for ``SQLQueryBuilder``s which support decoding results.
+public protocol SQLQueryFetcher: SQLQueryBuilder {}
extension SQLQueryFetcher {
// MARK: First
- public func first(decoding: D.Type) -> EventLoopFuture
- where D: Decodable
- {
+ /// Returns the first output row, if any, decoded as a given type.
+ public func first(decoding: D.Type) -> EventLoopFuture {
self.first().flatMapThrowing {
- guard let row = $0 else {
- return nil
- }
- return try row.decode(model: D.self)
+ try $0?.decode(model: D.self)
}
}
- /// Collects the first raw output and returns it.
- ///
- /// builder.first()
- ///
- public func first() -> EventLoopFuture {
- return self.all().map { $0.first }
+ /// Returns the first output row, if any.
+ public func first() -> EventLoopFuture<(any SQLRow)?> {
+ if let partialBuilder = self as? (any SQLPartialResultBuilder & SQLQueryFetcher) {
+ return partialBuilder.limit(1).all().map(\.first)
+ } else {
+ return self.all().map(\.first)
+ }
}
// MARK: All
-
- public func all(decoding: D.Type) -> EventLoopFuture<[D]>
- where D: Decodable
- {
+ /// Returns all output rows, if any, decoded as a given type.
+ public func all(decoding: D.Type) -> EventLoopFuture<[D]> {
self.all().flatMapThrowing {
try $0.map {
try $0.decode(model: D.self)
@@ -42,11 +34,8 @@ extension SQLQueryFetcher {
}
/// Collects all raw output into an array and returns it.
- ///
- /// builder.all()
- ///
- public func all() -> EventLoopFuture<[SQLRow]> {
- var all: [SQLRow] = []
+ public func all() -> EventLoopFuture<[any SQLRow]> {
+ var all: [any SQLRow] = []
return self.run { row in
all.append(row)
}.map { all }
@@ -54,28 +43,14 @@ extension SQLQueryFetcher {
// MARK: Run
-
- public func run(decoding: D.Type, _ handler: @escaping (Result) -> ()) -> EventLoopFuture
- where D: Decodable
- {
- self.run {
- do {
- try handler(.success($0.decode(model: D.self)))
- } catch {
- handler(.failure(error))
- }
- }
+ /// Executes the query, decoding each output row as a given type and calling a provided handler with the result.
+ public func run(decoding: D.Type, _ handler: @escaping (Result) -> ()) -> EventLoopFuture {
+ self.run { row in handler(Result { try row.decode(model: D.self) }) }
}
-
/// Runs the query, passing output to the supplied closure as it is recieved.
- ///
- /// builder.run { print($0) }
- ///
- /// The returned future will signal completion of the query.
- public func run(_ handler: @escaping (SQLRow) -> ()) -> EventLoopFuture {
- return self.database.execute(sql: self.query) { row in
- handler(row)
- }
+ /// The returned future signals completion of the query.
+ public func run(_ handler: @escaping (any SQLRow) -> ()) -> EventLoopFuture {
+ self.database.execute(sql: self.query, handler)
}
}
diff --git a/Sources/SQLKit/Builders/SQLRawBuilder.swift b/Sources/SQLKit/Builders/SQLRawBuilder.swift
index d5e7d9a0..023546c5 100644
--- a/Sources/SQLKit/Builders/SQLRawBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLRawBuilder.swift
@@ -1,38 +1,32 @@
/// Builds raw SQL queries.
///
-/// db.raw("SELECT * FROM planets WHERE name = \(bind: "Earth")")
+/// db.raw("SELECT \(SQLLiteral.all) FROM \(ident: "planets") WHERE \(ident: "name") = \(bind: "Earth")")
/// .all(decoding: Planet.self)
-///
public final class SQLRawBuilder: SQLQueryBuilder, SQLQueryFetcher {
/// Raw query being built.
+ @usableFromInline
var sql: SQLQueryString
- /// See `SQLQueryBuilder`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- /// See `SQLQueryBuilder`.
- public var query: SQLExpression {
- return self.sql
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.sql
}
- /// Creates a new `SQLRawBuilder`.
- public init(_ sql: SQLQueryString, on database: SQLDatabase) {
+ /// Create a new ``SQLRawBuilder``.
+ @inlinable
+ public init(_ sql: SQLQueryString, on database: any SQLDatabase) {
self.database = database
self.sql = sql
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLRawBuilder`.
- ///
- /// db.raw("SELECT * FROM ...")...
- ///
- /// - parameters:
- /// - sql: The SQL query string.
- /// - returns: `SQLRawBuilder`.
+ /// Create a new ``SQLRawBuilder``.
public func raw(_ sql: SQLQueryString) -> SQLRawBuilder {
- return .init(sql, on: self)
+ .init(sql, on: self)
}
}
diff --git a/Sources/SQLKit/Builders/SQLReturningBuilder.swift b/Sources/SQLKit/Builders/SQLReturningBuilder.swift
index ea755364..3f27015b 100644
--- a/Sources/SQLKit/Builders/SQLReturningBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLReturningBuilder.swift
@@ -1,60 +1,65 @@
+/// Common definitions for any query builder which can return values from data-mutating queries.
public protocol SQLReturningBuilder: SQLQueryBuilder {
+ /// The specification of what the query should return, if any.
var returning: SQLReturning? { get set }
}
extension SQLReturningBuilder {
/// Specify a list of columns to be part of the result set of the query.
- /// Each provided name is a string assumed to be a valid SQL identifier and
- /// is not qualified.
///
- /// - parameters:
- /// - columns: The names of the columns to return.
- /// - returns: `SQLReturningResultBuilder` to complete the chain.
+ /// - Returns: A ``SQLReturningResultBuilder`` which must be used to execute the query.
+ @inlinable
public func returning(_ columns: String...) -> SQLReturningResultBuilder {
- let sqlColumns = columns.map { (column) -> SQLColumn in
- if column == "*" {
- return SQLColumn(SQLLiteral.all)
- } else {
- return SQLColumn(column)
- }
- }
-
- self.returning = .init(sqlColumns)
- return SQLReturningResultBuilder(self)
+ self.returning(columns.map { SQLColumn($0 == "*" ? SQLLiteral.all : SQLIdentifier($0)) })
}
/// Specify a list of columns to be returned as the result of the query.
- /// Each input is an arbitrary expression.
///
- /// - parameters:
- /// - columns: A list of expressions identifying the columns to return.
- /// - returns: `SQLReturningResultBuilder` to complete the chain.
- public func returning(_ columns: SQLExpression...) -> SQLReturningResultBuilder {
- self.returning = .init(columns)
- return SQLReturningResultBuilder(self)
+ /// - Returns: A ``SQLReturningResultBuilder`` which must be used to execute the query.
+ @inlinable
+ public func returning(_ columns: any SQLExpression...) -> SQLReturningResultBuilder {
+ self.returning(columns)
}
/// Specify a list of columns to be returned as the result of the query.
- /// Each input is an arbitrary expression.
///
- /// - parameters:
- /// - column: An array of expressions identifying the columns to return.
- /// - returns: `SQLReturningResultBuilder` to complete the chain.
- public func returning(_ columns: [SQLExpression]) -> SQLReturningResultBuilder {
+ /// - Returns: A ``SQLReturningResultBuilder`` which must be used to execute the query.
+ @inlinable
+ public func returning(_ columns: [any SQLExpression]) -> SQLReturningResultBuilder {
self.returning = .init(columns)
return SQLReturningResultBuilder(self)
}
}
-/// Return type from `SQLReturningBuilder` methods which allows `SQLQueryFetcher` calls
-/// such as `first()` and `all()`. Therefore `returning(...)` must be the second last method
-/// in the query chain.
-public final class SQLReturningResultBuilder: SQLQueryFetcher {
- public var query: SQLExpression
- public var database: SQLDatabase
+/// A builder returned from the methods of ``SQLReturningBuilder``; this builder wraps the original
+/// builder with one which provides ``SQLQueryFetcher`` conformance. As such, the
+/// ``SQLReturningBuilder/returning(_:)-84avj`` methods always be the last ones in any call chain.
+///
+/// Example:
+///
+/// // Correct:
+/// db.insert(into: "foo").model(foo).returning("id").first() // Returns a row containing an "id" column
+///
+/// // Incorrect:
+/// db.insert(into: "foo").returning("id").model(foo).first() // Syntax error
+///
+/// - Note: The only reason we can't make ``SQLReturningResultBuilder`` conditionally conform to the
+/// other builder protocols and thus remove the "last-in-chain" restriction is that it has historically
+/// exposed its ``query`` and ``database`` properties as both mutable and public, whereas they are
+/// get-only in the ``SQLQueryBuilder`` protocol. As a result, we cannot simply store the original
+/// builder instead, because users may have been leveraging the ability to modify the query and/or
+/// database, whereas those mutations could not be applied to the original builder. An unfortunate
+/// example of Hyrum's Law, that.
+public final class SQLReturningResultBuilder: SQLQueryFetcher {
+ /// See ``SQLQueryBuilder/query``.
+ public var query: any SQLExpression
+
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
+ @usableFromInline
init(_ builder: QueryBuilder) {
- query = builder.query
- database = builder.database
+ self.query = builder.query
+ self.database = builder.database
}
}
diff --git a/Sources/SQLKit/Builders/SQLSecondaryPredicateBuilder.swift b/Sources/SQLKit/Builders/SQLSecondaryPredicateBuilder.swift
index 870318e5..b4f259bd 100644
--- a/Sources/SQLKit/Builders/SQLSecondaryPredicateBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLSecondaryPredicateBuilder.swift
@@ -1,15 +1,15 @@
-/// Builds secondary `SQLExpression` predicates, i.e., `HAVING` clauses.
+/// Common definitions for any query builder which permits specifying a secondary predicate.
///
-/// builder.having("name", .equal, "Earth")
-///
-/// Expressions will be added using `AND` logic by default. Use `orHaving` to join via `OR` logic.
+/// A "secondary predicate" is a `HAVING` clause on a query using `GROUP BY`.
///
-/// builder.having("name", .equal, "Earth").orHaving("name", .equal, "Mars")
+/// builder.having("name", .equal, "Earth")
///
-/// See `SQLSecondaryPredicateGroupBuilder` for building expression groups.
+/// Expressions specified with ``having(_:)`` are considered conjunctive (`AND`).
+/// Expressions specified with ``orHaving(_:)`` are considered inclusively disjunctive (`OR`).
+/// See ``SQLSecondaryPredicateGroupBuilder`` for details of grouping expressions (i.e. with parenthesis).
public protocol SQLSecondaryPredicateBuilder: AnyObject {
/// Expression being built.
- var secondaryPredicate: SQLExpression? { get set }
+ var secondaryPredicate: (any SQLExpression)? { get set }
}
extension SQLSecondaryPredicateBuilder {
@@ -20,15 +20,10 @@ extension SQLSecondaryPredicateBuilder {
/// This method compares two _columns_.
///
/// SELECT * FROM "users" HAVING "firstName" = "lastName"
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func having(_ lhs: String, _ op: SQLBinaryOperator, column rhs: String) -> Self {
- return self.having(SQLIdentifier(lhs), op, SQLIdentifier(rhs))
+ self.having(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to column comparison to this builder's `HAVING` clause by `AND`ing.
@@ -38,15 +33,10 @@ extension SQLSecondaryPredicateBuilder {
/// This method compares two _columns_.
///
/// SELECT * FROM "users" HAVING "firstName" = "lastName"
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, column rhs: SQLIdentifier) -> Self {
- return self.having(SQLColumn(lhs), op, SQLColumn(rhs))
+ self.having(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to encodable comparison to this builder's `HAVING` clause by `AND`ing.
@@ -55,16 +45,11 @@ extension SQLSecondaryPredicateBuilder {
///
/// The encodable value supplied will be bound to the query as a parameter.
///
- /// SELECT * FROM "planets" HAVING "name" = ? // Earth
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Type-nonspecific `Encodable` value.
- /// - Returns: `self` for chaining.
+ /// SELECT * FROM "planets" HAVING "name" = $0 ["Earth"]
+ @inlinable
@discardableResult
@_disfavoredOverload // try to prefer the generic version
- public func having(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: Encodable) -> Self {
+ public func having(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any Encodable) -> Self {
return self.having(SQLColumn(lhs), op, SQLBind(rhs))
}
@@ -74,18 +59,11 @@ extension SQLSecondaryPredicateBuilder {
///
/// The encodable value supplied will be bound to the query as a parameter.
///
- /// SELECT * FROM "planets" HAVING "name" = ? // Earth
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed `Encodable` value.
- /// - Returns: `self` for chaining.
+ /// SELECT * FROM "planets" HAVING "name" = $0 ["Earth"]
+ @inlinable
@discardableResult
- public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self
- where E: Encodable
- {
- return self.having(SQLColumn(lhs), op, SQLBind(rhs))
+ public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self {
+ self.having(SQLColumn(lhs), op, SQLBind(rhs))
}
/// Adds a column to encodable array comparison to this builder's `HAVING` clause by `AND`ing.
@@ -94,91 +72,48 @@ extension SQLSecondaryPredicateBuilder {
///
/// The encodable values supplied will be bound to the query as parameters.
///
- /// SELECT * FROM "planets" HAVING "name" IN (?, ?) // Earth, Mars
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed array of `Encodable` values.
- /// - Returns: `self` for chaining.
+ /// SELECT * FROM "planets" HAVING "name" IN ($0, $1) ["Earth", "Mars"]
+ @inlinable
@discardableResult
- public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self
- where E: Encodable
- {
- return self.having(SQLColumn(lhs), op, SQLBind.group(rhs))
+ public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self {
+ self.having(SQLColumn(lhs), op, SQLBind.group(rhs))
}
/// Adds a column to expression comparison to this builder' `HAVING` clause by `AND`ing.
- ///
- /// builder.having("name", .equal, SQLLiteral.string("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func having(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.having(SQLIdentifier(lhs), op, rhs)
+ public func having(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.having(SQLColumn(lhs), op, rhs)
}
/// Adds a column to expression comparison to this builder's `HAVING` clause by `AND`ing.
- ///
- /// builder.having(SQLIdentifier("name"), .equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.having(SQLColumn(lhs), op, rhs)
+ public func having(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.having(SQLColumn(lhs), op, rhs)
}
/// Adds an expression to expression comparison to this builder's `HAVING` clause by `AND`ing.
- ///
- /// builder.having(SQLColumn("name"), .equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func having(_ lhs: SQLExpression, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.having(lhs, op as SQLExpression, rhs)
+ public func having(_ lhs: any SQLExpression, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.having(lhs, op as any SQLExpression, rhs)
}
/// Adds an expression to expression comparison with arbitrary operator expression to this
/// builder's `HAVING` clause by `AND`ing.
- ///
- /// builder.having(SQLColumn("name"), SQLBinaryOperator.equal, SQLBind("Earth"))
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Operator expression.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func having(_ lhs: SQLExpression, _ op: SQLExpression, _ rhs: SQLExpression) -> Self {
- return self.having(SQLBinaryExpression(left: lhs, op: op, right: rhs))
+ public func having(_ lhs: any SQLExpression, _ op: any SQLExpression, _ rhs: any SQLExpression) -> Self {
+ self.having(SQLBinaryExpression(left: lhs, op: op, right: rhs))
}
/// Adds an expression to this builder's `HAVING` clause by `AND`ing.
- ///
- /// builder.having(SQLBinaryOperation(SQLColumn("name"), SQLBinaryOperator.notEqual, SQLLiteral.null))
- ///
- /// - Parameter expression: Expression to be added to the predicate.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func having(_ expression: SQLExpression) -> Self {
+ public func having(_ expression: any SQLExpression) -> Self {
if let existing = self.secondaryPredicate {
- self.secondaryPredicate = SQLBinaryExpression(
- left: existing,
- op: SQLBinaryOperator.and,
- right: expression
- )
+ self.secondaryPredicate = SQLBinaryExpression(left: existing, op: SQLBinaryOperator.and, right: expression)
} else {
self.secondaryPredicate = expression
}
@@ -189,130 +124,81 @@ extension SQLSecondaryPredicateBuilder {
extension SQLSecondaryPredicateBuilder {
/// Adds a column to column comparison to this builder's `HAVING` clause by `OR`ing.
///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column name.
- /// - Returns: `self` for chaining.
+ /// builder.having(SQLLiteral.boolean(false)).orHaving("firstName", .equal, column: "lastName")
+ ///
+ /// This method compares two _columns_.
+ ///
+ /// SELECT * FROM "users" HAVING 0 OR "firstName" = "lastName"
+ @inlinable
@discardableResult
public func orHaving(_ lhs: String, _ op: SQLBinaryOperator, column rhs: String) -> Self {
- return self.orHaving(SQLIdentifier(lhs), op, column: SQLIdentifier(rhs))
+ self.orHaving(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to column comparison to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side column identifier.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, column rhs: SQLIdentifier) -> Self {
- return self.orHaving(SQLColumn(lhs), op, SQLColumn(rhs))
+ self.orHaving(SQLColumn(lhs), op, SQLColumn(rhs))
}
/// Adds a column to encodable comparison to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Type-nonspecific `Encodable` value.
- /// - Returns: Self for chaining.
+ @inlinable
@discardableResult
@_disfavoredOverload // try to prefer the generic version
- public func orHaving(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: Encodable) -> Self {
- return self.orHaving(SQLColumn(lhs), op, SQLBind(rhs))
+ public func orHaving(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any Encodable) -> Self {
+ self.orHaving(SQLColumn(lhs), op, SQLBind(rhs))
}
/// Adds a column to encodable comparison to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed `Encodable` value.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self
- where E: Encodable
- {
- return self.orHaving(SQLColumn(lhs), op, SQLBind(rhs))
+ public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: E) -> Self {
+ self.orHaving(SQLColumn(lhs), op, SQLBind(rhs))
}
/// Adds a column to encodable array comparison to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Typed array of `Encodable` values.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self
- where E: Encodable
- {
- return self.orHaving(SQLColumn(lhs), op, SQLBind.group(rhs))
+ public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: [E]) -> Self {
+ self.orHaving(SQLColumn(lhs), op, SQLBind.group(rhs))
}
/// Adds a column to expression comparison to the `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column name.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orHaving(SQLIdentifier(lhs), op, rhs)
+ public func orHaving(_ lhs: String, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orHaving(SQLColumn(lhs), op, rhs)
}
/// Adds a column to expression comparison to the `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side column identifier.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orHaving(SQLColumn(lhs), op, rhs)
+ public func orHaving(_ lhs: SQLIdentifier, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orHaving(SQLColumn(lhs), op, rhs)
}
/// Adds an expression to expression comparison to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Binary operator to use for comparison.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: SQLExpression, _ op: SQLBinaryOperator, _ rhs: SQLExpression) -> Self {
- return self.orHaving(lhs, op as SQLExpression, rhs)
+ public func orHaving(_ lhs: any SQLExpression, _ op: SQLBinaryOperator, _ rhs: any SQLExpression) -> Self {
+ self.orHaving(lhs, op as any SQLExpression, rhs)
}
/// Adds an expression to expression comparison with arbitrary operator expression to this
/// builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameters:
- /// - lhs: Left-hand side expression.
- /// - op: Operator expression.
- /// - rhs: Right-hand side expression.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ lhs: SQLExpression, _ op: SQLExpression, _ rhs: SQLExpression) -> Self {
- return self.orHaving(SQLBinaryExpression(left: lhs, op: op, right: rhs))
+ public func orHaving(_ lhs: any SQLExpression, _ op: any SQLExpression, _ rhs: any SQLExpression) -> Self {
+ self.orHaving(SQLBinaryExpression(left: lhs, op: op, right: rhs))
}
/// Adds an expression to this builder's `HAVING` clause by `OR`ing.
- ///
- /// - Parameter expression: Expression to be added to the predicate.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func orHaving(_ expression: SQLExpression) -> Self {
+ public func orHaving(_ expression: any SQLExpression) -> Self {
if let existing = self.secondaryPredicate {
- self.secondaryPredicate = SQLBinaryExpression(
- left: existing,
- op: SQLBinaryOperator.or,
- right: expression
- )
+ self.secondaryPredicate = SQLBinaryExpression(left: existing, op: SQLBinaryOperator.or, right: expression)
} else {
self.secondaryPredicate = expression
}
diff --git a/Sources/SQLKit/Builders/SQLSecondaryPredicateGroupBuilder.swift b/Sources/SQLKit/Builders/SQLSecondaryPredicateGroupBuilder.swift
index 6a499695..623ada5b 100644
--- a/Sources/SQLKit/Builders/SQLSecondaryPredicateGroupBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLSecondaryPredicateGroupBuilder.swift
@@ -1,41 +1,28 @@
-/// Nested `SQLSecondaryPredicateBuilder` for building expression groups.
-///
-/// ```swift
-/// builder.having("type", .equal, .smallRocky).having {
-/// $0.having("name", .equal, "Earth")
-/// .orHaving("name", .equal, "Mars")
-/// }
-/// ```
+/// Nested ``SQLSecondaryPredicateBuilder`` for building expression groups.
public final class SQLSecondaryPredicateGroupBuilder: SQLSecondaryPredicateBuilder {
- // See `SQLSecondaryPredicateBuilder.secondaryPredicate`.
- public var secondaryPredicate: SQLExpression?
+ /// See ``SQLSecondaryPredicateBuilder/secondaryPredicate``.
+ public var secondaryPredicate: (any SQLExpression)?
- /// Creates a new `SQLSecondaryPredicateGroupBuilder`.
- internal init() { }
+ /// Create a new ``SQLSecondaryPredicateGroupBuilder``.
+ @usableFromInline
+ init() {}
}
extension SQLSecondaryPredicateBuilder {
- /// Builds a grouped `HAVING` expression by conjunction ('AND').
+ /// Builds a grouped `HAVING` expression by conjunction (`AND`).
///
- /// The following expression:
+ /// builder.having("type", .equal, .smallRocky).having {
+ /// $0.having("name", .equal, "Earth").orHaving("name", .equal, "Mars")
+ /// }
///
- /// ```swift
- /// builder.having("type", .equal, .smallRocky).having {
- /// $0.having("name", .equal, "Earth")
- /// .orHaving("name", .equal, "Mars")
- /// }
- /// ```
+ /// The above code would result in the following SQL.
///
- /// ... will result in SQL similar to:
- ///
- /// ```sql
- /// HAVING "type" = 'smallRocky' AND
- /// ("name" = 'Earth' OR "name" = 'Mars')
- /// ```
+ /// HAVING "type" = "smallRocky" AND ("name" = "Earth" OR "name" = "Mars")
+ @inlinable
@discardableResult
- public func having(group: (SQLSecondaryPredicateGroupBuilder) -> (SQLSecondaryPredicateGroupBuilder)) -> Self {
+ public func having(group: (SQLSecondaryPredicateGroupBuilder) throws -> (SQLSecondaryPredicateGroupBuilder)) rethrows -> Self {
let builder = SQLSecondaryPredicateGroupBuilder()
- _ = group(builder)
+ _ = try group(builder)
if let sub = builder.secondaryPredicate {
return self.having(SQLGroupExpression(sub))
} else {
@@ -45,25 +32,18 @@ extension SQLSecondaryPredicateBuilder {
/// Builds a grouped `HAVING` expression by disjunction ('OR').
///
- /// The following expression:
- ///
- /// ```swift
- /// builder.having("name", .equal, "Jupiter").orHaving {
- /// $0.having("name", .equal, "Earth")
- /// .having("type", .equal, .smallRocky)
- /// }
- /// ```
+ /// builder.having("name", .equal, "Jupiter").orHaving {
+ /// $0.having("name", .equal, "Earth").having("type", .equal, PlanetType.smallRocky)
+ /// }
///
- /// ... will result in SQL similar to:
+ /// The above code would result in the following SQL.
///
- /// ```sql
- /// HAVING "name" = 'Jupiter' OR
- /// ("name" = 'Earth' AND "type" = 'smallRocky')
- /// ```
+ /// HAVING "name" = "Jupiter" OR ("name" = "Earth" AND "type" = "smallRocky")
+ @inlinable
@discardableResult
- public func orHaving(group: (SQLSecondaryPredicateGroupBuilder) -> (SQLSecondaryPredicateGroupBuilder)) -> Self {
+ public func orHaving(group: (SQLSecondaryPredicateGroupBuilder) throws -> (SQLSecondaryPredicateGroupBuilder)) rethrows -> Self {
let builder = SQLSecondaryPredicateGroupBuilder()
- _ = group(builder)
+ _ = try group(builder)
if let sub = builder.secondaryPredicate {
return self.orHaving(SQLGroupExpression(sub))
} else {
diff --git a/Sources/SQLKit/Builders/SQLSelectBuilder.swift b/Sources/SQLKit/Builders/SQLSelectBuilder.swift
index d9bb162e..fce54a62 100644
--- a/Sources/SQLKit/Builders/SQLSelectBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLSelectBuilder.swift
@@ -1,73 +1,34 @@
-extension SQLDatabase {
- /// Creates a new `SQLSelectBuilder`.
- ///
- /// ```sql
- /// db.select()
- /// .column("*")
- /// .from("planets")
- /// .where("name", .equal, SQLBind("Earth"))
- /// .all()
- /// ```
- public func select() -> SQLSelectBuilder {
- return .init(on: self)
- }
-}
-
-/// A builder for constructing, executing, and retrieving results from `SELECT` queries.
-public final class SQLSelectBuilder: SQLQueryFetcher, SQLQueryBuilder, SQLSubqueryClauseBuilder {
- // See `SQLQueryBuilder.query`.
- public var query: SQLExpression {
- return self.select
- }
-
- // See `SQLSubqueryClauseBuilder.select`.
+/// Builds ``SQLSelect`` queries.
+///
+/// - Note: This is effectively nothing but a concrete conformance to ``SQLSubqueryClauseBuilder``
+/// which provides storage for the ``SQLSelect`` and adds ``SQLQueryFetcher`` so the query can
+/// actually be executed.
+public final class SQLSelectBuilder: SQLQueryBuilder, SQLQueryFetcher, SQLSubqueryClauseBuilder {
+ /// See ``SQLSubqueryClauseBuilder/select``.
public var select: SQLSelect
- // See `SQLQueryBuilder.database`.
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
+
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.select
+ }
- /// Create a new `SQLSelectBuilder` on the given database.
- public init(on database: SQLDatabase) {
+ /// Create a new ``SQLSelectBuilder``.
+ @inlinable
+ public init(on database: any SQLDatabase) {
self.select = .init()
self.database = database
}
}
-// - MARK: Additional distinct clauses
-
-extension SQLSelectBuilder {
- /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
- /// overwriting any previously specified columns.
- ///
- /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
- ///
- /// - Returns: `self` for chaining.
- @discardableResult
- public func distinct(on column: String, _ columns: String...) -> Self {
- return self.distinct(on: ([column] + columns).map(SQLIdentifier.init(_:)))
- }
-
- /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
- /// overwriting any previously specified columns.
- ///
- /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
- ///
- /// - Returns: `self` for chaining.
- @discardableResult
- public func distinct(on column: SQLExpression, _ columns: SQLExpression...) -> Self {
- return self.distinct(on: [column] + columns)
- }
-
- /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
- /// overwriting any previously specified columns.
- ///
- /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
- ///
- /// - Returns: `self` for chaining.
- @discardableResult
- public func distinct(on columns: [SQLExpression]) -> Self {
- self.select.isDistinct = true
- self.select.columns = columns
- return self
+extension SQLDatabase {
+ /// Create a new ``SQLSelectBuilder``.
+ @inlinable
+ public func select() -> SQLSelectBuilder {
+ .init(on: self)
}
}
+
diff --git a/Sources/SQLKit/Builders/SQLSubqeryClauseBuilder.swift b/Sources/SQLKit/Builders/SQLSubqeryClauseBuilder.swift
index a81c3d12..97fa6cda 100644
--- a/Sources/SQLKit/Builders/SQLSubqeryClauseBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLSubqeryClauseBuilder.swift
@@ -9,43 +9,50 @@
///
/// - Note: The primary motivation for the existence of this protocol is to make it easier
/// to construct `SELECT` queries without specifying a database or providing the
-/// `SQLQueryBuilder` and `SQLQueryFetcher` methods in inappropriate contexts.
+/// ``SQLQueryBuilder`` and ``SQLQueryFetcher`` methods in inappropriate contexts.
public protocol SQLSubqueryClauseBuilder: SQLJoinBuilder, SQLPredicateBuilder, SQLSecondaryPredicateBuilder, SQLPartialResultBuilder {
+ /// The ``SQLSelect`` query being built.
var select: SQLSelect { get set }
}
extension SQLSubqueryClauseBuilder {
- public var joins: [SQLExpression] {
+ /// See ``SQLJoinBuilder/joins``.
+ public var joins: [any SQLExpression] {
get { self.select.joins }
set { self.select.joins = newValue }
}
}
extension SQLSubqueryClauseBuilder {
- public var predicate: SQLExpression? {
+ /// See ``SQLPredicateBuilder/predicate``.
+ public var predicate: (any SQLExpression)? {
get { return self.select.predicate }
set { self.select.predicate = newValue }
}
}
extension SQLSubqueryClauseBuilder {
- public var secondaryPredicate: SQLExpression? {
+ /// See ``SQLSecondaryPredicateBuilder/secondaryPredicate``.
+ public var secondaryPredicate: (any SQLExpression)? {
get { return self.select.having }
set { self.select.having = newValue }
}
}
extension SQLSubqueryClauseBuilder {
- public var orderBys: [SQLExpression] {
+ /// See ``SQLPartialResultBuilder/orderBys``.
+ public var orderBys: [any SQLExpression] {
get { self.select.orderBy }
set { self.select.orderBy = newValue }
}
+ /// See ``SQLPartialResultBuilder/limit``.
public var limit: Int? {
get { self.select.limit }
set { self.select.limit = newValue }
}
+ /// See ``SQLPartialResultBuilder/offset``.
public var offset: Int? {
get { self.select.offset }
set { self.select.offset = newValue }
@@ -56,134 +63,125 @@ extension SQLSubqueryClauseBuilder {
extension SQLSubqueryClauseBuilder {
/// Adds a `DISTINCT` clause to the query.
- ///
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func distinct() -> Self {
self.select.isDistinct = true
return self
}
+
+ /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
+ /// overwriting any previously specified columns.
+ ///
+ /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
+ @inlinable
+ @discardableResult
+ public func distinct(on column: String, _ columns: String...) -> Self {
+ self.distinct(on: ([column] + columns).map(SQLIdentifier.init(_:)))
+ }
+
+ /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
+ /// overwriting any previously specified columns.
+ ///
+ /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
+ @inlinable
+ @discardableResult
+ public func distinct(on column: any SQLExpression, _ columns: any SQLExpression...) -> Self {
+ self.distinct(on: [column] + columns)
+ }
+
+ /// Adds a `DISTINCT` clause to the select statement and explicitly specifies columns to select,
+ /// overwriting any previously specified columns.
+ ///
+ /// - Warning: This does _NOT_ invoke PostgreSQL's `DISTINCT ON (...)` syntax!
+ @inlinable
+ @discardableResult
+ public func distinct(on columns: [any SQLExpression]) -> Self {
+ self.select.isDistinct = true
+ self.select.columns = columns
+ return self
+ }
}
// MARK: - Columns
extension SQLSubqueryClauseBuilder {
- /// Specify a column to be part of the result set of the query. The column is a string
- /// assumed to be a valid SQL identifier and is not qualified.
+ /// Specify a column to be part of the result set of the query.
///
- /// The string `*` (a single asterisk) is recognized and replaced with `SQLLiteral.all`.
- ///
- /// - Parameter column: The name of the column to return, or `*` for all.
- /// - Returns: `self` for chaining.
+ /// The string `*` (a single asterisk) is replaced with ``SQLLiteral/all``.
+ @inlinable
@discardableResult
public func column(_ column: String) -> Self {
- if column == "*" {
- return self.column(SQLLiteral.all)
- } else {
- return self.column(SQLColumn(SQLIdentifier(column)))
- }
+ self.column(column == "*" ? SQLLiteral.all : SQLColumn(column))
}
- /// Specify a column to be part of the result set of the query. The column is a string
- /// assumed to be a valid SQL identifier, qualified by a table name, also a string assumed
- /// to be a valid SQL identifier.
+ /// Specify a column qualified with a table name to be part of the result set of the query.
///
- /// The string `*` (a single asterisk) is recognized and replaced with `SQLLiteral.all`.
- ///
- /// - Parameters:
- /// - table: The name of a table to qualify the column name.
- /// - column: The name of the column to return, or `*` for all.
- /// - Returns: `self` for chaining.
+ /// The string `*` (a single asterisk) is replaced with ``SQLLiteral/all``.
+ @inlinable
@discardableResult
public func column(table: String, column: String) -> Self {
- if column == "*" {
- return self.column(SQLColumn(SQLLiteral.all, table: SQLIdentifier(table)))
- } else {
- return self.column(SQLColumn(SQLIdentifier(column), table: SQLIdentifier(table)))
- }
+ self.column(SQLColumn(column == "*" ? SQLLiteral.all : SQLIdentifier(column), table: SQLIdentifier(table)))
}
- /// Specify a column to retrieve as a `String`, and an alias for it with another `String`.
- ///
- /// - Parameters:
- /// - column: The name of the column to return.
- /// - alias: The label to give the returned column.
- /// - Returns: `self` for chaining.
+ /// Specify a column to retrieve with an aliased name.
+ @inlinable
@discardableResult
public func column(_ column: String, as alias: String) -> Self {
- return self.column(SQLIdentifier(column), as: SQLIdentifier(alias))
+ return self.column(SQLColumn(column), as: SQLIdentifier(alias))
}
- /// Specify a column to retrieve as an `SQLExpression`, and an alias for it with a `String`.
- ///
- /// - Parameters:
- /// - column: An expression identifying the desired data to return.
- /// - alias: A string specifying the desired label of the identified data.
- /// - Returns: `self` for chaining.
+ /// Specify a column to retrieve with an aliased name.
+ @inlinable
@discardableResult
- public func column(_ column: SQLExpression, as alias: String) -> Self {
- return self.column(column, as: SQLIdentifier(alias))
+ public func column(_ column: any SQLExpression, as alias: String) -> Self {
+ self.column(column, as: SQLIdentifier(alias))
}
- /// Specify a column to retrieve as an `SQLExpression`, and an alias for it with another `SQLExpression`.
- ///
- /// - Parameters:
- /// - column: An expression identifying the desired data to return.
- /// - alias: An expression specifying the desired label of the identified data.
- /// - Returns: `self` for chaining.
+ /// Specify a column to retrieve with an aliased name.
+ @inlinable
@discardableResult
- public func column(_ column: SQLExpression, as alias: SQLExpression) -> Self {
- return self.column(SQLAlias(column, as: alias))
+ public func column(_ column: any SQLExpression, as alias: any SQLExpression) -> Self {
+ self.column(SQLAlias(column, as: alias))
}
/// Specify an arbitrary expression as a column to be part of the result set of the query.
- ///
- /// - Parameter expr: An expression identifying the desired data to return.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func column(_ expr: SQLExpression) -> Self {
+ public func column(_ expr: any SQLExpression) -> Self {
self.select.columns.append(expr)
return self
}
- /// Specify a list of columns to be part of the result set of the query. Each provided name
- /// is a string assumed to be a valid SQL identifier and is not qualified. The string `*` is
- /// recognized and replaced with `SQLLiteral.all`.
- ///
- /// - Parameter columns: The names of the columns to return.
- /// - Returns: `self` for chaining.
+ /// Specify a list of columns to be part of the result set of the query. The string `*` is
+ /// replaced with ``SQLLiteral/all``.
+ @inlinable
@discardableResult
public func columns(_ columns: String...) -> Self {
- return self.columns(columns)
+ self.columns(columns)
}
- /// Specify a list of columns to be part of the result set of the query. Each provided name
- /// is a string assumed to be a valid SQL identifier and is not qualified. The string `*` is
- /// recognized and replaced with `SQLLiteral.all`.
- ///
- /// - Parameter columns: The names of the columns to return.
- /// - Returns: `self` for chaining.
+ /// Specify a list of columns to be part of the result set of the query. The string `*` is
+ /// replaced with ``SQLLiteral/all``.
+ @inlinable
@discardableResult
public func columns(_ columns: [String]) -> Self {
- return columns.reduce(self) { $0.column($1) }
+ self.columns(columns.map { $0 == "*" ? SQLLiteral.all as any SQLExpression : SQLColumn($0) })
}
/// Specify a list of arbitrary expressions as columns to be part of the result set of the query.
- ///
- /// - Parameter columns: A list of expressions identifying the desired data to return.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func columns(_ columns: SQLExpression...) -> Self {
- return self.columns(columns)
+ public func columns(_ columns: any SQLExpression...) -> Self {
+ self.columns(columns)
}
/// Specify a list of arbitrary expressions as columns to be part of the result set of the query.
- ///
- /// - Parameter columns: A list of expressions identifying the desired data to return.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func columns(_ columns: [SQLExpression]) -> Self {
- return columns.reduce(self) { $0.column($1) }
+ public func columns(_ columns: [any SQLExpression]) -> Self {
+ self.select.columns.append(contentsOf: columns)
+ return self
}
}
@@ -191,49 +189,42 @@ extension SQLSubqueryClauseBuilder {
extension SQLSubqueryClauseBuilder {
/// Include the given table in the list of those used by the query, without performing an
- /// explicit join. The table specifier is a string assumed to be a valid SQL identifier.
+ /// explicit join.
///
/// - Parameter table: The name of the table to use.
/// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func from(_ table: String) -> Self {
- return self.from(SQLIdentifier(table))
+ self.from(SQLIdentifier(table))
}
/// Include the given table in the list of those used by the query, without performing an
- /// explicit join. The table specifier may be any expression.
+ /// explicit join.
///
/// - Parameter table: An expression identifying the table to use.
/// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func from(_ table: SQLExpression) -> Self {
+ public func from(_ table: any SQLExpression) -> Self {
self.select.tables.append(table)
return self
}
- /// Include the given table in the list of those used by the query, without performing an
- /// explicit join. An alias for the table may be provided. The table and alias specifiers
- /// are strings assumed to be valid SQL identifiers.
- ///
- /// - Parameters:
- /// - table: The name of the table to use.
- /// - alias: The alias to use for the table.
- /// - Returns: `self` for chaining.
+ /// Include the given table and an alias for it in the list of those used by the query, without
+ /// performing an explicit join.
+ @inlinable
@discardableResult
public func from(_ table: String, as alias: String) -> Self {
- return self.from(SQLIdentifier(table), as: SQLIdentifier(alias))
+ self.from(SQLIdentifier(table), as: SQLIdentifier(alias))
}
- /// Include the given table in the list of those used by the query, without performing an
- /// explicit join. The table and alias specifiers may be arbitrary expressions.
- ///
- /// - Parameters:
- /// - table: An expression identifying the table to use.
- /// - alias: An expression providing the alias to use for the table.
- /// - Returns: `self` for chaining.
+ /// Include the given table and an alias for it in the list of those used by the query, without
+ /// performing an explicit join.
+ @inlinable
@discardableResult
- public func from(_ table: SQLExpression, as alias: SQLExpression) -> Self {
- return self.from(SQLAlias(table, as: alias))
+ public func from(_ table: any SQLExpression, as alias: any SQLExpression) -> Self {
+ self.from(SQLAlias(table, as: alias))
}
}
@@ -241,20 +232,16 @@ extension SQLSubqueryClauseBuilder {
extension SQLSubqueryClauseBuilder {
/// Adds a `GROUP BY` clause to the query with the specified column.
- ///
- /// - Parameter column: Name of column to group results by. Appended to any previously added grouping expressions.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func groupBy(_ column: String) -> Self {
- return self.groupBy(SQLColumn(column))
+ self.groupBy(SQLColumn(column))
}
/// Adds a `GROUP BY` clause to the query with the specified expression.
- ///
- /// - Parameter expression: Expression to group results by. Appended to any previously added grouping expressions.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
- public func groupBy(_ expression: SQLExpression) -> Self {
+ public func groupBy(_ expression: any SQLExpression) -> Self {
self.select.groupBy.append(expression)
return self
}
@@ -277,16 +264,13 @@ extension SQLSubqueryClauseBuilder {
///
/// - Warning: If the database in use does not support locking reads, the locking clause
/// will be silently ignored regardless of its value.
- ///
- /// - Parameter lockingClause: The type of lock to obtain. See ``SQLLockingClause``.
- /// - Returns: `self` for chaining.
+ @inlinable
@discardableResult
public func `for`(_ lockingClause: SQLLockingClause) -> Self {
- return self.lockingClause(lockingClause)
+ self.lockingClause(lockingClause as any SQLExpression)
}
- /// Adds a locking clause to this query as specified by an arbitrary ``SQLExpression``.
- /// If called more than once, the last call wins.
+ /// Adds a locking clause to this query. If called more than once, the last call wins.
///
/// ```swift
/// db.select()...lockingClause(...)
@@ -298,11 +282,12 @@ extension SQLSubqueryClauseBuilder {
/// this construct.
///
/// - Note: This method allows providing an arbitrary SQL expression as the locking clause.
- ///
- /// - Parameter lockingClause: The locking clause as an SQL expression.
- /// - Returns: `self` for chaining.
+ ///
+ /// - Warning: If the database in use does not support locking reads, the locking clause
+ /// will be silently ignored regardless of its value.
+ @inlinable
@discardableResult
- public func lockingClause(_ lockingClause: SQLExpression) -> Self {
+ public func lockingClause(_ lockingClause: any SQLExpression) -> Self {
self.select.lockingClause = lockingClause
return self
}
diff --git a/Sources/SQLKit/Builders/SQLUnionBuilder.swift b/Sources/SQLKit/Builders/SQLUnionBuilder.swift
index 6290c2e7..764241f8 100644
--- a/Sources/SQLKit/Builders/SQLUnionBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLUnionBuilder.swift
@@ -1,39 +1,67 @@
+/// Builds ``SQLUnion`` queries.
public final class SQLUnionBuilder: SQLQueryBuilder, SQLQueryFetcher, SQLPartialResultBuilder {
- public var query: SQLExpression { self.union }
-
+ /// The ``SQLUnion`` being built.
public var union: SQLUnion
- public var database: SQLDatabase
- public init(on database: SQLDatabase, initialQuery: SQLSelect) {
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
+
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.union
+ }
+
+ /// Create a new ``SQLUnionBuilder``.
+ @inlinable
+ public init(on database: any SQLDatabase, initialQuery: SQLSelect) {
self.union = .init(initialQuery: initialQuery)
self.database = database
}
+ /// Add a query to the union in `UNION DISTINCT` mode
+ /// (all results from both queries are returned, with duplicates removed).
+ @inlinable
public func union(distinct query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .union))
return self
}
+ /// Add a query to the union in `UNION ALL` mode
+ /// (all results from both queries are returned, with duplicates preserved).
+ @inlinable
public func union(all query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .unionAll))
return self
}
+ /// Add a query to the union in `INTERSECT DISTINCT` mode
+ /// (only results that come from both queries are returned, with duplicates removed).
+ @inlinable
public func intersect(distinct query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .intersect))
return self
}
+ /// Add a query to the union in `INTERSECT ALL` mode
+ /// (only results that come from both queries are returned, with duplicates preserved).
+ @inlinable
public func intersect(all query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .intersectAll))
return self
}
+ /// Add a query to the union in `EXCEPT DISTINCT` mode
+ /// (only results that come from the left query but not the right are returned, with duplicates removed).
+ @inlinable
public func except(distinct query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .except))
return self
}
+ /// Add a query to the union in `EXCEPT ALL` mode
+ /// (only results that come from both queries are returned, with duplicates preserved).
+ @inlinable
public func except(all query: SQLSelect) -> Self {
self.union.add(query, joiner: .init(type: .exceptAll))
return self
@@ -41,16 +69,22 @@ public final class SQLUnionBuilder: SQLQueryBuilder, SQLQueryFetcher, SQLPartial
}
extension SQLUnionBuilder {
- public var orderBys: [SQLExpression] {
+ /// See ``SQLPartialResultBuilder/orderBys``.
+ @inlinable
+ public var orderBys: [any SQLExpression] {
get { self.union.orderBys }
set { self.union.orderBys = newValue }
}
+ /// See ``SQLPartialResultBuilder/limit``.
+ @inlinable
public var limit: Int? {
get { self.union.limit }
set { self.union.limit = newValue }
}
+ /// See ``SQLPartialResultBuilder/offset``.
+ @inlinable
public var offset: Int? {
get { self.union.offset }
set { self.union.offset = newValue }
@@ -58,98 +92,121 @@ extension SQLUnionBuilder {
}
extension SQLDatabase {
- public func union(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self, initialQuery: predicate(.init(on: self)).select)
+ /// Create a new ``SQLUnionBuilder``, providing a builder to create the first query.
+ @inlinable
+ public func union(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ .init(on: self, initialQuery: try predicate(.init(on: self)).select)
}
}
extension SQLUnionBuilder {
- public func union(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.union(distinct: predicate(.init(on: self.database)).select)
+ /// Call ``union(distinct:)-6q90v`` with a query generated by a builder.
+ @inlinable
+ public func union(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.union(distinct: predicate(.init(on: self.database)).select)
}
- public func union(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.union(all: predicate(.init(on: self.database)).select)
+ /// Call ``union(all:)-76ei4`` with a query generated by a builder.
+ @inlinable
+ public func union(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.union(all: predicate(.init(on: self.database)).select)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func union(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.union(distinct: predicate)
+ /// Alias ``union(distinct:)-79krl`` so it acts as the "default".
+ @inlinable
+ public func union(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.union(distinct: predicate)
}
- public func intersect(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.intersect(distinct: predicate(.init(on: self.database)).select)
+ /// Call ``intersect(distinct:)-1i7fc`` with a query generated by a builder.
+ @inlinable
+ public func intersect(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.intersect(distinct: predicate(.init(on: self.database)).select)
}
- public func intersect(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.intersect(all: predicate(.init(on: self.database)).select)
+ /// Call ``intersect(all:)-5r4e9`` with a query generated by a builder.
+ @inlinable
+ public func intersect(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.intersect(all: predicate(.init(on: self.database)).select)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func intersect(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.intersect(distinct: predicate)
+ /// Alias ``intersect(distinct:)-3t74e`` so it acts as the "default".
+ @inlinable
+ public func intersect(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.intersect(distinct: predicate)
}
- public func except(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.except(distinct: predicate(.init(on: self.database)).select)
+ /// Call ``except(distinct:)-8pdro`` with a query generated by a builder.
+ @inlinable
+ public func except(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.except(distinct: predicate(.init(on: self.database)).select)
}
- public func except(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.except(all: predicate(.init(on: self.database)).select)
+ /// Call ``except(all:)-3i25o`` with a query generated by a builder.
+ @inlinable
+ public func except(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.except(all: predicate(.init(on: self.database)).select)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func except(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> Self {
- return self.except(distinct: predicate)
+ /// Alias ``except(distinct:)-2xe8f`` so it acts as the "default".
+ @inlinable
+ public func except(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> Self {
+ try self.except(distinct: predicate)
}
}
extension SQLSelectBuilder {
- public func union(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .union(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/union(distinct:)-79krl``.
+ @inlinable
+ public func union(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).union(distinct: predicate)
}
- public func union(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .union(all: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/union(all:)-8lkyh``.
+ @inlinable
+ public func union(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).union(all: predicate)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func union(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .union(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/union(_:)``.
+ @inlinable
+ public func union(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try self.union(distinct: predicate)
}
- public func intersect(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .intersect(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/intersect(distinct:)-15945``.
+ @inlinable
+ public func intersect(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).intersect(distinct: predicate)
}
- public func intersect(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .intersect(all: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/intersect(all:)-8i8ic``.
+ @inlinable
+ public func intersect(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).intersect(all: predicate)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func intersect(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .intersect(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/intersect(_:)``.
+ @inlinable
+ public func intersect(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try self.intersect(distinct: predicate)
}
- public func except(distinct predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .except(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/except(distinct:)-2m81r``.
+ @inlinable
+ public func except(distinct predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).except(distinct: predicate)
}
- public func except(all predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .except(all: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/except(all:)-16hlm``.
+ @inlinable
+ public func except(all predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try .init(on: self.database, initialQuery: self.select).except(all: predicate)
}
- /// Alias the `distinct` variant so it acts as the "default".
- public func except(_ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) -> SQLUnionBuilder {
- return SQLUnionBuilder(on: self.database, initialQuery: self.select)
- .except(distinct: predicate(.init(on: self.database)).select)
+ /// See ``SQLUnionBuilder/except(_:)``.
+ @inlinable
+ public func except(_ predicate: (SQLSelectBuilder) throws -> SQLSelectBuilder) rethrows -> SQLUnionBuilder {
+ try self.except(distinct: predicate)
}
}
diff --git a/Sources/SQLKit/Builders/SQLUpdateBuilder.swift b/Sources/SQLKit/Builders/SQLUpdateBuilder.swift
index 851868a1..fb09341c 100644
--- a/Sources/SQLKit/Builders/SQLUpdateBuilder.swift
+++ b/Sources/SQLKit/Builders/SQLUpdateBuilder.swift
@@ -1,65 +1,59 @@
-/// Builds `SQLUpdate` queries.
+/// Builds ``SQLUpdate`` queries.
///
-/// db.update(Planet.self)
-/// .set(\Planet.name == "Earth")
-/// .where(\Planet.name == "Eatrh")
+/// db.update(Planet.schema)
+/// .set("name", to: "Earth")
+/// .where("name", .equal, "Earth")
/// .run()
-///
-/// See `SQLQueryBuilder` and `SQLPredicateBuilder` for more information.
public final class SQLUpdateBuilder: SQLQueryBuilder, SQLPredicateBuilder, SQLReturningBuilder, SQLColumnUpdateBuilder {
- /// `Update` query being built.
+ /// ``SQLUpdate`` query being built.
public var update: SQLUpdate
- public var database: SQLDatabase
+ /// See ``SQLQueryBuilder/database``.
+ public var database: any SQLDatabase
- public var query: SQLExpression {
- return self.update
+ /// See ``SQLQueryBuilder/query``.
+ @inlinable
+ public var query: any SQLExpression {
+ self.update
}
- public var values: [SQLExpression] {
- get { return self.update.values }
+ /// See ``SQLColumnUpdateBuilder/values``.
+ @inlinable
+ public var values: [any SQLExpression] {
+ get { self.update.values }
set { self.update.values = newValue }
}
- public var predicate: SQLExpression? {
- get { return self.update.predicate }
+ /// See ``SQLPredicateBuilder/predicate``.
+ @inlinable
+ public var predicate: (any SQLExpression)? {
+ get { self.update.predicate }
set { self.update.predicate = newValue }
}
+ /// See ``SQLReturningBuilder/returning``.
+ @inlinable
public var returning: SQLReturning? {
- get { return self.update.returning }
+ get { self.update.returning }
set { self.update.returning = newValue }
}
- /// Creates a new `SQLUpdateBuilder`.
- public init(_ update: SQLUpdate, on database: SQLDatabase) {
+ /// Create a new ``SQLUpdateBuilder``.
+ @inlinable
+ public init(_ update: SQLUpdate, on database: any SQLDatabase) {
self.update = update
self.database = database
}
}
-// MARK: Connection
-
extension SQLDatabase {
- /// Creates a new `SQLUpdateBuilder`.
- ///
- /// db.update("planets")...
- ///
- /// - parameters:
- /// - table: Table to update.
- /// - returns: Newly created `SQLUpdateBuilder`.
+ /// Create a new ``SQLUpdateBuilder``.
public func update(_ table: String) -> SQLUpdateBuilder {
- return self.update(SQLIdentifier(table))
+ self.update(SQLIdentifier(table))
}
- /// Creates a new `SQLUpdateBuilder`.
- ///
- /// db.update("planets")...
- ///
- /// - parameters:
- /// - table: Table to update.
- /// - returns: Newly created `SQLUpdateBuilder`.
- public func update(_ table: SQLExpression) -> SQLUpdateBuilder {
- return .init(.init(table: table), on: self)
+ /// Create a new ``SQLUpdateBuilder``.
+ public func update(_ table: any SQLExpression) -> SQLUpdateBuilder {
+ .init(.init(table: table), on: self)
}
}
diff --git a/Sources/SQLKit/Concurrency/SQLDatabase+Concurrency.swift b/Sources/SQLKit/Concurrency/SQLDatabase+Concurrency.swift
index 61906161..de95ac7f 100644
--- a/Sources/SQLKit/Concurrency/SQLDatabase+Concurrency.swift
+++ b/Sources/SQLKit/Concurrency/SQLDatabase+Concurrency.swift
@@ -1,7 +1,7 @@
import NIOCore
public extension SQLDatabase {
- func execute(sql query: SQLExpression, _ onRow: @escaping (SQLRow) -> ()) async throws -> Void {
+ func execute(sql query: any SQLExpression, _ onRow: @escaping @Sendable (any SQLRow) -> ()) async throws {
try await self.execute(sql: query, onRow).get()
}
}
diff --git a/Sources/SQLKit/Concurrency/SQLQueryBuilder+Concurrency.swift b/Sources/SQLKit/Concurrency/SQLQueryBuilder+Concurrency.swift
index 7fc459a0..6d222d6d 100644
--- a/Sources/SQLKit/Concurrency/SQLQueryBuilder+Concurrency.swift
+++ b/Sources/SQLKit/Concurrency/SQLQueryBuilder+Concurrency.swift
@@ -2,6 +2,6 @@ import NIOCore
public extension SQLQueryBuilder {
func run() async throws -> Void {
- return try await self.run().get()
+ try await self.run().get()
}
}
diff --git a/Sources/SQLKit/Concurrency/SQLQueryFetcher+Concurrency.swift b/Sources/SQLKit/Concurrency/SQLQueryFetcher+Concurrency.swift
index d3f35692..359d6850 100644
--- a/Sources/SQLKit/Concurrency/SQLQueryFetcher+Concurrency.swift
+++ b/Sources/SQLKit/Concurrency/SQLQueryFetcher+Concurrency.swift
@@ -2,26 +2,26 @@ import NIOCore
public extension SQLQueryFetcher {
func first(decoding: D.Type) async throws -> D? where D: Decodable {
- return try await self.first(decoding: D.self).get()
+ try await self.first(decoding: D.self).get()
}
- func first() async throws -> SQLRow? {
- return try await self.first().get()
+ func first() async throws -> (any SQLRow)? {
+ try await self.first().get()
}
func all(decoding: D.Type) async throws -> [D] where D: Decodable {
- return try await self.all(decoding: D.self).get()
+ try await self.all(decoding: D.self).get()
}
- func all() async throws -> [SQLRow] {
- return try await self.all().get()
+ func all() async throws -> [any SQLRow] {
+ try await self.all().get()
}
- func run(decoding: D.Type, _ handler: @escaping (Result) -> ()) async throws -> Void where D: Decodable {
- return try await self.run(decoding: D.self, handler).get()
+ func run(decoding: D.Type, _ handler: @escaping @Sendable (Result) -> ()) async throws -> Void where D: Decodable {
+ try await self.run(decoding: D.self, handler).get()
}
- func run(_ handler: @escaping (SQLRow) -> ()) async throws -> Void {
- return try await self.run(handler).get()
+ func run(_ handler: @escaping @Sendable (any SQLRow) -> ()) async throws -> Void {
+ try await self.run(handler).get()
}
}
diff --git a/Sources/SQLKit/Exports.swift b/Sources/SQLKit/Exports.swift
index 033b7d2a..f9dc10ac 100644
--- a/Sources/SQLKit/Exports.swift
+++ b/Sources/SQLKit/Exports.swift
@@ -1,4 +1,10 @@
-#if !BUILDING_DOCC
+#if swift(>=5.8)
+
+@_documentation(visibility: internal) @_exported import protocol NIO.EventLoop
+@_documentation(visibility: internal) @_exported import class NIO.EventLoopFuture
+@_documentation(visibility: internal) @_exported import struct Logging.Logger
+
+#else
@_exported import protocol NIO.EventLoop
@_exported import class NIO.EventLoopFuture
diff --git a/Sources/SQLKit/Query/SQLAlias.swift b/Sources/SQLKit/Query/SQLAlias.swift
index d17402b1..312e5c63 100644
--- a/Sources/SQLKit/Query/SQLAlias.swift
+++ b/Sources/SQLKit/Query/SQLAlias.swift
@@ -1,12 +1,14 @@
public struct SQLAlias: SQLExpression {
- public var expression: SQLExpression
- public var alias: SQLExpression
+ public var expression: any SQLExpression
+ public var alias: any SQLExpression
- public init(_ expression: SQLExpression, as alias: SQLExpression) {
+ @inlinable
+ public init(_ expression: any SQLExpression, as alias: any SQLExpression) {
self.expression = expression
self.alias = alias
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
self.expression.serialize(to: &serializer)
serializer.write(" AS ")
@@ -15,7 +17,8 @@ public struct SQLAlias: SQLExpression {
}
extension SQLAlias {
- public init(_ expression: SQLExpression, as alias: String) {
+ @inlinable
+ public init(_ expression: any SQLExpression, as alias: String) {
self.init(expression, as: SQLIdentifier(alias))
}
}
diff --git a/Sources/SQLKit/Query/SQLAlterColumnDefinitionType.swift b/Sources/SQLKit/Query/SQLAlterColumnDefinitionType.swift
index c0f8ac72..fdd01d1c 100644
--- a/Sources/SQLKit/Query/SQLAlterColumnDefinitionType.swift
+++ b/Sources/SQLKit/Query/SQLAlterColumnDefinitionType.swift
@@ -3,19 +3,22 @@
/// column [alterColumnDefinitionTypeClause] dataType
///
public struct SQLAlterColumnDefinitionType: SQLExpression {
- public var column: SQLExpression
- public var dataType: SQLExpression
+ public var column: any SQLExpression
+ public var dataType: any SQLExpression
+ @inlinable
public init(column: SQLIdentifier, dataType: SQLDataType) {
self.column = column
self.dataType = dataType
}
- public init(column: SQLExpression, dataType: SQLExpression) {
+ @inlinable
+ public init(column: any SQLExpression, dataType: any SQLExpression) {
self.column = column
self.dataType = dataType
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append(self.column)
diff --git a/Sources/SQLKit/Query/SQLAlterEnum.swift b/Sources/SQLKit/Query/SQLAlterEnum.swift
index 240f3f44..b22d5e9d 100644
--- a/Sources/SQLKit/Query/SQLAlterEnum.swift
+++ b/Sources/SQLKit/Query/SQLAlterEnum.swift
@@ -1,8 +1,11 @@
-// ALTER TYPE enum_type ADD VALUE 'new_value';
+/// `ALTER TYPE enum_type ADD VALUE 'new_value';`
+///
+/// See ``SQLAlterEnumBuilder``.
public struct SQLAlterEnum: SQLExpression {
- public var name: SQLExpression
- public var value: SQLExpression?
+ public var name: any SQLExpression
+ public var value: (any SQLExpression)?
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append("ALTER TYPE")
diff --git a/Sources/SQLKit/Query/SQLAlterTable.swift b/Sources/SQLKit/Query/SQLAlterTable.swift
index f3cc4781..097e7400 100644
--- a/Sources/SQLKit/Query/SQLAlterTable.swift
+++ b/Sources/SQLKit/Query/SQLAlterTable.swift
@@ -1,28 +1,25 @@
/// `ALTER TABLE` query.
///
-/// db.alter(table: Planet.self)
-/// .column(for: \.name)
-/// .run()
-///
-/// See `SQLAlterTableBuilder` for more information.
+/// See ``SQLAlterTableBuilder``.
public struct SQLAlterTable: SQLExpression {
- public var name: SQLExpression
+ public var name: any SQLExpression
/// New name
- public var renameTo: SQLExpression?
+ public var renameTo: (any SQLExpression)?
/// Columns to add.
- public var addColumns: [SQLExpression]
+ public var addColumns: [any SQLExpression]
/// Columns to update.
- public var modifyColumns: [SQLExpression]
+ public var modifyColumns: [any SQLExpression]
/// Columns to delete.
- public var dropColumns: [SQLExpression]
+ public var dropColumns: [any SQLExpression]
/// Table constraints, such as `FOREIGN KEY`, to add.
- public var addTableConstraints: [SQLExpression]
+ public var addTableConstraints: [any SQLExpression]
/// Table constraints, such as `FOREIGN KEY`, to delete.
- public var dropTableConstraints: [SQLExpression]
+ public var dropTableConstraints: [any SQLExpression]
- /// Creates a new `SQLAlterTable`. See `SQLAlterTableBuilder`.
- public init(name: SQLExpression) {
+ /// Creates a new ``SQLAlterTable``. See ``SQLAlterTableBuilder``.
+ @inlinable
+ public init(name: any SQLExpression) {
self.name = name
self.renameTo = nil
self.addColumns = []
diff --git a/Sources/SQLKit/Query/SQLBinaryExpression.swift b/Sources/SQLKit/Query/SQLBinaryExpression.swift
index ebdcc4e6..7f7bc114 100644
--- a/Sources/SQLKit/Query/SQLBinaryExpression.swift
+++ b/Sources/SQLKit/Query/SQLBinaryExpression.swift
@@ -1,25 +1,28 @@
public struct SQLBinaryExpression: SQLExpression {
- public let left: SQLExpression
- public let op: SQLExpression
- public let right: SQLExpression
+ public let left: any SQLExpression
+ public let op: any SQLExpression
+ public let right: any SQLExpression
- public init(left: SQLExpression, op: SQLExpression, right: SQLExpression) {
+ @inlinable
+ public init(left: any SQLExpression, op: any SQLExpression, right: any SQLExpression) {
self.left = left
self.op = op
self.right = right
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- self.left.serialize(to: &serializer)
- serializer.write(" ")
- self.op.serialize(to: &serializer)
- serializer.write(" ")
- self.right.serialize(to: &serializer)
+ serializer.statement {
+ $0.append(self.left)
+ $0.append(self.op)
+ $0.append(self.right)
+ }
}
}
extension SQLBinaryExpression {
- public init(_ left: SQLExpression, _ op: SQLBinaryOperator, _ right: SQLExpression) {
+ @inlinable
+ public init(_ left: any SQLExpression, _ op: SQLBinaryOperator, _ right: any SQLExpression) {
self.init(left: left, op: op, right: right)
}
}
diff --git a/Sources/SQLKit/Query/SQLBinaryOperator.swift b/Sources/SQLKit/Query/SQLBinaryOperator.swift
index 027a1cd8..1d41414a 100644
--- a/Sources/SQLKit/Query/SQLBinaryOperator.swift
+++ b/Sources/SQLKit/Query/SQLBinaryOperator.swift
@@ -60,6 +60,7 @@ public enum SQLBinaryOperator: SQLExpression {
/// `IS NOT`
case isNot
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
case .equal: serializer.write("=")
@@ -88,10 +89,6 @@ public enum SQLBinaryOperator: SQLExpression {
|| is not implemented because MySQL doesn't always support it, even though everyone else does.
Use `SQLFunction("CONCAT", args...)` for MySQL or `SQLRaw("||")` with Postgres and SQLite.
""")
-
- // "warning: Default will never be executed".
- //default:
- // fatalError("\(self) operator is not implemented, probably because it needs extra work across SQL dialects.")
}
}
}
diff --git a/Sources/SQLKit/Query/SQLBind.swift b/Sources/SQLKit/Query/SQLBind.swift
index a0d1d153..a00625c9 100644
--- a/Sources/SQLKit/Query/SQLBind.swift
+++ b/Sources/SQLKit/Query/SQLBind.swift
@@ -1,18 +1,21 @@
/// A parameterizied value bound to the SQL query.
public struct SQLBind: SQLExpression {
- public let encodable: Encodable
+ public let encodable: any Encodable
- public init(_ encodable: Encodable) {
+ @inlinable
+ public init(_ encodable: any Encodable) {
self.encodable = encodable
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.write(bind: self.encodable)
}
}
extension SQLBind {
- public static func group(_ items: [Encodable]) -> SQLExpression {
+ @inlinable
+ public static func group(_ items: [any Encodable]) -> any SQLExpression {
SQLGroupExpression(items.map(SQLBind.init))
}
}
diff --git a/Sources/SQLKit/Query/SQLColumnAssignment.swift b/Sources/SQLKit/Query/SQLColumnAssignment.swift
index 2c0185fd..bdd854c7 100644
--- a/Sources/SQLKit/Query/SQLColumnAssignment.swift
+++ b/Sources/SQLKit/Query/SQLColumnAssignment.swift
@@ -1,49 +1,56 @@
/// Encapsulates a `col_name=value` expression in the context of an `UPDATE` query's value
-/// assignment list. This is distinct from an `SQLBinaryExpression` using the `.equal`
+/// assignment list. This is distinct from an ``SQLBinaryExpression`` using the `.equal`
/// operator in that the left side must be an _unqualified_ column name, the operator must
-/// be `=`, and the right side may use `SQLExcludedColumn` when the assignment appears in
-/// the `assignments` list of a `SQLConflictAction.update` specification.
+/// be `=`, and the right side may use ``SQLExcludedColumn`` when the assignment appears in
+/// the `assignments` list of a ``SQLConflictAction/update(assignments:predicate:)`` specification.
public struct SQLColumnAssignment: SQLExpression {
/// The name of the column to assign.
- public var columnName: SQLExpression
+ public var columnName: any SQLExpression
/// The value to assign.
- public var value: SQLExpression
+ public var value: any SQLExpression
/// Create a column assignment from a column identifier and value expression.
- public init(setting columnName: SQLExpression, to value: SQLExpression) {
+ @inlinable
+ public init(setting columnName: any SQLExpression, to value: any SQLExpression) {
self.columnName = columnName
self.value = value
}
/// Create a column assignment from a column identifier and value binding.
- public init(setting columnName: SQLExpression, to value: Encodable) {
+ @inlinable
+ public init(setting columnName: any SQLExpression, to value: any Encodable) {
self.init(setting: columnName, to: SQLBind(value))
}
/// Create a column assignment from a column name and value binding.
- public init(setting columnName: String, to value: Encodable) {
+ @inlinable
+ public init(setting columnName: String, to value: any Encodable) {
self.init(setting: columnName, to: SQLBind(value))
}
/// Create a column assignment from a column name and value expression.
- public init(setting columnName: String, to value: SQLExpression) {
+ @inlinable
+ public init(setting columnName: String, to value: any SQLExpression) {
self.init(setting: SQLColumn(columnName), to: value)
}
/// Create a column assignment from a column name and using the excluded value
- /// from an upsert's values list. See `SQLExcludedColumn`.
+ /// from an upsert's values list. See ``SQLExcludedColumn``.
+ @inlinable
public init(settingExcludedValueFor columnName: String) {
self.init(settingExcludedValueFor: SQLColumn(columnName))
}
/// Create a column assignment from a column identifier and using the excluded value
- /// from an upsert's values list. See `SQLExcludedColumn`.
- public init(settingExcludedValueFor column: SQLExpression) {
+ /// from an upsert's values list. See ``SQLExcludedColumn``.
+ @inlinable
+ public init(settingExcludedValueFor column: any SQLExpression) {
self.init(setting: column, to: SQLExcludedColumn(column))
}
/// See `SQLExpression.serialize(to:)`.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append(self.columnName)
diff --git a/Sources/SQLKit/Query/SQLColumnConstraintAlgorithm.swift b/Sources/SQLKit/Query/SQLColumnConstraintAlgorithm.swift
index 38e53772..02e28d3d 100644
--- a/Sources/SQLKit/Query/SQLColumnConstraintAlgorithm.swift
+++ b/Sources/SQLKit/Query/SQLColumnConstraintAlgorithm.swift
@@ -1,4 +1,4 @@
-/// Column constraint algorithms used by `SQLConstraint`
+/// Column constraint algorithms used by ``SQLColumnDefinition``
public enum SQLColumnConstraintAlgorithm: SQLExpression {
/// `PRIMARY KEY`column constraint.
case primaryKey(autoIncrement: Bool)
@@ -10,61 +10,68 @@ public enum SQLColumnConstraintAlgorithm: SQLExpression {
case unique
/// `CHECK` column constraint.
- case check(SQLExpression)
+ case check(any SQLExpression)
/// `COLLATE` column constraint.
- case collate(name: SQLExpression)
+ case collate(name: any SQLExpression)
/// `DEFAULT` column constraint.
- case `default`(SQLExpression)
+ case `default`(any SQLExpression)
/// `FOREIGN KEY` column constraint.
- case foreignKey(references: SQLExpression)
+ case foreignKey(references: any SQLExpression)
/// `GENERATED ALWAYS AS` column constraint.
- case generated(SQLExpression)
+ case generated(any SQLExpression)
- /// Just serializes `SQLExpression`
- case custom(SQLExpression)
+ /// Just serializes ``SQLExpression``
+ case custom(any SQLExpression)
/// `PRIMARY KEY` with auto incrementing turned on.
+ @inlinable
public static var primaryKey: SQLColumnConstraintAlgorithm {
- return .primaryKey(autoIncrement: true)
+ .primaryKey(autoIncrement: true)
}
/// `COLLATE` column constraint.
+ @inlinable
public static func collate(name: String) -> SQLColumnConstraintAlgorithm {
- return .collate(name: SQLIdentifier(name))
+ .collate(name: SQLIdentifier(name))
}
/// `DEFAULT` column constraint.
+ @inlinable
public static func `default`(_ value: String) -> SQLColumnConstraintAlgorithm {
- return .default(SQLLiteral.string(value))
+ .default(SQLLiteral.string(value))
}
/// `DEFAULT` column constraint.
+ @inlinable
public static func `default`(_ value: T) -> SQLColumnConstraintAlgorithm {
- return .default(SQLLiteral.numeric("\(value)"))
+ .default(SQLLiteral.numeric("\(value)"))
}
/// `DEFAULT` column constraint.
+ @inlinable
public static func `default`(_ value: T) -> SQLColumnConstraintAlgorithm {
- return .default(SQLLiteral.numeric("\(value)"))
+ .default(SQLLiteral.numeric("\(value)"))
}
/// `DEFAULT` column constraint.
+ @inlinable
public static func `default`(_ value: Bool) -> SQLColumnConstraintAlgorithm {
- return .default(SQLLiteral.boolean(value))
+ .default(SQLLiteral.boolean(value))
}
/// `FOREIGN KEY` column constraint.
+ @inlinable
public static func references(
_ table: String,
_ column: String,
onDelete: SQLForeignKeyAction? = nil,
onUpdate: SQLForeignKeyAction? = nil
) -> SQLColumnConstraintAlgorithm {
- return self.references(
+ self.references(
SQLIdentifier(table),
SQLIdentifier(column),
onDelete: onDelete,
@@ -73,13 +80,14 @@ public enum SQLColumnConstraintAlgorithm: SQLExpression {
}
/// `FOREIGN KEY` column constraint.
+ @inlinable
public static func references(
- _ table: SQLExpression,
- _ column: SQLExpression,
- onDelete: SQLExpression? = nil,
- onUpdate: SQLExpression? = nil
+ _ table: any SQLExpression,
+ _ column: any SQLExpression,
+ onDelete: (any SQLExpression)? = nil,
+ onUpdate: (any SQLExpression)? = nil
) -> SQLColumnConstraintAlgorithm {
- return .foreignKey(
+ .foreignKey(
references: SQLForeignKey(
table: table,
columns: [column],
diff --git a/Sources/SQLKit/Query/SQLColumnDefinition.swift b/Sources/SQLKit/Query/SQLColumnDefinition.swift
index 621b83cd..e667a13f 100644
--- a/Sources/SQLKit/Query/SQLColumnDefinition.swift
+++ b/Sources/SQLKit/Query/SQLColumnDefinition.swift
@@ -1,15 +1,16 @@
-/// Table column definition. DDL. Used by `SQLCreateTable` and `SQLAlterTable`.
+/// Table column definition. DDL. Used by ``SQLCreateTable`` and ``SQLAlterTable``.
///
-/// See `SQLCreateTableBuilder` and `SQLAlterTableBuilder`.
+/// See ``SQLCreateTableBuilder`` and ``SQLAlterTableBuilder``.
public struct SQLColumnDefinition: SQLExpression {
- public var column: SQLExpression
+ public var column: any SQLExpression
- public var dataType: SQLExpression
+ public var dataType: any SQLExpression
- public var constraints: [SQLExpression]
+ public var constraints: [any SQLExpression]
- /// Creates a new `SQLColumnDefinition` from column identifier, data type, and zero or more constraints.
- public init(column: SQLExpression, dataType: SQLExpression, constraints: [SQLExpression] = []) {
+ /// Creates a new ``SQLColumnDefinition`` from column identifier, data type, and zero or more constraints.
+ @inlinable
+ public init(column: any SQLExpression, dataType: any SQLExpression, constraints: [any SQLExpression] = []) {
self.column = column
self.dataType = dataType
self.constraints = constraints
@@ -30,16 +31,17 @@ extension SQLColumnDefinition {
/// Create a new column definition from a string, data type, and array of constraints.
///
/// Turns this:
- /// ```swift
- /// SQLColumnDefinition(
- /// column: SQLIdentifier("id"),
- /// dataType: SQLDataType.bigInt,
- /// constraints: [SQLColumnConstraintAlgorithm.primaryKey, SQLColumnConstraintAlgorithm.notNull]
- /// )
- /// ```
+ ///
+ /// SQLColumnDefinition(
+ /// column: SQLIdentifier("id"),
+ /// dataType: SQLDataType.bigInt,
+ /// constraints: [SQLColumnConstraintAlgorithm.primaryKey, SQLColumnConstraintAlgorithm.notNull]
+ /// )
+ ///
/// into this:
///
- /// `SQLColumnDefinition("id", dataType: .bigint, constraints: [.primaryKey, .notNull]`
+ /// SQLColumnDefinition("id", dataType: .bigint, constraints: [.primaryKey, .notNull])
+ @inlinable
public init(_ name: String, dataType: SQLDataType, constraints: [SQLColumnConstraintAlgorithm] = []) {
self.init(column: SQLIdentifier(name), dataType: dataType, constraints: constraints)
}
diff --git a/Sources/SQLKit/Query/SQLColumnIdentifier.swift b/Sources/SQLKit/Query/SQLColumnIdentifier.swift
index f682c4ba..b3e80644 100644
--- a/Sources/SQLKit/Query/SQLColumnIdentifier.swift
+++ b/Sources/SQLKit/Query/SQLColumnIdentifier.swift
@@ -1,16 +1,19 @@
public struct SQLColumn: SQLExpression {
- public var name: SQLExpression
- public var table: SQLExpression?
+ public var name: any SQLExpression
+ public var table: (any SQLExpression)?
+ @inlinable
public init(_ name: String, table: String? = nil) {
self.init(SQLIdentifier(name), table: table.flatMap(SQLIdentifier.init(_:)))
}
- public init(_ name: SQLExpression, table: SQLExpression? = nil) {
+ @inlinable
+ public init(_ name: any SQLExpression, table: (any SQLExpression)? = nil) {
self.name = name
self.table = table
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
if let table = self.table {
table.serialize(to: &serializer)
diff --git a/Sources/SQLKit/Query/SQLConflictAction.swift b/Sources/SQLKit/Query/SQLConflictAction.swift
index f9b5ad65..3a0839cc 100644
--- a/Sources/SQLKit/Query/SQLConflictAction.swift
+++ b/Sources/SQLKit/Query/SQLConflictAction.swift
@@ -6,10 +6,10 @@
/// in the query as an update action would, MySQL can only express the `noAction` case
/// with `INSERT IGNORE`. This requires considering the conflict action twice in the same
/// query when serializing, and to decide what to emit in either location based on both
-/// the specific action _and_ the dialect's supported snytax. As a result, the logic for
-/// this has to live in `SQLInsert`, and it is not possible to serialize a conflict action
+/// the specific action _and_ the dialect's supported sybtax. As a result, the logic for
+/// this has to live in ``SQLInsert``, and it is not possible to serialize a conflict action
/// to SQL in isolation (but again, _only_ because MySQL couldn't be bothered), and this
-/// enum can not conform to `SQLExpression`.
+/// enum can not conform to ``SQLExpression``.
public enum SQLConflictAction {
/// Specifies that conflicts this action is applied to should be ignored, allowing the query to complete
/// successfully without inserting any new rows or changing any existing rows.
@@ -18,7 +18,7 @@ public enum SQLConflictAction {
/// Specifies that conflicts this action is applied to shall cause the INSERT to be converted to an UPDATE
/// on the same schema which performs the column updates specified by the associated column assignments and,
/// where supported by the database, constrained by the associated predicate. The column assignments may
- /// include `SQLExcludedColumn` expressions to refer to values which would have been inserted into the row
+ /// include ``SQLExcludedColumn`` expressions to refer to values which would have been inserted into the row
/// if the conflict had not taken place.
- case update(assignments: [SQLExpression], predicate: SQLExpression?)
+ case update(assignments: [any SQLExpression], predicate: (any SQLExpression)?)
}
diff --git a/Sources/SQLKit/Query/SQLConflictResolutionStrategy.swift b/Sources/SQLKit/Query/SQLConflictResolutionStrategy.swift
index 44035422..3c68d8dd 100644
--- a/Sources/SQLKit/Query/SQLConflictResolutionStrategy.swift
+++ b/Sources/SQLKit/Query/SQLConflictResolutionStrategy.swift
@@ -12,35 +12,40 @@ public struct SQLConflictResolutionStrategy: SQLExpression {
/// The column or columns which comprise the uniquness constraint to which the strategy
/// should apply. The exact rules for how a matching constraint is found when an exact
/// match is not found vary between databases. Not all database implement conflict targets.
- public var targetColumns: [SQLExpression]
+ public var targetColumns: [any SQLExpression]
/// An action to take to resolve a conflict in the unique index.
public var action: SQLConflictAction
/// Create a resolution strategy over the given column name and an action.
+ @inlinable
public init(target targetColumn: String, action: SQLConflictAction) {
self.init(targets: [targetColumn], action: action)
}
/// Create a resolution strategy over the given column names and an action.
+ @inlinable
public init(targets targetColumns: [String], action: SQLConflictAction) {
self.init(targets: targetColumns.map { SQLColumn($0) }, action: action)
}
/// Create a resolution strategy over the given column and an action.
- public init(target targetColumn: SQLExpression, action: SQLConflictAction) {
+ @inlinable
+ public init(target targetColumn: any SQLExpression, action: SQLConflictAction) {
self.init(targets: [targetColumn], action: action)
}
/// Create a resolution strategy over the given columns and an action.
- public init(targets targetColumns: [SQLExpression], action: SQLConflictAction) {
+ @inlinable
+ public init(targets targetColumns: [any SQLExpression], action: SQLConflictAction) {
self.targetColumns = targetColumns
self.action = action
}
/// An expression to be embedded into the same `INSERT` query as the strategy expression to
/// work around MySQL's desire to make life difficult.
- public func queryModifier(for serializer: SQLSerializer) -> SQLExpression? {
+ @inlinable
+ public func queryModifier(for serializer: SQLSerializer) -> (any SQLExpression)? {
if serializer.dialect.upsertSyntax == .mysqlLike, case .noAction = self.action {
return SQLInsertModifier()
}
@@ -79,7 +84,11 @@ public struct SQLConflictResolutionStrategy: SQLExpression {
/// Simple helper for working around MySQL's refusal to implement standard SQL. Only emits SQL when needed.
public struct SQLInsertModifier: SQLExpression {
+ @usableFromInline
+ init() {}
+
// See `SQLSerializer.serialize(to:)`.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.write("IGNORE")
}
diff --git a/Sources/SQLKit/Query/SQLConstraint.swift b/Sources/SQLKit/Query/SQLConstraint.swift
index d86f3a9d..561184c2 100644
--- a/Sources/SQLKit/Query/SQLConstraint.swift
+++ b/Sources/SQLKit/Query/SQLConstraint.swift
@@ -1,16 +1,17 @@
-/// Constraints for `SQLCreateTable` (column and table constraints).
+/// Constraints for ``SQLCreateTable`` (column and table constraints).
public struct SQLConstraint: SQLExpression {
/// Name of constraint
///
/// `CONSTRAINT `
- public var name: SQLExpression?
+ public var name: (any SQLExpression)?
/// Algorithm. See `SQLTableConstraintAlgorithm`
/// and `SQLColumnConstraintAlgorithm`
/// TODO: Make optional.
- public var algorithm: SQLExpression
+ public var algorithm: any SQLExpression
- public init(algorithm: SQLExpression, name: SQLExpression? = nil) {
+ @inlinable
+ public init(algorithm: any SQLExpression, name: (any SQLExpression)? = nil) {
self.name = name
self.algorithm = algorithm
}
diff --git a/Sources/SQLKit/Query/SQLCreateEnum.swift b/Sources/SQLKit/Query/SQLCreateEnum.swift
index e9126dab..923e637d 100644
--- a/Sources/SQLKit/Query/SQLCreateEnum.swift
+++ b/Sources/SQLKit/Query/SQLCreateEnum.swift
@@ -1,17 +1,19 @@
/// The `CREATE TYPE` command is used to create a new types in a database.
///
-/// See `SQLCreateEnumBuilder`.
+/// See ``SQLCreateEnumBuilder``.
public struct SQLCreateEnum: SQLExpression {
/// Name of type to create.
- public var name: SQLExpression
+ public var name: any SQLExpression
- public var values: [SQLExpression]
+ public var values: [any SQLExpression]
- public init(name: SQLExpression, values: [SQLExpression]) {
+ @inlinable
+ public init(name: any SQLExpression, values: [any SQLExpression]) {
self.name = name
self.values = values
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append("CREATE TYPE")
diff --git a/Sources/SQLKit/Query/SQLCreateIndex.swift b/Sources/SQLKit/Query/SQLCreateIndex.swift
index f9a58268..0b33ba31 100644
--- a/Sources/SQLKit/Query/SQLCreateIndex.swift
+++ b/Sources/SQLKit/Query/SQLCreateIndex.swift
@@ -1,19 +1,20 @@
/// `CREATE INDEX` query.
///
-/// See `SQLCreateIndexBuilder`.
+/// See ``SQLCreateIndexBuilder``.
public struct SQLCreateIndex: SQLExpression {
- public var name: SQLExpression
+ public var name: any SQLExpression
- public var table: SQLExpression?
+ public var table: (any SQLExpression)?
/// Type of index to create, see `SQLIndexModifier`.
- public var modifier: SQLExpression?
+ public var modifier: (any SQLExpression)?
/// Columns to index.
- public var columns: [SQLExpression]
+ public var columns: [any SQLExpression]
/// Creates a new `SQLCreateIndex.
- public init(name: SQLExpression) {
+ @inlinable
+ public init(name: any SQLExpression) {
self.name = name
self.table = nil
self.modifier = nil
diff --git a/Sources/SQLKit/Query/SQLCreateTable.swift b/Sources/SQLKit/Query/SQLCreateTable.swift
index 697cf670..ac148b44 100644
--- a/Sources/SQLKit/Query/SQLCreateTable.swift
+++ b/Sources/SQLKit/Query/SQLCreateTable.swift
@@ -1,9 +1,9 @@
/// The `CREATE TABLE` command is used to create a new table in a database.
///
-/// See `SQLCreateTableBuilder`.
+/// See ``SQLCreateTableBuilder``.
public struct SQLCreateTable: SQLExpression {
/// Name of table to create.
- public var table: SQLExpression
+ public var table: any SQLExpression
/// If the "TEMP" or "TEMPORARY" keyword occurs between the "CREATE" and "TABLE" then the new table is created in the temp database.
public var temporary: Bool
@@ -16,16 +16,17 @@ public struct SQLCreateTable: SQLExpression {
public var ifNotExists: Bool
/// Columns to create.
- public var columns: [SQLExpression]
+ public var columns: [any SQLExpression]
/// Table constraints, such as `FOREIGN KEY`, to add.
- public var tableConstraints: [SQLExpression]
+ public var tableConstraints: [any SQLExpression]
/// A subquery which, when present, is used to fill in the contents of the new table.
- public var asQuery: SQLExpression?
+ public var asQuery: (any SQLExpression)?
/// Creates a new `SQLCreateTable` query.
- public init(name: SQLExpression) {
+ @inlinable
+ public init(name: any SQLExpression) {
self.table = name
self.temporary = false
self.ifNotExists = false
diff --git a/Sources/SQLKit/Query/SQLCreateTrigger.swift b/Sources/SQLKit/Query/SQLCreateTrigger.swift
index 4c0ca917..8869af6f 100644
--- a/Sources/SQLKit/Query/SQLCreateTrigger.swift
+++ b/Sources/SQLKit/Query/SQLCreateTrigger.swift
@@ -1,55 +1,56 @@
-/// The `CREATE TRIGGER` command is used to create a trigger against a table
+/// The `CREATE TRIGGER` command is used to create a trigger against a table.
///
-/// See `SQLCreateTribberBuilder`
+/// See ``SQLCreateTriggerBuilder``.
public struct SQLCreateTrigger: SQLExpression {
/// Name of the trigger to create.
- public var name: SQLExpression
+ public var name: any SQLExpression
/// The table which uses the trigger.
- public var table: SQLExpression
+ public var table: any SQLExpression
- /// The column(s) which are watched by the trigger
- public var columns: [SQLExpression]?
+ /// The column(s) which are watched by the trigger.
+ public var columns: [any SQLExpression]?
- /// Whether or not this is a CONSTRAINT trigger.
+ /// Whether or not this is a `CONSTRAINT` trigger.
public var isConstraint: Bool
/// When the trigger should run.
- public var when: SQLExpression
+ public var when: any SQLExpression
/// The event which causes the trigger to execute.
- public var event: SQLExpression
+ public var event: any SQLExpression
- /// The timing of the tirgger. This can only be specified for constraint triggers
- public var timing: SQLExpression?
+ /// The timing of the tirgger. This can only be specified for constraint triggers.
+ public var timing: (any SQLExpression)?
/// Used for foreign key constraints and is not recommended for general use.
- public var referencedTable: SQLExpression?
+ public var referencedTable: (any SQLExpression)?
/// Whether the trigger is fired: once for every row or just once per SQL statement.
- public var each: SQLExpression?
+ public var each: (any SQLExpression)?
/// The condition as to when the trigger should be fired.
- public var condition: SQLExpression?
+ public var condition: (any SQLExpression)?
- /// The name of the function which must take no arguments and return a TRIGGER type.
- public var procedure: SQLExpression?
+ /// The name of the function which must take no arguments and return a `TRIGGER` type.
+ public var procedure: (any SQLExpression)?
/// The MySQL account to be used when checking access privileges at trigger activation time.
- /// Use 'user_name'@'host_name', CURRENT_USER, or CURRENT_USER()
- public var definer: SQLExpression?
+ /// Use `'user_name'@'host_name'`, `CURRENT_USER`, or `CURRENT_USER()`
+ public var definer: (any SQLExpression)?
/// The trigger body to execute for dialects that support it.
- /// - Note: You should **not** include BEGIN/END statements. They are added automatically.
- public var body: [SQLExpression]?
+ /// - Note: You should **not** include `BEGIN`/`END` statements. They are added automatically.
+ public var body: [any SQLExpression]?
- /// A `SQLTriggerOrder` used by MySQL
- public var order: SQLExpression?
+ /// A ``SQLTriggerOrder`` used by MySQL
+ public var order: (any SQLExpression)?
- /// The other trigger name for for the `order`
- public var orderTriggerName: SQLExpression?
+ /// The other trigger name for for the ``order``
+ public var orderTriggerName: (any SQLExpression)?
- public init(trigger: SQLExpression, table: SQLExpression, when: SQLExpression, event: SQLExpression) {
+ @inlinable
+ public init(trigger: any SQLExpression, table: any SQLExpression, when: any SQLExpression, event: any SQLExpression) {
self.name = trigger
self.table = table
self.when = when
@@ -57,122 +58,57 @@ public struct SQLCreateTrigger: SQLExpression {
self.isConstraint = false
}
+ @inlinable
public init(trigger: String, table: String, when: SQLTriggerWhen, event: SQLTriggerEvent) {
self.init(trigger: SQLIdentifier(trigger), table: SQLIdentifier(table), when: when, event: event)
}
public func serialize(to serializer: inout SQLSerializer) {
- let triggerCreateSyntax = serializer.dialect.triggerSyntax.create
-
- serializer.statement { statement in
- if triggerCreateSyntax.contains(.postgreSQLChecks), let when = self.when as? SQLTriggerWhen, when == .instead {
- if let event = self.event as? SQLTriggerEvent, event == .update && columns != nil {
- fatalError("INSTEAD OF UPDATE events do not support lists of columns")
- }
-
- if let each = each as? SQLTriggerEach, each != .row {
- fatalError("INSTEAD OF triggers must be FOR EACH ROW")
- }
- }
-
- statement.append("CREATE")
-
- if triggerCreateSyntax.contains(.supportsConstraints) {
- if self.isConstraint {
- if let when = self.when as? SQLTriggerWhen, when != .after {
- fatalError("CONSTRAINT triggers may only be SQLTriggerWhen.after")
- }
-
- // This goofy looking double-if is because it could exist but be a SQLExpression
- if self.each != nil {
- if let eachEnum = self.each as? SQLTriggerEach, eachEnum != .row {
- fatalError("CONSTRAINT triggers may only be specified FOR EACH ROW")
- }
- }
-
- statement.append("CONSTRAINT")
- }
+ let syntax = serializer.dialect.triggerSyntax.create
+ let when = self.when as? SQLTriggerWhen, event = self.event as? SQLTriggerEvent, each = self.each as? SQLTriggerEach
+
+ if syntax.contains(.postgreSQLChecks) {
+ assert(when != .instead || event != .update || self.columns == nil, "INSTEAD OF UPDATE events do not support lists of columns")
+ assert(when != .instead || each == .row, "INSTEAD OF triggers must be FOR EACH ROW")
+ assert(!syntax.contains(.supportsUpdateColumns) || (columns?.isEmpty ?? true) || event != .update, "Only UPDATE triggers may specify a list of columns.")
+ assert(!syntax.contains(.supportsCondition) || when != .instead || self.condition == nil, "INSTEAD OF triggers do not support WHEN conditions.")
+ if syntax.contains(.supportsConstraints) {
+ assert(!self.isConstraint || when == .after, "CONSTRAINT triggers may only be SQLTriggerWhen.after")
+ assert(!self.isConstraint || each == .row, "CONSTRAINT triggers may only be specified FOR EACH ROW")
+ assert(self.isConstraint || self.timing == nil, "May only specify SQLTriggerTiming on CONSTRAINT triggers.")
}
-
- statement.append("TRIGGER")
- statement.append(self.name)
- statement.append(self.when)
- statement.append(self.event)
-
- if let columns = self.columns, !columns.isEmpty, triggerCreateSyntax.contains(.supportsUpdateColumns) {
- if triggerCreateSyntax.contains(.postgreSQLChecks) {
- if let event = self.event as? SQLTriggerEvent {
- guard event == .update else {
- fatalError("Only UPDATE triggers may specify a list of columns.")
- }
-
- if let when = self.when as? SQLTriggerWhen, when == .instead {
- fatalError("INSTEAD OF UPDATE triggers do not support lists of columns.")
- }
- }
- }
-
- statement.append("OF")
- statement.append(SQLList(columns))
- }
-
- statement.append("ON")
- statement.append(self.table)
-
- if let referencedTable = self.referencedTable, triggerCreateSyntax.contains(.supportsConstraints) {
- statement.append("FROM")
- statement.append(referencedTable)
- }
-
- if let timing = self.timing, triggerCreateSyntax.contains(.supportsConstraints) {
- guard self.isConstraint else {
- fatalError("May only specify SQLTriggerTiming on CONSTRAINT triggers.")
- }
-
- statement.append(timing)
- }
-
- if triggerCreateSyntax.contains(.requiresForEachRow) {
- statement.append(SQLTriggerEach.row)
- } else if triggerCreateSyntax.contains(.supportsForEach) {
- if triggerCreateSyntax.contains(.supportsConstraints), isConstraint {
- statement.append(SQLTriggerEach.row)
- } else if let each = self.each {
- statement.append(each)
- }
+ }
+ assert(!syntax.contains(.supportsBody) || self.body != nil, "Must define a trigger body.")
+ assert(syntax.contains(.supportsBody) || self.procedure != nil, "Must define a trigger procedure.")
+
+ serializer.statement {
+ $0.append("CREATE")
+ if syntax.contains(.supportsConstraints), self.isConstraint { $0.append("CONSTRAINT") }
+ $0.append("TRIGGER", self.name)
+ $0.append(self.when)
+ $0.append(self.event)
+ if let columns = self.columns, !columns.isEmpty, syntax.contains(.supportsUpdateColumns) { $0.append("OF", SQLList(columns)) }
+ $0.append("ON", self.table)
+ if let referencedTable = self.referencedTable, syntax.contains(.supportsConstraints) { $0.append("FROM", referencedTable) }
+ if let timing = self.timing, syntax.contains(.supportsConstraints) { $0.append(timing) }
+ if syntax.contains(.requiresForEachRow) || (syntax.isSuperset(of: [.supportsForEach, .supportsConstraints]) && self.isConstraint) {
+ $0.append(SQLTriggerEach.row)
+ } else if syntax.contains(.supportsForEach), let each = self.each {
+ $0.append(each)
}
-
- if let condition = self.condition, triggerCreateSyntax.contains(.supportsCondition) {
- if let when = self.when as? SQLTriggerWhen, when == .instead, triggerCreateSyntax.contains(.postgreSQLChecks) {
- fatalError("INSTEAD OF triggers do not support WHEN conditions.")
- }
-
- statement.append("WHEN")
-
- let cond = triggerCreateSyntax.contains(.conditionRequiresParentheses) ? SQLGroupExpression(condition) : condition
- statement.append(cond)
+ if let condition = self.condition, syntax.contains(.supportsCondition) {
+ $0.append("WHEN", syntax.contains(.conditionRequiresParentheses) ? SQLGroupExpression(condition) : condition)
}
-
- if let order = order, let orderTriggerName = orderTriggerName, triggerCreateSyntax.contains(.supportsOrder) {
- statement.append(order)
- statement.append(orderTriggerName)
+ if let order = self.order, let orderTriggerName = self.orderTriggerName, syntax.contains(.supportsOrder) {
+ $0.append(order)
+ $0.append(orderTriggerName)
}
-
- if triggerCreateSyntax.contains(.supportsBody) {
- guard let body = body else {
- fatalError("Must define a trigger body.")
- }
-
- statement.append("BEGIN")
- body.forEach { statement.append($0) }
- statement.append("END;")
- } else {
- guard let procedure = procedure else {
- fatalError("Must define a trigger procedure.")
- }
-
- statement.append("EXECUTE PROCEDURE")
- statement.append(procedure)
+ if syntax.contains(.supportsBody), let body = self.body {
+ $0.append("BEGIN")
+ $0.append(SQLList(body, separator: SQLRaw(" ")))
+ $0.append("END;")
+ } else if let procedure = self.procedure {
+ $0.append("EXECUTE PROCEDURE", procedure)
}
}
}
@@ -183,19 +119,13 @@ public enum SQLTriggerWhen: SQLExpression {
case after
case instead
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let str: String
-
switch self {
- case .before:
- str = "BEFORE"
- case .after:
- str = "AFTER"
- case .instead:
- str = "INSTEAD OF"
+ case .before: serializer.write("BEFORE")
+ case .after: serializer.write("AFTER")
+ case .instead: serializer.write("INSTEAD OF")
}
-
- SQLRaw(str).serialize(to: &serializer)
}
}
@@ -205,21 +135,14 @@ public enum SQLTriggerEvent: SQLExpression {
case delete
case truncate
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let str: String
-
switch self {
- case .insert:
- str = "INSERT"
- case .update:
- str = "UPDATE"
- case .delete:
- str = "DELETE"
- case .truncate:
- str = "TRUNCATE"
+ case .insert: serializer.write("INSERT")
+ case .update: serializer.write("UPDATE")
+ case .delete: serializer.write("DELETE")
+ case .truncate: serializer.write("TRUNCATE")
}
-
- SQLRaw(str).serialize(to: &serializer)
}
}
@@ -229,21 +152,14 @@ public enum SQLTriggerTiming: SQLExpression {
case initiallyImmediate
case initiallyDeferred
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let str: String
-
switch self {
- case .deferrable:
- str = "DEFERRABLE"
- case .notDeferrable:
- str = "NOT DEFERRABLE"
- case .initiallyImmediate:
- str = "INITIALLY IMMEDIATE"
- case .initiallyDeferred:
- str = "INITIALLY DEFERRED"
+ case .deferrable: serializer.write("DEFERRABLE")
+ case .notDeferrable: serializer.write("NOT DEFERRABLE")
+ case .initiallyImmediate: serializer.write("INITIALLY IMMEDIATE")
+ case .initiallyDeferred: serializer.write("INITIALLY DEFERRED")
}
-
- SQLRaw(str).serialize(to: &serializer)
}
}
@@ -251,17 +167,12 @@ public enum SQLTriggerEach: SQLExpression {
case row
case statement
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let str: String
-
switch self {
- case .row:
- str = "ROW"
- case .statement:
- str = "STATEMENT"
+ case .row: serializer.write("FOR EACH ROW")
+ case .statement: serializer.write("FOR EACH STATEMENT")
}
-
- SQLRaw("FOR EACH \(str)").serialize(to: &serializer)
}
}
@@ -269,14 +180,11 @@ public enum SQLTriggerOrder: SQLExpression {
case follows
case precedes
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let str: String
-
switch self {
- case .follows: str = "FOLLOWS"
- case .precedes: str = "PRECEDES"
+ case .follows: serializer.write("FOLLOWS")
+ case .precedes: serializer.write("PRECEDES")
}
-
- SQLRaw(str).serialize(to: &serializer)
}
}
diff --git a/Sources/SQLKit/Query/SQLDataType.swift b/Sources/SQLKit/Query/SQLDataType.swift
index bfae0a76..a942095d 100644
--- a/Sources/SQLKit/Query/SQLDataType.swift
+++ b/Sources/SQLKit/Query/SQLDataType.swift
@@ -6,34 +6,28 @@ public enum SQLDataType: SQLExpression {
case text
case real
case blob
+ case custom(any SQLExpression)
@available(*, deprecated, message: "This is a test utility method that was incorrectly made public. Use `.custom()` directly instead.")
+ @inlinable
public static func type(_ string: String) -> Self {
.custom(SQLIdentifier(string))
}
-
- case custom(SQLExpression)
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
- let sql: SQLExpression
+ let sql: any SQLExpression
if let dialect = serializer.dialect.customDataType(for: self) {
sql = dialect
} else {
switch self {
- case .smallint:
- sql = SQLRaw("SMALLINT")
- case .int:
- sql = SQLRaw("INTEGER")
- case .bigint:
- sql = SQLRaw("BIGINT")
- case .text:
- sql = SQLRaw("TEXT")
- case .real:
- sql = SQLRaw("REAL")
- case .blob:
- sql = SQLRaw("BLOB")
- case .custom(let expression):
- sql = expression
+ case .smallint: sql = SQLRaw("SMALLINT")
+ case .int: sql = SQLRaw("INTEGER")
+ case .bigint: sql = SQLRaw("BIGINT")
+ case .text: sql = SQLRaw("TEXT")
+ case .real: sql = SQLRaw("REAL")
+ case .blob: sql = SQLRaw("BLOB")
+ case .custom(let exp): sql = exp
}
}
sql.serialize(to: &serializer)
diff --git a/Sources/SQLKit/Query/SQLDelete.swift b/Sources/SQLKit/Query/SQLDelete.swift
index 3918af61..9ae1ec55 100644
--- a/Sources/SQLKit/Query/SQLDelete.swift
+++ b/Sources/SQLKit/Query/SQLDelete.swift
@@ -1,30 +1,29 @@
/// `DELETE ... FROM` query.
///
-/// See `SQLDeleteBuilder`.
+/// See ``SQLDeleteBuilder``.
public struct SQLDelete: SQLExpression {
/// Identifier of table to delete from.
- public var table: SQLExpression
+ public var table: any SQLExpression
/// If the `WHERE` clause is not present, all records in the table are deleted. If a WHERE clause is supplied,
/// then only those rows for which the WHERE clause boolean expression is true are deleted. Rows for which
/// the expression is false or NULL are retained.
- public var predicate: SQLExpression?
+ public var predicate: (any SQLExpression)?
/// Optionally append a `RETURNING` clause that, where supported, returns the supplied supplied columns.
public var returning: SQLReturning?
/// Creates a new `SQLDelete`.
- public init(table: SQLExpression) {
+ @inlinable
+ public init(table: any SQLExpression) {
self.table = table
}
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
- $0.append("DELETE FROM")
- $0.append(self.table)
+ $0.append("DELETE FROM", self.table)
if let predicate = self.predicate {
- $0.append("WHERE")
- $0.append(predicate)
+ $0.append("WHERE", predicate)
}
if let returning = self.returning {
$0.append(returning)
diff --git a/Sources/SQLKit/Query/SQLDirection.swift b/Sources/SQLKit/Query/SQLDirection.swift
index 4e8d179b..cc6a43b2 100644
--- a/Sources/SQLKit/Query/SQLDirection.swift
+++ b/Sources/SQLKit/Query/SQLDirection.swift
@@ -6,16 +6,13 @@ public enum SQLDirection: SQLExpression {
/// Order in which NOT NULL values come first.
case notNull
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
- case .ascending:
- serializer.write("ASC")
- case .descending:
- serializer.write("DESC")
- case .null:
- serializer.write("NULL")
- case .notNull:
- serializer.write("NOT NULL")
+ case .ascending: serializer.write("ASC")
+ case .descending: serializer.write("DESC")
+ case .null: serializer.write("NULL")
+ case .notNull: serializer.write("NOT NULL")
}
}
}
diff --git a/Sources/SQLKit/Query/SQLDistinct.swift b/Sources/SQLKit/Query/SQLDistinct.swift
index 321334e8..e9d191d1 100644
--- a/Sources/SQLKit/Query/SQLDistinct.swift
+++ b/Sources/SQLKit/Query/SQLDistinct.swift
@@ -1,18 +1,22 @@
public struct SQLDistinct: SQLExpression {
- public let args: [SQLExpression]
+ public let args: [any SQLExpression]
+ @inlinable
public init(_ args: String...) {
- self.args = args.map(SQLIdentifier.init(_:))
+ self.init(args.map(SQLIdentifier.init(_:)))
}
- public init(_ args: SQLExpression...) {
- self.args = args
+ @inlinable
+ public init(_ args: any SQLExpression...) {
+ self.init(args)
}
- public init(_ args: [SQLExpression]) {
+ @inlinable
+ public init(_ args: [any SQLExpression]) {
self.args = args
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
guard !args.isEmpty else { return }
serializer.write("DISTINCT")
@@ -21,6 +25,7 @@ public struct SQLDistinct: SQLExpression {
}
extension SQLDistinct {
+ @inlinable
public static var all: SQLDistinct {
.init(SQLLiteral.all)
}
diff --git a/Sources/SQLKit/Query/SQLDropBehaviour.swift b/Sources/SQLKit/Query/SQLDropBehaviour.swift
index d7c60880..2ebad7df 100644
--- a/Sources/SQLKit/Query/SQLDropBehaviour.swift
+++ b/Sources/SQLKit/Query/SQLDropBehaviour.swift
@@ -9,10 +9,11 @@ public enum SQLDropBehavior: SQLExpression {
/// Automatically drop objects that depend on the table (such as views).
case cascade
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
case .restrict: serializer.write("RESTRICT")
- case .cascade: serializer.write("CASCADE")
+ case .cascade: serializer.write("CASCADE")
}
}
}
diff --git a/Sources/SQLKit/Query/SQLDropEnum.swift b/Sources/SQLKit/Query/SQLDropEnum.swift
index fb5ed618..30aaa169 100644
--- a/Sources/SQLKit/Query/SQLDropEnum.swift
+++ b/Sources/SQLKit/Query/SQLDropEnum.swift
@@ -1,9 +1,9 @@
/// `DROP TYPE` query.
///
-/// See `SQLDropEnumBuilder`.
+/// See ``SQLDropEnumBuilder``.
public struct SQLDropEnum: SQLExpression {
/// Type to drop.
- public let name: SQLExpression
+ public let name: any SQLExpression
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the type does not exist.
@@ -15,13 +15,15 @@ public struct SQLDropEnum: SQLExpression {
public var cascade: Bool
/// Creates a new `SQLDropEnum`.
- public init(name: SQLExpression) {
+ @inlinable
+ public init(name: any SQLExpression) {
self.name = name
self.ifExists = false
self.cascade = false
}
/// See `SQLExpression`.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append("DROP TYPE")
diff --git a/Sources/SQLKit/Query/SQLDropIndex.swift b/Sources/SQLKit/Query/SQLDropIndex.swift
index 0135d6e5..b0472308 100644
--- a/Sources/SQLKit/Query/SQLDropIndex.swift
+++ b/Sources/SQLKit/Query/SQLDropIndex.swift
@@ -1,9 +1,9 @@
/// `DROP INDEX` query.
///
-/// See `SQLDropIndexBuilder`.
+/// See ``SQLDropIndexBuilder``.
public struct SQLDropIndex: SQLExpression {
/// Index to drop.
- public var name: SQLExpression
+ public var name: any SQLExpression
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the index does not exist.
@@ -11,15 +11,16 @@ public struct SQLDropIndex: SQLExpression {
/// The object (usually a table) on which the index exists. Not all databases support specifying
/// this, while others require it.
- public var owningObject: SQLExpression?
+ public var owningObject: (any SQLExpression)?
/// The optional drop behavior clause specifies if objects that depend on the
/// index should also be dropped or not, for databases that support this
/// (either `CASCADE` or `RESTRICT`).
- public var behavior: SQLExpression?
+ public var behavior: (any SQLExpression)?
/// Creates a new `SQLDropIndex`.
- public init(name: SQLExpression) {
+ @inlinable
+ public init(name: any SQLExpression) {
self.name = name
self.ifExists = false
}
@@ -33,8 +34,7 @@ public struct SQLDropIndex: SQLExpression {
}
$0.append(self.name)
if let owningObject = self.owningObject {
- $0.append("ON")
- $0.append(owningObject)
+ $0.append("ON", owningObject)
}
if $0.dialect.supportsDropBehavior {
$0.append(self.behavior ?? SQLDropBehavior.restrict)
diff --git a/Sources/SQLKit/Query/SQLDropTable.swift b/Sources/SQLKit/Query/SQLDropTable.swift
index 02177d85..c6e0a15e 100644
--- a/Sources/SQLKit/Query/SQLDropTable.swift
+++ b/Sources/SQLKit/Query/SQLDropTable.swift
@@ -1,9 +1,9 @@
/// `DROP TABLE` query.
///
-/// See `SQLDropTableBuilder`.
+/// See ``SQLDropTableBuilder``.
public struct SQLDropTable: SQLExpression {
/// Table to drop.
- public let table: SQLExpression
+ public let table: any SQLExpression
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the table does not exist.
@@ -12,21 +12,22 @@ public struct SQLDropTable: SQLExpression {
/// The optional drop behavior clause specifies if objects that depend on the
/// table should also be dropped or not, for databases that support this
/// (either `CASCADE` or `RESTRICT`).
- public var behavior: SQLExpression?
+ public var behavior: (any SQLExpression)?
/// If the "TEMPORARY" keyword occurs between "DROP" and "TABLE" then only temporary tables are dropped,
/// and the drop does not cause an implicit transaction commit.
public var temporary: Bool
- /// Creates a new `SQLDropTable`.
- public init(table: SQLExpression) {
+ /// Creates a new ``SQLDropTable``.
+ @inlinable
+ public init(table: any SQLExpression) {
self.table = table
self.ifExists = false
self.behavior = nil
self.temporary = false
}
- /// See `SQLExpression`.
+ /// See ``SQLExpression/serialize(to:)``.
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
$0.append("DROP")
@@ -43,7 +44,7 @@ public struct SQLDropTable: SQLExpression {
}
$0.append(self.table)
if $0.dialect.supportsDropBehavior {
- $0.append(self.behavior ?? (SQLDropBehavior.restrict as SQLExpression))
+ $0.append(self.behavior ?? (SQLDropBehavior.restrict as any SQLExpression))
}
}
}
diff --git a/Sources/SQLKit/Query/SQLDropTrigger.swift b/Sources/SQLKit/Query/SQLDropTrigger.swift
index 6a1ca8ed..1ca10d80 100644
--- a/Sources/SQLKit/Query/SQLDropTrigger.swift
+++ b/Sources/SQLKit/Query/SQLDropTrigger.swift
@@ -1,12 +1,12 @@
-/// `DROP TYPE` query.
+/// `DROP TRIGGER` query.
///
-/// See `SQLDropTriggerBuilder`.
+/// See ``SQLDropTriggerBuilder``.
public struct SQLDropTrigger: SQLExpression {
/// Trigger to drop.
- public let name: SQLExpression
+ public let name: any SQLExpression
/// The table the trigger is attached to
- public var table: SQLExpression?
+ public var table: (any SQLExpression)?
/// The optional `IF EXISTS` clause suppresses the error that would normally
/// result if the type does not exist.
@@ -17,30 +17,26 @@ public struct SQLDropTrigger: SQLExpression {
/// that depend on those objects.
public var cascade = false
- /// Creates a new `SQLDropTrigger`
- public init(name: SQLExpression) {
+ /// Creates a new ``SQLDropTrigger``.
+ @inlinable
+ public init(name: any SQLExpression) {
self.name = name
}
- /// See `SQLExpression`
+ /// See ``SQLExpression/serialize(to:)``.
public func serialize(to serializer: inout SQLSerializer) {
let dialect = serializer.dialect
let triggerDropSyntax = dialect.triggerSyntax.drop
serializer.statement {
$0.append("DROP TRIGGER")
-
if self.ifExists && dialect.supportsIfExists {
$0.append("IF EXISTS")
}
-
$0.append(self.name)
-
if let table = self.table, triggerDropSyntax.contains(.supportsTableName) {
- $0.append("ON")
- $0.append(table)
+ $0.append("ON", table)
}
-
if self.cascade && triggerDropSyntax.contains(.supportsCascade) {
$0.append("CASCADE")
}
diff --git a/Sources/SQLKit/Query/SQLEnumDataType.swift b/Sources/SQLKit/Query/SQLEnumDataType.swift
index c2d1be95..7d443518 100644
--- a/Sources/SQLKit/Query/SQLEnumDataType.swift
+++ b/Sources/SQLKit/Query/SQLEnumDataType.swift
@@ -1,12 +1,15 @@
extension SQLDataType {
+ @inlinable
public static func `enum`(_ cases: String...) -> Self {
self.enum(cases)
}
+ @inlinable
public static func `enum`(_ cases: [String]) -> Self {
self.enum(cases.map { SQLLiteral.string($0) })
}
- public static func `enum`(_ cases: [SQLExpression]) -> Self {
+ @inlinable
+ public static func `enum`(_ cases: [any SQLExpression]) -> Self {
self.custom(SQLEnumDataType(cases: cases))
}
}
@@ -14,14 +17,17 @@ extension SQLDataType {
public struct SQLEnumDataType: SQLExpression {
/// The possible values of the enum type.
///
- /// Commonly implemented as a `SQLGroupExpression`
- var cases: [SQLExpression]
+ /// Commonly implemented as a ``SQLGroupExpression``.
+ @usableFromInline
+ var cases: [any SQLExpression]
+ @inlinable
public init(cases: [String]) {
- self.cases = cases.map { SQLLiteral.string($0) }
+ self.init(cases: cases.map { SQLLiteral.string($0) })
}
- public init(cases: [SQLExpression]) {
+ @inlinable
+ public init(cases: [any SQLExpression]) {
self.cases = cases
}
diff --git a/Sources/SQLKit/Query/SQLExcludedColumn.swift b/Sources/SQLKit/Query/SQLExcludedColumn.swift
index 14d8df4d..efcecab8 100644
--- a/Sources/SQLKit/Query/SQLExcludedColumn.swift
+++ b/Sources/SQLKit/Query/SQLExcludedColumn.swift
@@ -6,23 +6,26 @@
/// to nothing at all.
///
/// - Warning: At the time of this writing, MySQL 8.0's recommended "table alias" syntax for
-/// excluded columns is not implemented, due to there currently being no way for a `SQLDialect`
+/// excluded columns is not implemented, due to there currently being no way for a ``SQLDialect``
/// to vary its contents based on the database server version (for that matter, we don't even
/// have support for retrieving the version from `MySQLNIO`). For now, the deprecared `VALUES()`
/// function is used unconditionally, which will throw warnings starting from MySQL 8.0.20.
/// If this affects your usage, use a raw query or manually construct the necessary expressions
/// to specify and use the alias for now.
public struct SQLExcludedColumn: SQLExpression {
- public var name: SQLExpression
+ public var name: any SQLExpression
+ @inlinable
public init(_ name: String) {
self.init(SQLColumn(name))
}
- public init(_ name: SQLExpression) {
+ @inlinable
+ public init(_ name: any SQLExpression) {
self.name = name
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch serializer.dialect.upsertSyntax {
case .standard:
diff --git a/Sources/SQLKit/Query/SQLForeignKey.swift b/Sources/SQLKit/Query/SQLForeignKey.swift
index 125f992e..89a1e299 100644
--- a/Sources/SQLKit/Query/SQLForeignKey.swift
+++ b/Sources/SQLKit/Query/SQLForeignKey.swift
@@ -1,18 +1,19 @@
/// `FOREIGN KEY` clause.
public struct SQLForeignKey: SQLExpression {
- public let table: SQLExpression
+ public let table: any SQLExpression
- public let columns: [SQLExpression]
+ public let columns: [any SQLExpression]
- public let onDelete: SQLExpression?
+ public let onDelete: (any SQLExpression)?
- public let onUpdate: SQLExpression?
+ public let onUpdate: (any SQLExpression)?
+ @inlinable
public init(
- table: SQLExpression,
- columns: [SQLExpression],
- onDelete: SQLExpression?,
- onUpdate: SQLExpression?
+ table: any SQLExpression,
+ columns: [any SQLExpression],
+ onDelete: (any SQLExpression)?,
+ onUpdate: (any SQLExpression)?
) {
self.table = table
self.columns = columns
diff --git a/Sources/SQLKit/Query/SQLForeignKeyAction.swift b/Sources/SQLKit/Query/SQLForeignKeyAction.swift
index 6c70ed82..7e99ce37 100644
--- a/Sources/SQLKit/Query/SQLForeignKeyAction.swift
+++ b/Sources/SQLKit/Query/SQLForeignKeyAction.swift
@@ -1,4 +1,4 @@
-/// RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT
+/// `RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT`
public enum SQLForeignKeyAction: SQLExpression {
/// Produce an error indicating that the deletion or update would create a foreign key constraint violation.
/// If the constraint is deferred, this error will be produced at constraint check time if there still exist any referencing rows.
@@ -18,6 +18,7 @@ public enum SQLForeignKeyAction: SQLExpression {
/// (There must be a row in the referenced table matching the default values, if they are not null, or the operation will fail.)
case setDefault
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
case .noAction: serializer.write("NO ACTION")
diff --git a/Sources/SQLKit/Query/SQLFunction.swift b/Sources/SQLKit/Query/SQLFunction.swift
index f8d101f8..42fd4062 100644
--- a/Sources/SQLKit/Query/SQLFunction.swift
+++ b/Sources/SQLKit/Query/SQLFunction.swift
@@ -1,21 +1,24 @@
public struct SQLFunction: SQLExpression {
public let name: String
- public let args: [SQLExpression]
-
+ public let args: [any SQLExpression]
+ @inlinable
public init(_ name: String, args: String...) {
self.init(name, args: args.map { SQLIdentifier($0) })
}
+ @inlinable
public init(_ name: String, args: [String]) {
self.init(name, args: args.map { SQLIdentifier($0) })
}
- public init(_ name: String, args: SQLExpression...) {
+ @inlinable
+ public init(_ name: String, args: any SQLExpression...) {
self.init(name, args: args)
}
- public init(_ name: String, args: [SQLExpression] = []) {
+ @inlinable
+ public init(_ name: String, args: [any SQLExpression] = []) {
self.name = name
self.args = args
}
@@ -27,12 +30,14 @@ public struct SQLFunction: SQLExpression {
}
extension SQLFunction {
- public static func coalesce(_ expressions: [SQLExpression]) -> SQLFunction {
- return .init("COALESCE", args: expressions)
+ @inlinable
+ public static func coalesce(_ expressions: [any SQLExpression]) -> SQLFunction {
+ .init("COALESCE", args: expressions)
}
/// Convenience for creating a `COALESCE(foo)` function call (returns the first non-null expression).
- public static func coalesce(_ exprs: SQLExpression...) -> SQLFunction {
- return self.coalesce(exprs)
+ @inlinable
+ public static func coalesce(_ exprs: any SQLExpression...) -> SQLFunction {
+ self.coalesce(exprs)
}
}
diff --git a/Sources/SQLKit/Query/SQLGroupExpression.swift b/Sources/SQLKit/Query/SQLGroupExpression.swift
index f92a0411..b092f4d6 100644
--- a/Sources/SQLKit/Query/SQLGroupExpression.swift
+++ b/Sources/SQLKit/Query/SQLGroupExpression.swift
@@ -1,11 +1,13 @@
public struct SQLGroupExpression: SQLExpression {
- public let expressions: [SQLExpression]
+ public let expressions: [any SQLExpression]
- public init(_ expression: SQLExpression) {
+ @inlinable
+ public init(_ expression: any SQLExpression) {
self.expressions = [expression]
}
- public init(_ expressions: [SQLExpression]) {
+ @inlinable
+ public init(_ expressions: [any SQLExpression]) {
self.expressions = expressions
}
diff --git a/Sources/SQLKit/Query/SQLIdentifier.swift b/Sources/SQLKit/Query/SQLIdentifier.swift
index edddea61..132b630e 100644
--- a/Sources/SQLKit/Query/SQLIdentifier.swift
+++ b/Sources/SQLKit/Query/SQLIdentifier.swift
@@ -4,10 +4,12 @@ public struct SQLIdentifier: SQLExpression {
public var string: String
/// Creates a new `SQLIdentifier`.
+ @inlinable
public init(_ string: String) {
self.string = string
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.dialect.identifierQuote.serialize(to: &serializer)
serializer.write(self.string)
@@ -16,6 +18,7 @@ public struct SQLIdentifier: SQLExpression {
}
extension SQLIdentifier: ExpressibleByStringLiteral {
+ @inlinable
public init(stringLiteral value: StringLiteralType) {
self.init(value)
}
diff --git a/Sources/SQLKit/Query/SQLInsert.swift b/Sources/SQLKit/Query/SQLInsert.swift
index dc556733..0cda25cc 100644
--- a/Sources/SQLKit/Query/SQLInsert.swift
+++ b/Sources/SQLKit/Query/SQLInsert.swift
@@ -1,17 +1,17 @@
/// `INSERT INTO ...` statement.
///
-/// See `SQLInsertBuilder`.
+/// See ``SQLInsertBuilder``.
public struct SQLInsert: SQLExpression {
- public var table: SQLExpression
+ public var table: any SQLExpression
/// Array of column identifiers to insert values for.
- public var columns: [SQLExpression]
+ public var columns: [any SQLExpression]
/// Two-dimensional array of values to insert. The count of each nested array _must_
/// be equal to the count of `columns`.
///
/// Use the `DEFAULT` literal to omit a value and that is specified as a column.
- public var values: [[SQLExpression]]
+ public var values: [[any SQLExpression]]
/// A unique key conflict resolution strategy.
public var conflictStrategy: SQLConflictResolutionStrategy?
@@ -20,7 +20,8 @@ public struct SQLInsert: SQLExpression {
public var returning: SQLReturning?
/// Creates a new `SQLInsert`.
- public init(table: SQLExpression) {
+ @inlinable
+ public init(table: any SQLExpression) {
self.table = table
self.columns = []
self.values = []
diff --git a/Sources/SQLKit/Query/SQLJoin.swift b/Sources/SQLKit/Query/SQLJoin.swift
index 3b0c7f5b..606cbaaf 100644
--- a/Sources/SQLKit/Query/SQLJoin.swift
+++ b/Sources/SQLKit/Query/SQLJoin.swift
@@ -1,13 +1,14 @@
///// `JOIN` clause.
public struct SQLJoin: SQLExpression {
- public var method: SQLExpression
+ public var method: any SQLExpression
- public var table: SQLExpression
+ public var table: any SQLExpression
- public var expression: SQLExpression
+ public var expression: any SQLExpression
/// Creates a new `SQLJoin`.
- public init(method: SQLExpression, table: SQLExpression, expression: SQLExpression) {
+ @inlinable
+ public init(method: any SQLExpression, table: any SQLExpression, expression: any SQLExpression) {
self.method = method
self.table = table
self.expression = expression
diff --git a/Sources/SQLKit/Query/SQLJoinMethod.swift b/Sources/SQLKit/Query/SQLJoinMethod.swift
index 9b932b92..1d13dc2f 100644
--- a/Sources/SQLKit/Query/SQLJoinMethod.swift
+++ b/Sources/SQLKit/Query/SQLJoinMethod.swift
@@ -4,6 +4,7 @@ public enum SQLJoinMethod: SQLExpression {
case left
case right
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
case .inner: serializer.write("INNER")
diff --git a/Sources/SQLKit/Query/SQLList.swift b/Sources/SQLKit/Query/SQLList.swift
index d0d6c35d..964eb7df 100644
--- a/Sources/SQLKit/Query/SQLList.swift
+++ b/Sources/SQLKit/Query/SQLList.swift
@@ -1,8 +1,9 @@
public struct SQLList: SQLExpression {
- public var expressions: [SQLExpression]
- public var separator: SQLExpression
+ public var expressions: [any SQLExpression]
+ public var separator: any SQLExpression
- public init(_ expressions: [SQLExpression], separator: SQLExpression = SQLRaw(", ")) {
+ @inlinable
+ public init(_ expressions: [any SQLExpression], separator: any SQLExpression = SQLRaw(", ")) {
self.expressions = expressions
self.separator = separator
}
diff --git a/Sources/SQLKit/Query/SQLLiteral.swift b/Sources/SQLKit/Query/SQLLiteral.swift
index 46e73e69..e1066f0c 100644
--- a/Sources/SQLKit/Query/SQLLiteral.swift
+++ b/Sources/SQLKit/Query/SQLLiteral.swift
@@ -3,21 +3,22 @@ public enum SQLLiteral: SQLExpression {
/// *
case all
- /// Creates a new `SQLLiteral` from a string.
+ /// Creates a new ``SQLLiteral`` from a string.
case string(String)
- /// Creates a new `SQLLiteral` from a numeric string (no quotes).
+ /// Creates a new ``SQLLiteral`` from a numeric string (no quotes).
case numeric(String)
- /// Creates a new null `SQLLiteral`, i.e., `NULL`.
+ /// Creates a new null ``SQLLiteral``, i.e., `NULL`.
case null
- /// Creates a new default `SQLLiteral` literal, i.e., `DEFAULT` or sometimes `NULL`.
+ /// Creates a new default ``SQLLiteral`` literal, i.e., `DEFAULT` or sometimes `NULL`.
case `default`
- /// Creates a new boolean `SQLLiteral`, i.e., `FALSE` or sometimes `0`.
+ /// Creates a new boolean ``SQLLiteral``, i.e., `FALSE` or sometimes `0`.
case boolean(Bool)
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
switch self {
case .all:
diff --git a/Sources/SQLKit/Query/SQLLockingClause.swift b/Sources/SQLKit/Query/SQLLockingClause.swift
index a8349d60..ef5fe6fe 100644
--- a/Sources/SQLKit/Query/SQLLockingClause.swift
+++ b/Sources/SQLKit/Query/SQLLockingClause.swift
@@ -12,6 +12,7 @@ public enum SQLLockingClause: SQLExpression {
case share
/// See ``SQLExpression/serialize(to:)``.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.statement {
switch self {
diff --git a/Sources/SQLKit/Query/SQLOrderBy.swift b/Sources/SQLKit/Query/SQLOrderBy.swift
index 7cc57502..77e74bd4 100644
--- a/Sources/SQLKit/Query/SQLOrderBy.swift
+++ b/Sources/SQLKit/Query/SQLOrderBy.swift
@@ -1,15 +1,17 @@
/// `ORDER BY` clause.
public struct SQLOrderBy: SQLExpression {
- public var expression: SQLExpression
+ public var expression: any SQLExpression
- public var direction: SQLExpression
+ public var direction: any SQLExpression
/// Creates a new `SQLOrderBy`.
- public init(expression: SQLExpression, direction: SQLExpression) {
+ @inlinable
+ public init(expression: any SQLExpression, direction: any SQLExpression) {
self.expression = expression
self.direction = direction
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
self.expression.serialize(to: &serializer)
serializer.write(" ")
diff --git a/Sources/SQLKit/Query/SQLQueryString.swift b/Sources/SQLKit/Query/SQLQueryString.swift
index 08dd3cdb..52a5dfa1 100644
--- a/Sources/SQLKit/Query/SQLQueryString.swift
+++ b/Sources/SQLKit/Query/SQLQueryString.swift
@@ -1,7 +1,9 @@
public struct SQLQueryString {
- var fragments: [SQLExpression]
+ @usableFromInline
+ var fragments: [any SQLExpression]
/// Create a query string from a plain string containing raw SQL.
+ @inlinable
public init(_ string: S) {
self.fragments = [SQLRaw(string.description)]
}
@@ -9,6 +11,7 @@ public struct SQLQueryString {
extension SQLQueryString: ExpressibleByStringLiteral {
/// See `ExpressibleByStringLiteral.init(stringLiteral:)`
+ @inlinable
public init(stringLiteral value: String) {
self.init(value)
}
@@ -16,6 +19,7 @@ extension SQLQueryString: ExpressibleByStringLiteral {
extension SQLQueryString: ExpressibleByStringInterpolation {
/// See `ExpressibleByStringInterpolation.init(stringInterpolation:)`
+ @inlinable
public init(stringInterpolation: SQLQueryString) {
self.fragments = stringInterpolation.fragments
}
@@ -23,12 +27,14 @@ extension SQLQueryString: ExpressibleByStringInterpolation {
extension SQLQueryString: StringInterpolationProtocol {
/// See `StringInterpolationProtocol.init(literalCapacity:interpolationCount:)`
+ @inlinable
public init(literalCapacity: Int, interpolationCount: Int) {
self.fragments = []
}
/// Adds raw SQL to the string. Despite the use of the term "literal" dictated by the interpolation protocol, this
/// produces `SQLRaw` content, _not_ SQL string literals.
+ @inlinable
public mutating func appendLiteral(_ literal: String) {
self.fragments.append(SQLRaw(literal))
}
@@ -36,40 +42,47 @@ extension SQLQueryString: StringInterpolationProtocol {
/// Adds an interpolated string of raw SQL. Despite the use of the term "literal" dictated by the interpolation
/// protocol, this produces `SQLRaw` content, _not_ SQL string literals.
@available(*, deprecated, message: "Use 'raw' label")
+ @inlinable
public mutating func appendInterpolation(_ literal: String) {
self.fragments.append(SQLRaw(literal))
}
/// Adds an interpolated string of raw SQL. Despite the use of the term "literal" dictated by the interpolation
/// protocol, this produces `SQLRaw` content, _not_ SQL string literals.
+ @inlinable
public mutating func appendInterpolation(raw value: String) {
self.fragments.append(SQLRaw(value.description))
}
- /// Embed an `Encodable` value as a binding in the SQL query.
- public mutating func appendInterpolation(bind value: Encodable) {
+ /// Embed an ``Encodable`` value as a binding in the SQL query.
+ @inlinable
+ public mutating func appendInterpolation(bind value: any Encodable) {
self.fragments.append(SQLBind(value))
}
- /// Embed multiple `Encodable` values as bindings in the SQL query, separating the bind placeholders with commas.
+ /// Embed multiple ``Encodable`` values as bindings in the SQL query, separating the bind placeholders with commas.
/// Most commonly useful when working with the `IN` operator.
- public mutating func appendInterpolation(binds values: [Encodable]) {
+ @inlinable
+ public mutating func appendInterpolation(binds values: [any Encodable]) {
self.fragments.append(SQLList(values.map(SQLBind.init)))
}
/// Embed an integer as a literal value, as if via `SQLLiteral.numeric()`
/// Use this preferentially to ensure values are appropriately represented in the database's dialect.
+ @inlinable
public mutating func appendInterpolation(literal: I) {
self.fragments.append(SQLLiteral.numeric("\(literal)"))
}
/// Embed a `Bool` as a literal value, as if via `SQLLiteral.boolean()`
+ @inlinable
public mutating func appendInterpolation(_ value: Bool) {
self.fragments.append(SQLLiteral.boolean(value))
}
/// Embed a `String` as a literal value, as if via `SQLLiteral.string()`
/// Use this preferentially to ensure string values are appropriately represented in the database's dialect.
+ @inlinable
public mutating func appendInterpolation(literal: String) {
self.fragments.append(SQLLiteral.string(literal))
}
@@ -83,6 +96,7 @@ extension SQLQueryString: StringInterpolationProtocol {
/// Rendered by the SQLite dialect:
///
/// SELECT 'a'||'b'||'c'||'d' FROM nowhere
+ @inlinable
public mutating func appendInterpolation(literals: [String], joinedBy joiner: String) {
self.fragments.append(SQLList(literals.map(SQLLiteral.string(_:)), separator: SQLRaw(joiner)))
}
@@ -90,6 +104,7 @@ extension SQLQueryString: StringInterpolationProtocol {
/// Embed a `String` as an SQL identifier, as if with `SQLIdentifier`
/// Use this preferentially to ensure table names, column names, and other non-keyword identifiers are appropriately
/// represented in the database's dialect.
+ @inlinable
public mutating func appendInterpolation(ident: String) {
self.fragments.append(SQLIdentifier(ident))
}
@@ -105,27 +120,32 @@ extension SQLQueryString: StringInterpolationProtocol {
/// Rendered by the SQLite dialect:
///
/// SELECT "a", "b", "c", "d" FROM "nowhere"
+ @inlinable
public mutating func appendInterpolation(idents: [String], joinedBy joiner: String) {
self.fragments.append(SQLList(idents.map(SQLIdentifier.init(_:)), separator: SQLRaw(joiner)))
}
/// Embed any `SQLExpression` into the string, to be serialized according to its type.
- public mutating func appendInterpolation(_ expression: SQLExpression) {
+ @inlinable
+ public mutating func appendInterpolation(_ expression: any SQLExpression) {
self.fragments.append(expression)
}
}
extension SQLQueryString {
+ @inlinable
public static func +(lhs: SQLQueryString, rhs: SQLQueryString) -> SQLQueryString {
return "\(lhs)\(rhs)"
}
+ @inlinable
public static func +=(lhs: inout SQLQueryString, rhs: SQLQueryString) {
lhs.fragments.append(contentsOf: rhs.fragments)
}
}
extension Array where Element == SQLQueryString {
+ @inlinable
public func joined(separator: String) -> SQLQueryString {
let separator = "\(raw: separator)" as SQLQueryString
return self.first.map { self.dropFirst().lazy.reduce($0) { $0 + separator + $1 } } ?? ""
@@ -133,7 +153,8 @@ extension Array where Element == SQLQueryString {
}
extension SQLQueryString: SQLExpression {
- /// See `SQLExpression.serialize(to:)`
+ /// See ``SQLExpression/serialize(to:)``.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
self.fragments.forEach { $0.serialize(to: &serializer) }
}
diff --git a/Sources/SQLKit/Query/SQLRaw.swift b/Sources/SQLKit/Query/SQLRaw.swift
index f3121346..3ca98435 100644
--- a/Sources/SQLKit/Query/SQLRaw.swift
+++ b/Sources/SQLKit/Query/SQLRaw.swift
@@ -1,13 +1,14 @@
public struct SQLRaw: SQLExpression {
public var sql: String
-
public var binds: [Encodable]
+ @inlinable
public init(_ sql: String, _ binds: [Encodable] = []) {
self.sql = sql
self.binds = binds
}
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
serializer.write(self.sql)
serializer.binds += self.binds
diff --git a/Sources/SQLKit/Query/SQLReturning.swift b/Sources/SQLKit/Query/SQLReturning.swift
index b43b2180..e2ae165f 100644
--- a/Sources/SQLKit/Query/SQLReturning.swift
+++ b/Sources/SQLKit/Query/SQLReturning.swift
@@ -1,15 +1,16 @@
/// `RETURNING ...` statement.
-///
public struct SQLReturning: SQLExpression {
- public var columns: [SQLExpression]
+ public var columns: [any SQLExpression]
/// Creates a new `SQLReturning`.
+ @inlinable
public init(_ column: SQLColumn) {
- self.columns = [column]
+ self.init([column])
}
/// Creates a new `SQLReturning`.
- public init(_ columns: [SQLExpression]) {
+ @inlinable
+ public init(_ columns: [any SQLExpression]) {
self.columns = columns
}
diff --git a/Sources/SQLKit/Query/SQLSelect.swift b/Sources/SQLKit/Query/SQLSelect.swift
index 0291021a..14773f04 100644
--- a/Sources/SQLKit/Query/SQLSelect.swift
+++ b/Sources/SQLKit/Query/SQLSelect.swift
@@ -1,23 +1,23 @@
/// `SELECT` statement.
///
-/// See `SQLSelectBuilder` for building this query.
+/// See ``SQLSelectBuilder``.
public struct SQLSelect: SQLExpression {
- public var columns: [SQLExpression]
- public var tables: [SQLExpression]
+ public var columns: [any SQLExpression]
+ public var tables: [any SQLExpression]
public var isDistinct: Bool
- public var joins: [SQLExpression]
+ public var joins: [any SQLExpression]
- public var predicate: SQLExpression?
+ public var predicate: (any SQLExpression)?
/// Zero or more `GROUP BY` clauses.
- public var groupBy: [SQLExpression]
+ public var groupBy: [any SQLExpression]
- public var having: SQLExpression?
+ public var having: (any SQLExpression)?
/// Zero or more `ORDER BY` clauses.
- public var orderBy: [SQLExpression]
+ public var orderBy: [any SQLExpression]
/// If set, limits the maximum number of results.
public var limit: Int?
@@ -29,10 +29,11 @@ public struct SQLSelect: SQLExpression {
///
/// SELECT ... FOR UPDATE
///
- /// See `SQLSelectBuilder.for` and `SQLLockingClause`.
- public var lockingClause: SQLExpression?
+ /// See ``SQLSelectBuilder/for(_:)`` and ``SQLLockingClause``.
+ public var lockingClause: (any SQLExpression)?
- /// Creates a new `SQLSelect`.
+ /// Creates a new ``SQLSelect``.
+ @inlinable
public init() {
self.columns = []
self.tables = []
diff --git a/Sources/SQLKit/Query/SQLTableConstraintAlgorithm.swift b/Sources/SQLKit/Query/SQLTableConstraintAlgorithm.swift
index cee37c62..de320fac 100644
--- a/Sources/SQLKit/Query/SQLTableConstraintAlgorithm.swift
+++ b/Sources/SQLKit/Query/SQLTableConstraintAlgorithm.swift
@@ -1,16 +1,16 @@
-/// Table constraint algorithms used by `SQLConstraint`
+/// Table constraint algorithms used by ``SQLCreateTable``.
public enum SQLTableConstraintAlgorithm: SQLExpression {
/// `PRIMARY KEY` table constraint.
- case primaryKey(columns: [SQLExpression])
+ case primaryKey(columns: [any SQLExpression])
/// `UNIQUE` table constraint.
- case unique(columns: [SQLExpression])
+ case unique(columns: [any SQLExpression])
/// `CHECK` table constraint.
- case check(SQLExpression)
+ case check(any SQLExpression)
/// `FOREIGN KEY` table constraint.
- case foreignKey(columns: [SQLExpression], references: SQLExpression)
+ case foreignKey(columns: [any SQLExpression], references: any SQLExpression)
public func serialize(to serializer: inout SQLSerializer) {
switch self {
diff --git a/Sources/SQLKit/Query/SQLUnion.swift b/Sources/SQLKit/Query/SQLUnion.swift
index 931da535..0a4be401 100644
--- a/Sources/SQLKit/Query/SQLUnion.swift
+++ b/Sources/SQLKit/Query/SQLUnion.swift
@@ -3,7 +3,7 @@ public struct SQLUnion: SQLExpression {
public var unions: [(SQLUnionJoiner, SQLSelect)]
/// Zero or more `ORDER BY` clauses.
- public var orderBys: [SQLExpression]
+ public var orderBys: [any SQLExpression]
/// If set, limits the maximum number of results.
public var limit: Int?
@@ -11,6 +11,7 @@ public struct SQLUnion: SQLExpression {
/// If set, offsets the results.
public var offset: Int?
+ @inlinable
public init(initialQuery: SQLSelect, unions: [(SQLUnionJoiner, SQLSelect)] = []) {
self.initialQuery = initialQuery
self.unions = unions
@@ -19,10 +20,12 @@ public struct SQLUnion: SQLExpression {
self.orderBys = []
}
+ @inlinable
public mutating func add(_ query: SQLSelect, all: Bool) {
self.add(query, joiner: .init(type: all ? .unionAll : .union))
}
+ @inlinable
public mutating func add(_ query: SQLSelect, joiner: SQLUnionJoiner) {
self.unions.append((joiner, query))
}
@@ -75,6 +78,7 @@ public struct SQLUnionJoiner: SQLExpression {
public var type: `Type`
@available(*, deprecated, message: "Use .type` instead.")
+ @inlinable
public var all: Bool {
get { [.unionAll, .intersectAll, .exceptAll].contains(self.type) }
set { switch (self.type, newValue) {
@@ -89,10 +93,12 @@ public struct SQLUnionJoiner: SQLExpression {
}
@available(*, deprecated, message: "Use .init(type:)` instead.")
+ @inlinable
public init(all: Bool) {
self.init(type: all ? .unionAll : .union)
}
+ @inlinable
public init(type: `Type`) {
self.type = type
}
diff --git a/Sources/SQLKit/Query/SQLUpdate.swift b/Sources/SQLKit/Query/SQLUpdate.swift
index b848d01d..e14899dd 100644
--- a/Sources/SQLKit/Query/SQLUpdate.swift
+++ b/Sources/SQLKit/Query/SQLUpdate.swift
@@ -1,21 +1,22 @@
/// `UPDATE` statement.
///
-/// See `SQLUpdateBuilder`.
+/// See ``SQLUpdateBuilder``.
public struct SQLUpdate: SQLExpression {
/// Table to update.
- public var table: SQLExpression
+ public var table: any SQLExpression
/// Zero or more identifier: expression pairs to update.
- public var values: [SQLExpression]
+ public var values: [any SQLExpression]
/// Optional predicate to limit updated rows.
- public var predicate: SQLExpression?
+ public var predicate: (any SQLExpression)?
/// Optionally append a `RETURNING` clause that, where supported, returns the supplied supplied columns.
public var returning: SQLReturning?
- /// Creates a new `SQLUpdate`.
- public init(table: SQLExpression) {
+ /// Creates a new ``SQLUpdate``.
+ @inlinable
+ public init(table: any SQLExpression) {
self.table = table
self.values = []
self.predicate = nil
diff --git a/Sources/SQLKit/SQLDatabase.swift b/Sources/SQLKit/SQLDatabase.swift
index d9b01108..d72d3bde 100644
--- a/Sources/SQLKit/SQLDatabase.swift
+++ b/Sources/SQLKit/SQLDatabase.swift
@@ -42,7 +42,7 @@ public protocol SQLDatabase {
/// pool which assigns loops to connections at point of use, or because the underlying implementation
/// is based on Swift Concurrency or some other asynchronous execution technology), it is recommended
/// to return an event loop from ``NIOCore/EventLoopGroup/any()``.
- var eventLoop: EventLoop { get }
+ var eventLoop: any EventLoop { get }
/// The version number the connection reports for itself, provided as a type conforming to the
/// ``SQLDatabaseReportedVersion`` protocol. If the version number is not applicable (such as for
@@ -56,12 +56,12 @@ public protocol SQLDatabase {
/// `libsqlite3` library). A significant part of the motivation to finally add this property comes
/// from a larger desire to enable customizing a given ``SQLDialect``'s configuration based on the
/// actual feature set available at runtime instead of having to hardcode a "safe" baseline.
- var version: SQLDatabaseReportedVersion? { get }
+ var version: (any SQLDatabaseReportedVersion)? { get }
/// The descriptor for the SQL dialect supported by the given database. It is permitted for different
/// connections to the same database to have different dialects, though it's unclear how this would
/// be useful in practice.
- var dialect: SQLDialect { get }
+ var dialect: any SQLDialect { get }
/// The logging level used for reporting queries run on the given database to the database's logger.
/// Defaults to ``Logging/Logger/Level/debug``.
@@ -80,11 +80,9 @@ public protocol SQLDatabase {
/// Requests that the given generic SQL query be serialized and executed on the database, and that
/// the ``onRow`` closure be invoked once for each result row the query returns (if any).
- ///
- /// - Note: See also ``SQLDatabase/execute(sql:_:)-2gf3v``.
func execute(
- sql query: SQLExpression,
- _ onRow: @escaping (SQLRow) -> ()
+ sql query: any SQLExpression,
+ _ onRow: @escaping (any SQLRow) -> ()
) -> EventLoopFuture
}
@@ -109,7 +107,7 @@ extension SQLDatabase {
///
/// 1. A corresponding string of raw SQL in the database's dialect, and,
/// 2. An array of inputs to use as the values of any bound parameters of the query.
- public func serialize(_ expression: SQLExpression) -> (sql: String, binds: [Encodable]) {
+ public func serialize(_ expression: any SQLExpression) -> (sql: String, binds: [any Encodable]) {
var serializer = SQLSerializer(database: self)
expression.serialize(to: &serializer)
return (serializer.sql, serializer.binds)
@@ -119,7 +117,7 @@ extension SQLDatabase {
extension SQLDatabase {
/// Returns a ``SQLDatabase`` which is exactly the same database as the original, except that
/// all logging done to the new ``SQLDatabase`` will go to the specified ``Logger`` instead.
- public func logging(to logger: Logger) -> SQLDatabase {
+ public func logging(to logger: Logger) -> any SQLDatabase {
CustomLoggerSQLDatabase(database: self, logger: logger)
}
}
@@ -129,12 +127,14 @@ extension SQLDatabase {
///
/// - Note: Since ``SQLDatabase/logging(to:)`` returns a generic ``SQLDatabase``, this type's
/// actual implementation need not be part of the public API.
-private struct CustomLoggerSQLDatabase: SQLDatabase {
- let database: SQLDatabase
+private struct CustomLoggerSQLDatabase: SQLDatabase {
+ let database: D
let logger: Logger
- var eventLoop: EventLoop { self.database.eventLoop }
- var version: SQLDatabaseReportedVersion? { self.database.version }
- var dialect: SQLDialect { self.database.dialect }
- func execute(sql query: SQLExpression, _ onRow: @escaping (SQLRow) -> ()) -> EventLoopFuture { self.database.execute(sql: query, onRow) }
+ var eventLoop: any EventLoop { self.database.eventLoop }
+ var version: (any SQLDatabaseReportedVersion)? { self.database.version }
+ var dialect: any SQLDialect { self.database.dialect }
+ func execute(sql query: any SQLExpression, _ onRow: @escaping (any SQLRow) -> ()) -> EventLoopFuture {
+ self.database.execute(sql: query, onRow)
+ }
var queryLogLevel: Logger.Level? { self.database.queryLogLevel }
}
diff --git a/Sources/SQLKit/SQLDatabaseReportedVersion.swift b/Sources/SQLKit/SQLDatabaseReportedVersion.swift
index 859beb11..d30c1691 100644
--- a/Sources/SQLKit/SQLDatabaseReportedVersion.swift
+++ b/Sources/SQLKit/SQLDatabaseReportedVersion.swift
@@ -46,6 +46,7 @@ extension SQLDatabaseReportedVersion {
/// - Parameters:
/// - otherVersion: The version to compare against. `type(of: self)` must be the same as `type(of: otherVersion)`.
/// - Returns: `true` if `otherVersion` is greater than `self`, otherwise `false`.
+ @inlinable
public func isNotNewer(than otherVersion: SQLDatabaseReportedVersion) -> Bool {
self.isEqual(to: otherVersion) || self.isOlder(than: otherVersion)
}
@@ -57,6 +58,7 @@ extension SQLDatabaseReportedVersion {
/// - Parameters:
/// - otherVersion: The version to compare against. `type(of: self)` must be the same as `type(of: otherVersion)`.
/// - Returns: `true` if `otherVersion` is equal to or less than `self`, otherwise `false`.
+ @inlinable
public func isNewer(than otherVersion: SQLDatabaseReportedVersion) -> Bool {
!self.isNotNewer(than: otherVersion)
}
@@ -68,6 +70,7 @@ extension SQLDatabaseReportedVersion {
/// - Parameters:
/// - otherVersion: The version to compare against. `type(of: self)` must be the same as `type(of: otherVersion)`.
/// - Returns: `true` if `otherVersion` is less than `self`, otherwise `false`.
+ @inlinable
public func isNotOlder(than otherVersion: SQLDatabaseReportedVersion) -> Bool {
!self.isOlder(than: otherVersion)
}
diff --git a/Sources/SQLKit/SQLDialect.swift b/Sources/SQLKit/SQLDialect.swift
index 0a08e333..bac41e3d 100644
--- a/Sources/SQLKit/SQLDialect.swift
+++ b/Sources/SQLKit/SQLDialect.swift
@@ -22,14 +22,14 @@ public protocol SQLDialect {
/// immediately preceding and following each identifier.
///
/// No default is provided.
- var identifierQuote: SQLExpression { get }
+ var identifierQuote: any SQLExpression { get }
/// An expression (usually an `SQLRaw`) giving the character(s) used to quote literal
/// string values which appear in a query, such as enumerator names. The literal quote
/// is placed immediately preceding and following each string literal.
///
/// Defaults to an apostrophe (`'`).
- var literalStringQuote: SQLExpression { get }
+ var literalStringQuote: any SQLExpression { get }
/// `true` if the dialect supports auto-increment for primary key values when inserting
/// new rows, `false` if not. See also ``autoIncrementClause`` and ``autoIncrementFunction``.
@@ -45,7 +45,7 @@ public protocol SQLDialect {
/// ``autoIncrementFunction`` is _not_ `nil`.
///
/// No default is provided.
- var autoIncrementClause: SQLExpression { get }
+ var autoIncrementClause: any SQLExpression { get }
/// An expression inserted in a column definition when a `.primaryKey(autoincrement: true)`
/// constraint is specified for the column. The expression will be immediately preceded by
@@ -59,7 +59,7 @@ public protocol SQLDialect {
///
/// - Note: The design of this and the other autoincrement-released properties is less than
/// ideal, but it's public API and we're stuck with it for now.
- var autoIncrementFunction: SQLExpression? { get }
+ var autoIncrementFunction: (any SQLExpression)? { get }
/// A function which returns an expression to be used as the placeholder for the `position`th
/// bound parameter in a query. The function can ignore the value of `position` if the syntax
@@ -69,20 +69,20 @@ public protocol SQLDialect {
/// the first parameter has position `1`. This value is guaranteed to be greater than zero.
///
/// No default is provided.
- func bindPlaceholder(at position: Int) -> SQLExpression
+ func bindPlaceholder(at position: Int) -> any SQLExpression
/// A function which returns an SQL expression (usually an `SQLRaw`) representing the given
/// literal boolean value.
///
/// No default is provided.
- func literalBoolean(_ value: Bool) -> SQLExpression
+ func literalBoolean(_ value: Bool) -> any SQLExpression
/// An expression (usually an `SQLRaw`) giving the syntax used to express both "use this as
/// the default value" in a column definition and "use the default value for this column" in
/// a value list. ``SQLLiteral.literal`` always serializes to this expression.
///
/// Defaults to `SQLRaw("DEFAULT")`.
- var literalDefault: SQLExpression { get }
+ var literalDefault: any SQLExpression { get }
/// `true` if the dialect supports the `IF EXISTS` modifier for all types of `DROP` queries
/// (such as `SQLDropEnum`, `SQLDropIndex`, `SQLDropTable`, and `SQLDropTrigger`) and the
@@ -129,7 +129,7 @@ public protocol SQLDialect {
/// types and their default definitions.
///
/// Defaults to returning `nil` for all inputs.
- func customDataType(for dataType: SQLDataType) -> SQLExpression?
+ func customDataType(for dataType: SQLDataType) -> (any SQLExpression)?
/// A function which is consulted whenever a constraint name will be serialized into a
/// query. The dialect must return an expression for an identifer which is unique to the
@@ -142,7 +142,7 @@ public protocol SQLDialect {
/// a sufficiently large output size, such as SHA-256, is one possible correct implementation.
///
/// Defaults to returning the input identifier unchanged.
- func normalizeSQLConstraint(identifier: SQLExpression) -> SQLExpression
+ func normalizeSQLConstraint(identifier: any SQLExpression) -> any SQLExpression
/// The type of `UPSERT` syntax supported by the dialect. See ``SQLUpsertSyntax`` for possible
/// values and more information.
@@ -159,12 +159,12 @@ public protocol SQLDialect {
/// A serialization for ``SQLLockingClause/share``, representing a request for a shared "reader"
/// lock on rows retrieved by a `SELECT` query. A `nil` value means the database doesn't
/// support shared locking requests, which causes the locking clause to be silently ignored.
- var sharedSelectLockExpression: SQLExpression? { get }
+ var sharedSelectLockExpression: (any SQLExpression)? { get }
/// A serialization for ``SQLLockingClause/update``, representing a request for an exclusive
/// "writer" lock on rows retrieved by a `SELECT` query. A `nil` value means the database doesn't
/// support exclusive locking requests, which causes the locking clause to be silently ignored.
- var exclusiveSelectLockExpression: SQLExpression? { get }
+ var exclusiveSelectLockExpression: (any SQLExpression)? { get }
}
@@ -175,22 +175,23 @@ public struct SQLAlterTableSyntax {
/// ALTER TABLE table [alterColumnDefinitionClause] column column_definition
///
/// `nil` indicates lack of support for altering existing column definitions.
- public var alterColumnDefinitionClause: SQLExpression?
+ public var alterColumnDefinitionClause: (any SQLExpression)?
/// Expression for altering a column definition's type.
///
/// ALTER TABLE table [alterColumnDefinitionClause] column [alterColumnDefinitionTypeClause] dataType
///
/// `nil` indicates that no extra keyword is required.
- public var alterColumnDefinitionTypeKeyword: SQLExpression?
+ public var alterColumnDefinitionTypeKeyword: (any SQLExpression)?
/// If true, the dialect supports chaining multiple modifications together. If false,
/// the dialect requires separate statements for each change.
public var allowsBatch: Bool
+ @inlinable
public init(
- alterColumnDefinitionClause: SQLExpression? = nil,
- alterColumnDefinitionTypeKeyword: SQLExpression? = nil,
+ alterColumnDefinitionClause: (any SQLExpression)? = nil,
+ alterColumnDefinitionTypeKeyword: (any SQLExpression)? = nil,
allowsBatch: Bool = true
) {
self.alterColumnDefinitionClause = alterColumnDefinitionClause
@@ -308,18 +309,18 @@ public struct SQLUnionFeatures: OptionSet {
/// so as to avoid breaking all existing dialects every time a new requirement is added to the
/// protocol and allow gradual adoption of new capabilities.
extension SQLDialect {
- public var literalDefault: SQLExpression { SQLRaw("DEFAULT") }
- public var literalStringQuote: SQLExpression { SQLRaw("'") }
+ public var literalDefault: any SQLExpression { SQLRaw("DEFAULT") }
+ public var literalStringQuote: any SQLExpression { SQLRaw("'") }
public var supportsIfExists: Bool { true }
- public var autoIncrementFunction: SQLExpression? { nil }
+ public var autoIncrementFunction: (any SQLExpression)? { nil }
public var supportsDropBehavior: Bool { false }
public var supportsReturning: Bool { false }
public var alterTableSyntax: SQLAlterTableSyntax { .init() }
public var triggerSyntax: SQLTriggerSyntax { .init() }
- public func customDataType(for dataType: SQLDataType) -> SQLExpression? { nil }
- public func normalizeSQLConstraint(identifier: SQLExpression) -> SQLExpression { identifier }
+ public func customDataType(for dataType: SQLDataType) -> (any SQLExpression)? { nil }
+ public func normalizeSQLConstraint(identifier: any SQLExpression) -> any SQLExpression { identifier }
public var upsertSyntax: SQLUpsertSyntax { .unsupported }
public var unionFeatures: SQLUnionFeatures { [.union, .unionAll] }
- public var sharedSelectLockExpression: SQLExpression? { nil }
- public var exclusiveSelectLockExpression: SQLExpression? { nil }
+ public var sharedSelectLockExpression: (any SQLExpression)? { nil }
+ public var exclusiveSelectLockExpression: (any SQLExpression)? { nil }
}
diff --git a/Sources/SQLKit/SQLError.swift b/Sources/SQLKit/SQLError.swift
index 43399ce1..53217355 100644
--- a/Sources/SQLKit/SQLError.swift
+++ b/Sources/SQLKit/SQLError.swift
@@ -8,19 +8,19 @@ public protocol SQLError: Error {
/// Types of SQL errors.
public struct SQLErrorType: Equatable {
/// An IO error occured during database query.
- public static var io: SQLErrorType { return .init(code: .io) }
+ public static var io: SQLErrorType { .init(code: .io) }
/// A constraint violation occurred during database query.
- public static var constraint: SQLErrorType { return .init(code: .constraint) }
+ public static var constraint: SQLErrorType { .init(code: .constraint) }
/// Insufficient permissions to perform database query.
- public static var permission: SQLErrorType { return .init(code: .permission) }
+ public static var permission: SQLErrorType { .init(code: .permission) }
/// Invalid syntax encountered in database query.
- public static var syntax: SQLErrorType { return .init(code: .syntax) }
+ public static var syntax: SQLErrorType { .init(code: .syntax) }
/// An unknown error occured while performing database query.
- public static var unknown: SQLErrorType { return .init(code: .unknown) }
+ public static var unknown: SQLErrorType { .init(code: .unknown) }
// MARK: Private
diff --git a/Sources/SQLKit/SQLQueryEncoder.swift b/Sources/SQLKit/SQLQueryEncoder.swift
index c69ecd53..4cad2671 100644
--- a/Sources/SQLKit/SQLQueryEncoder.swift
+++ b/Sources/SQLKit/SQLQueryEncoder.swift
@@ -1,18 +1,4 @@
public struct SQLQueryEncoder {
- public var prefix: String? = nil
- public var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys
- public var nilEncodingStrategy: NilEncodingStrategy = .default
-
- public init() { }
-
- public func encode(_ encodable: E) throws -> [(String, SQLExpression)]
- where E: Encodable
- {
- let encoder = _Encoder(options: options)
- try encodable.encode(to: encoder)
- return encoder.row
- }
-
public enum NilEncodingStrategy {
/// Skips nilable columns with nil values during encoding.
case `default`
@@ -25,7 +11,27 @@ public struct SQLQueryEncoder {
case useDefaultKeys
/// A key encoding strategy that converts camel-case keys to snake-case keys.
case convertToSnakeCase
- case custom(([CodingKey]) -> CodingKey)
+ case custom(([any CodingKey]) -> any CodingKey)
+ }
+
+ public var prefix: String? = nil
+ public var keyEncodingStrategy: KeyEncodingStrategy = .useDefaultKeys
+ public var nilEncodingStrategy: NilEncodingStrategy = .default
+
+ public init() {
+ self.init(prefix: nil, keyEncodingStrategy: .useDefaultKeys, nilEncodingStrategy: .default)
+ }
+
+ init(prefix: String?, keyEncodingStrategy: KeyEncodingStrategy, nilEncodingStrategy: NilEncodingStrategy) {
+ self.prefix = prefix
+ self.keyEncodingStrategy = keyEncodingStrategy
+ self.nilEncodingStrategy = nilEncodingStrategy
+ }
+
+ public func encode(_ encodable: E) throws -> [(String, any SQLExpression)] {
+ let encoder = _Encoder(options: options)
+ try encodable.encode(to: encoder)
+ return encoder.row
}
fileprivate struct _Options {
@@ -37,89 +43,49 @@ public struct SQLQueryEncoder {
/// The options set on the top-level decoder.
fileprivate var options: _Options {
_Options(
- prefix: prefix,
- keyEncodingStrategy: keyEncodingStrategy,
- nilEncodingStrategy: nilEncodingStrategy)
+ prefix: self.prefix,
+ keyEncodingStrategy: self.keyEncodingStrategy,
+ nilEncodingStrategy: self.nilEncodingStrategy)
}
}
private final class _Encoder: Encoder {
fileprivate let options: SQLQueryEncoder._Options
+ var row: [(String, any SQLExpression)] = []
+ var codingPath: [any CodingKey] { [] }
+ var userInfo: [CodingUserInfoKey: Any] { [:] }
- var codingPath: [CodingKey] {
- return []
- }
-
- var userInfo: [CodingUserInfoKey : Any] {
- return [:]
- }
-
- var row: [(String, SQLExpression)]
-
- init(options: SQLQueryEncoder._Options) {
- self.row = []
- self.options = options
- }
+ init(options: SQLQueryEncoder._Options) { self.options = options }
- func container(keyedBy type: Key.Type) -> KeyedEncodingContainer where Key : CodingKey {
+ func container(keyedBy: Key.Type) -> KeyedEncodingContainer {
switch options.nilEncodingStrategy {
- case .asNil:
- return KeyedEncodingContainer(_NilColumnKeyedEncoder(self))
- case .default:
- return KeyedEncodingContainer(_KeyedEncoder(self))
+ case .asNil: return KeyedEncodingContainer(_NilColumnKeyedEncoder(encoder: self))
+ case .default: return KeyedEncodingContainer(_KeyedEncoder(encoder: self))
}
}
+ func unkeyedContainer() -> any UnkeyedEncodingContainer { fatalError() }
+ func singleValueContainer() -> any SingleValueEncodingContainer { fatalError() }
- struct _NilColumnKeyedEncoder: KeyedEncodingContainerProtocol
- where Key: CodingKey
- {
- var codingPath: [CodingKey] {
- return []
- }
+ struct _NilColumnKeyedEncoder: KeyedEncodingContainerProtocol {
+ var codingPath: [any CodingKey] { self.encoder.codingPath }
let encoder: _Encoder
- init(_ encoder: _Encoder) {
- self.encoder = encoder
- }
-
func column(for key: Key) -> String {
var encodedKey = key.stringValue
switch self.encoder.options.keyEncodingStrategy {
- case .useDefaultKeys:
- break
- case .convertToSnakeCase:
- encodedKey = _convertToSnakeCase(encodedKey)
- case .custom(let customKeyEncodingFunc):
- encodedKey = customKeyEncodingFunc([key]).stringValue
+ case .useDefaultKeys: break
+ case .convertToSnakeCase: encodedKey = _convertToSnakeCase(encodedKey)
+ case .custom(let customKeyEncodingFunc): encodedKey = customKeyEncodingFunc([key]).stringValue
}
-
- if let prefix = self.encoder.options.prefix {
- return prefix + encodedKey
- } else {
- return encodedKey
- }
- }
-
- mutating func encodeNil(forKey key: Key) throws {
- self.encoder.row.append((self.column(for: key), SQLLiteral.null))
+ if let prefix = self.encoder.options.prefix { return prefix + encodedKey } else { return encodedKey }
}
-
- mutating func encode(_ value: T, forKey key: Key) throws where T : Encodable {
- if let value = value as? SQLExpression {
- self.encoder.row.append((self.column(for: key), value))
- } else {
- self.encoder.row.append((self.column(for: key), SQLBind(value)))
- }
+ mutating func encodeNil(forKey key: Key) throws { self.encoder.row.append((self.column(for: key), SQLLiteral.null)) }
+ mutating func encode(_ value: T, forKey key: Key) throws {
+ self.encoder.row.append((self.column(for: key), (value as? SQLExpression) ?? SQLBind(value)))
}
-
- mutating func _encodeIfPresent(_ value: T?, forKey key: Key) throws where T : Encodable {
- if let value = value {
- try encode(value, forKey: key)
- } else {
- try encodeNil(forKey: key)
- }
+ mutating func _encodeIfPresent(_ value: T?, forKey key: Key) throws {
+ if let value = value { try encode(value, forKey: key) } else { try encodeNil(forKey: key) }
}
-
- mutating func encodeIfPresent(_ value: T?, forKey key: Key) throws where T : Encodable { try _encodeIfPresent(value, forKey: key)}
+ mutating func encodeIfPresent(_ value: T?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key)}
mutating func encodeIfPresent(_ value: Int?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
mutating func encodeIfPresent(_ value: Int8?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
mutating func encodeIfPresent(_ value: Int16?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
@@ -133,96 +99,31 @@ private final class _Encoder: Encoder {
mutating func encodeIfPresent(_ value: Float?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
mutating func encodeIfPresent(_ value: String?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
mutating func encodeIfPresent(_ value: Bool?, forKey key: Key) throws { try _encodeIfPresent(value, forKey: key) }
-
- mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey {
- fatalError()
- }
-
- mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
- fatalError()
- }
-
- mutating func superEncoder() -> Encoder {
- return self.encoder
- }
-
- mutating func superEncoder(forKey key: Key) -> Encoder {
- return self.encoder
- }
+ mutating func nestedContainer(keyedBy: N.Type, forKey: Key) -> KeyedEncodingContainer { fatalError() }
+ mutating func nestedUnkeyedContainer(forKey: Key) -> any UnkeyedEncodingContainer { fatalError() }
+ mutating func superEncoder() -> any Encoder { self.encoder }
+ mutating func superEncoder(forKey key: Key) -> any Encoder { self.encoder }
}
-
- func unkeyedContainer() -> UnkeyedEncodingContainer {
- fatalError()
- }
-
- func singleValueContainer() -> SingleValueEncodingContainer {
- fatalError()
- }
-
- struct _KeyedEncoder: KeyedEncodingContainerProtocol
- where Key: CodingKey
- {
- var codingPath: [CodingKey] {
- return []
- }
+ struct _KeyedEncoder: KeyedEncodingContainerProtocol {
+ var codingPath: [CodingKey] { self.encoder.codingPath }
let encoder: _Encoder
- init(_ encoder: _Encoder) {
- self.encoder = encoder
- }
-
func column(for key: Key) -> String {
var encodedKey = key.stringValue
switch self.encoder.options.keyEncodingStrategy {
- case .useDefaultKeys:
- break
- case .convertToSnakeCase:
- encodedKey = _convertToSnakeCase(encodedKey)
- case .custom(let customKeyEncodingFunc):
- encodedKey = customKeyEncodingFunc([key]).stringValue
- }
-
- if let prefix = self.encoder.options.prefix {
- return prefix + encodedKey
- } else {
- return encodedKey
- }
- }
-
- mutating func encodeNil(forKey key: Key) throws {
- self.encoder.row.append((self.column(for: key), SQLLiteral.null))
- }
-
- mutating func encode(_ value: T, forKey key: Key) throws where T : Encodable {
- if let value = value as? SQLExpression {
- self.encoder.row.append((self.column(for: key), value))
- } else {
- self.encoder.row.append((self.column(for: key), SQLBind(value)))
+ case .useDefaultKeys: break
+ case .convertToSnakeCase: encodedKey = _convertToSnakeCase(encodedKey)
+ case .custom(let customKeyEncodingFunc): encodedKey = customKeyEncodingFunc([key]).stringValue
}
+ if let prefix = self.encoder.options.prefix { return prefix + encodedKey } else { return encodedKey }
}
-
- mutating func nestedContainer(keyedBy keyType: NestedKey.Type, forKey key: Key) -> KeyedEncodingContainer where NestedKey : CodingKey {
- fatalError()
- }
-
- mutating func nestedUnkeyedContainer(forKey key: Key) -> UnkeyedEncodingContainer {
- fatalError()
- }
-
- mutating func superEncoder() -> Encoder {
- return self.encoder
- }
-
- mutating func superEncoder(forKey key: Key) -> Encoder {
- return self.encoder
- }
-
- func unkeyedContainer() -> UnkeyedEncodingContainer {
- fatalError()
- }
-
- func singleValueContainer() -> SingleValueEncodingContainer {
- fatalError()
+ mutating func encodeNil(forKey key: Key) throws { self.encoder.row.append((self.column(for: key), SQLLiteral.null)) }
+ mutating func encode(_ value: T, forKey key: Key) throws {
+ self.encoder.row.append((self.column(for: key), (value as? SQLExpression) ?? SQLBind(value)))
}
+ mutating func nestedContainer(keyedBy: N.Type, forKey: Key) -> KeyedEncodingContainer { fatalError() }
+ mutating func nestedUnkeyedContainer(forKey: Key) -> any UnkeyedEncodingContainer { fatalError() }
+ mutating func superEncoder() -> any Encoder { self.encoder }
+ mutating func superEncoder(forKey: Key) -> any Encoder { self.encoder }
}
}
@@ -234,32 +135,16 @@ private extension _Encoder {
/// If the result of the conversion is a duplicate key, then only one value will be present in the result.
static func _convertToSnakeCase(_ stringKey: String) -> String {
guard !stringKey.isEmpty else { return stringKey }
-
- enum Status {
- case uppercase
- case lowercase
- case number
- }
-
- var status = Status.lowercase
- var snakeCasedString = ""
- var i = stringKey.startIndex
+ enum Status { case uppercase, lowercase }
+ var status = Status.lowercase, snakeCasedString = "", i = stringKey.startIndex
while i < stringKey.endIndex {
- let nextIndex = stringKey.index(i, offsetBy: 1)
-
+ let nextIndex = stringKey.index(after: i)
if stringKey[i].isUppercase {
switch status {
- case .uppercase:
- if nextIndex < stringKey.endIndex {
- if stringKey[nextIndex].isLowercase {
- snakeCasedString.append("_")
- }
- }
- case .lowercase,
- .number:
- if i != stringKey.startIndex {
- snakeCasedString.append("_")
- }
+ case .uppercase where nextIndex < stringKey.endIndex && stringKey[nextIndex].isLowercase,
+ .lowercase where i != stringKey.startIndex:
+ snakeCasedString.append("_")
+ default: break
}
status = .uppercase
snakeCasedString.append(stringKey[i].lowercased())
@@ -267,10 +152,8 @@ private extension _Encoder {
status = .lowercase
snakeCasedString.append(stringKey[i])
}
-
i = nextIndex
}
-
return snakeCasedString
}
}
diff --git a/Sources/SQLKit/SQLSerializer.swift b/Sources/SQLKit/SQLSerializer.swift
index 41a95a69..861416f9 100644
--- a/Sources/SQLKit/SQLSerializer.swift
+++ b/Sources/SQLKit/SQLSerializer.swift
@@ -1,23 +1,28 @@
public struct SQLSerializer {
public var sql: String
- public var binds: [Encodable]
- public let database: SQLDatabase
- public var dialect: SQLDialect {
+ public var binds: [any Encodable]
+ public let database: any SQLDatabase
+
+ @inlinable
+ public var dialect: any SQLDialect {
self.database.dialect
}
- public init(database: SQLDatabase) {
+ @inlinable
+ public init(database: any SQLDatabase) {
self.sql = ""
self.binds = []
self.database = database
}
- public mutating func write(bind encodable: Encodable) {
+ @inlinable
+ public mutating func write(bind encodable: any Encodable) {
self.binds.append(encodable)
self.dialect.bindPlaceholder(at: self.binds.count)
.serialize(to: &self)
}
+ @inlinable
public mutating func write(_ sql: String) {
self.sql += sql
}
diff --git a/Sources/SQLKit/SQLStatement.swift b/Sources/SQLKit/SQLStatement.swift
index 427274fd..828886db 100644
--- a/Sources/SQLKit/SQLStatement.swift
+++ b/Sources/SQLKit/SQLStatement.swift
@@ -1,6 +1,7 @@
extension SQLSerializer {
+ @inlinable
public mutating func statement(_ closure: (inout SQLStatement) -> ()) {
- var sql = SQLStatement(parts: [], database: self.database)
+ var sql = SQLStatement(database: self.database)
closure(&sql)
sql.serialize(to: &self)
}
@@ -9,40 +10,53 @@ extension SQLSerializer {
/// A helper type for building complete SQL statements up from fragments.
/// See also `SQLSerializer.statement(_:)`.
public struct SQLStatement: SQLExpression {
- public var parts: [SQLExpression]
- let database: SQLDatabase
+ public var parts: [any SQLExpression] = []
+
+ @usableFromInline
+ let database: any SQLDatabase
+
+ @usableFromInline
+ init(database: any SQLDatabase) {
+ self.database = database
+ }
- /// Convenience accessor for the database's `SQLDialect`.
+ /// Convenience accessor for the database's ``SQLDialect``.
///
/// - Note: Statement closures can't access the serializer directly due to exclusive access rules.
- public var dialect: SQLDialect {
+ @inlinable
+ public var dialect: any SQLDialect {
self.database.dialect
}
/// Add raw text to the output.
+ @inlinable
public mutating func append(_ raw: String) {
self.append(SQLRaw(raw))
}
- /// Add raw text followed by an `SQLExpression` to tbe output.
+ /// Add raw text followed by an ``SQLExpression`` to tbe output.
///
/// - Note: "Text + expr" pairs appear quite often when building statments.
- public mutating func append(_ raw: String, _ part: SQLExpression) {
+ @inlinable
+ public mutating func append(_ raw: String, _ part: any SQLExpression) {
self.append(raw)
self.append(part)
}
- /// Add an `SQLExpression` of any kind to the output.
- public mutating func append(_ part: SQLExpression) {
+ /// Add an ``SQLExpression`` of any kind to the output.
+ @inlinable
+ public mutating func append(_ part: any SQLExpression) {
self.parts.append(part)
}
/// Add an ``SQLExpression`` of any kind to the output, but only if it isn't `nil`.
- public mutating func append(_ maybePart: SQLExpression?) {
+ @inlinable
+ public mutating func append(_ maybePart: (any SQLExpression)?) {
maybePart.map { self.append($0) }
}
- // See `SQLSerializer.serialize(to:)`.
+ /// See ``SQLExpression/serialize(to:)``.
+ @inlinable
public func serialize(to serializer: inout SQLSerializer) {
for i in self.parts.indices {
if i > self.parts.startIndex {
diff --git a/Sources/SQLKitBenchmark/SQLBenchmark+Enum.swift b/Sources/SQLKitBenchmark/SQLBenchmark+Enum.swift
index 0c00e01c..787d7ddc 100644
--- a/Sources/SQLKitBenchmark/SQLBenchmark+Enum.swift
+++ b/Sources/SQLKitBenchmark/SQLBenchmark+Enum.swift
@@ -62,7 +62,7 @@ extension SQLBenchmarker {
// delete all gas giants
try $0
.delete(from: "planets")
- .where("type", .equal, PlanetType.gasGiant as SQLExpression)
+ .where("type", .equal, PlanetType.gasGiant as any SQLExpression)
.run().wait()
// drop gas giant enum value
diff --git a/Sources/SQLKitBenchmark/SQLBenchmark+Upsert.swift b/Sources/SQLKitBenchmark/SQLBenchmark+Upsert.swift
index bbfdedd6..ae1d40fb 100644
--- a/Sources/SQLKitBenchmark/SQLBenchmark+Upsert.swift
+++ b/Sources/SQLKitBenchmark/SQLBenchmark+Upsert.swift
@@ -26,16 +26,16 @@ extension SQLBenchmarker {
.init("number_of_silly_pointless_fields_in_this_table", dataType: .bigint, constraints: [.default(Int64.max), .notNull]),
] }
// list of column identifiers for the test table
- fileprivate static var testCols: [SQLExpression] { self.testColDefs.map(\.column) }
+ fileprivate static var testCols: [any SQLExpression] { self.testColDefs.map(\.column) }
// generate a row of values for the test table suitable for passing to SQLInsertBuilder
- fileprivate static func testVals(id: Int? = nil, planet: Int, poi: String, setting: Double? = nil, start: Date = Date(), finish: Date? = nil, update: Date? = nil) -> [SQLExpression] { [
+ fileprivate static func testVals(id: Int? = nil, planet: Int, poi: String, setting: Double? = nil, start: Date = Date(), finish: Date? = nil, update: Date? = nil) -> [any SQLExpression] { [
id.map { SQLBind($0) } ?? SQLLiteral.default, SQLBind(planet), SQLBind(poi), SQLBind(setting),
SQLBind(start.timeIntervalSince1970), SQLBind(finish?.timeIntervalSince1970), SQLBind(update?.timeIntervalSince1970),
SQLBind(Int64.random(in: .min ... .max)) // SQLite makes specifying "use the default" hard for no reason
] }
// do an insert of the given rows allowing extra config of the insert, if ok is false then assert that the insert
// errors out otherwise assert that it does not
- fileprivate func testInsert(ok: Bool, _ vals: [SQLExpression], on database: SQLDatabase, file: StaticString = #file, line: UInt = #line, _ moreConfig: (SQLInsertBuilder) -> SQLInsertBuilder = { $0 }) {
+ fileprivate func testInsert(ok: Bool, _ vals: [any SQLExpression], on database: any SQLDatabase, file: StaticString = #filePath, line: UInt = #line, _ moreConfig: (SQLInsertBuilder) -> SQLInsertBuilder = { $0 }) {
if !ok {
XCTAssertThrowsError(
try moreConfig(database.insert(into: Self.testSchema).columns(Self.testCols).values(vals)).run().wait(), "",
@@ -49,7 +49,7 @@ extension SQLBenchmarker {
}
}
// retrieve a count of all rows matching the criteria by the predicate, with the caller configuring the predicate
- fileprivate func testCount(on database: SQLDatabase, _ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) throws -> Int {
+ fileprivate func testCount(on database: any SQLDatabase, _ predicate: (SQLSelectBuilder) -> SQLSelectBuilder) throws -> Int {
try predicate(database.select().column(SQLFunction("COUNT", args: SQLLiteral.all)).from(Self.testSchema))
.all().flatMapThrowing { try $0[0].decode(column: $0[0].allColumns[0], as: Int.self) }.wait()
}
@@ -73,7 +73,7 @@ extension SQLBenchmarker {
/// Tests the "ignore conflicts" functionality. (Technically part of upserts.)
public func testUpserts_ignoreAction() throws {
- try self.runTest {
+ self.runTest {
testInsert(ok: true, Self.testVals(id: 1, planet: 5, poi: "0"), on: $0) { $0.ignoringConflicts(with: ["id"]) }
guard $0.dialect.upsertSyntax != .mysqlLike else { return }
diff --git a/Sources/SQLKitBenchmark/SQLBenchmarker.swift b/Sources/SQLKitBenchmark/SQLBenchmarker.swift
index 17fee5bf..1caa5820 100644
--- a/Sources/SQLKitBenchmark/SQLBenchmarker.swift
+++ b/Sources/SQLKitBenchmark/SQLBenchmarker.swift
@@ -2,9 +2,9 @@ import SQLKit
import XCTest
public final class SQLBenchmarker {
- internal let database: SQLDatabase
+ internal let database: any SQLDatabase
- public init(on database: SQLDatabase) {
+ public init(on database: any SQLDatabase) {
self.database = database
}
@@ -24,8 +24,8 @@ public final class SQLBenchmarker {
internal func runTest(
_ name: String = #function,
- _ test: (SQLDatabase) throws -> ()
- ) throws {
+ _ test: (any SQLDatabase) throws -> ()
+ ) rethrows {
self.database.logger.notice("[SQLBenchmark] Running \(name)...")
try test(self.database)
}
diff --git a/Tests/SQLKitTests/Async/AsyncSQLKitTests.swift b/Tests/SQLKitTests/Async/AsyncSQLKitTests.swift
index 377ac788..6f0d060a 100644
--- a/Tests/SQLKitTests/Async/AsyncSQLKitTests.swift
+++ b/Tests/SQLKitTests/Async/AsyncSQLKitTests.swift
@@ -297,10 +297,10 @@ final class AsyncSQLKitTests: XCTestCase {
func testSimpleJoin() async throws {
try await db.select().column("*")
.from("planets")
- .join("moons", on: "moons.planet_id=planets.id")
+ .join("moons", on: "\(ident: "moons").\(ident: "planet_id")=\(ident: "planets").\(ident: "id")" as SQLQueryString)
.run()
- XCTAssertEqual(db.results[0], "SELECT * FROM `planets` INNER JOIN `moons` ON moons.planet_id=planets.id")
+ XCTAssertEqual(db.results[0], "SELECT * FROM `planets` INNER JOIN `moons` ON `moons`.`planet_id`=`planets`.`id`")
}
func testMessyJoin() async throws {
diff --git a/Tests/SQLKitTests/Async/AsyncSQLKitTriggerTests.swift b/Tests/SQLKitTests/Async/AsyncSQLKitTriggerTests.swift
index 6616495f..62fe4f32 100644
--- a/Tests/SQLKitTests/Async/AsyncSQLKitTriggerTests.swift
+++ b/Tests/SQLKitTests/Async/AsyncSQLKitTriggerTests.swift
@@ -55,7 +55,7 @@ final class AsyncSQLKitTriggerTests: XCTestCase {
db._dialect = dialect
try await db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
- .body(body)
+ .body(self.body.map { SQLRaw($0) })
.order(precedence: .precedes, otherTriggerName: "other")
.run()
XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` FOR EACH ROW PRECEDES `other` BEGIN \(bodyText()) END;")
@@ -67,10 +67,10 @@ final class AsyncSQLKitTriggerTests: XCTestCase {
db._dialect = dialect
try await db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
- .body(body)
- .condition("foo = bar")
+ .body(self.body.map { SQLRaw($0) })
+ .condition("\(ident: "foo") = \(ident: "bar")" as SQLQueryString)
.run()
- XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` WHEN foo = bar BEGIN \(bodyText()) END;")
+ XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` WHEN `foo` = `bar` BEGIN \(bodyText()) END;")
}
func testPostgreSqlTriggerCreates() async throws {
@@ -83,11 +83,11 @@ final class AsyncSQLKitTriggerTests: XCTestCase {
.each(.row)
.isConstraint()
.timing(.initiallyDeferred)
- .condition("foo = bar")
+ .condition("\(ident: "foo") = \(ident: "bar")" as SQLQueryString)
.procedure("qwer")
.referencedTable(SQLIdentifier("galaxies"))
.run()
- XCTAssertEqual(db.results.popLast(), "CREATE CONSTRAINT TRIGGER `foo` AFTER INSERT ON `planet` FROM `galaxies` INITIALLY DEFERRED FOR EACH ROW WHEN (foo = bar) EXECUTE PROCEDURE `qwer`")
+ XCTAssertEqual(db.results.popLast(), "CREATE CONSTRAINT TRIGGER `foo` AFTER INSERT ON `planet` FROM `galaxies` INITIALLY DEFERRED FOR EACH ROW WHEN (`foo` = `bar`) EXECUTE PROCEDURE `qwer`")
}
}
diff --git a/Tests/SQLKitTests/SQLKitTests.swift b/Tests/SQLKitTests/SQLKitTests.swift
index 376c2f5c..f157ce9d 100644
--- a/Tests/SQLKitTests/SQLKitTests.swift
+++ b/Tests/SQLKitTests/SQLKitTests.swift
@@ -317,10 +317,10 @@ final class SQLKitTests: XCTestCase {
func testSimpleJoin() throws {
try db.select().column("*")
.from("planets")
- .join("moons", on: "moons.planet_id=planets.id")
+ .join("moons", on: "\(ident: "moons").\(ident: "planet_id")=\(ident: "planets").\(ident: "id")" as SQLQueryString)
.run().wait()
- XCTAssertEqual(db.results[0], "SELECT * FROM `planets` INNER JOIN `moons` ON moons.planet_id=planets.id")
+ XCTAssertEqual(db.results[0], "SELECT * FROM `planets` INNER JOIN `moons` ON `moons`.`planet_id`=`planets`.`id`")
}
func testMessyJoin() throws {
diff --git a/Tests/SQLKitTests/SQLKitTriggerTests.swift b/Tests/SQLKitTests/SQLKitTriggerTests.swift
index 2c526071..04c5d3b1 100644
--- a/Tests/SQLKitTests/SQLKitTriggerTests.swift
+++ b/Tests/SQLKitTests/SQLKitTriggerTests.swift
@@ -55,7 +55,7 @@ final class SQLKitTriggerTests: XCTestCase {
db._dialect = dialect
try db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
- .body(body)
+ .body(self.body.map { SQLRaw($0) })
.order(precedence: .precedes, otherTriggerName: "other")
.run().wait()
XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` FOR EACH ROW PRECEDES `other` BEGIN \(bodyText()) END;")
@@ -67,10 +67,10 @@ final class SQLKitTriggerTests: XCTestCase {
db._dialect = dialect
try db.create(trigger: "foo", table: "planet", when: .before, event: .insert)
- .body(body)
- .condition("foo = bar")
+ .body(self.body.map { SQLRaw($0) })
+ .condition("\(ident: "foo") = \(ident: "bar")" as SQLQueryString)
.run().wait()
- XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` WHEN foo = bar BEGIN \(bodyText()) END;")
+ XCTAssertEqual(db.results.popLast(), "CREATE TRIGGER `foo` BEFORE INSERT ON `planet` WHEN `foo` = `bar` BEGIN \(bodyText()) END;")
}
func testPostgreSqlTriggerCreates() throws {
@@ -83,11 +83,11 @@ final class SQLKitTriggerTests: XCTestCase {
.each(.row)
.isConstraint()
.timing(.initiallyDeferred)
- .condition("foo = bar")
+ .condition("\(ident: "foo") = \(ident: "bar")" as SQLQueryString)
.procedure("qwer")
.referencedTable(SQLIdentifier("galaxies"))
.run().wait()
- XCTAssertEqual(db.results.popLast(), "CREATE CONSTRAINT TRIGGER `foo` AFTER INSERT ON `planet` FROM `galaxies` INITIALLY DEFERRED FOR EACH ROW WHEN (foo = bar) EXECUTE PROCEDURE `qwer`")
+ XCTAssertEqual(db.results.popLast(), "CREATE CONSTRAINT TRIGGER `foo` AFTER INSERT ON `planet` FROM `galaxies` INITIALLY DEFERRED FOR EACH ROW WHEN (`foo` = `bar`) EXECUTE PROCEDURE `qwer`")
}
}
diff --git a/Tests/SQLKitTests/Utilities.swift b/Tests/SQLKitTests/Utilities.swift
index 7447cfd9..bfdea0dd 100644
--- a/Tests/SQLKitTests/Utilities.swift
+++ b/Tests/SQLKitTests/Utilities.swift
@@ -5,12 +5,10 @@ import Logging
final class TestDatabase: SQLDatabase {
let logger: Logger
- let eventLoop: EventLoop
+ let eventLoop: any EventLoop
var results: [String]
- var bindResults: [[Encodable]]
- var dialect: SQLDialect {
- self._dialect
- }
+ var bindResults: [[any Encodable]]
+ var dialect: any SQLDialect { self._dialect }
var _dialect: GenericDialect
init() {
@@ -21,7 +19,7 @@ final class TestDatabase: SQLDatabase {
self._dialect = GenericDialect()
}
- func execute(sql query: SQLExpression, _ onRow: @escaping (SQLRow) -> ()) -> EventLoopFuture {
+ func execute(sql query: any SQLExpression, _ onRow: @escaping (any SQLRow) -> ()) -> EventLoopFuture {
var serializer = SQLSerializer(database: self)
query.serialize(to: &serializer)
results.append(serializer.sql)
@@ -32,7 +30,7 @@ final class TestDatabase: SQLDatabase {
struct TestRow: SQLRow {
enum Datum { // yes, this is just Optional by another name
- case some(Encodable)
+ case some(any Encodable)
case none
}
@@ -72,23 +70,23 @@ struct TestRow: SQLRow {
struct GenericDialect: SQLDialect {
var name: String { "generic" }
- func bindPlaceholder(at position: Int) -> SQLExpression { SQLRaw("?") }
- func literalBoolean(_ value: Bool) -> SQLExpression { SQLRaw("\(value)") }
+ func bindPlaceholder(at position: Int) -> any SQLExpression { SQLRaw("?") }
+ func literalBoolean(_ value: Bool) -> any SQLExpression { SQLRaw("\(value)") }
var supportsAutoIncrement: Bool = true
var supportsIfExists: Bool = true
var supportsReturning: Bool = true
- var identifierQuote: SQLExpression = SQLRaw("`")
- var literalStringQuote: SQLExpression = SQLRaw("'")
+ var identifierQuote: any SQLExpression = SQLRaw("`")
+ var literalStringQuote: any SQLExpression = SQLRaw("'")
var enumSyntax: SQLEnumSyntax = .inline
- var autoIncrementClause: SQLExpression = SQLRaw("AUTOINCREMENT")
- var autoIncrementFunction: SQLExpression? = nil
+ var autoIncrementClause: any SQLExpression = SQLRaw("AUTOINCREMENT")
+ var autoIncrementFunction: (any SQLExpression)? = nil
var supportsDropBehavior: Bool = false
var triggerSyntax = SQLTriggerSyntax(create: [], drop: [])
var alterTableSyntax = SQLAlterTableSyntax(alterColumnDefinitionClause: SQLRaw("MODIFY"), alterColumnDefinitionTypeKeyword: nil)
var upsertSyntax: SQLUpsertSyntax = .standard
var unionFeatures: SQLUnionFeatures = []
- var sharedSelectLockExpression: SQLExpression? { SQLRaw("FOR SHARE") }
- var exclusiveSelectLockExpression: SQLExpression? { SQLRaw("FOR UPDATE") }
+ var sharedSelectLockExpression: (any SQLExpression)? { SQLRaw("FOR SHARE") }
+ var exclusiveSelectLockExpression: (any SQLExpression)? { SQLRaw("FOR UPDATE") }
mutating func setTriggerSyntax(create: SQLTriggerSyntax.Create = [], drop: SQLTriggerSyntax.Drop = []) {
self.triggerSyntax.create = create