Repository: zeromicro/go-zero
Pull request #2407 (2022-09-17 15:24:36)

feat(goctl):Add ignore-columns flag
Added `--ignore-columns, -i` flag to specify the columns that need to be ignored when inserting or updating rows, for example: you need to ignore the `create_time` and `update_time` columns, you can use `--ignore-columns, -i` flag for model generation command reference is as follows

```
# generated from sql file
$ goctl model mysql ddl --src $sql_file --dir $output -i 'create_time' -i 'update_time'
# or use comma splitter
# $ goctl model mysql ddl --src $sql_file --dir $output -i 'create_time,update_time'

# generated from dsn
$ goctl model mysql datasource --url $dsn --table $patterns --dir $output -i 'create_time' -i 'update_time'
# or use comma splitter
# $ goctl model mysql datasource --url $dsn --table $patterns --dir $output -i 'create_time,update_time'
```


Patch:
diff --git a/gateway/internal/eventhandler.go b/gateway/internal/eventhandler.go
index a840930f..e3a0f0c2 100644
--- a/gateway/internal/eventhandler.go
+++ b/gateway/internal/eventhandler.go
@@ -27,15 +27,6 @@ func NewEventHandler(writer io.Writer, resolver jsonpb.AnyResolver) *EventHandle
 	}
 }
 
