-
Notifications
You must be signed in to change notification settings - Fork 874
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
Implement scanning iterator AllRowsScanned
#2241
base: master
Are you sure you want to change the base?
Conversation
Yielding one more time will result in runtime panics, if an error occurs like here https://go.dev/play/p/S6VGunr5iZT. |
@felix-roehrich, you are correct. There should be a return statement instead of break -- I've had a feeling that something is off regarding iteration stop, but, as said, not much tests yet. We have two options here:
|
Previous approach could cause panic in case loop broken by developer and error occurred on rows.
Or, actually, not.. If i dont miss anything -- there is no reason for user of iterator to check
So there was single error of mine putting |
Yes, lgtm now. I just feel the name is not appropriate, especially 'Scanned' being past tense, something like I do not believe that breaking the support promise will or should happen, so imo this will have to wait until 1.24 is released. Anyway, thanks for looking at this and let's see what @ jackc says. |
You didn't mention him BTW, there is space between "at" and nickname🤭 Regarding the name, GO proposes the name |
The docs also have a lot of examples without |
True, agree. |
Regarding the Go version, 1.24 is due any day now so that wouldn't be a problem. It would also would have been possible to use build tags to provide backwards compatibility. As far as an iterator in general, perhaps it might be sense to support eventually, but I'm not sold on iterators right now. In this particular implementation, error handling still seems awkward to me. Maybe I'm simply very familiar with the current style of process all the rows and check for errors at the end. But it seems weird to check for error inside of the row handler and then not check after the rows are processed. Also, I'm not sure how this iterator approach is better than a variant of ForEachRow that used templates and RowToFunc. If anything, I would expect that to be a more ergonomic interface for your original use case. Ultimately, before integrating iterator support into core pgx, I'd like to see a use case that has significantly better ergonomics than can be done without it. |
I personally don't understand how to use Regarding |
|
I was referring to making a new variant of ForEachRow that used generics and RowToFunc to solve this. |
Aaah.. func ForScannedRows[T any](rows pgx.Rows, scanner pgx.RowToFunc[T], fn ScannedRowIteratorFn[T]) error {
defer rows.Close()
for rows.Next() {
row, err := scanner(rows)
if err != nil {
return err
}
cont, err := fn(&row)
if err != nil {
return err
}
if !cont {
break
}
}
return rows.Err() //nolint:wrapcheck
} If to repeat functionality from the PR description with that function: func functorIter() (Result, error) {
var rows pgx.Rows
result := Result{}
err := ForScannedRows(rows, pgx.RowToStructByName, func(row *Data) (bool, error) {
result[row.ID] = row
return true, nil
})
if err != nil {
return nil, err
}
return result, nil
} It is "almost" the the same, except the fact that it is not possible to return from iteration handler, as it is different scope. We have to stop iteration and only then return from outer function. Overall i like that approach less. |
Your Returning from the outer scope does seem to be the major functional difference. But the error handling inside the loop just feels so ugly... I think I'd like something like this to get some real world use outside of pgx before committing to any particular interface. Especially, given that the implementation is a standalone function. |
Yes, the upside of this approach is separation of concerns and ability to share logic. |
@jackc if it somehow can ease your perception regarding errors handling within loop. Disregarding iterators -- i can prepare PR introducing generic version of foreach, maybe call it |
I've reviewed this again, and I'm starting to lean toward we will want iterators in pgx eventually, but I'm not ready to commit to any particular interface yet. My reluctance is probably due to my lack of experience with the new custom iterator system. I just don't have enough experience to make a good decision. I'd like to defer this until either I have more confidence in my ability to make a good decision or we have additional feedback of this being used successfully. We can always add it later, but once it is in a pgx release, we can never take it out in the current major version. And since the implementation is small and can be easily copied into any application that needs it, it doesn't actually stop anyone from using it immediately.
I looked at the first example:
This is actually a little scary to me. I'm looking at Go code that is hard to understand. While I am in favor of Go having gained generics and iterators, the naysayers did have a point. Go code can be a lot more opaque than it used to be.
Let's hold off on this too. While I'm not ready to merge iterators just yet, they probably are the idiomatic solution going forward. So if we add something now it will probably be more or less deprecated in a year or less. |
Understood, I feel that. I guess PR would be better kept open, so others would not make duplicated PRs and join the discussion? |
Yes, let's leave this open and review in the future. |
This pull request introduces an iterator that scans rows on the go.
The motivation for this implementation arose from the need to scan rows into a map. The current built-in functions are sub-optimal for this purpose, as they require scanning into a slice first and then iterating again to copy data into the result map.
4ex:
This PR is a proposal and currently lacks examples and thorough testing of the function. Further documentation and tests will be provided if the proposal is considered for merging.
There are several points for discussion: