Repository: zeromicro/go-zero
Pull request #2248 (2022-08-10 03:56:25)

feat: Add strict flag
In strict mode, if there has a constraint unsigned on a column, it wouldn't be ignored, for example:
```
...
age int unsigned not null default 0 
...
```
in strict mode, the datatype of golang will be convert into `uint`, or else, it will be convert into `int`


Patch:
diff --git a/core/mapping/utils.go b/core/mapping/utils.go
index 7f052bdf..7d8af4df 100644
--- a/core/mapping/utils.go
+++ b/core/mapping/utils.go
@@ -311,10 +311,20 @@ func parseNumberRange(str string) (*numberRange, error) {
 		right = math.MaxFloat64
 	}
 
-	if left >= right {
+	if left > right {
 		return nil, errNumberRange
 	}
 
+	// [2:2] valid
+	// [2:2) invalid
+	// (2:2] invalid
+	// (2:2) invalid
+	if left == right {
+		if !leftInclude || !rightInclude {
+			return nil, errNumberRange
+		}
+	}
+
 	return &numberRange{
 		left:         left,
 		leftInclude:  leftInclude,
diff --git a/tools/goctl/model/cmd.go b/tools/goctl/model/cmd.go
index 99de7c40..131fe048 100644
--- a/tools/goctl/model/cmd.go
+++ b/tools/goctl/model/cmd.go
@@ -2,6 +2,7 @@ package model
 
 import (
 	"github.com/spf13/cobra"
+
 	"github.com/zeromicro/go-zero/tools/goctl/model/mongo"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/command"
 )
@@ -77,6 +78,7 @@ func init() {
 	pgDatasourceCmd.Flags().StringVarP(&command.VarStringDir, "dir", "d", "", "The target dir")
 	pgDatasourceCmd.Flags().StringVar(&command.VarStringStyle, "style", "", "The file naming format, see [https://github.com/zeromicro/go-zero/tree/master/tools/goctl/config/readme.md]")
 	pgDatasourceCmd.Flags().BoolVar(&command.VarBoolIdea, "idea", false, "For idea plugin [optional]")
+	pgDatasourceCmd.Flags().BoolVar(&command.VarBoolStrict, "strict", false, "Generate model in strict mode")
 	pgDatasourceCmd.Flags().StringVar(&command.VarStringHome, "home", "", "The goctl home path of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority")
 	pgDatasourceCmd.Flags().StringVar(&command.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\n\tThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
 	pgDatasourceCmd.Flags().StringVar(&command.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote")
@@ -90,6 +92,8 @@ func init() {
 	mongoCmd.Flags().StringVar(&mongo.VarStringRemote, "remote", "", "The remote git repo of the template, --home and --remote cannot be set at the same time, if they are, --remote has higher priority\nThe git repo directory must be consistent with the https://github.com/zeromicro/go-zero-template directory structure")
 	mongoCmd.Flags().StringVar(&mongo.VarStringBranch, "branch", "", "The branch of the remote repo, it does work with --remote")
 
+	mysqlCmd.PersistentFlags().BoolVar(&command.VarBoolStrict, "strict", false, "Generate model in strict mode")
+
 	mysqlCmd.AddCommand(datasourceCmd)
 	mysqlCmd.AddCommand(ddlCmd)
 	pgCmd.AddCommand(pgDatasourceCmd)
diff --git a/tools/goctl/model/sql/command/command.go b/tools/goctl/model/sql/command/command.go
index 9fd65777..a12f2a2b 100644
--- a/tools/goctl/model/sql/command/command.go
+++ b/tools/goctl/model/sql/command/command.go
@@ -10,6 +10,7 @@ import (
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/core/stores/postgres"
 	"github.com/zeromicro/go-zero/core/stores/sqlx"
+
 	"github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/command/migrationnotes"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/gen"
@@ -47,6 +48,8 @@ var (
 	VarStringRemote string
 	// VarStringBranch describes the git branch of the repository.
 	VarStringBranch string
+	// VarBoolStrict describes whether the strict mode is enabled.
+	VarBoolStrict bool
 )
 
 var errNotMatched = errors.New("sql not matched")
@@ -77,7 +80,16 @@ func MysqlDDL(_ *cobra.Command, _ []string) error {
 		return err
 	}
 
-	return fromDDL(src, dir, cfg, cache, idea, database)
+	arg := ddlArg{
+		src:      src,
+		dir:      dir,
+		cfg:      cfg,
+		cache:    cache,
+		idea:     idea,
+		database: database,
+		strict:   VarBoolStrict,
+	}
+	return fromDDL(arg)
 }
 
 // MySqlDataSource generates model code from datasource
@@ -108,7 +120,16 @@ func MySqlDataSource(_ *cobra.Command, _ []string) error {
 		return err
 	}
 
-	return fromMysqlDataSource(url, dir, patterns, cfg, cache, idea)
+	arg := dataSourceArg{
+		url:      url,
+		dir:      dir,
+		tablePat: patterns,
+		cfg:      cfg,
+		cache:    cache,
+		idea:     idea,
+		strict:   VarBoolStrict,
+	}
+	return fromMysqlDataSource(arg)
 }
 
 type pattern map[string]struct{}
@@ -180,12 +201,20 @@ func PostgreSqlDataSource(_ *cobra.Command, _ []string) error {
 		return err
 	}
 
-	return fromPostgreSqlDataSource(url, pattern, dir, schema, cfg, cache, idea)
+	return fromPostgreSqlDataSource(url, pattern, dir, schema, cfg, cache, idea, VarBoolStrict)
 }
 
-func fromDDL(src, dir string, cfg *config.Config, cache, idea bool, database string) error {
-	log := console.NewConsole(idea)
-	src = strings.TrimSpace(src)
+type ddlArg struct {
+	src, dir    string
+	cfg         *config.Config
+	cache, idea bool
+	database    string
+	strict      bool
+}
+
+func fromDDL(arg ddlArg) error {
+	log := console.NewConsole(arg.idea)
+	src := strings.TrimSpace(arg.src)
 	if len(src) == 0 {
 		return errors.New("expected path or path globbing patterns, but nothing found")
 	}
@@ -199,13 +228,13 @@ func fromDDL(src, dir string, cfg *config.Config, cache, idea bool, database str
 		return errNotMatched
 	}
 
-	generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
+	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg, gen.WithConsoleOption(log))
 	if err != nil {
 		return err
 	}
 
 	for _, file := range files {
-		err = generator.StartFromDDL(file, cache, database)
+		err = generator.StartFromDDL(file, arg.cache, arg.strict, arg.database)
 		if err != nil {
 			return err
 		}
@@ -214,25 +243,33 @@ func fromDDL(src, dir string, cfg *config.Config, cache, idea bool, database str
 	return nil
 }
 
-func fromMysqlDataSource(url, dir string, tablePat pattern, cfg *config.Config, cache, idea bool) error {
-	log := console.NewConsole(idea)
-	if len(url) == 0 {
+type dataSourceArg struct {
+	url, dir    string
+	tablePat    pattern
+	cfg         *config.Config
+	cache, idea bool
+	strict      bool
+}
+
+func fromMysqlDataSource(arg dataSourceArg) error {
+	log := console.NewConsole(arg.idea)
+	if len(arg.url) == 0 {
 		log.Error("%v", "expected data source of mysql, but nothing found")
 		return nil
 	}
 
-	if len(tablePat) == 0 {
+	if len(arg.tablePat) == 0 {
 		log.Error("%v", "expected table or table globbing patterns, but nothing found")
 		return nil
 	}
 
-	dsn, err := mysql.ParseDSN(url)
+	dsn, err := mysql.ParseDSN(arg.url)
 	if err != nil {
 		return err
 	}
 
 	logx.Disable()
-	databaseSource := strings.TrimSuffix(url, "/"+dsn.DBName) + "/information_schema"
+	databaseSource := strings.TrimSuffix(arg.url, "/"+dsn.DBName) + "/information_schema"
 	db := sqlx.NewMysql(databaseSource)
 	im := model.NewInformationSchemaModel(db)
 
@@ -243,7 +280,7 @@ func fromMysqlDataSource(url, dir string, tablePat pattern, cfg *config.Config,
 
 	matchTables := make(map[string]*model.Table)
 	for _, item := range tables {
-		if !tablePat.Match(item) {
+		if !arg.tablePat.Match(item) {
 			continue
 		}
 
@@ -264,15 +301,15 @@ func fromMysqlDataSource(url, dir string, tablePat pattern, cfg *config.Config,
 		return errors.New("no tables matched")
 	}
 
-	generator, err := gen.NewDefaultGenerator(dir, cfg, gen.WithConsoleOption(log))
+	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg, gen.WithConsoleOption(log))
 	if err != nil {
 		return err
 	}
 
-	return generator.StartFromInformationSchema(matchTables, cache)
+	return generator.StartFromInformationSchema(matchTables, arg.cache, arg.strict)
 }
 
-func fromPostgreSqlDataSource(url, pattern, dir, schema string, cfg *config.Config, cache, idea bool) error {
+func fromPostgreSqlDataSource(url, pattern, dir, schema string, cfg *config.Config, cache, idea, strict bool) error {
 	log := console.NewConsole(idea)
 	if len(url) == 0 {
 		log.Error("%v", "expected data source of postgresql, but nothing found")
@@ -324,5 +361,5 @@ func fromPostgreSqlDataSource(url, pattern, dir, schema string, cfg *config.Conf
 		return err
 	}
 
-	return generator.StartFromInformationSchema(matchTables, cache)
+	return generator.StartFromInformationSchema(matchTables, cache, strict)
 }
diff --git a/tools/goctl/model/sql/converter/types.go b/tools/goctl/model/sql/converter/types.go
index 4949656e..173b2316 100644
--- a/tools/goctl/model/sql/converter/types.go
+++ b/tools/goctl/model/sql/converter/types.go
@@ -132,28 +132,28 @@ var commonMysqlDataTypeMapString = map[string]string{
 }
 
 // ConvertDataType converts mysql column type into golang type
-func ConvertDataType(dataBaseType int, isDefaultNull, unsigned bool) (string, error) {
+func ConvertDataType(dataBaseType int, isDefaultNull, unsigned, strict bool) (string, error) {
 	tp, ok := commonMysqlDataTypeMapInt[dataBaseType]
 	if !ok {
 		return "", fmt.Errorf("unsupported database type: %v", dataBaseType)
 	}
 
-	return mayConvertNullType(tp, isDefaultNull, unsigned), nil
+	return mayConvertNullType(tp, isDefaultNull, unsigned, strict), nil
 }
 
 // ConvertStringDataType converts mysql column type into golang type
-func ConvertStringDataType(dataBaseType string, isDefaultNull, unsigned bool) (string, error) {
+func ConvertStringDataType(dataBaseType string, isDefaultNull, unsigned, strict bool) (string, error) {
 	tp, ok := commonMysqlDataTypeMapString[strings.ToLower(dataBaseType)]
 	if !ok {
 		return "", fmt.Errorf("unsupported database type: %s", dataBaseType)
 	}
 
-	return mayConvertNullType(tp, isDefaultNull, unsigned), nil
+	return mayConvertNullType(tp, isDefaultNull, unsigned, strict), nil
 }
 
-func mayConvertNullType(goDataType string, isDefaultNull, unsigned bool) string {
+func mayConvertNullType(goDataType string, isDefaultNull, unsigned, strict bool) string {
 	if !isDefaultNull {
-		if unsigned {
+		if unsigned && strict {
 			ret, ok := unsignedTypeMap[goDataType]
 			if ok {
 				return ret
diff --git a/tools/goctl/model/sql/gen/gen.go b/tools/goctl/model/sql/gen/gen.go
index 8a8a17a0..4b7b3ed1 100644
--- a/tools/goctl/model/sql/gen/gen.go
+++ b/tools/goctl/model/sql/gen/gen.go
@@ -102,8 +102,8 @@ func newDefaultOption() Option {
 	}
 }
 
-func (g *defaultGenerator) StartFromDDL(filename string, withCache bool, database string) error {
-	modelList, err := g.genFromDDL(filename, withCache, database)
+func (g *defaultGenerator) StartFromDDL(filename string, withCache, strict bool, database string) error {
+	modelList, err := g.genFromDDL(filename, withCache, strict, database)
 	if err != nil {
 		return err
 	}
@@ -111,10 +111,10 @@ func (g *defaultGenerator) StartFromDDL(filename string, withCache bool, databas
 	return g.createFile(modelList)
 }
 
-func (g *defaultGenerator) StartFromInformationSchema(tables map[string]*model.Table, withCache bool) error {
+func (g *defaultGenerator) StartFromInformationSchema(tables map[string]*model.Table, withCache, strict bool) error {
 	m := make(map[string]*codeTuple)
 	for _, each := range tables {
-		table, err := parser.ConvertDataType(each)
+		table, err := parser.ConvertDataType(each, strict)
 		if err != nil {
 			return err
 		}
@@ -201,11 +201,11 @@ func (g *defaultGenerator) createFile(modelList map[string]*codeTuple) error {
 }
 
 // ret1: key-table name,value-code
-func (g *defaultGenerator) genFromDDL(filename string, withCache bool, database string) (
+func (g *defaultGenerator) genFromDDL(filename string, withCache, strict bool, database string) (
 	map[string]*codeTuple, error,
 ) {
 	m := make(map[string]*codeTuple)
-	tables, err := parser.Parse(filename, database)
+	tables, err := parser.Parse(filename, database, strict)
 	if err != nil {
 		return nil, err
 	}
diff --git a/tools/goctl/model/sql/parser/parser.go b/tools/goctl/model/sql/parser/parser.go
index 814888e6..1caab859 100644
--- a/tools/goctl/model/sql/parser/parser.go
+++ b/tools/goctl/model/sql/parser/parser.go
@@ -8,6 +8,7 @@ import (
 
 	"github.com/zeromicro/ddl-parser/parser"
 	"github.com/zeromicro/go-zero/core/collection"
+
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/converter"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/model"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/util"
@@ -61,7 +62,7 @@ func parseNameOriginal(ts []*parser.Table) (nameOriginals [][]string) {
 }
 
 // Parse parses ddl into golang structure
-func Parse(filename, database string) ([]*Table, error) {
+func Parse(filename, database string, strict bool) ([]*Table, error) {
 	p := parser.NewParser()
 	tables, err := p.From(filename)
 	if err != nil {
@@ -124,7 +125,7 @@ func Parse(filename, database string) ([]*Table, error) {
 			return nil, fmt.Errorf("%s: unexpected join primary key", prefix)
 		}
 
-		primaryKey, fieldM, err := convertColumns(columns, primaryColumn)
+		primaryKey, fieldM, err := convertColumns(columns, primaryColumn, strict)
 		if err != nil {
 			return nil, err
 		}
@@ -190,7 +191,7 @@ func checkDuplicateUniqueIndex(uniqueIndex map[string][]*Field, tableName string
 	}
 }
 
-func convertColumns(columns []*parser.Column, primaryColumn string) (Primary, map[string]*Field, error) {
+func convertColumns(columns []*parser.Column, primaryColumn string, strict bool) (Primary, map[string]*Field, error) {
 	var (
 		primaryKey Primary
 		fieldM     = make(map[string]*Field)
@@ -219,7 +220,7 @@ func convertColumns(columns []*parser.Column, primaryColumn string) (Primary, ma
 			}
 		}
 
-		dataType, err := converter.ConvertDataType(column.DataType.Type(), isDefaultNull, column.DataType.Unsigned())
+		dataType, err := converter.ConvertDataType(column.DataType.Type(), isDefaultNull, column.DataType.Unsigned(), strict)
 		if err != nil {
 			return Primary{}, nil, err
 		}
@@ -264,10 +265,10 @@ func (t *Table) ContainsTime() bool {
 }
 
 // ConvertDataType converts mysql data type into golang data type
-func ConvertDataType(table *model.Table) (*Table, error) {
+func ConvertDataType(table *model.Table, strict bool) (*Table, error) {
 	isPrimaryDefaultNull := table.PrimaryKey.ColumnDefault == nil && table.PrimaryKey.IsNullAble == "YES"
 	isPrimaryUnsigned := strings.Contains(table.PrimaryKey.DbColumn.ColumnType, "unsigned")
-	primaryDataType, err := converter.ConvertStringDataType(table.PrimaryKey.DataType, isPrimaryDefaultNull, isPrimaryUnsigned)
+	primaryDataType, err := converter.ConvertStringDataType(table.PrimaryKey.DataType, isPrimaryDefaultNull, isPrimaryUnsigned, strict)
 	if err != nil {
 		return nil, err
 	}
@@ -292,7 +293,7 @@ func ConvertDataType(table *model.Table) (*Table, error) {
 		AutoIncrement: strings.Contains(table.PrimaryKey.Extra, "auto_increment"),
 	}
 
-	fieldM, err := getTableFields(table)
+	fieldM, err := getTableFields(table, strict)
 	if err != nil {
 		return nil, err
 	}
@@ -342,12 +343,12 @@ func ConvertDataType(table *model.Table) (*Table, error) {
 	return &reply, nil
 }
 
-func getTableFields(table *model.Table) (map[string]*Field, error) {
+func getTableFields(table *model.Table, strict bool) (map[string]*Field, error) {
 	fieldM := make(map[string]*Field)
 	for _, each := range table.Columns {
 		isDefaultNull := each.ColumnDefault == nil && each.IsNullAble == "YES"
 		isPrimaryUnsigned := strings.Contains(each.ColumnType, "unsigned")
-		dt, err := converter.ConvertStringDataType(each.DataType, isDefaultNull, isPrimaryUnsigned)
+		dt, err := converter.ConvertStringDataType(each.DataType, isDefaultNull, isPrimaryUnsigned, strict)
 		if err != nil {
 			return nil, err
 		}


Test patch:
diff --git a/core/mapping/marshaler_test.go b/core/mapping/marshaler_test.go
index fe98b8f6..5e34dd85 100644
--- a/core/mapping/marshaler_test.go
+++ b/core/mapping/marshaler_test.go
@@ -281,6 +281,58 @@ func TestMarshal_RangeIllegal(t *testing.T) {
 	}
 }
 
+func TestMarshal_RangeLeftEqualsToRight(t *testing.T) {
+	tests := []struct {
+		name  string
+		value interface{}
+		err   error
+	}{
+		{
+			name: "left inclusive, right inclusive",
+			value: struct {
+				Int int `json:"int,range=[2:2]"`
+			}{
+				Int: 2,
+			},
+		},
+		{
+			name: "left inclusive, right exclusive",
+			value: struct {
+				Int int `json:"int,range=[2:2)"`
+			}{
+				Int: 2,
+			},
+			err: errNumberRange,
+		},
+		{
+			name: "left exclusive, right inclusive",
+			value: struct {
+				Int int `json:"int,range=(2:2]"`
+			}{
+				Int: 2,
+			},
+			err: errNumberRange,
+		},
+		{
+			name: "left exclusive, right exclusive",
+			value: struct {
+				Int int `json:"int,range=(2:2)"`
+			}{
+				Int: 2,
+			},
+			err: errNumberRange,
+		},
+	}
+
+	for _, test := range tests {
+		test := test
+		t.Run(test.name, func(t *testing.T) {
+			_, err := Marshal(test.value)
+			assert.Equal(t, test.err, err)
+		})
+	}
+}
+
 func TestMarshal_FromString(t *testing.T) {
 	v := struct {
 		Age int `json:"age,string"`
diff --git a/tools/goctl/model/sql/command/command_test.go b/tools/goctl/model/sql/command/command_test.go
index bd35713f..330a6630 100644
--- a/tools/goctl/model/sql/command/command_test.go
+++ b/tools/goctl/model/sql/command/command_test.go
@@ -10,6 +10,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+
 	"github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/gen"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
@@ -27,12 +28,25 @@ func TestFromDDl(t *testing.T) {
 	err := gen.Clean()
 	assert.Nil(t, err)
 
-	err = fromDDL("./user.sql", pathx.MustTempDir(), cfg, true, false, "go_zero")
+	err = fromDDL(ddlArg{
+		src:      "./user.sql",
+		dir:      pathx.MustTempDir(),
+		cfg:      cfg,
+		cache:    true,
+		database: "go-zero",
+		strict:   false,
+	})
 	assert.Equal(t, errNotMatched, err)
 
 	// case dir is not exists
 	unknownDir := filepath.Join(pathx.MustTempDir(), "test", "user.sql")
-	err = fromDDL(unknownDir, pathx.MustTempDir(), cfg, true, false, "go_zero")
+	err = fromDDL(ddlArg{
+		src:      unknownDir,
+		dir:      pathx.MustTempDir(),
+		cfg:      cfg,
+		cache:    true,
+		database: "go_zero",
+	})
 	assert.True(t, func() bool {
 		switch err.(type) {
 		case *os.PathError:
@@ -43,7 +57,12 @@ func TestFromDDl(t *testing.T) {
 	}())
 
 	// case empty src
-	err = fromDDL("", pathx.MustTempDir(), cfg, true, false, "go_zero")
+	err = fromDDL(ddlArg{
+		dir:      pathx.MustTempDir(),
+		cfg:      cfg,
+		cache:    true,
+		database: "go_zero",
+	})
 	if err != nil {
 		assert.Equal(t, "expected path or path globbing patterns, but nothing found", err.Error())
 	}
@@ -75,7 +94,13 @@ func TestFromDDl(t *testing.T) {
 
 	filename := filepath.Join(tempDir, "usermodel.go")
 	fromDDL := func(db string) {
-		err = fromDDL(filepath.Join(tempDir, "user*.sql"), tempDir, cfg, true, false, db)
+		err = fromDDL(ddlArg{
+			src:      filepath.Join(tempDir, "user*.sql"),
+			dir:      tempDir,
+			cfg:      cfg,
+			cache:    true,
+			database: db,
+		})
 		assert.Nil(t, err)
 
 		_, err = os.Stat(filename)
diff --git a/tools/goctl/model/sql/converter/types_test.go b/tools/goctl/model/sql/converter/types_test.go
index e16e0613..c5f8353d 100644
--- a/tools/goctl/model/sql/converter/types_test.go
+++ b/tools/goctl/model/sql/converter/types_test.go
@@ -8,23 +8,23 @@ import (
 )
 
 func TestConvertDataType(t *testing.T) {
-	v, err := ConvertDataType(parser.TinyInt, false, false)
+	v, err := ConvertDataType(parser.TinyInt, false, false, true)
 	assert.Nil(t, err)
 	assert.Equal(t, "int64", v)
 
-	v, err = ConvertDataType(parser.TinyInt, false, true)
+	v, err = ConvertDataType(parser.TinyInt, false, true, true)
 	assert.Nil(t, err)
 	assert.Equal(t, "uint64", v)
 
-	v, err = ConvertDataType(parser.TinyInt, true, false)
+	v, err = ConvertDataType(parser.TinyInt, true, false, true)
 	assert.Nil(t, err)
 	assert.Equal(t, "sql.NullInt64", v)
 
-	v, err = ConvertDataType(parser.Timestamp, false, false)
+	v, err = ConvertDataType(parser.Timestamp, false, false, true)
 	assert.Nil(t, err)
 	assert.Equal(t, "time.Time", v)
 
-	v, err = ConvertDataType(parser.Timestamp, true, false)
+	v, err = ConvertDataType(parser.Timestamp, true, false, true)
 	assert.Nil(t, err)
 	assert.Equal(t, "sql.NullTime", v)
 }
diff --git a/tools/goctl/model/sql/gen/gen_test.go b/tools/goctl/model/sql/gen/gen_test.go
index 89b6e714..e73ecc6e 100644
--- a/tools/goctl/model/sql/gen/gen_test.go
+++ b/tools/goctl/model/sql/gen/gen_test.go
@@ -15,6 +15,7 @@ import (
 	"github.com/stretchr/testify/require"
 	"github.com/zeromicro/go-zero/core/logx"
 	"github.com/zeromicro/go-zero/core/stringx"
+
 	"github.com/zeromicro/go-zero/tools/goctl/config"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/builderx"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/parser"
@@ -40,7 +41,7 @@ func TestCacheModel(t *testing.T) {
 	})
 	assert.Nil(t, err)
 
-	err = g.StartFromDDL(sqlFile, true, "go_zero")
+	err = g.StartFromDDL(sqlFile, true, false, "go_zero")
 	assert.Nil(t, err)
 	assert.True(t, func() bool {
 		_, err := os.Stat(filepath.Join(cacheDir, "TestUserModel.go"))
@@ -51,7 +52,7 @@ func TestCacheModel(t *testing.T) {
 	})
 	assert.Nil(t, err)
 
-	err = g.StartFromDDL(sqlFile, false, "go_zero")
+	err = g.StartFromDDL(sqlFile, false, false, "go_zero")
 	assert.Nil(t, err)
 	assert.True(t, func() bool {
 		_, err := os.Stat(filepath.Join(noCacheDir, "testusermodel.go"))
@@ -78,7 +79,7 @@ func TestNamingModel(t *testing.T) {
 	})
 	assert.Nil(t, err)
 
-	err = g.StartFromDDL(sqlFile, true, "go_zero")
+	err = g.StartFromDDL(sqlFile, true, false, "go_zero")
 	assert.Nil(t, err)
 	assert.True(t, func() bool {
 		_, err := os.Stat(filepath.Join(camelDir, "TestUserModel.go"))
@@ -89,7 +90,7 @@ func TestNamingModel(t *testing.T) {
 	})
 	assert.Nil(t, err)
 
-	err = g.StartFromDDL(sqlFile, true, "go_zero")
+	err = g.StartFromDDL(sqlFile, true, false, "go_zero")
 	assert.Nil(t, err)
 	assert.True(t, func() bool {
 		_, err := os.Stat(filepath.Join(snakeDir, "test_user_model.go"))
@@ -186,7 +187,7 @@ func Test_genPublicModel(t *testing.T) {
 	})
 	require.NoError(t, err)
 
-	tables, err := parser.Parse(modelFilename, "")
+	tables, err := parser.Parse(modelFilename, "", false)
 	require.Equal(t, 1, len(tables))
 
 	code, err := g.genModelCustom(*tables[0], false)
diff --git a/tools/goctl/model/sql/parser/parser_test.go b/tools/goctl/model/sql/parser/parser_test.go
index b5907f34..0d205287 100644
--- a/tools/goctl/model/sql/parser/parser_test.go
+++ b/tools/goctl/model/sql/parser/parser_test.go
@@ -7,6 +7,7 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/model"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
@@ -17,7 +18,7 @@ func TestParsePlainText(t *testing.T) {
 	err := ioutil.WriteFile(sqlFile, []byte("plain text"), 0o777)
 	assert.Nil(t, err)
 
-	_, err = Parse(sqlFile, "go_zero")
+	_, err = Parse(sqlFile, "go_zero", false)
 	assert.NotNil(t, err)
 }
 
@@ -26,7 +27,7 @@ func TestParseSelect(t *testing.T) {
 	err := ioutil.WriteFile(sqlFile, []byte("select * from user"), 0o777)
 	assert.Nil(t, err)
 
-	tables, err := Parse(sqlFile, "go_zero")
+	tables, err := Parse(sqlFile, "go_zero", false)
 	assert.Nil(t, err)
 	assert.Equal(t, 0, len(tables))
 }
@@ -39,7 +40,7 @@ func TestParseCreateTable(t *testing.T) {
 	err := ioutil.WriteFile(sqlFile, []byte(user), 0o777)
 	assert.Nil(t, err)
 
-	tables, err := Parse(sqlFile, "go_zero")
+	tables, err := Parse(sqlFile, "go_zero", false)
 	assert.Equal(t, 1, len(tables))
 	table := tables[0]
 	assert.Nil(t, err)