-
Notifications
You must be signed in to change notification settings - Fork 17.9k
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
database/sql: value converter interface for rows.Scan #22544
Comments
CC @kardianos |
I could get behind such an interface potentially. There is some room for optimization there. Right now the interface scans an entire row at a time. And as you say there isn't a way to override the convert / assignment behavior. I haven't put any time into attempting to design such an interface. Do you have any concrete proposal you'd like to put forward? |
You can add interface type ConvertAssigner interface {
ConvertAssign(dest, src interface{}) error
} And check it in func (rs *Rows) Scan(dest ...interface{}) error {
rs.closemu.RLock()
if rs.closed {
rs.closemu.RUnlock()
return errors.New("sql: Rows are closed")
}
rs.closemu.RUnlock()
if rs.lastcols == nil {
return errors.New("sql: Scan called without calling Next")
}
if len(dest) != len(rs.lastcols) {
return fmt.Errorf("sql: expected %d destination arguments in Scan, not %d", len(rs.lastcols), len(dest))
}
convertAssign := defaultConvertAssign // defaultConvertAssign is a new name for the convertAssign function
if rows, ok := rs.rowsi.(ConvertAssigner); ok {
convertAssign = rows.ConvertAssign
}
for i, sv := range rs.lastcols {
if err := convertAssign(dest[i], sv); err != nil {
return fmt.Errorf("sql: Scan error on column index %d: %v", i, err)
}
}
return nil
} Concrete driver can be more effective than |
Do I read it correctly, that the driver could convert its value to the Scan'd destination, and know the type of the destination? Another use case is what we're using interface{} for know: for LOBs we now return a goracle.Lob, and the user must type-assert it; if the driver could get anything, an io.Reader or []byte or string could be filled directly. |
I met the same issue. Lets say that exposing rows, err := db.Query(stmt, args...)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
item, err := myCustomScanner(rows.Values())
if err != nil {
return nil, err
}
// use item...
} |
@kardianos, is there a concrete proposal to be evaluated yet? I'm not clear on the current status. |
@rsc No concrete proposal. The closest is #22544 (comment) and that doesn't document all of the effects it would have / corner cases / ways it is used. The suggested implementation would need some work as well. I'll self assign and look into this in the next month. |
@kardianos why not just expose
Please consider that, |
Exposing |
why not have a generalized interface for Scan that leaves parsing and conversions up to the user (instead of exposing rs.lastcols):
Doing this lets the developer do direct conversions, or marshaling to whatever form they want without needing to buffer the data in arrays |
Myself and my family has been sick a bit this last month. Pushing out again. |
Hi, @kardianos |
@rsc I think there is value in this proposal. I have two proposals for this. I lean to the first one, but that would require an interface assertion for each value which may be expensive, but it would remove the type assertions for the src that convertAssign does anyway because the src.Scan will knows its own type. Define an interface for value sources to be scanned fromRight now a user may pass in to Rows.Scan a sql.Scanner type https://tip.golang.org/pkg/database/sql/#Scanner . But I would argue that this is the wrong direction. Really, the database driver value should have the The only code that would change would be:
Define a generic custom converter a connection can implementIn
Then in Rows.Scan we first attempt to use the ConvertAssign interface defined on the driver.Conn. If it returns a drvier.ErrSkip it uses the existing convertAssign function. This does not address inefficiencies with storing data in an intermediate For Go2: If Rows.Scan was restricted to one all per Next, or if Rows.Scan was allowed to progress the columns it scanned on secuessive Scan calls
But this would not be compatible with Go1. |
I think we should implement both proposals: |
@AlekSi Both proposals would depend on driver authors. Users have a Scanner type they can use today to pass into the Scan method. Proposal 1 would put a scanner call to src (not dest), so the drivers would create a custom type and put that in the row buffer
To contrast with what exists today, a user would need to pass a special type into Scan.
The two proposals have the same effect. The difference is that with proposal 1 you don't need to do a type assertion on the dest, because the dest type is known already. |
Change https://golang.org/cl/107995 mentions this issue: |
@kardianos, does CL 107995 mean that you agree that this proposal should be accepted? Please mark proposal-accepted if so. |
This proposal was accepted over five years ago, but it's not even clear what the proposal is. The only CL link, https://golang.org/cl/107995, is broken (at least for me). I don't see evidence in the code of either of the additions suggested in #22544 (comment), so I assume this was never implemented. Can someone clarify the state of this issue? |
Let's close as superseded by #67546 |
Starting with Go 1.8 database/sql have an interface driver.ColumnConverter for convert value. I think it will be better to provide the interface for Scan too. Currently,
row.Scan
usingconvertAssign
and it very slowThe text was updated successfully, but these errors were encountered: