Skip to content

Releases: vapor/sql-kit

Adds '*' support for table-qualified column select

30 Jun 10:08
dd87127
Compare
Choose a tag to compare
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

11 May 15:01
211ce34
Compare
Choose a tag to compare
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

28 Apr 08:51
07f3fc3
Compare
Choose a tag to compare
This patch was authored by @kemchenj and released by @0xTim.

Fixes the following warnings now produced by the Swift 5.4 compiler:

warning: using 'class' keyword for protocol inheritance is deprecated; use 'AnyObject' instead
public protocol SQLPredicateBuilder: class {
                                     ^~~~~
                                     AnyObject

fix select query without from

06 Mar 17:17
2e1ffbf
Compare
Choose a tag to compare
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

18 Feb 14:56
6401540
Compare
Choose a tag to compare
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

18 Feb 14:56
6401540
Compare
Choose a tag to compare
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)"

12 Feb 10:47
cc04500
Compare
Choose a tag to compare
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

04 Feb 10:00
3453fd7
Compare
Choose a tag to compare
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

11 Jul 19:34
ea9928b
Compare
Choose a tag to compare
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

10 Jul 23:19
49d4aa0
Compare
Choose a tag to compare
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.