-func (h *EventHandler) OnResolveMethod(_ *desc.MethodDescriptor) {
-}
-
-func (h *EventHandler) OnSendHeaders(_ metadata.MD) {
-}
-
-func (h *EventHandler) OnReceiveHeaders(_ metadata.MD) {
-}
-
 func (h *EventHandler) OnReceiveResponse(message proto.Message) {
 	if err := h.marshaler.Marshal(h.writer, message); err != nil {
 		logx.Error(err)
@@ -45,3 +36,12 @@ func (h *EventHandler) OnReceiveResponse(message proto.Message) {
 func (h *EventHandler) OnReceiveTrailers(status *status.Status, _ metadata.MD) {
 	h.Status = status
 }
+
+func (h *EventHandler) OnResolveMethod(_ *desc.MethodDescriptor) {
+}
+
+func (h *EventHandler) OnSendHeaders(_ metadata.MD) {
+}
+
+func (h *EventHandler) OnReceiveHeaders(_ metadata.MD) {
+}
diff --git a/tools/goctl/internal/version/version.go b/tools/goctl/internal/version/version.go
index abb8bb80..2fc7caaf 100644
--- a/tools/goctl/internal/version/version.go
+++ b/tools/goctl/internal/version/version.go
@@ -6,7 +6,7 @@ import (
 )
 
 // BuildVersion is the version of goctl.
-const BuildVersion = "1.4.1"
+const BuildVersion = "1.4.2"
 
 var tag = map[string]int{"pre-alpha": 0, "alpha": 1, "pre-bata": 2, "beta": 3, "released": 4, "": 5}
 
diff --git a/tools/goctl/model/cmd.go b/tools/goctl/model/cmd.go
index 131fe048..4b76cd2b 100644
--- a/tools/goctl/model/cmd.go
+++ b/tools/goctl/model/cmd.go
@@ -93,6 +93,7 @@ func init() {
 	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.PersistentFlags().StringSliceVarP(&command.VarStringSliceIgnoreColumns, "ignore-columns", "i", []string{"create_at", "created_at", "create_time", "update_at", "updated_at", "update_time"}, "Ignore columns while creating or updating rows")
 
 	mysqlCmd.AddCommand(datasourceCmd)
 	mysqlCmd.AddCommand(ddlCmd)
diff --git a/tools/goctl/model/sql/command/command.go b/tools/goctl/model/sql/command/command.go
index a12f2a2b..f00b4229 100644
--- a/tools/goctl/model/sql/command/command.go
+++ b/tools/goctl/model/sql/command/command.go
@@ -7,10 +7,11 @@ import (
 
 	"github.com/go-sql-driver/mysql"
 	"github.com/spf13/cobra"
+
+	"github.com/zeromicro/go-zero/core/collection"
 	"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"
@@ -50,6 +51,8 @@ var (
 	VarStringBranch string
 	// VarBoolStrict describes whether the strict mode is enabled.
 	VarBoolStrict bool
+	// VarStringSliceIgnoreColumns represents the columns which are ignored.
+	VarStringSliceIgnoreColumns []string
 )
 
 var errNotMatched = errors.New("sql not matched")
@@ -81,13 +84,14 @@ func MysqlDDL(_ *cobra.Command, _ []string) error {
 	}
 
 	arg := ddlArg{
-		src:      src,
-		dir:      dir,
-		cfg:      cfg,
-		cache:    cache,
-		idea:     idea,
-		database: database,
-		strict:   VarBoolStrict,
+		src:           src,
+		dir:           dir,
+		cfg:           cfg,
+		cache:         cache,
+		idea:          idea,
+		database:      database,
+		strict:        VarBoolStrict,
+		ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
 	}
 	return fromDDL(arg)
 }
@@ -121,17 +125,29 @@ func MySqlDataSource(_ *cobra.Command, _ []string) error {
 	}
 
 	arg := dataSourceArg{
-		url:      url,
-		dir:      dir,
-		tablePat: patterns,
-		cfg:      cfg,
-		cache:    cache,
-		idea:     idea,
-		strict:   VarBoolStrict,
+		url:           url,
+		dir:           dir,
+		tablePat:      patterns,
+		cfg:           cfg,
+		cache:         cache,
+		idea:          idea,
+		strict:        VarBoolStrict,
+		ignoreColumns: mergeColumns(VarStringSliceIgnoreColumns),
 	}
 	return fromMysqlDataSource(arg)
 }
 
+func mergeColumns(columns []string) []string {
+	set := collection.NewSet()
+	for _, v := range columns {
+		fields := strings.FieldsFunc(v, func(r rune) bool {
+			return r == ','
+		})
+		set.AddStr(fields...)
+	}
+	return set.KeysStr()
+}
+
 type pattern map[string]struct{}
 
 func (p pattern) Match(s string) bool {
@@ -205,11 +221,12 @@ func PostgreSqlDataSource(_ *cobra.Command, _ []string) error {
 }
 
 type ddlArg struct {
-	src, dir    string
-	cfg         *config.Config
-	cache, idea bool
-	database    string
-	strict      bool
+	src, dir      string
+	cfg           *config.Config
+	cache, idea   bool
+	database      string
+	strict        bool
+	ignoreColumns []string
 }
 
 func fromDDL(arg ddlArg) error {
@@ -228,7 +245,8 @@ func fromDDL(arg ddlArg) error {
 		return errNotMatched
 	}
 
-	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg, gen.WithConsoleOption(log))
+	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
+		gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
 	if err != nil {
 		return err
 	}
@@ -244,11 +262,12 @@ func fromDDL(arg ddlArg) error {
 }
 
 type dataSourceArg struct {
-	url, dir    string
-	tablePat    pattern
-	cfg         *config.Config
-	cache, idea bool
-	strict      bool
+	url, dir      string
+	tablePat      pattern
+	cfg           *config.Config
+	cache, idea   bool
+	strict        bool
+	ignoreColumns []string
 }
 
 func fromMysqlDataSource(arg dataSourceArg) error {
@@ -301,7 +320,8 @@ func fromMysqlDataSource(arg dataSourceArg) error {
 		return errors.New("no tables matched")
 	}
 
-	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg, gen.WithConsoleOption(log))
+	generator, err := gen.NewDefaultGenerator(arg.dir, arg.cfg,
+		gen.WithConsoleOption(log), gen.WithIgnoreColumns(arg.ignoreColumns))
 	if err != nil {
 		return err
 	}
diff --git a/tools/goctl/model/sql/example/makefile b/tools/goctl/model/sql/example/makefile
index c08e09ee..99368309 100644
--- a/tools/goctl/model/sql/example/makefile
+++ b/tools/goctl/model/sql/example/makefile
@@ -5,6 +5,10 @@ fromDDLWithCache:
 	goctl template clean
 	goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache" -cache
 
+fromDDLWithCacheAndIgnoreColumns:
+	goctl template clean
+	goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/ignore_columns/cache" -cache -i 'gmt_create,create_at' -i 'gmt_modified,update_at'
+
 fromDDLWithCacheAndDb:
 	goctl template clean
 	goctl model mysql ddl -src="./sql/*.sql" -dir="./sql/model/cache_db" -database="1gozero" -cache
diff --git a/tools/goctl/model/sql/gen/gen.go b/tools/goctl/model/sql/gen/gen.go
index 4b7b3ed1..187653b6 100644
--- a/tools/goctl/model/sql/gen/gen.go
+++ b/tools/goctl/model/sql/gen/gen.go
@@ -26,10 +26,11 @@ type (
 	defaultGenerator struct {
 		console.Console
 		// source string
-		dir          string
-		pkg          string
-		cfg          *config.Config
-		isPostgreSql bool
+		dir           string
+		pkg           string
+		cfg           *config.Config
+		isPostgreSql  bool
+		ignoreColumns []string
 	}
 
 	// Option defines a function with argument defaultGenerator
@@ -82,14 +83,21 @@ func NewDefaultGenerator(dir string, cfg *config.Config, opt ...Option) (*defaul
 	return generator, nil
 }
 
-// WithConsoleOption creates a console option
+// WithConsoleOption creates a console option.
 func WithConsoleOption(c console.Console) Option {
 	return func(generator *defaultGenerator) {
 		generator.Console = c
 	}
 }
 
-// WithPostgreSql marks  defaultGenerator.isPostgreSql true
+// WithIgnoreColumns ignores the columns while insert or update rows.
+func WithIgnoreColumns(ignoreColumns []string) Option {
+	return func(generator *defaultGenerator) {
+		generator.ignoreColumns = ignoreColumns
+	}
+}
+
+// WithPostgreSql marks  defaultGenerator.isPostgreSql true.
 func WithPostgreSql() Option {
 	return func(generator *defaultGenerator) {
 		generator.isPostgreSql = true
@@ -235,6 +243,16 @@ type Table struct {
 	PrimaryCacheKey        Key
 	UniqueCacheKey         []Key
 	ContainsUniqueCacheKey bool
+	ignoreColumns          []string
+}
+
+func (t Table) isIgnoreColumns(columnName string) bool {
+	for _, v := range t.ignoreColumns {
+		if v == columnName {
+			return true
+		}
+	}
+	return false
 }
 
 func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, error) {
@@ -249,6 +267,7 @@ func (g *defaultGenerator) genModel(in parser.Table, withCache bool) (string, er
 	table.PrimaryCacheKey = primaryKey
 	table.UniqueCacheKey = uniqueKey
 	table.ContainsUniqueCacheKey = len(uniqueKey) > 0
+	table.ignoreColumns = g.ignoreColumns
 
 	importsCode, err := genImports(table, withCache, in.ContainsTime())
 	if err != nil {
diff --git a/tools/goctl/model/sql/gen/insert.go b/tools/goctl/model/sql/gen/insert.go
index ad9a54ba..683d98f2 100644
--- a/tools/goctl/model/sql/gen/insert.go
+++ b/tools/goctl/model/sql/gen/insert.go
@@ -31,7 +31,7 @@ func genInsert(table Table, withCache, postgreSql bool) (string, string, error)
 	var count int
 	for _, field := range table.Fields {
 		camel := util.SafeString(field.Name.ToCamel())
-		if camel == "CreateTime" || camel == "UpdateTime" || camel == "CreateAt" || camel == "UpdateAt" {
+		if table.isIgnoreColumns(field.Name.Source()) {
 			continue
 		}
 
diff --git a/tools/goctl/model/sql/gen/update.go b/tools/goctl/model/sql/gen/update.go
index d209b577..09554aa3 100644
--- a/tools/goctl/model/sql/gen/update.go
+++ b/tools/goctl/model/sql/gen/update.go
@@ -21,7 +21,7 @@ func genUpdate(table Table, withCache, postgreSql bool) (
 	}
 	for _, field := range table.Fields {
 		camel := util.SafeString(field.Name.ToCamel())
-		if camel == "CreateTime" || camel == "UpdateTime" || camel == "CreateAt" || camel == "UpdateAt" {
+		if table.isIgnoreColumns(field.Name.Source()) {
 			continue
 		}
 
diff --git a/tools/goctl/model/sql/gen/vars.go b/tools/goctl/model/sql/gen/vars.go
index 49534095..9df8c32a 100644
--- a/tools/goctl/model/sql/gen/vars.go
+++ b/tools/goctl/model/sql/gen/vars.go
@@ -1,8 +1,10 @@
 package gen
 
 import (
+	"fmt"
 	"strings"
 
+	"github.com/zeromicro/go-zero/core/collection"
 	"github.com/zeromicro/go-zero/tools/goctl/model/sql/template"
 	"github.com/zeromicro/go-zero/tools/goctl/util"
 	"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
@@ -32,6 +34,17 @@ func genVars(table Table, withCache, postgreSql bool) (string, error) {
 		"withCache":             withCache,
 		"postgreSql":            postgreSql,
 		"data":                  table,
+		"ignoreColumns": func() string {
+			var set = collection.NewSet()
+			for _, c := range table.ignoreColumns {
+				if postgreSql {
+					set.AddStr(fmt.Sprintf(`"%s"`, c))
+				} else {
+					set.AddStr(fmt.Sprintf("\"`%s`\"", c))
+				}
+			}
+			return strings.Join(set.KeysStr(), ", ")
+		}(),
 	})
 	if err != nil {
 		return "", err
diff --git a/tools/goctl/model/sql/template/vars.go b/tools/goctl/model/sql/template/vars.go
index da093d80..8db226ff 100644
--- a/tools/goctl/model/sql/template/vars.go
+++ b/tools/goctl/model/sql/template/vars.go
@@ -1,20 +1,7 @@
 package template
 
-import "fmt"
+import _ "embed"
 
 // Vars defines a template for var block in model
-var Vars = fmt.Sprintf(
-	`
-var (
-	{{.lowerStartCamelObject}}FieldNames          = builder.RawFieldNames(&{{.upperStartCamelObject}}{}{{if .postgreSql}},true{{end}})
-	{{.lowerStartCamelObject}}Rows                = strings.Join({{.lowerStartCamelObject}}FieldNames, ",")
-	{{.lowerStartCamelObject}}RowsExpectAutoSet   = {{if .postgreSql}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "%screate_time%s", "%supdate_time%s", "%screate_at%s", "%supdate_at%s"), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}",{{end}} "%screate_time%s", "%supdate_time%s", "%screate_at%s", "%supdate_at%s"), ","){{end}}
-	{{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "%screate_time%s", "%supdate_time%s", "%screate_at%s", "%supdate_at%s")){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", "%screate_time%s", "%supdate_time%s", "%screate_at%s", "%supdate_at%s"), "=?,") + "=?"{{end}}
-
-	{{if .withCache}}{{.cacheKeys}}{{end}}
-)
-`, "", "", "", "", "", "", "", "", // postgreSql mode
-	"`", "`", "`", "`", "`", "`", "`", "`",
-	"", "", "", "", "", "", "", "", // postgreSql mode
-	"`", "`", "`", "`", "`", "`", "`", "`",
-)
+//go:embed vars.tpl
+var Vars string
diff --git a/tools/goctl/model/sql/template/vars.tpl b/tools/goctl/model/sql/template/vars.tpl
new file mode 100644
index 00000000..c11fe535
--- /dev/null
+++ b/tools/goctl/model/sql/template/vars.tpl
@@ -0,0 +1,8 @@
+var (
+{{.lowerStartCamelObject}}FieldNames = builder.RawFieldNames(&{{.upperStartCamelObject}}{}{{if .postgreSql}}, true{{end}})
+{{.lowerStartCamelObject}}Rows = strings.Join({{.lowerStartCamelObject}}FieldNames, ",")
+{{.lowerStartCamelObject}}RowsExpectAutoSet = {{if .postgreSql}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}", {{end}} {{.ignoreColumns}}), ","){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, {{if .autoIncrement}}"{{.originalPrimaryKey}}", {{end}} {{.ignoreColumns}}), ","){{end}}
+{{.lowerStartCamelObject}}RowsWithPlaceHolder = {{if .postgreSql}}builder.PostgreSqlJoin(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}})){{else}}strings.Join(stringx.Remove({{.lowerStartCamelObject}}FieldNames, "{{.originalPrimaryKey}}", {{.ignoreColumns}}), "=?,") + "=?"{{end}}
+
+{{if .withCache}}{{.cacheKeys}}{{end}}
+)


Test patch:
diff --git a/gateway/internal/eventhandler_test.go b/gateway/internal/eventhandler_test.go
new file mode 100644
index 00000000..cf81ace1
--- /dev/null
+++ b/gateway/internal/eventhandler_test.go
@@ -0,0 +1,20 @@
+package internal
+
+import (
+	"io"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+func TestEventHandler(t *testing.T) {
+	h := NewEventHandler(io.Discard, nil)
+	h.OnResolveMethod(nil)
+	h.OnSendHeaders(nil)
+	h.OnReceiveHeaders(nil)
+	h.OnReceiveTrailers(status.New(codes.OK, ""), nil)
+	assert.Equal(t, codes.OK, h.Status.Code())
+	h.OnReceiveResponse(nil)
+}