-
-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Use coalesce to provide default values for aggregate functions that can result in null #582
Conversation
…an result in null
The tests failed on PostgreSQL with this error: The same issue (or very similar) has come up before: This PR seems to have addressed it vapor/postgres-kit#102 so someone familiar with the past work on this issue might have an idea what to do. @tanner0101 @natanrolnik |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall the changes look great. Unfortunately, semver is hard.
@@ -254,7 +254,9 @@ public protocol QuerySupporting: Database { | |||
/// - parameters: | |||
/// - method: Aggregate method to use. | |||
/// - field: Keys to aggregate. Can be zero. | |||
static func queryAggregate(_ aggregate: QueryAggregate, _ fields: [QueryKey]) -> QueryKey | |||
/// - default: Value to fall back on if the aggregate function returns NULL. | |||
static func queryAggregate<C>(_ aggregate: QueryAggregate, _ fields: [QueryKey], default: C?) -> QueryKey |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is, very unfortunately, a semver-major breaking change. QuerySupporting
is a public protocol, so changing its definition will cause projects that rely on this protocol to stop compiling.
Some ideas around this:
Protocol finagling
Add this method as a new protocol requirement with a default implementation that tries to fail gracefully. SQL+QuerySupporting.swift
will still implement the updated version so everything should work.
The downside here is it makes the code messy and might not work since SQL+QuerySupporting.swift
is also just providing extensions. Swift should try to take the more specific extension, but one can never be sure.
Break the API
All of Vapor's Fluent implementations use the SQL+QuerySupporting.swift
methods, so we would definitely not break those with this change (obviously as they are being tested as a part of CI). But, it's possible other people have created Fluent driver implementations that could be broken by this change. If we were to merge it as is, we need to give them fair warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Either way, we should at least offer an extension with the signature:
static func queryAggregate(_ aggregate: QueryAggregate, _ fields: [QueryKey]) -> QueryKey
To minimize the amount of breakage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if we just give C
the default value of nil
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@calebkleveter If I understand what you mean, any conformers to the protocol would still be required to add conformance for this new method. Even if we add an extension where it has a default value, that just helps to not break callers. Conformers must provide methods for everything required by the protocol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated with a slightly messy (but stable) way to avoid introducing a breaking change.
/// - returns: A `Future` containing the aggregate. | ||
public func aggregate<D, T>(_ method: Database.QueryAggregate, field: KeyPath<Result, T>, as type: D.Type = D.self) -> Future<D> | ||
where D: Decodable | ||
public func aggregate<D, T>(_ method: Database.QueryAggregate, field: KeyPath<Result, T>, as type: D.Type = D.self, default: D?) -> Future<D> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moving from Decodable
to Codable
is a breaking change as well. I can't see how this would affect anyone, but we need to consider this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now resolved. I'm using .literal
instead of .encodable
to avoid having to make the value Encodable
. Using the String(describing:)
initializer feels a little clumsy but it does pass the tests.
@rausnitz btw wanted to update you that vapor/postgres-kit#102 is merged and tagged as 1.1.1. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work w/ the protocols. We should be good to merge after this final issue.
@tanner0101 Can you clarify what the final issue is? |
@tanner0101 How can I go about getting something like this on the agenda for Vapor 4? Once breaking changes aren't a concern, I think it would be much better for functions that use |
} | ||
} | ||
|
||
return .expression(.coalesce(.function(.function(name, args)), .literal(.numeric(String(describing: `default`)))), alias: .identifier("fluentAggregate")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this could risk SQL injection. We need to bind any values the user could input.
@rausnitz the final issue is regarding the Regarding Fluent 4, the new core, FluentKit, has been moved to https://github.com/vapor/fluent-kit. This package going forward will be an integration package for FluentKit and Vapor. An issue on FluentKit regarding aggregate defaults would be appreciated. It should be easy to fix there since we don't have to worry about breaking changes. |
65b186a
to
703ccb0
Compare
@tanner0101 I took another approach that at least builds successfully, but the tests are failing so I'm not sure if it'll actually work. On the postgresql test the issue is |
For Fluent Postgres, the checkout branch needs to be changed from But they will all fail because of the change from
I also think that would be a better solution and that's what Fluent 4 will do. Is there anything that would prevent us from doing that here? I think we could change |
This is related to: vapor/fluent-kit#5 |
Implements the enhancement described in vapor/sql-kit#42