Format
`sql:"[column name],[tag options...]"`
Example:
type User struct{
ID int64 `sql:",pk,auto_increment"`
Name string `sql:",size:10"`
}Tags are case insensitive, however snake_case is preferred. Tags are optional to use when declaring models, sqlgen supports the following tags:
| Tag Name | Alias | Description | Example |
|---|---|---|---|
primary_key |
pk |
Specifies column as primary key, you may also refer to here | sql:",pk" |
auto_increment |
- | Specifies column auto incrementable, you may also refer to here | sql:",auto_increment" |
binary |
- | Specifies column value using serializer encoding.BinaryMarshaler and deserializer encoding.BinaryUnmarshaler |
sql:",binary" |
size |
- | Specifies column data size/length | sql:",size:10" |
encode |
- | Specifies custom sql.Valuer |
sql:",encode:github.com/si3nloong/sqlgen/encoding.MarshalStringSlice" |
decode |
- | Specifies custom sql.Scanner |
sql:",decode:github.com/si3nloong/sqlgen/types.Bool" |
So what kind of Go data type do sqlgen support?
string,[]byte,[]runeint,int8,int16,int32,int64uint,uint8,uint16,uint32,uint64time.Timeslicemapstructpointerof above
Almost everything, as long as it's a valid Go data type, sqlgen has it own logic to map to the respective encoder and decoder.
Example:
type Email string
func (e Email) IsValid() bool {
return true
}
type User struct{
ID int64 `sql:",pk,auto_increment"`
Name string `sql:",size:10"`
Age *int
Email Email
Address struct {
Line1 string
Line2 string
Country struct {
Code string
GeoPoint [2]float64
}
}
CreatedAt time.Time
}It will generated the code like belows:
// Code generated by sqlgen; DO NOT EDIT.
package playground
import (
"time"
"github.com/shopspring/decimal"
"github.com/si3nloong/sqlgen/sequel"
"github.com/si3nloong/sqlgen/sequel/encoding"
)
func (User) TableName() string {
return "user"
}
func (User) HasPK() {}
func (User) IsAutoIncr() {}
func (v *User) ScanAutoIncr(val int64) error {
v.ID = int64(val)
return nil
}
func (v User) PK() (string, int, any) {
return "id", 0, v.ID
}
func (User) Columns() []string {
return []string{"id", "name", "age", "email", "address", "money_in_bank", "created_at"} // 7
}
func (v User) Values() []any {
return []any{
v.Name, // 1 - name
v.AgeValue(), // 2 - age
(string)(v.Email), // 3 - email
encoding.JSONValue(v.Address), // 4 - address
v.MoneyInBank, // 5 - money_in_bank
v.CreatedAt, // 6 - created_at
}
}
func (v *User) Addrs() []any {
if v.Age == nil {
v.Age = new(int)
}
return []any{
&v.ID, // 0 - id
&v.Name, // 1 - name
encoding.IntScanner[int](&v.Age), // 2 - age
encoding.StringScanner[Email](&v.Email), // 3 - email
encoding.JSONScanner(&v.Address), // 4 - address
&v.MoneyInBank, // 5 - money_in_bank
&v.CreatedAt, // 6 - created_at
}
}
func (User) InsertColumns() []string {
return []string{"name", "age", "email", "address", "money_in_bank", "created_at"} // 6
}
func (User) InsertPlaceholders(row int) string {
return "(?,?,?,?,?,?)" // 6
}
func (v User) InsertOneStmt() (string, []any) {
return "INSERT INTO `user` (`name`,`age`,`email`,`address`,`money_in_bank`,`created_at`) VALUES (?,?,?,?,?,?);", []any{v.Name, v.AgeValue(), (string)(v.Email), encoding.JSONValue(v.Address), v.MoneyInBank, v.CreatedAt}
}
func (v User) FindOneByPKStmt() (string, []any) {
return "SELECT `id`,`name`,`age`,`email`,`address`,`money_in_bank`,`created_at` FROM `user` WHERE `id` = ? LIMIT 1;", []any{v.ID}
}
func (v User) UpdateOneByPKStmt() (string, []any) {
return "UPDATE `user` SET `name` = ?,`age` = ?,`email` = ?,`address` = ?,`money_in_bank` = ?,`created_at` = ? WHERE `id` = ?;", []any{v.Name, v.AgeValue(), (string)(v.Email), encoding.JSONValue(v.Address), v.MoneyInBank, v.CreatedAt, v.ID}
}
func (v User) IDValue() any {
return v.ID
}
func (v User) NameValue() any {
return v.Name
}
func (v User) AgeValue() any {
if v.Age != nil {
return (int64)(*v.Age)
}
return nil
}
func (v User) EmailValue() any {
return (string)(v.Email)
}
func (v User) AddressValue() any {
return encoding.JSONValue(v.Address)
}
func (v User) MoneyInBankValue() any {
return v.MoneyInBank
}
func (v User) CreatedAtValue() any {
return v.CreatedAt
}
func (v User) ColumnID() sequel.ColumnClause[int64] {
return sequel.BasicColumn("id", v.ID)
}
func (v User) ColumnName() sequel.ColumnClause[string] {
return sequel.BasicColumn("name", v.Name)
}
func (v User) ColumnAge() sequel.ColumnConvertClause[*int] {
return sequel.Column("age", v.Age, func(val *int) any {
if val != nil {
return (int64)(*val)
}
return nil
})
}
func (v User) ColumnEmail() sequel.ColumnConvertClause[Email] {
return sequel.Column("email", v.Email, func(val Email) any {
return (string)(val)
})
}
type UserAddressField = struct {
Line1 string
Line2 string
Country struct {
Code string
GeoPoint [2]float64
}
}
func (v User) ColumnAddress() sequel.ColumnConvertClause[UserAddressField] {
return sequel.Column("address", v.Address, func(val UserAddressField) any {
return encoding.JSONValue(val)
})
}
func (v User) ColumnMoneyInBank() sequel.ColumnConvertClause[decimal.Decimal] {
return sequel.Column("money_in_bank", v.MoneyInBank, func(val decimal.Decimal) any {
return val
})
}
func (v User) ColumnCreatedAt() sequel.ColumnClause[time.Time] {
return sequel.BasicColumn("created_at", v.CreatedAt)
}If you notice, even the type decimal.Decimal from github.com/shopspring/decimal will work, as long as the type is implement the interface of sql.Scanner and driver.Valuer.