Scan is a Go package for scanning database rows into structs or slices of primitive types.
This project is forked from https://github.com/blockloop/scan and adjusted to be generic, while all dependencies are removed.
go get github.com/goapt/scanimport "github.com/goapt/scan"db, err := sql.Open("sqlite3", "database.sqlite")
rows, err := db.Query("SELECT * FROM persons")
defer rows.Close()
persons, err := scan.Rows[Person](rows)
fmt.Printf("%#v", persons)
// []Person{
// {ID: 1, Name: "brett"},
// {ID: 2, Name: "fred"},
// {ID: 3, Name: "stacy"},
// }rows, err := db.Query("SELECT name FROM persons")
defer rows.Close()
names, err := scan.Rows[string](rows)
fmt.Printf("%#v", names)
// []string{
// "brett",
// "fred",
// "stacy",
// }rows, err := db.Query("SELECT * FROM persons where name = 'brett' LIMIT 1")
person, err := scan.Row[Person](rows)
defer rows.Close()
fmt.Printf("%#v", person)
// Person{ ID: 1, Name: "brett" }rows, err := db.Query("SELECT age FROM persons where name = 'brett' LIMIT 1")
defer rows.Close()
age, err := scan.Row[int8](rows)
fmt.Printf("%d", age)
// 100rows, err := db.Query(`
SELECT person.id,person.name,company.name FROM person
JOIN company on company.id = person.company_id
LIMIT 1
`)
defer rows.Close()
type Person struct {
ID int `db:"person.id"`
Name string `db:"person.name"`
Company struct {
Name string `db:"company.name"`
}
}
person, err := scan.Row[Person](rows)
err = json.NewEncoder(os.Stdout).Encode(&person)
// Output:
// {"ID":1,"Name":"brett","Company":{"Name":"costco"}}By default, column names are mapped to and from database column names using basic title case conversion. You can override this behavior by setting ColumnsMapper and ScannerMapper to custom functions.
Columns scans a struct and returns a string slice of the assumed column names based on the db tag or the struct field name respectively. To avoid assumptions, use ColumnsStrict which will only return the fields tagged with the db tag. Both Columns and ColumnsStrict are variadic. They both accept a string slice of column names to exclude from the list. It is recommended that you cache this slice.
package main
type User struct {
ID int64
Name string
Age int
BirthDate string `db:"bday"`
Zipcode string `db:"-"`
Store struct {
ID int
// ...
}
}
var nobody = new(User)
var userInsertCols = scan.Columns(nobody, "ID")
// []string{ "Name", "Age", "bday" }
var userSelectCols = scan.Columns(nobody)
// []string{ "ID", "Name", "Age", "bday" }Values scans a struct and returns the values associated with the provided columns. Values uses a sync.Map to cache fields of structs to greatly improve the performance of scanning types. The first time a struct is scanned it's exported fields locations are cached. When later retrieving values from the same struct it should be much faster.
user := &User{
ID: 1,
Name: "Brett",
Age: 100,
}
vals := scan.Values([]string{"ID", "Name"}, user)
// []interface{}{ 1, "Brett" }While many other projects support similar features (i.e. sqlx) scan allows you to use any database lib such as the stdlib to write fluent SQL statements and pass the resulting rows to scan for scanning.