driver.Valuer is a Go interface for transforming a value before database/sql sends it through a database driver. Its single method, Value, returns a driver.Value and an error. section 10.3
Use Valuer when a database representation should differ from the application representation:
- Define Value() (driver.Value, error) on a custom type. section 10.3
- Return the encoded or otherwise transformed representation from Value. section 10.3.2
- Pass the custom value to ExecContext or another database operation; database/sql invokes Value automatically. section 10.3.3
Encoding a URL before storage
A custom base64String type implements Value by encoding its contents with base64.StdEncoding.EncodeToString. Shorten passes base64String(lnk.URL) to ExecContext, so the database receives the encoded string while the application keeps using a URL-shaped value. Pairing Valuer with sql.Scanner lets Resolve decode the stored string when it scans into a *base64String. section 10.3.2 section 10.3.3
Listing 10.11 Valuer and Scanner (link/sqlite/shortener.go)
package sqlite
import (
"database/sql/driver"
"encoding/base64"
. . .
)
type base64String string
// Value implements the driver.Valuer interface.
func (bs base64String) Value() (driver.Value, error) {
return base64.StdEncoding.EncodeToString([]byte(bs)), nil
}
// Scan implements the sql.Scanner interface.
func (bs *base64String) Scan(src any) error {
ss, ok := src.(string) #1
if !ok {
return fmt.Errorf("decoding: %q is %T, not string", ss, src)
}
dst, err := base64.StdEncoding.DecodeString(ss)
if err != nil {
return fmt.Errorf("decoding %q: %w", ss, err)
}
*bs = base64String(dst) #2
return nil
}
// String implements the fmt.Stringer interface.
func (bs base64String) String() string {
return string(bs)
}
Inspect the complete base64String implementation, including the Value method and its paired Scan method. section 10.3.2
