mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
add a new fieldopt "omitempty"
This commit is contained in:
@@ -75,6 +75,7 @@ type ATable struct {
|
||||
Ignored int32 `db:"-"` // If we set field name as "-", Struct will ignore it.
|
||||
unexported int // Unexported field is not visible to Struct.
|
||||
Quoted string `db:"quoted" fieldopt:"withquote"` // Add quote to the field using back quote or double quote. See `Flavor#Quote`.
|
||||
Empty uint `db:"empty" fieldopt:"omitempty"` // Omit the field in UPDATE if it is a nil or zero value.
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ var (
|
||||
|
||||
const (
|
||||
fieldOptWithQuote = "withquote"
|
||||
fieldOptOmitEmpty = "omitempty"
|
||||
)
|
||||
|
||||
// Struct represents a struct type.
|
||||
@@ -32,10 +33,11 @@ const (
|
||||
type Struct struct {
|
||||
Flavor Flavor
|
||||
|
||||
structType reflect.Type
|
||||
fieldAlias map[string]string
|
||||
taggedFields map[string][]string
|
||||
quotedFields map[string]struct{}
|
||||
structType reflect.Type
|
||||
fieldAlias map[string]string
|
||||
taggedFields map[string][]string
|
||||
quotedFields map[string]struct{}
|
||||
omitEmptyFields map[string]struct{}
|
||||
}
|
||||
|
||||
// NewStruct analyzes type information in structValue
|
||||
@@ -56,6 +58,7 @@ func NewStruct(structValue interface{}) *Struct {
|
||||
s.fieldAlias = map[string]string{}
|
||||
s.taggedFields = map[string][]string{}
|
||||
s.quotedFields = map[string]struct{}{}
|
||||
s.omitEmptyFields = map[string]struct{}{}
|
||||
s.parse(t)
|
||||
return s
|
||||
}
|
||||
@@ -113,6 +116,8 @@ func (s *Struct) parse(t reflect.Type) {
|
||||
switch opt {
|
||||
case fieldOptWithQuote:
|
||||
s.quotedFields[alias] = struct{}{}
|
||||
case fieldOptOmitEmpty:
|
||||
s.omitEmptyFields[alias] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -178,18 +183,26 @@ func (s *Struct) UpdateForTag(table string, tag string, value interface{}) *Upda
|
||||
return ub
|
||||
}
|
||||
|
||||
quoted := s.quoteFields(fields)
|
||||
v := dereferencedValue(value)
|
||||
v := reflect.ValueOf(value)
|
||||
v = dereferencedValue(v)
|
||||
|
||||
if v.Type() != s.structType {
|
||||
return ub
|
||||
}
|
||||
|
||||
quoted := s.quoteFields(fields)
|
||||
assignments := make([]string, 0, len(fields))
|
||||
|
||||
for i, f := range fields {
|
||||
name := s.fieldAlias[f]
|
||||
data := v.FieldByName(name).Interface()
|
||||
val := v.FieldByName(name)
|
||||
|
||||
if _, ok := s.omitEmptyFields[f]; ok && isEmptyValue(val) {
|
||||
continue
|
||||
}
|
||||
|
||||
val = dereferencedValue(val)
|
||||
data := val.Interface()
|
||||
assignments = append(assignments, ub.Assign(quoted[i], data))
|
||||
}
|
||||
|
||||
@@ -226,11 +239,14 @@ func (s *Struct) InsertIntoForTag(table string, tag string, value ...interface{}
|
||||
vs := make([]reflect.Value, 0, len(value))
|
||||
|
||||
for _, item := range value {
|
||||
v := dereferencedValue(item)
|
||||
v := reflect.ValueOf(item)
|
||||
v = dereferencedValue(v)
|
||||
|
||||
if v.Type() == s.structType {
|
||||
vs = append(vs, v)
|
||||
}
|
||||
}
|
||||
|
||||
if len(vs) == 0 {
|
||||
return ib
|
||||
}
|
||||
@@ -241,6 +257,7 @@ func (s *Struct) InsertIntoForTag(table string, tag string, value ...interface{}
|
||||
for _, f := range fields {
|
||||
cols = append(cols, f)
|
||||
name := s.fieldAlias[f]
|
||||
|
||||
for i, v := range vs {
|
||||
data := v.FieldByName(name).Interface()
|
||||
values[i] = append(values[i], data)
|
||||
@@ -289,7 +306,8 @@ func (s *Struct) AddrForTag(tag string, value interface{}) []interface{} {
|
||||
// AddrWithCols takes address of all columns defined in cols from the value.
|
||||
// The returned result can be used in `Row#Scan` directly.
|
||||
func (s *Struct) AddrWithCols(cols []string, value interface{}) []interface{} {
|
||||
v := dereferencedValue(value)
|
||||
v := reflect.ValueOf(value)
|
||||
v = dereferencedValue(v)
|
||||
|
||||
if v.Type() != s.structType {
|
||||
return nil
|
||||
@@ -352,12 +370,29 @@ func dereferencedType(t reflect.Type) reflect.Type {
|
||||
return t
|
||||
}
|
||||
|
||||
func dereferencedValue(value interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(value)
|
||||
|
||||
func dereferencedValue(v reflect.Value) reflect.Value {
|
||||
for k := v.Kind(); k == reflect.Ptr || k == reflect.Interface; k = v.Kind() {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
return v
|
||||
}
|
||||
|
||||
func isEmptyValue(value reflect.Value) bool {
|
||||
switch value.Kind() {
|
||||
case reflect.Interface, reflect.Ptr, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice:
|
||||
return value.IsNil()
|
||||
case reflect.Bool:
|
||||
return !value.Bool()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return value.Int() == 0
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
return value.Uint() == 0
|
||||
case reflect.String:
|
||||
return value.String() == ""
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return value.Float() == 0
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
+70
-27
@@ -238,15 +238,15 @@ type User struct {
|
||||
}
|
||||
|
||||
type Order struct {
|
||||
ID int64 `db:"id" fieldtag:"new"`
|
||||
State State `db:"state" fieldtag:"new,paid,done"`
|
||||
SkuID int64 `db:"sku_id" fieldtag:"new"`
|
||||
UserID int64 `db:"user_id" fieldtag:"new"`
|
||||
Price int64 `db:"price" fieldtag:"new,update"`
|
||||
Discount int64 `db:"discount" fieldtag:"new,update"`
|
||||
Description string `db:"description" fieldtag:"new,update"`
|
||||
CreatedAt int64 `db:"created_at" fieldtag:"new"`
|
||||
ModifiedAt int64 `db:"modified_at" fieldtag:"new,update,paid,done"`
|
||||
ID int64 `db:"id" fieldtag:"new"`
|
||||
State State `db:"state" fieldtag:"new,paid,done"`
|
||||
SkuID int64 `db:"sku_id" fieldtag:"new"`
|
||||
UserID int64 `db:"user_id" fieldtag:"new"`
|
||||
Price int64 `db:"price" fieldtag:"new,update"`
|
||||
Discount int64 `db:"discount" fieldtag:"new,update"`
|
||||
Desc string `db:"desc" fieldtag:"new,update" fieldopt:"withquote"`
|
||||
CreatedAt int64 `db:"created_at" fieldtag:"new"`
|
||||
ModifiedAt int64 `db:"modified_at" fieldtag:"new,update,paid,done"`
|
||||
}
|
||||
|
||||
type State int
|
||||
@@ -318,15 +318,15 @@ func ExampleStruct_useTag() {
|
||||
// Suppose we defined following type and global variable.
|
||||
//
|
||||
// type Order struct {
|
||||
// ID int64 `db:"id" fieldtag:"update,paid"`
|
||||
// State int `db:"state" fieldtag:"paid"`
|
||||
// SkuID int64 `db:"sku_id"`
|
||||
// UserID int64 `db:"user_id"`
|
||||
// Price int64 `db:"price" fieldtag:"update"`
|
||||
// Discount int64 `db:"discount" fieldtag:"update"`
|
||||
// Description string `db:"description" fieldtag:"update"`
|
||||
// CreatedAt int64 `db:"created_at"`
|
||||
// ModifiedAt int64 `db:"modified_at" fieldtag:"update,paid"`
|
||||
// ID int64 `db:"id" fieldtag:"update,paid"`
|
||||
// State int `db:"state" fieldtag:"paid"`
|
||||
// SkuID int64 `db:"sku_id"`
|
||||
// UserID int64 `db:"user_id"`
|
||||
// Price int64 `db:"price" fieldtag:"update"`
|
||||
// Discount int64 `db:"discount" fieldtag:"update"`
|
||||
// Desc string `db:"desc" fieldtag:"update" fieldopt:"withquote"` // `desc` is a keyword.
|
||||
// CreatedAt int64 `db:"created_at"`
|
||||
// ModifiedAt int64 `db:"modified_at" fieldtag:"update,paid"`
|
||||
// }
|
||||
//
|
||||
// var orderStruct = NewStruct(new(Order))
|
||||
@@ -334,19 +334,20 @@ func ExampleStruct_useTag() {
|
||||
createOrder := func(table string) {
|
||||
now := time.Now().Unix()
|
||||
order := &Order{
|
||||
ID: 1234,
|
||||
State: OrderStateCreated,
|
||||
SkuID: 5678,
|
||||
UserID: 7527,
|
||||
Price: 1000,
|
||||
Discount: 0,
|
||||
Description: "Best goods",
|
||||
CreatedAt: now,
|
||||
ModifiedAt: now,
|
||||
ID: 1234,
|
||||
State: OrderStateCreated,
|
||||
SkuID: 5678,
|
||||
UserID: 7527,
|
||||
Price: 1000,
|
||||
Discount: 0,
|
||||
Desc: "Best goods",
|
||||
CreatedAt: now,
|
||||
ModifiedAt: now,
|
||||
}
|
||||
b := orderStruct.InsertInto(table, &order)
|
||||
sql, args := b.Build()
|
||||
db.Exec(sql, args)
|
||||
fmt.Println(sql)
|
||||
}
|
||||
updatePrice := func(table string) {
|
||||
tag := "update"
|
||||
@@ -368,6 +369,7 @@ func ExampleStruct_useTag() {
|
||||
b.Where(b.E("id", order.ID))
|
||||
sql, args = b.Build()
|
||||
db.Exec(sql, args...)
|
||||
fmt.Println(sql)
|
||||
}
|
||||
updateState := func(table string) {
|
||||
tag := "paid"
|
||||
@@ -395,6 +397,7 @@ func ExampleStruct_useTag() {
|
||||
b.Where(b.E("id", order.ID))
|
||||
sql, args = b.Build()
|
||||
db.Exec(sql, args...)
|
||||
fmt.Println(sql)
|
||||
}
|
||||
|
||||
table := "order"
|
||||
@@ -405,6 +408,8 @@ func ExampleStruct_useTag() {
|
||||
fmt.Println("done")
|
||||
|
||||
// Output:
|
||||
// INSERT INTO order (id, state, sku_id, user_id, price, discount, `desc`, created_at, modified_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
// UPDATE order SET price = ?, discount = ?, `desc` = ?, modified_at = ? WHERE id = ?
|
||||
// done
|
||||
}
|
||||
|
||||
@@ -567,3 +572,41 @@ func TestStructWithQuote(t *testing.T) {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql)
|
||||
}
|
||||
}
|
||||
|
||||
type structOmitEmpty struct {
|
||||
A int `db:"aa" fieldopt:"omitempty,withquote"`
|
||||
B *string `db:"bb" fieldopt:"omitempty"`
|
||||
C uint16 `db:"cc" fieldopt:",omitempty"`
|
||||
D *float64 `fieldopt:"omitempty"`
|
||||
E bool `db:"ee"`
|
||||
}
|
||||
|
||||
func TestStructOmitEmpty(t *testing.T) {
|
||||
st := NewStruct(new(structOmitEmpty)).For(MySQL)
|
||||
sql1, _ := st.Update("foo", new(structOmitEmpty)).Build()
|
||||
|
||||
if expected := "UPDATE foo SET ee = ?"; sql1 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql1)
|
||||
}
|
||||
|
||||
a := 123
|
||||
b := "bbbb"
|
||||
c := uint16(234)
|
||||
d := 123.45
|
||||
e := true
|
||||
sql2, args2 := st.Update("foo", &structOmitEmpty{
|
||||
A: a,
|
||||
B: &b,
|
||||
C: c,
|
||||
D: &d,
|
||||
E: e,
|
||||
}).Build()
|
||||
|
||||
if expected := "UPDATE foo SET `aa` = ?, bb = ?, cc = ?, D = ?, ee = ?"; sql2 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql2)
|
||||
}
|
||||
|
||||
if expected := []interface{}{a, b, c, d, e}; !reflect.DeepEqual(expected, args2) {
|
||||
t.Fatalf("invalid args. [expected:%#v] [actual:%#v]", expected, args2)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user