Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve field type check from db to golang #2023

Merged
merged 10 commits into from
Jul 22, 2022
2 changes: 1 addition & 1 deletion cmd/gf/internal/cmd/gendao/gendao_do.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ func generateDo(ctx context.Context, db gdb.DB, tableNames, newTableNames []stri
var (
newTableName = newTableNames[i]
doFilePath = gfile.Join(doDirPath, gstr.CaseSnake(newTableName)+".go")
structDefinition = generateStructDefinition(generateStructDefinitionInput{
structDefinition = generateStructDefinition(ctx, generateStructDefinitionInput{
CGenDaoInternalInput: in,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
Expand Down
2 changes: 1 addition & 1 deletion cmd/gf/internal/cmd/gendao/gendao_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ func generateEntity(ctx context.Context, db gdb.DB, tableNames, newTableNames []
in,
newTableName,
gstr.CaseCamel(newTableName),
generateStructDefinition(generateStructDefinitionInput{
generateStructDefinition(ctx, generateStructDefinitionInput{
CGenDaoInternalInput: in,
StructName: gstr.CaseCamel(newTableName),
FieldMap: fieldMap,
Expand Down
102 changes: 28 additions & 74 deletions cmd/gf/internal/cmd/gendao/gendao_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package gendao

import (
"bytes"
"context"
"fmt"
"strings"

"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
Expand All @@ -19,13 +19,20 @@ type generateStructDefinitionInput struct {
IsDo bool // Is generating DTO struct.
}

func generateStructDefinition(in generateStructDefinitionInput) string {
const (
typeDate = "date"
typeDatetime = "datetime"
typeInt64Bytes = "int64-bytes"
typeUint64Bytes = "uint64-bytes"
)

func generateStructDefinition(ctx context.Context, in generateStructDefinitionInput) string {
buffer := bytes.NewBuffer(nil)
array := make([][]string, len(in.FieldMap))
names := sortFieldKeyForDao(in.FieldMap)
for index, name := range names {
field := in.FieldMap[name]
array[index] = generateStructFieldDefinition(field, in)
array[index] = generateStructFieldDefinition(ctx, field, in)
}
tw := tablewriter.NewWriter(buffer)
tw.SetBorder(false)
Expand All @@ -50,92 +57,39 @@ func generateStructDefinition(in generateStructDefinitionInput) string {
}

// generateStructFieldForModel generates and returns the attribute definition for specified field.
func generateStructFieldDefinition(field *gdb.TableField, in generateStructDefinitionInput) []string {
func generateStructFieldDefinition(
ctx context.Context, field *gdb.TableField, in generateStructDefinitionInput,
) []string {
var (
err error
typeName string
jsonTag = getJsonTagFromCase(field.Name, in.JsonCase)
)
t, _ := gregex.ReplaceString(`\(.+\)`, "", field.Type)
t = gstr.Split(gstr.Trim(t), " ")[0]
t = gstr.ToLower(t)

switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
typeName = "[]byte"

case "bit", "int", "int2", "tinyint", "small_int", "smallint", "medium_int", "mediumint", "serial":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "uint"
} else {
typeName = "int"
}

case "int4", "int8", "big_int", "bigint", "bigserial":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "uint64"
} else {
typeName = "int64"
}

// pgsql int32 slice.
case "_int2":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "[]uint"
} else {
typeName = "[]int"
}

// pgsql int64 slice.
case "_int4", "_int8":
if gstr.ContainsI(field.Type, "unsigned") {
typeName = "[]uint64"
} else {
typeName = "[]int64"
}

case "real":
typeName = "float32"

case "float", "double", "decimal", "smallmoney", "numeric":
typeName = "float64"

case "bool":
typeName = "bool"

case "datetime", "timestamp", "date", "time":
typeName, err = gdb.CheckValueForLocalType(ctx, field.Type, nil)
if err != nil {
panic(err)
}
switch typeName {
case typeDate, typeDatetime:
if in.StdTime {
typeName = "time.Time"
} else {
typeName = "*gtime.Time"
}

case typeInt64Bytes:
typeName = "int64"

case typeUint64Bytes:
typeName = "uint64"

// Special type handle.
case "json", "jsonb":
if in.GJsonSupport {
typeName = "*gjson.Json"
} else {
typeName = "string"
}
default:
// Automatically detect its data type.
switch {
case strings.Contains(t, "int"):
typeName = "int"
case strings.Contains(t, "text") || strings.Contains(t, "char"):
typeName = "string"
case strings.Contains(t, "float") || strings.Contains(t, "double"):
typeName = "float64"
case strings.Contains(t, "bool"):
typeName = "bool"
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
typeName = "[]byte"
case strings.Contains(t, "date") || strings.Contains(t, "time"):
if in.StdTime {
typeName = "time.Time"
} else {
typeName = "*gtime.Time"
}
default:
typeName = "string"
}
}

var (
Expand Down
32 changes: 17 additions & 15 deletions contrib/drivers/mysql/mysql_core_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1190,24 +1190,26 @@ func Test_DB_TableField(t *testing.T) {
"field_varchar": "abc",
"field_varbinary": "aaa",
}
res, err := db.Model(name).Data(data).Insert()
if err != nil {
gtest.Fatal(err)
}
gtest.C(t, func(t *gtest.T) {
res, err := db.Model(name).Data(data).Insert()
if err != nil {
t.Fatal(err)
}

n, err := res.RowsAffected()
if err != nil {
gtest.Fatal(err)
} else {
gtest.Assert(n, 1)
}
n, err := res.RowsAffected()
if err != nil {
t.Fatal(err)
} else {
t.Assert(n, 1)
}

result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).All()
if err != nil {
gtest.Fatal(err)
}
result, err := db.Model(name).Fields("*").Where("field_int = ?", 2).All()
if err != nil {
t.Fatal(err)
}
t.Assert(result[0], data)
})

gtest.Assert(result[0], data)
}

func Test_DB_Prefix(t *testing.T) {
Expand Down
110 changes: 28 additions & 82 deletions database/gdb/gdb_core_structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ import (
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/internal/json"
"github.com/gogf/gf/v2/os/gtime"
"github.com/gogf/gf/v2/text/gregex"
"github.com/gogf/gf/v2/text/gstr"
"github.com/gogf/gf/v2/util/gconv"
"github.com/gogf/gf/v2/util/gutil"
)
Expand Down Expand Up @@ -130,53 +128,42 @@ func (c *Core) ConvertValueForLocal(ctx context.Context, fieldType string, field
if fieldType == "" {
return fieldValue, nil
}
typeName, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
typeName = strings.ToLower(typeName)
typeName, err := CheckValueForLocalType(ctx, fieldType, fieldValue)
if err != nil {
return nil, err
}
switch typeName {
case
"binary",
"varbinary",
"blob",
"tinyblob",
"mediumblob",
"longblob":
case typeBytes:
if strings.Contains(typeName, "binary") || strings.Contains(typeName, "blob") {
return fieldValue, nil
}
return gconv.Bytes(fieldValue), nil

case
"int",
"tinyint",
"small_int",
"smallint",
"medium_int",
"mediumint",
"serial":
if gstr.ContainsI(fieldType, "unsigned") {
return gconv.Uint(gconv.String(fieldValue)), nil
}
case typeInt:
return gconv.Int(gconv.String(fieldValue)), nil

case
"big_int",
"bigint",
"bigserial":
if gstr.ContainsI(fieldType, "unsigned") {
return gconv.Uint64(gconv.String(fieldValue)), nil
}
case typeUint:
return gconv.Uint(gconv.String(fieldValue)), nil

case typeInt64:
return gconv.Int64(gconv.String(fieldValue)), nil

case "real":
case typeUint64:
return gconv.Uint64(gconv.String(fieldValue)), nil

case typeInt64Bytes:
return gbinary.BeDecodeToInt64(gconv.Bytes(fieldValue)), nil

case typeUint64Bytes:
return gbinary.BeDecodeToUint64(gconv.Bytes(fieldValue)), nil

case typeFloat32:
return gconv.Float32(gconv.String(fieldValue)), nil

case
"float",
"double",
"decimal",
"money",
"numeric",
"smallmoney":
case typeFloat64:
return gconv.Float64(gconv.String(fieldValue)), nil

case "bit":
case typeBool:
s := gconv.String(fieldValue)
// mssql is true|false string.
if strings.EqualFold(s, "true") {
Expand All @@ -185,66 +172,25 @@ func (c *Core) ConvertValueForLocal(ctx context.Context, fieldType string, field
if strings.EqualFold(s, "false") {
return 0, nil
}
return gbinary.BeDecodeToInt64(gconv.Bytes(fieldValue)), nil

case "bool":
return gconv.Bool(fieldValue), nil

case "date":
case typeDate:
// Date without time.
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t).Format("Y-m-d"), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t.Format("Y-m-d"), nil

case
"datetime",
"timestamp",
"timestamptz":
case typeDatetime:
if t, ok := fieldValue.(time.Time); ok {
return gtime.NewFromTime(t), nil
}
t, _ := gtime.StrToTime(gconv.String(fieldValue))
return t, nil

default:
// Auto-detect field type, using key match.
switch {
case strings.Contains(typeName, "text") || strings.Contains(typeName, "char") || strings.Contains(typeName, "character"):
return gconv.String(fieldValue), nil

case strings.Contains(typeName, "float") || strings.Contains(typeName, "double") || strings.Contains(typeName, "numeric"):
return gconv.Float64(gconv.String(fieldValue)), nil

case strings.Contains(typeName, "bool"):
return gconv.Bool(gconv.String(fieldValue)), nil

case strings.Contains(typeName, "binary") || strings.Contains(typeName, "blob"):
return fieldValue, nil

case strings.Contains(typeName, "int"):
return gconv.Int(gconv.String(fieldValue)), nil

case strings.Contains(typeName, "time"):
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s, nil
}
return t, nil

case strings.Contains(typeName, "date"):
s := gconv.String(fieldValue)
t, err := gtime.StrToTime(s)
if err != nil {
return s, nil
}
return t, nil

default:
return gconv.String(fieldValue), nil
}
return gconv.String(fieldValue), nil
}
}

Expand Down
2 changes: 1 addition & 1 deletion database/gdb/gdb_core_utility.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ func (c *Core) QuoteString(s string) string {
// if true it does nothing to the table name, or else adds the prefix to the table name.
func (c *Core) QuotePrefixTableName(table string) string {
charLeft, charRight := c.db.GetChars()
return doHandleTableName(table, c.db.GetPrefix(), charLeft, charRight)
return doQuoteTableName(table, c.db.GetPrefix(), charLeft, charRight)
}

// GetChars returns the security char for current database.
Expand Down
7 changes: 1 addition & 6 deletions database/gdb/gdb_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,6 @@ type iInterfaces interface {
Interfaces() []interface{}
}

// iMapStrAny is the interface support for converting struct parameter to map.
type iMapStrAny interface {
MapStrAny() map[string]interface{}
}

// iTableName is the interface for retrieving table name fro struct.
type iTableName interface {
TableName() string
Expand Down Expand Up @@ -187,7 +182,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
//
// Note that, this will automatically check the table prefix whether already added, if true it does
// nothing to the table name, or else adds the prefix to the table name and returns new table name with prefix.
func doHandleTableName(table, prefix, charLeft, charRight string) string {
func doQuoteTableName(table, prefix, charLeft, charRight string) string {
var (
index = 0
chars = charLeft + charRight
Expand Down
Loading