sqlc-dev / sqlc

Generate type-safe code from SQL
https://sqlc.dev
MIT License
12.24k stars 778 forks source link

Support creating new objects for sql.Scanner #3542

Open Cyberax opened 1 month ago

Cyberax commented 1 month ago

What do you want to change?

sqlc has support for custom data types, including pointer types. I was hoping to use it to support custom deserialization for a type that supports the sql.Scanner interface. I tried this:

overrides:
  go:
    overrides:
      - column: customer.val
        go_type:
          import: "github.com/mycompany/app/proto/golang/data"
          type: Customer
        pointer: true

And the generated code looks like this:

func (q *Queries) FindCustomerById(ctx context.Context, id string) (*data.Customer, error) {
    row := q.db.QueryRow(ctx, findCustomerById, id)
    var val *data.Customer
    err := row.Scan(&val)
    return val, err
}

This predictably fails at runtime, because the database driver does not know what to do with a double pointer. It would be nice to add a feature to support creation of new objects:

func (q *Queries) FindCustomerById(ctx context.Context, id string) (*data.Customer, error) {
    row := q.db.QueryRow(ctx, findCustomerById, id)
    var val *data.Customer = new(data.Customer)
    err := row.Scan(&val)
    return val, err
}

I can use values without pointer: true, but it's a pain because Protobuf objects have embedded mutexes. And all of the Protobuf infrastructure is designed to deal with pointers.

If there is a consensus that it's a feature worth implementing, I'll provide a PR with it.

What database engines need to be changed?

PostgreSQL, MySQL, SQLite

What programming language backends need to be changed?

Go

kyleconroy commented 1 month ago

The with: pointer was originally designed for mapping NULL values to nil. This feels like a power-user feature, so maybe we add another field like pointer_new: true? We probably need to workshop the name a bit.

Cyberax commented 1 month ago

Of course. Simply changing the behavior will break the backwards compatibility, so I was planning to add a new field. pointer_new sounds fine to me, or maybe construct_object.

I'll send a PR soon.