feat: get table info

This commit is contained in:
zhuyasen
2024-11-24 15:05:26 +08:00
parent 21272aee58
commit e3b6e11e6b
7 changed files with 287 additions and 56 deletions

View File

@@ -261,10 +261,11 @@ func ConvertToSQLByMgoFields(tableName string, fields []*MgoField) (string, map[
protoObjectStrs = append(protoObjectStrs, field.ProtoObjectStr)
}
}
if isHaveID {
fieldStr = " id bigint unsigned primary key,\n" + fieldStr
}
fieldStr = strings.TrimSuffix(fieldStr, ",\n")
if isHaveID {
fieldStr = " `id` varchar(24),\n" + fieldStr + ",\n PRIMARY KEY (id)"
}
if len(objectStrs) > 0 {
srcMongoTypeMap[SubStructKey] = strings.Join(objectStrs, "\n") + "\n"

View File

@@ -30,6 +30,8 @@ type options struct {
IsEmbed bool // is gorm.Model embedded
IsWebProto bool // true: proto file include router path and swagger info, false: normal proto file without router and swagger
IsExtendedAPI bool // true: extended api (9 api), false: basic api (5 api)
IsCustomTemplate bool // true: custom extend template, false: sponge template
}
var defaultOptions = options{
@@ -149,6 +151,13 @@ func WithExtendedAPI() Option {
}
}
// WithCustomTemplate set custom template
func WithCustomTemplate() Option {
return func(o *options) {
o.IsCustomTemplate = true
}
}
func parseOption(options []Option) options {
o := defaultOptions
for _, f := range options {

View File

@@ -37,6 +37,8 @@ const (
CodeTypeService = "service"
// CodeTypeCrudInfo crud info json data
CodeTypeCrudInfo = "crud_info"
// CodeTypeTableInfo table info json data
CodeTypeTableInfo = "table_info"
// DBDriverMysql mysql driver
DBDriverMysql = "mysql"
@@ -87,6 +89,7 @@ func ParseSQL(sql string, options ...Option) (map[string]string, error) {
importPath := make(map[string]struct{})
tableNames := make([]string, 0, len(stmts))
primaryKeysCodes := make([]string, 0, len(stmts))
tableInfoCodes := make([]string, 0, len(stmts))
for _, stmt := range stmts {
if ct, ok := stmt.(*ast.CreateTableStmt); ok {
code, err2 := makeCode(ct, opt)
@@ -101,6 +104,7 @@ func ParseSQL(sql string, options ...Option) (map[string]string, error) {
modelJSONCodes = append(modelJSONCodes, code.modelJSON)
tableNames = append(tableNames, toCamel(ct.Table.Name.String()))
primaryKeysCodes = append(primaryKeysCodes, code.crudInfo)
tableInfoCodes = append(tableInfoCodes, string(code.tableInfo))
for _, s := range code.importPaths {
importPath[s] = struct{}{}
}
@@ -124,24 +128,27 @@ func ParseSQL(sql string, options ...Option) (map[string]string, error) {
}
var codesMap = map[string]string{
CodeTypeModel: modelCode,
CodeTypeJSON: strings.Join(modelJSONCodes, "\n\n"),
CodeTypeDAO: strings.Join(updateFieldsCodes, "\n\n"),
CodeTypeHandler: strings.Join(handlerStructCodes, "\n\n"),
CodeTypeProto: strings.Join(protoFileCodes, "\n\n"),
CodeTypeService: strings.Join(serviceStructCodes, "\n\n"),
TableName: strings.Join(tableNames, ", "),
CodeTypeCrudInfo: strings.Join(primaryKeysCodes, "||||"),
CodeTypeModel: modelCode,
CodeTypeJSON: strings.Join(modelJSONCodes, "\n\n"),
CodeTypeDAO: strings.Join(updateFieldsCodes, "\n\n"),
CodeTypeHandler: strings.Join(handlerStructCodes, "\n\n"),
CodeTypeProto: strings.Join(protoFileCodes, "\n\n"),
CodeTypeService: strings.Join(serviceStructCodes, "\n\n"),
TableName: strings.Join(tableNames, ", "),
CodeTypeCrudInfo: strings.Join(primaryKeysCodes, "||||"),
CodeTypeTableInfo: strings.Join(tableInfoCodes, "||||"),
}
return codesMap, nil
}
type tmplData struct {
TableName string
TName string
TableNamePrefix string
RawTableName string // raw table name, example: foo_bar
TableName string // table name in camel case, example: FooBar
TName string // table name first letter in lower case, example: fooBar
NameFunc bool
RawTableName string
Fields []tmplField
Comment string
SubStructs string // sub structs for model
@@ -351,25 +358,31 @@ type codeText struct {
protoFile string
serviceStruct string
crudInfo string
tableInfo []byte
}
// nolint
func makeCode(stmt *ast.CreateTableStmt, opt options) (*codeText, error) {
importPath := make([]string, 0, 1)
data := tmplData{
TableName: stmt.Table.Name.String(),
RawTableName: stmt.Table.Name.String(),
//Fields: make([]tmplField, 0, 1),
DBDriver: opt.DBDriver,
TableNamePrefix: opt.TablePrefix,
RawTableName: stmt.Table.Name.String(),
DBDriver: opt.DBDriver,
}
tablePrefix := opt.TablePrefix
if tablePrefix != "" && strings.HasPrefix(data.TableName, tablePrefix) {
tablePrefix := data.TableNamePrefix
if tablePrefix != "" && strings.HasPrefix(data.RawTableName, tablePrefix) {
data.NameFunc = true
data.TableName = data.TableName[len(tablePrefix):]
data.TableName = toCamel(data.RawTableName[len(tablePrefix):])
} else {
data.TableName = toCamel(data.RawTableName)
}
data.TName = firstLetterToLower(data.TableName)
if opt.ForceTableName || data.RawTableName != inflection.Plural(data.RawTableName) {
data.NameFunc = true
}
switch opt.DBDriver {
case DBDriverMongodb:
if opt.JSONNamedType != 0 {
@@ -379,9 +392,6 @@ func makeCode(stmt *ast.CreateTableStmt, opt options) (*codeText, error) {
}
}
data.TableName = toCamel(data.TableName)
data.TName = firstLetterToLower(data.TableName)
// find table comment
for _, o := range stmt.Options {
if o.Tp == ast.TableOptionComment {
@@ -518,6 +528,13 @@ func makeCode(stmt *ast.CreateTableStmt, opt options) (*codeText, error) {
data.Fields = append(data.Fields, field)
}
if v, ok := opt.FieldTypes[SubStructKey]; ok {
data.SubStructs = v
}
if v, ok := opt.FieldTypes[ProtoSubStructKey]; ok {
data.ProtoSubStructs = v
}
if len(data.Fields) == 0 {
return nil, errors.New("no columns found in table " + data.TableName)
}
@@ -525,11 +542,9 @@ func makeCode(stmt *ast.CreateTableStmt, opt options) (*codeText, error) {
data.CrudInfo = newCrudInfo(data)
data.CrudInfo.IsCommonType = data.isCommonStyle(opt.IsEmbed)
if v, ok := opt.FieldTypes[SubStructKey]; ok {
data.SubStructs = v
}
if v, ok := opt.FieldTypes[ProtoSubStructKey]; ok {
data.ProtoSubStructs = v
if opt.IsCustomTemplate {
tableInfo := newTableInfo(data)
return &codeText{tableInfo: tableInfo.getCode()}, nil
}
updateFieldsCode, err := getUpdateFieldsCode(data, opt.IsEmbed)

View File

@@ -11,10 +11,8 @@ import (
)
func TestParseSQL(t *testing.T) {
sqls := []string{`create table user
(
id bigint unsigned auto_increment
primary key,
sqls := []string{`create table user (
id bigint unsigned auto_increment,
created_at datetime null,
updated_at datetime null,
deleted_at datetime null,
@@ -26,34 +24,32 @@ func TestParseSQL(t *testing.T) {
gender tinyint not null comment '性别1:男2:女3:未知',
status tinyint not null comment '账号状态1:未激活2:已激活3:封禁',
login_state tinyint not null comment '登录状态1:未登录2:已登录',
primary key (id),
constraint user_email_uindex
unique (email)
);`,
`create table user_order
(
id varchar(36) not null comment '订单id'
primary key,
`create table user_order (
id varchar(36) not null comment '订单id',
product_id varchar(36) not null comment '商品id',
user_id bigint unsigned not null comment '用户id',
status smallint null comment '0:未支付, 1:已支付, 2:已取消',
created_at timestamp null comment '创建时间',
updated_at timestamp null comment '更新时间'
updated_at timestamp null comment '更新时间',
primary key (id)
);`,
`create table user_str
(
user_id varchar(36) not null comment '用户id'
primary key,
`create table user_str (
user_id varchar(36) not null comment '用户id',
username varchar(50) not null comment '用户名',
email varchar(100) not null comment '邮箱',
created_at datetime null comment '创建时间',
primary key (user_id),
constraint email
unique (email)
);`,
`create table user_no_primary
(
`create table user_no_primary (
username varchar(50) not null comment '用户名',
email varchar(100) not null comment '邮箱',
user_id varchar(36) not null comment '用户id',
@@ -66,6 +62,9 @@ func TestParseSQL(t *testing.T) {
codes, err := ParseSQL(sql, WithJSONTag(0), WithEmbed())
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
@@ -74,6 +73,9 @@ func TestParseSQL(t *testing.T) {
codes, err = ParseSQL(sql, WithJSONTag(1), WithWebProto(), WithDBDriver(DBDriverMysql))
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
@@ -82,6 +84,9 @@ func TestParseSQL(t *testing.T) {
codes, err = ParseSQL(sql, WithJSONTag(0), WithDBDriver(DBDriverPostgresql))
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
@@ -90,38 +95,70 @@ func TestParseSQL(t *testing.T) {
codes, err = ParseSQL(sql, WithJSONTag(0), WithDBDriver(DBDriverSqlite))
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
//printCode(codes)
codes, err = ParseSQL(sql, WithDBDriver(DBDriverSqlite), WithCustomTemplate())
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
break
}
}
//printCode(codes)
}
}
func TestParseSqlWithTablePrefix(t *testing.T) {
sql := `CREATE TABLE t_person_info (
id BIGINT(11) PRIMARY KEY AUTO_INCREMENT NOT NULL COMMENT 'id',
id BIGINT(11) AUTO_INCREMENT NOT NULL COMMENT 'id',
age INT(11) unsigned NULL,
name VARCHAR(30) NOT NULL DEFAULT 'default_name' COMMENT 'name',
created_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
login_at datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
gender INT(8) NULL,
num INT(11) DEFAULT 3 NULL,
comment TEXT
comment TEXT,
PRIMARY KEY (id)
) COMMENT="person info";`
codes, err := ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithNullStyle(NullDisable))
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
t.Log(codes[CodeTypeJSON])
//printCode(codes)
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithCustomTemplate())
assert.Nil(t, err)
for k, v := range codes {
if k != CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
jsonData := codes[CodeTypeTableInfo]
t.Log(jsonData)
t.Log(UnMarshalTableInfo(jsonData))
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithEmbed())
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
@@ -130,6 +167,9 @@ func TestParseSqlWithTablePrefix(t *testing.T) {
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithWebProto())
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}
@@ -138,6 +178,9 @@ func TestParseSqlWithTablePrefix(t *testing.T) {
codes, err = ParseSQL(sql, WithTablePrefix("t_"), WithJSONTag(0), WithDBDriver(DBDriverPostgresql))
assert.Nil(t, err)
for k, v := range codes {
if k == CodeTypeTableInfo {
continue
}
assert.NotEmpty(t, k)
assert.NotEmpty(t, v)
}

View File

@@ -0,0 +1,129 @@
package parser
import (
"encoding/json"
"fmt"
"github.com/huandu/xstrings"
"github.com/jinzhu/inflection"
)
// TableInfo is the struct for extend template
type TableInfo struct {
TableNamePrefix string // table name prefix, example: t_
TableName string // original table name, example: foo_bar
TableNameCamel string // camel case, example: FooBar
TableNameCamelFCL string // camel case and first character lower, example: fooBar
TableNamePluralCamel string // plural, camel case, example: FooBars
TableNamePluralCamelFCL string // plural, camel case and first character lower, example: fooBars
TableNameSnake string // snake case, example: foo_bar
TableComment string // table comment
Columns []Field // columns of the table
PrimaryKey *PrimaryKey // primary key information
DBDriver string // database driver, example: mysql, postgresql, sqlite3, mongodb
ColumnSubStructure string // column sub structure for model
ColumnSubMessage string // sub message for protobuf
}
// Field is the struct for column information
type Field struct {
ColumnName string // original column name, example: foo_bar
ColumnNameCamel string // first character lower, example: FooBar
ColumnNameCamelFCL string // first character lower, example: fooBar
ColumnComment string // column comment
IsPrimaryKey bool // is primary key
GoType string // convert to go type
Tag string // tag for model struct field, default gorm tag
}
// PrimaryKey is the struct for primary key information, it used for generate CRUD code
type PrimaryKey struct {
Name string // primary key name, example: foo_bar
NameCamel string // primary key name, camel case, example: FooBar
NameCamelFCL string // primary key name, camel case and first character lower, example: fooBar
NamePluralCamel string // primary key name, plural, camel case, example: FooBars
NamePluralCamelFCL string // primary key name, plural, camel case and first character lower, example: fooBars
GoType string // go type, example: int, string
GoTypeFCU string // go type, first character upper, example: Int64, String
IsStringType bool // go type is string or not
}
func newTableInfo(data tmplData) TableInfo {
pluralName := inflection.Plural(data.TableName)
return TableInfo{
TableNamePrefix: data.TableNamePrefix,
TableName: data.RawTableName,
TableNameCamel: data.TableName,
TableNameCamelFCL: data.TName,
TableNamePluralCamel: customEndOfLetterToLower(data.TableName, pluralName),
TableNamePluralCamelFCL: customFirstLetterToLower(customEndOfLetterToLower(data.TableName, pluralName)),
TableNameSnake: xstrings.ToSnakeCase(data.TName),
TableComment: data.Comment,
Columns: getColumns(data.Fields),
PrimaryKey: getPrimaryKeyInfo(data.CrudInfo),
DBDriver: data.DBDriver,
ColumnSubStructure: data.SubStructs,
ColumnSubMessage: data.ProtoSubStructs,
}
}
func (table TableInfo) getCode() []byte {
code, err := json.Marshal(&table)
if err != nil {
fmt.Printf("table: %v, json.Marshal error: %v\n", table.TableName, err)
}
return code
}
func getColumns(fields []tmplField) []Field {
var columns []Field
for _, field := range fields {
columns = append(columns, Field{
ColumnName: field.ColName,
ColumnNameCamel: field.Name,
ColumnNameCamelFCL: customFirstLetterToLower(field.Name),
ColumnComment: field.Comment,
IsPrimaryKey: field.IsPrimaryKey,
GoType: field.GoType,
Tag: field.Tag,
})
}
return columns
}
func getPrimaryKeyInfo(info *CrudInfo) *PrimaryKey {
if info == nil {
return nil
}
return &PrimaryKey{
Name: info.ColumnName,
NameCamel: info.ColumnNameCamel,
NameCamelFCL: info.ColumnNameCamelFCL,
NamePluralCamel: info.ColumnNamePluralCamel,
NamePluralCamelFCL: info.ColumnNamePluralCamelFCL,
GoType: info.GoType,
GoTypeFCU: info.GoTypeFCU,
IsStringType: info.IsStringType,
}
}
// UnMarshalTableInfo unmarshal the json data to TableInfo struct
func UnMarshalTableInfo(data string) (map[string]interface{}, error) {
info := map[string]interface{}{}
err := json.Unmarshal([]byte(data), &info)
if err != nil {
return info, err
}
return info, nil
}