From 73a9dbbe6f45452c3225b1c548a8fc9d4fd8da46 Mon Sep 17 00:00:00 2001 From: meifakun Date: Mon, 19 Aug 2024 22:50:58 +0800 Subject: [PATCH 1/2] fix tag split --- core/mapper.go | 106 ---------------------------- core/mapper_test.go | 39 +---------- core/tag.go | 163 ++++++++++++++++++++++++++++++++++++++++++++ core/tag_test.go | 138 +++++++++++++++++++++++++++++++++++++ 4 files changed, 302 insertions(+), 144 deletions(-) create mode 100644 core/tag.go create mode 100644 core/tag_test.go diff --git a/core/mapper.go b/core/mapper.go index 4f833f4..86c2323 100644 --- a/core/mapper.go +++ b/core/mapper.go @@ -1054,8 +1054,6 @@ func getFeildInfo(field *reflectx.FieldInfo) *FieldInfo { return info } -const tagPrefix = "db" - // CreateMapper returns a valid mapper using the configured NameMapper func. func CreateMapper(prefix string, nameMapper func(string) string, tagMapper func(string, string) []string) *Mapper { if nameMapper == nil { @@ -1067,110 +1065,6 @@ func CreateMapper(prefix string, nameMapper func(string) string, tagMapper func( return &Mapper{mapper: reflectx.NewMapperTagFunc(prefix, nameMapper, tagMapper)} } -var xormkeyTags = map[string]struct{}{ - "pk": {}, - "autoincr": {}, - "null": {}, - "notnull": {}, - "unique": {}, - "extends": {}, - "index": {}, - "<-": {}, - "->": {}, - "created": {}, - "updated": {}, - "deleted": {}, - "version": {}, - "default": {}, - "json": {}, - "jsonb": {}, - "bit": {}, - "tinyint": {}, - "smallint": {}, - "mediumint": {}, - "int": {}, - "integer": {}, - "bigint": {}, - "char": {}, - "varchar": {}, - "tinytext": {}, - "text": {}, - "mediumtext": {}, - "longtext": {}, - "binary": {}, - "varbinary": {}, - "date": {}, - "datetime": {}, - "time": {}, - "timestamp": {}, - "timestampz": {}, - "real": {}, - "float": {}, - "double": {}, - "decimal": {}, - "numeric": {}, - "tinyblob": {}, - "clob": {}, - "blob": {}, - "mediumblob": {}, - "longblob": {}, - "bytea": {}, - "bool": {}, - "serial": {}, -} - -func TagSplitForXORM(s string, fieldName string) []string { - parts := strings.Fields(s) - if len(parts) == 0 { - return parts - } - // name := parts[0] - // idx := strings.IndexByte(name, '(') - // if idx >= 0 { - // name = name[:idx] - // } - - // if _, ok := xormkeyTags[name]; !ok { - // return parts - // } - - fieldNameIndex := -1 - for i := 0; i < len(parts); i++ { - name := parts[i] - - idx := strings.IndexByte(name, '(') - if idx >= 0 { - // unique(xxxx) 改成 unique=xxxx - parts[i] = name[:idx] + "=" + strings.TrimSuffix(name[idx+1:], ")") - continue - } - - if _, ok := xormkeyTags[name]; !ok { - if fieldNameIndex < 0 { - fieldNameIndex = i - } - // parts[i] = parts[0] - // parts[0] = tmp - // return parts - } - } - - if fieldNameIndex >= 0 { - fieldName := parts[fieldNameIndex] - parts[fieldNameIndex] = parts[0] - parts[0] = fieldName - return parts - } - copyed := make([]string, len(parts)+1) - copyed[0] = fieldName - copy(copyed[1:], parts) - return copyed -} - -var TagSplitForDb = func(s string, fieldName string) []string { - return strings.Split(s, ",") -} - var emptyField = &FieldInfo{ FieldInfo: &reflectx.FieldInfo{}, RValue: func(dialect Dialect, param *Param, v reflect.Value) (interface{}, error) { diff --git a/core/mapper_test.go b/core/mapper_test.go index 7f19306..98c9148 100644 --- a/core/mapper_test.go +++ b/core/mapper_test.go @@ -2537,41 +2537,4 @@ func TestMapperF(t *testing.T) { } }) }) -} - -func TestTagSplitForXORM(t *testing.T) { - - for _, test := range []struct { - s string - fieldName string - excepted []string - }{ - { - s: "a pk null", - fieldName: "a", - excepted: []string{"a", "pk", "null"}, - }, - { - s: "pk null", - fieldName: "a", - excepted: []string{"a", "pk", "null"}, - }, - { - s: "pk(aa) null", - fieldName: "a", - excepted: []string{"a", "pk=aa", "null"}, - }, - { - s: "null pk(aa)", - fieldName: "a", - excepted: []string{"a", "null", "pk=aa"}, - }, - } { - - ss := core.TagSplitForXORM(test.s, test.fieldName) - if !reflect.DeepEqual(ss, test.excepted) { - t.Error("excepted:", test.excepted) - t.Error("actual :", ss) - } - } -} +} \ No newline at end of file diff --git a/core/tag.go b/core/tag.go new file mode 100644 index 0000000..19783a2 --- /dev/null +++ b/core/tag.go @@ -0,0 +1,163 @@ +package core + +import ( + "fmt" + "strings" +) + +const tagPrefix = "db" + +var xormkeyTags = map[string]struct{}{ + "pk": {}, + "autoincr": {}, + "null": {}, + "notnull": {}, + "unique": {}, + "extends": {}, + "index": {}, + "<-": {}, + "->": {}, + "created": {}, + "updated": {}, + "deleted": {}, + "version": {}, + "default": {}, + "json": {}, + "jsonb": {}, + "bit": {}, + "tinyint": {}, + "smallint": {}, + "mediumint": {}, + "int": {}, + "integer": {}, + "bigint": {}, + "str": {}, + "char": {}, + "varchar": {}, + "tinytext": {}, + "text": {}, + "mediumtext": {}, + "longtext": {}, + "binary": {}, + "varbinary": {}, + "date": {}, + "datetime": {}, + "time": {}, + "timestamp": {}, + "timestampz": {}, + "real": {}, + "float": {}, + "double": {}, + "decimal": {}, + "numeric": {}, + "tinyblob": {}, + "clob": {}, + "blob": {}, + "mediumblob": {}, + "longblob": {}, + "bytea": {}, + "bool": {}, + "serial": {}, +} + +func TagSplitForXORM(s string, fieldName string) []string { + parts, err := splitXormTag(s) + if err != nil { + panic(err) + } + if len(parts) == 0 { + return []string{fieldName} + } + + copyed := make([]string, 1, len(parts)) + for i := 0; i < len(parts); i++ { + name := parts[i].name + + if len(parts[i].params) > 0 { + // unique(xxxx) 改成 unique=xxxx + copyed = append(copyed, name + "=" + strings.Join(parts[i].params, ",")) + continue + } + + if _, ok := xormkeyTags[name]; !ok { + if copyed[0] == "" { + copyed[0] = name + continue + } + } + + copyed = append(copyed, name) + } + if copyed[0] == "" { + copyed[0] = fieldName + } + return copyed +} + +var TagSplitForDb = func(s string, fieldName string) []string { + return strings.Split(s, ",") +} + +type tag struct { + name string + params []string +} + +func splitXormTag(tagStr string) ([]tag, error) { + tagStr = strings.TrimSpace(tagStr) + var ( + inQuote bool + inBigQuote bool + lastIdx int + curTag tag + paramStart int + tags []tag + ) + for i, t := range tagStr { + switch t { + case '\'': + inQuote = !inQuote + case ' ': + if !inQuote && !inBigQuote { + if lastIdx < i { + if curTag.name == "" { + curTag.name = tagStr[lastIdx:i] + } + tags = append(tags, curTag) + lastIdx = i + 1 + curTag = tag{} + } else if lastIdx == i { + lastIdx = i + 1 + } + } else if inBigQuote && !inQuote { + paramStart = i + 1 + } + case ',': + if !inQuote && !inBigQuote { + return nil, fmt.Errorf("comma[%d] of %s should be in quote or big quote", i, tagStr) + } + if !inQuote && inBigQuote { + curTag.params = append(curTag.params, strings.TrimSpace(tagStr[paramStart:i])) + paramStart = i + 1 + } + case '(': + inBigQuote = true + if !inQuote { + curTag.name = tagStr[lastIdx:i] + paramStart = i + 1 + } + case ')': + inBigQuote = false + if !inQuote { + curTag.params = append(curTag.params, tagStr[paramStart:i]) + } + } + } + if lastIdx < len(tagStr) { + if curTag.name == "" { + curTag.name = tagStr[lastIdx:] + } + tags = append(tags, curTag) + } + return tags, nil +} diff --git a/core/tag_test.go b/core/tag_test.go new file mode 100644 index 0000000..a668779 --- /dev/null +++ b/core/tag_test.go @@ -0,0 +1,138 @@ +package core + +import ( + "reflect" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestTagSplitForXORM(t *testing.T) { + for _, test := range []struct { + s string + fieldName string + excepted []string + }{ + { + s: "a pk null", + fieldName: "a", + excepted: []string{"a", "pk", "null"}, + }, + { + s: "pk null", + fieldName: "a", + excepted: []string{"a", "pk", "null"}, + }, + { + s: "pk(aa) null", + fieldName: "a", + excepted: []string{"a", "pk=aa", "null"}, + }, + { + s: "null pk(aa)", + fieldName: "a", + excepted: []string{"a", "null", "pk=aa"}, + }, + } { + + ss := TagSplitForXORM(test.s, test.fieldName) + if !reflect.DeepEqual(ss, test.excepted) { + t.Error("excepted:", test.excepted) + t.Error("actual :", ss) + } + } +} + +func TestTagSplitXormTag(t *testing.T) { + cases := []struct { + tag string + tags []tag + }{ + { + "not null default '2000-01-01 00:00:00' TIMESTAMP", []tag{ + { + name: "not", + }, + { + name: "null", + }, + { + name: "default", + }, + { + name: "'2000-01-01 00:00:00'", + }, + { + name: "TIMESTAMP", + }, + }, + }, + { + "TEXT", []tag{ + { + name: "TEXT", + }, + }, + }, + { + "default('2000-01-01 00:00:00')", []tag{ + { + name: "default", + params: []string{ + "'2000-01-01 00:00:00'", + }, + }, + }, + }, + { + "json binary", []tag{ + { + name: "json", + }, + { + name: "binary", + }, + }, + }, + { + "numeric(10, 2)", []tag{ + { + name: "numeric", + params: []string{"10", "2"}, + }, + }, + }, + { + "numeric(10, 2) notnull", []tag{ + { + name: "numeric", + params: []string{"10", "2"}, + }, + { + name: "notnull", + }, + }, + }, + { + "collate utf8mb4_bin", []tag{ + { + name: "collate", + }, + { + name: "utf8mb4_bin", + }, + }, + }, + } + + for _, kase := range cases { + t.Run(kase.tag, func(t *testing.T) { + tags, err := splitXormTag(kase.tag) + assert.NoError(t, err) + assert.EqualValues(t, len(tags), len(kase.tags)) + for i := 0; i < len(tags); i++ { + assert.Equal(t, kase.tags[i], tags[i]) + } + }) + } +} From 5e4e2fd216a6cdbd427da3bb71cc4714c1b56288 Mon Sep 17 00:00:00 2001 From: meifakun Date: Mon, 19 Aug 2024 22:54:36 +0800 Subject: [PATCH 2/2] fix bugs --- core/tag.go | 1 - 1 file changed, 1 deletion(-) diff --git a/core/tag.go b/core/tag.go index 19783a2..0e77aec 100644 --- a/core/tag.go +++ b/core/tag.go @@ -31,7 +31,6 @@ var xormkeyTags = map[string]struct{}{ "int": {}, "integer": {}, "bigint": {}, - "str": {}, "char": {}, "varchar": {}, "tinytext": {},