Skip to content
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: Ability to pass nil to sql.Row.Scan and sql.Rows.Scan #41607

Open
obwan02 opened this issue Sep 24, 2020 · 4 comments
Open

database/sql: Ability to pass nil to sql.Row.Scan and sql.Rows.Scan #41607

obwan02 opened this issue Sep 24, 2020 · 4 comments
Milestone

Comments

@obwan02
Copy link

@obwan02 obwan02 commented Sep 24, 2020

What version of Go are you using (go version)?

$ go version go1.15.2 linux/amd64

I have been working with the database/sql package and have noticed that it throws an error when trying to pass in nil values to *sql.Rows.Scan or *sql.Row.Scan.

This ability to be able to pass nil to Scan seems trivial as you would only query columns you would actually need. However, sometimes you want to ignore some columns, for cases like using the reflect package, making a package for other developers, or where your SQL statement can be dynamic on the number of columns it returns.

The current way around this issue is a bit messy. You have to make a new variable that is never used again after the call to Scan.
e.g.

type Foo struct {
    bar int
    baz string
}

func myfunc() {
    //...
    data := Foo{}
    row := statement.QueryRow()

    useless := new(interface{})
    err := row.Scan(&data.bar, useless, &data.baz)
   //...
}

In my opinion, this is against the design language of Go, as it is unclear what that variable is being used for. That ability to pass nil into Scan would make more sense.

A counter-point to this issue is that Scan forces developers to think properly about the SQL statement they are writing (or the structs they've created), as otherwise Scan throws an error at the developer saying that it can't accept nil values.
However, it is extremely frustrating writing such messy code in situations where you might need to ignore a column. Such situations could be where the SQL statement can be dynamic in the number of columns it returns, and the code for handling the rows returned has to process this.

A proposition to fix this would be by default Scan would throw an error if a nil value was passed into it, but, if the developer specifies, Scan could take in nil values. This way we could keep the functionality of Go telling you that you've passed in a nil value, and you could re-evaluate your code, but if necessary the developer can disable this error, and ignore columns (by passing in nil values). This could be done multiple ways, such as two separate functions, or a flag in the sql.Row and sql.Rows structs.

I don't think it matters how it's implemented, because any way would surely be better than creating an entirely pointless variable.

@obwan02 obwan02 changed the title [Feature Proposal] Ability to pass nil to sql.Row.Scan and sql.Rows.Scan (database/sql package) database/sql: Ability to pass nil to sql.Row.Scan and sql.Rows.Scan (database/sql package) Sep 24, 2020
@obwan02 obwan02 changed the title database/sql: Ability to pass nil to sql.Row.Scan and sql.Rows.Scan (database/sql package) database/sql: Ability to pass nil to sql.Row.Scan and sql.Rows.Scan Sep 24, 2020
@ianlancetaylor
Copy link
Contributor

@ianlancetaylor ianlancetaylor commented Sep 24, 2020

@kardianos
Copy link
Contributor

@kardianos kardianos commented Sep 28, 2020

If we pass in nil, then remove the ability for certain error checks. Adding an option or such to Scan would add another knob and wouldn't be ideal for maintenance or debugging.

I would like to make Scanning better, for v2.

@kardianos
Copy link
Contributor

@kardianos kardianos commented Sep 28, 2020

So to be clear, I would like to decline this proposal.

@cagedmantis cagedmantis added this to the Backlog milestone Sep 28, 2020
@dolmen
Copy link

@dolmen dolmen commented Jan 12, 2021

@obwan02 You can create a type that implements sql.Scanner to implement your useless scan target.

var IgnoreColumn ignoreColumn

type ignoreColumn struct{}

func (ignoreColumn) Scan(value interface{}) error {
    return nil
}

Here is how to use it in the example:

   err := row.Scan(&data.bar, IgnoreColumn, &data.baz)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
5 participants
You can’t perform that action at this time.