mirror of
https://github.com/hanzoai/sqlbuilder.git
synced 2026-10-01 00:37:42 +00:00
Merge pull request #50 from tolbier/omitempty_for_patch
Omitempty for TAG
This commit is contained in:
@@ -6,6 +6,7 @@ package sqlbuilder
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@@ -25,8 +26,13 @@ var (
|
||||
const (
|
||||
fieldOptWithQuote = "withquote"
|
||||
fieldOptOmitEmpty = "omitempty"
|
||||
|
||||
optName = "optName"
|
||||
optParams = "optParams"
|
||||
)
|
||||
|
||||
var optRegex = regexp.MustCompile(`(?P<` + optName + `>\w+)(\((?P<` + optParams + `>.*)\))?`)
|
||||
|
||||
// Struct represents a struct type.
|
||||
//
|
||||
// All methods in Struct are thread-safe.
|
||||
@@ -38,7 +44,17 @@ type Struct struct {
|
||||
fieldAlias map[string]string
|
||||
taggedFields map[string][]string
|
||||
quotedFields map[string]struct{}
|
||||
omitEmptyFields map[string]struct{}
|
||||
omitEmptyFields map[string]omitEmptyTagMap
|
||||
}
|
||||
type omitEmptyTagMap map[string]struct{}
|
||||
|
||||
func (sm omitEmptyTagMap) containsAny(tags ...string) (res bool) {
|
||||
for _, tag := range tags {
|
||||
if _, res = sm[tag]; res {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// NewStruct analyzes type information in structValue
|
||||
@@ -59,7 +75,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.omitEmptyFields = map[string]omitEmptyTagMap{}
|
||||
s.parse(t)
|
||||
return s
|
||||
}
|
||||
@@ -99,7 +115,7 @@ func (s *Struct) parse(t reflect.Type) {
|
||||
|
||||
// Parse FieldTag.
|
||||
fieldtag := field.Tag.Get(FieldTag)
|
||||
tags := strings.Split(fieldtag, ",")
|
||||
tags := splitTokens(fieldtag)
|
||||
|
||||
for _, t := range tags {
|
||||
if t != "" {
|
||||
@@ -111,19 +127,29 @@ func (s *Struct) parse(t reflect.Type) {
|
||||
|
||||
// Parse FieldOpt.
|
||||
fieldopt := field.Tag.Get(FieldOpt)
|
||||
opts := strings.Split(fieldopt, ",")
|
||||
|
||||
opts := optRegex.FindAllString(fieldopt, -1)
|
||||
for _, opt := range opts {
|
||||
switch opt {
|
||||
optMap := getOptMatchedMap(opt)
|
||||
switch optMap[optName] {
|
||||
case fieldOptOmitEmpty:
|
||||
tags := getTagsFromOptParams(optMap[optParams])
|
||||
s.appendOmitEmptyFieldsTags(alias, tags...)
|
||||
case fieldOptWithQuote:
|
||||
s.quotedFields[alias] = struct{}{}
|
||||
case fieldOptOmitEmpty:
|
||||
s.omitEmptyFields[alias] = struct{}{}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Struct) appendOmitEmptyFieldsTags(alias string, tags ...string) {
|
||||
if s.omitEmptyFields[alias] == nil {
|
||||
s.omitEmptyFields[alias] = omitEmptyTagMap{}
|
||||
}
|
||||
for _, tag := range tags {
|
||||
s.omitEmptyFields[alias][tag] = struct{}{}
|
||||
}
|
||||
}
|
||||
|
||||
// SelectFrom creates a new `SelectBuilder` with table name.
|
||||
// By default, all exported fields of the s are listed as columns in SELECT.
|
||||
//
|
||||
@@ -211,8 +237,10 @@ func (s *Struct) UpdateForTag(table string, tag string, value interface{}) *Upda
|
||||
val := v.FieldByName(name)
|
||||
|
||||
if isEmptyValue(val) {
|
||||
if _, ok := s.omitEmptyFields[f]; ok {
|
||||
continue
|
||||
if omitEmptyTagMap, ok := s.omitEmptyFields[f]; ok {
|
||||
if omitEmptyTagMap.containsAny("", tag) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val = dereferencedValue(val)
|
||||
@@ -439,6 +467,30 @@ func (s *Struct) quoteFields(fields []string) []string {
|
||||
return quoted
|
||||
}
|
||||
|
||||
func getOptMatchedMap(opt string) (res map[string]string) {
|
||||
res = map[string]string{}
|
||||
sm := optRegex.FindStringSubmatch(opt)
|
||||
for i, name := range optRegex.SubexpNames() {
|
||||
if name != "" {
|
||||
res[name] = sm[i]
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func getTagsFromOptParams(opts string) (tags []string) {
|
||||
tags = splitTokens(opts)
|
||||
if len(tags) == 0 {
|
||||
tags = append(tags, "")
|
||||
}
|
||||
return
|
||||
}
|
||||
func splitTokens(fieldtag string) (res []string) {
|
||||
res = strings.Split(fieldtag, ",")
|
||||
for i, v := range res {
|
||||
res[i] = strings.TrimSpace(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
func dereferencedType(t reflect.Type) reflect.Type {
|
||||
for k := t.Kind(); k == reflect.Ptr || k == reflect.Interface; k = t.Kind() {
|
||||
t = t.Elem()
|
||||
|
||||
@@ -644,6 +644,79 @@ func TestStructOmitEmpty(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
type structOmitEmptyForTag struct {
|
||||
A int `db:"aa" fieldopt:"omitempty,withquote" fieldtag:"patch"`
|
||||
B *string `db:"bb" fieldopt:"omitempty" fieldtag:"patch"`
|
||||
C uint16 `db:"cc" fieldopt:"omitempty()" fieldtag:"patch"`
|
||||
D *float64 `fieldopt:"omitempty(patch)" fieldtag:"patch"`
|
||||
E bool `db:"ee" fieldtag:"patch"`
|
||||
}
|
||||
|
||||
func TestStructOmitEmptyForTag(t *testing.T) {
|
||||
st := NewStruct(new(structOmitEmptyForTag)).For(MySQL)
|
||||
sql1, _ := st.Update("foo", new(structOmitEmptyForTag)).Build()
|
||||
|
||||
if expected := "UPDATE foo SET D = ?, ee = ?"; sql1 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql1)
|
||||
}
|
||||
|
||||
a := 123
|
||||
b := "bbbb"
|
||||
c := uint16(234)
|
||||
e := true
|
||||
sql2, args2 := st.UpdateForTag("foo", "patch", &structOmitEmptyForTag{
|
||||
A: a,
|
||||
B: &b,
|
||||
C: c,
|
||||
D: nil,
|
||||
E: e,
|
||||
}).Build()
|
||||
|
||||
if expected := "UPDATE foo SET `aa` = ?, bb = ?, cc = ?, ee = ?"; sql2 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql2)
|
||||
}
|
||||
|
||||
if expected := []interface{}{a, b, c, e}; !reflect.DeepEqual(expected, args2) {
|
||||
t.Fatalf("invalid args. [expected:%#v] [actual:%#v]", expected, args2)
|
||||
}
|
||||
}
|
||||
|
||||
type structOmitEmptyForMultipleTags struct {
|
||||
A int `db:"aa" fieldopt:"omitempty, omitempty(patch, patch2),withquote" fieldtag:"patch, patch2 "`
|
||||
B *string `db:"bb" fieldopt:"omitempty" fieldtag:"patch"`
|
||||
C uint16 `db:"cc" fieldopt:"omitempty, omitempty(patch2)" fieldtag:"patch2"`
|
||||
D *float64 `fieldopt:"omitempty(patch, patch2)" fieldtag:"patch,patch2"`
|
||||
E bool `db:"ee" fieldtag:"patch"`
|
||||
}
|
||||
|
||||
func TestStructOmitEmptyForMultipleTags(t *testing.T) {
|
||||
st := NewStruct(new(structOmitEmptyForMultipleTags)).For(MySQL)
|
||||
sql1, _ := st.Update("foo", new(structOmitEmptyForMultipleTags)).Build()
|
||||
|
||||
if expected := "UPDATE foo SET D = ?, ee = ?"; sql1 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql1)
|
||||
}
|
||||
|
||||
a := 123
|
||||
b := "bbbb"
|
||||
e := true
|
||||
sql2, args2 := st.UpdateForTag("foo", "patch2", &structOmitEmptyForMultipleTags{
|
||||
A: a,
|
||||
B: &b,
|
||||
C: 0,
|
||||
D: nil,
|
||||
E: e,
|
||||
}).Build()
|
||||
|
||||
if expected := "UPDATE foo SET `aa` = ?"; sql2 != expected {
|
||||
t.Fatalf("invalid sql. [expected:%v] [actual:%v]", expected, sql2)
|
||||
}
|
||||
|
||||
if expected := []interface{}{a}; !reflect.DeepEqual(expected, args2) {
|
||||
t.Fatalf("invalid args. [expected:%#v] [actual:%#v]", expected, args2)
|
||||
}
|
||||
}
|
||||
|
||||
type structWithPointers struct {
|
||||
A int `db:"aa" fieldopt:"omitempty"`
|
||||
B *string `db:"bb"`
|
||||
|
||||
Reference in New Issue
Block a user