Releases: vapor/sql-kit
Adds '*' support for table-qualified column select
This patch was authored by @NeedleInAJayStack and released by @0xTim.
This adds '*' support to table-qualified columns in selects that mirrors the non-table-qualified column selects. This is mainly a convenience.
For example, before this PR sql.select().column(table: "table", column: "*")
would resolve to SELECT `table`.`*`
, where '*' was an identifier which was invalid in most cases. Now it will resolve to SELECT `table`.*
Previously to make this query work, it would have to be sql.select().column(SQLColumn(SQLLiteral.all, table: SQLIdentifier("table"))
, which is kind of cumbersome.
Adds `@discardableResult` for building variables
This patch was authored by @NeedleInAJayStack and released by @0xTim.
This allows users to assign the builder object to a variable and append directly to it without encountering Result of call to '___' is unused
warnings. For example, the code below will not generate a warning:
let select = db.sql().select().columns("*").from("table1")
if shouldFilter {
select.where("col1", .equal, 5)
}
let result = select.all()
Previously, the line within the if statement would have a Result of call to 'where' is unused
. To clear the warning, the line would have needed an assignment like:
_ = select.where("col1", .equal, 5)
Fix warnings from Swift 5.4 compiler
fix select query without from
This patch was authored by @Freax13 and released by @gwynne.
Not all SELECT
queries require a FROM
statement. This pr fixes the query generation for queries without FROM
and allows the following example to work:
conn.sql()
.select()
.column(SQLAlias.init(SQLFunction("LAST_INSERT_ID"), as: SQLIdentifier.init("id")))
.first()
Allow Codables with nillable columns to encode as nil, if they wish
This patch was authored by @danramteke and released by @siemensikkema.
In #122 , we resurrected support for nillable columns, however, this caused some issues with downstream clients of this repo.
With this release, we allow clients to opt-in to this behavior.
Here is an example:
struct Gas: Codable {
let name: String
let color: String?
}
let db = TestDatabase()
var serializer = SQLSerializer(database: db)
let insertBuilder = try db.insert(into: "gasses").model(Gas(name: "oxygen", color: nil), nilEncodingStrategy: .asNil)
insertBuilder.insert.serialize(to: &serializer)
XCTAssertEqual(serializer.sql, "INSERT INTO `gasses` (`name`, `color`) VALUES (?, NULL)")
XCTAssertEqual(serializer.binds.count, 1)
XCTAssertEqual(serializer.binds[0] as? String, "oxygen")
Details
There is a new NilEncodingStrategy
right next to the existing KeyEncodingStrategy
. It defaults to the pre-existing behavior that works with Fluent. Clients of this library can pass in a nilEncodingStrategy option to opt-in to the nillable column behavior.
Allow Codables with nillable columns to encode as nil, if they wish
This patch was authored by @danramteke and released by @siemensikkema.
In #122 , we resurrected support for nillable columns, however, this caused some issues with downstream clients of this repo.
With this release, we allow clients to opt-in to this behavior.
Here is an example:
struct Gas: Codable {
let name: String
let color: String?
}
let db = TestDatabase()
var serializer = SQLSerializer(database: db)
let insertBuilder = try db.insert(into: "gasses").model(Gas(name: "oxygen", color: nil), nilEncodingStrategy: .asNil)
insertBuilder.insert.serialize(to: &serializer)
XCTAssertEqual(serializer.sql, "INSERT INTO `gasses` (`name`, `color`) VALUES (?, NULL)")
XCTAssertEqual(serializer.binds.count, 1)
XCTAssertEqual(serializer.binds[0] as? String, "oxygen")
Details
There is a new NilEncodingStrategy
right next to the existing KeyEncodingStrategy
. It defaults to the pre-existing behavior that works with Fluent. Clients of this library can pass in a nilEncodingStrategy option to opt-in to the nillable column behavior.
Revert "Resurrect support for Codables with nullable fields (#122)"
This patch was authored and released by @gwynne.
With apologies to @danramteke, this change reverts #122. Unfortunately, SQLKit's CI does not currently run the test suites of the various database drivers, which would have revealed that this change breaks the ability to use GENERATED BY DEFAULT AS IDENTITY
columns (more commonly called auto-increment in other databases) in PostgreSQL.
For example, when using SQLInsertBuilder.model(_:)
, in the past an unset id
column in the input model would be left entirely unspecified in the resulting query, which is equivalent to specifying it explicitly with the DEFAULT
keyword. With the SQLQueryEncoder
changes in place, the column is given an explicit NULL
value instead, which is not a valid input for a generated identifier column in PostgreSQL.
To make matters more confusing, NULL
is a valid input for MySQL's AUTO_INCREMENT
and SQLite's INTEGER PRIMARY KEY
- and of course, FluentKit does not rely on this particular piece of SQLKit for lowering models to query inputs. The result is that while this change invalidates invariants that Fluent does depend on, the problem only manifests when using PostgresKit via FluentPostgresDriver and the SQLKit layer.
Some additional study is needed to find a solution that does not invalidate Fluent's invariants. Ideally, a complete solution correctly manages the distinction between "unset non-optional", "unset optional", and "set but nil optional" values.
Resurrect support for Codables with nullable fields
This patch was authored by @danramteke and released by @siemensikkema.
When a type conforms to Codable
, its nullable fields will now be encoded as null, which will correctly map to nullable fields in the database table (#122, fixes #121).
Use 'raw' label for sql query interpolation
This patch was authored and released by @tanner0101.
Deprecates the old raw SQL string interpolation and adds a new one with raw
label (#114).
let field = "name"
let results = try db.raw("SELECT \(raw: field) FROM users").all().wait()
Fix `SQLReturningBuilder` not having `SQLQueryFetcher` methods available
This patch was authored by @grahamburgsma and released by @gwynne.
This fixes a mistake in #110, as you can't actually use it as intended and as the examples showed.
Should be able to write a query like this using the query builders:
db.delete()
.from("planets")
.returning("*")
.all()
However currently all()
is not available, as well as the other SQLQueryFetcher
methods like first()
. My initial thought was to make SQLReturningBuilder
extend from SQLQueryFetcher
instead of just SQLQueryBuilder
however that would allow queries like the following:
db.delete()
.from("planets")
.all()
Since that would be misleading and undesired, SQLReturningBuilder
methods now return a SQLReturningResultBuilder
which conforms to SQLQueryFetcher
allowing the desired behaviour, only when used with returning(...)
statements. This does mean that returning(...)
must be the second last method in the chain, but I think that makes sense.