-
-
Notifications
You must be signed in to change notification settings - Fork 80
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
Expose query metadata
#504
base: main
Are you sure you want to change the base?
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #504 +/- ##
==========================================
- Coverage 61.39% 61.01% -0.39%
==========================================
Files 125 125
Lines 10149 10291 +142
==========================================
+ Hits 6231 6279 +48
- Misses 3918 4012 +94
🚀 New features to boost your workflow:
|
@fabianfett I've thought a bit more about this and possibly we might want to have another type that wraps |
I'd love this addition! I have queries that should update exactly 1 row. I'd like to check that the update was successful, but I can't check without the metadata. (These queries currently don't return the row itself.) |
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'm not a fan of the proposed API changes: They either get everything into memory, or they force users to consume the rows synchronously. I consider both bad options.
However I do have an alternative in mind:
extension PostgresConnection {
// use this for queries where you don't expect any rows back.
@discardableResult
func execute(_ query: PostgresQuery) async throws -> PostgresQueryMetadata
// use this for queries where you want to consume the rows.
// we can use the `consume` scope to better ensure structured concurrency when consuming the rows.
func query<Result>(
_ query: PostgresQuery,
_ consume: (PostgresRowSequence) async throws -> Result
) async throws -> (Result, PostgresQueryMetadata)
}
@MahdiBM Wdyt?
I honestly don't think this is a problem. We're not really taking any options away.
To be honest this looks better, at least at the first glance. So I'd be happy to move this way. |
d1492cc
to
61da70d
Compare
metadata
in PostgresRowSequence
metadata
@MahdiBM what is the expected timeframe for this change to be included in a release ? Just wondering whether I should wait or fall back to using Connection.query in the interim ? I don't mind using a pre-release branch if there is one since we are in development still. |
@duncangroenewald IIRC you found a solution for now using that PostgresDatabase function that has a |
CodeCov report is not accurate about those funcs in the first 2 listed files not having tests. I've added both unit and integration tests. |
@MahdiBM - OK well if there is any chance of creating a beta branch with this added let me know as I would rather use that than have to go back and simplify things later on. |
5313614
to
f388f5e
Compare
@duncangroenewald I took a look and this branch is up to date with main, so you could temporarily depend on this branch (repo: https://github.com/MahdiBM/postgres-nio.git, branch: mmbm-row-seq-expose-metadata) if you want. |
thanks that branch of yours should be fine - just don't deleted it :) I'll let you know how it goes. |
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.
Quick review
// MARK: Drain on EventLoop | ||
|
||
func drain() -> EventLoopFuture<Void> { | ||
if self.eventLoop.inEventLoop { |
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 already implemented with cancel
.
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.
cancel is not enough:
1- it doesn't wait until the query is over so metadata is available.
If you try to get the metadata too soon, the PostgresNIO code will crash.
2- it doesn't cover all cases that might happen. For example when downstream is waiting for consumer.
That might happen when the user hasn't even tried to consume the rows.
} | ||
} | ||
|
||
private func drain0() -> EventLoopFuture<Void> { |
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 already implemented with cancel0
.
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.
See the other comment.
@@ -1,5 +1,6 @@ | |||
.DS_Store | |||
/.build | |||
/.index-build |
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.
why have you added this one?
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 like a .build directory but for sourcekit-lsp.
In the sourcekit-lsp bundled with swift 6.1 it'll be moved into .build dir itself, but that's not out.
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.
you need to have Swift 6.0.x and have experimental-indexing enabled (e.g. in the VSCode extension or using the sourcekit-lsp config file).
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.
SwiftNIO already has it as well:
https://github.com/apple/swift-nio/blob/main/.gitignore#L3
_ query: PostgresQuery, | ||
logger: Logger, | ||
file: String = #fileID, | ||
line: Int = #line, |
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.
@FranzBusch what is the #isolation
magic that we need here?
let metadata = try await connection.execute(insertionQuery, logger: .psqlTest) | ||
XCTAssertEqual(metadata.rows, rowsCount) |
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 a test i originally wrote myself in another PR. Moved it to use query metadata since that's what should have been done anyway.
@@ -19,20 +19,29 @@ final class PSQLRowStreamTests: XCTestCase { | |||
|
|||
XCTAssertEqual(try stream.all().wait(), []) | |||
XCTAssertEqual(stream.commandTag, "INSERT 0 1") | |||
|
|||
XCTAssertNoThrow(try stream.drain().wait()) |
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.
all these tests are covering real-world use-cases.
we hand the postgres-sequence over to users. If they have fully consumed the rows, a drain()
call should simply just do nothing. Not crash or ....
// Drain should work | ||
XCTAssertThrowsError(try stream.drain().wait()) { | ||
XCTAssertEqual($0 as? PSQLError, expectedError) |
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.
in case of error, drain will just throw the same error.
Though realistically if there is an error users will not reach this far in the code.
// attach consumers | ||
let rowSequence = stream.asyncSequence() | ||
XCTAssertEqual(dataSource.hitDemand, 0) | ||
let drainFuture = stream.drain() | ||
XCTAssertEqual(dataSource.hitDemand, 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.
This might happen when someone is using the new query
function. an async-seq is attached but user might simply ignore it.
// attach consumers | ||
let allFuture = stream.all() | ||
XCTAssertEqual(dataSource.hitDemand, 1) | ||
let drainFuture = stream.drain() | ||
XCTAssertEqual(dataSource.hitDemand, 2) |
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 reckon this case won't actually happen at all with the current query functions and technically should only happen if users are misusing something like the postgres-seq by escaping it and trying to consume it weirdly or in a Task. It hits the case .waitingForAll
code in drain0()
.
Should i remove it or just let it be incase?
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'd personally put a different preconditionFailure
in case .waitingForAll
and try to let users know what they did wrong + delete this test 🤔.
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.
Or maybe we should keep that code for the sake of completeness in drain0()
and having it not be like "only supposed to be used before you want to get the metadata"
Currently the functions that return
PostgresRowSequence
have no way of reporting back themetadata
of the query.This PR exposes the
metadata
.Checklist
query
andexecute
funcs