Skip to content

Latest commit

 

History

History
207 lines (184 loc) · 6.93 KB

File metadata and controls

207 lines (184 loc) · 6.93 KB

Declaring Models

Fields Tags

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, []rune
  • int, int8, int16, int32, int64
  • uint, uint8, uint16, uint32, uint64
  • time.Time
  • slice
  • map
  • struct
  • pointer of 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.