goctl model reactor (#15)

* reactor sql generation

* reactor sql generation

* add console & example

* optimize unit test & add document

* modify default config

* remove test file

* Revert "remove test file"

This reverts commit 81041f9e

* fix stringx.go & optimize example

* remove unused code
This commit is contained in:
Keson
2020-08-19 10:41:19 +08:00
committed by GitHub
parent 1252bd9cde
commit d21d770b5b
64 changed files with 1505 additions and 2306 deletions

View File

@@ -0,0 +1,11 @@
package parser
import (
"errors"
)
var (
unSupportDDL = errors.New("unexpected type")
tableBodyIsNotFound = errors.New("create table spec not found")
errPrimaryKey = errors.New("unexpected joint primary key")
)

View File

@@ -0,0 +1,135 @@
package parser
import (
"fmt"
"github.com/tal-tech/go-zero/tools/goctl/model/sql/converter"
"github.com/tal-tech/go-zero/tools/goctl/util/stringx"
"github.com/xwb1989/sqlparser"
)
const (
none = iota
primary
unique
normal
spatial
)
type (
Table struct {
Name stringx.String
PrimaryKey Primary
Fields []Field
}
Primary struct {
Field
AutoIncrement bool
}
Field struct {
Name stringx.String
DataBaseType string
DataType string
IsKey bool
IsPrimaryKey bool
Comment string
}
KeyType int
)
func Parse(ddl string) (*Table, error) {
stmt, err := sqlparser.ParseStrictDDL(ddl)
if err != nil {
return nil, err
}
ddlStmt, ok := stmt.(*sqlparser.DDL)
if !ok {
return nil, unSupportDDL
}
action := ddlStmt.Action
if action != sqlparser.CreateStr {
return nil, fmt.Errorf("expected [CREATE] action,but found: %s", action)
}
tableName := ddlStmt.NewName.Name.String()
tableSpec := ddlStmt.TableSpec
if tableSpec == nil {
return nil, tableBodyIsNotFound
}
columns := tableSpec.Columns
indexes := tableSpec.Indexes
keyMap := make(map[string]KeyType)
for _, index := range indexes {
info := index.Info
if info == nil {
continue
}
if info.Primary {
if len(index.Columns) > 1 {
return nil, errPrimaryKey
}
keyMap[index.Columns[0].Column.String()] = primary
continue
}
// can optimize
if len(index.Columns) > 1 {
continue
}
column := index.Columns[0]
columnName := column.Column.String()
camelColumnName := stringx.From(columnName).Snake2Camel()
// by default, createTime|updateTime findOne is not used.
if camelColumnName == "CreateTime" || camelColumnName == "UpdateTime" {
continue
}
if info.Unique {
keyMap[columnName] = unique
} else if info.Spatial {
keyMap[columnName] = spatial
} else {
keyMap[columnName] = normal
}
}
var (
fields []Field
primaryKey Primary
)
for _, column := range columns {
if column == nil {
continue
}
var comment string
if column.Type.Comment != nil {
comment = string(column.Type.Comment.Val)
}
dataType, err := converter.ConvertDataType(column.Type.Type)
if err != nil {
return nil, err
}
var field Field
field.Name = stringx.From(column.Name.String())
field.DataBaseType = column.Type.Type
field.DataType = dataType
field.Comment = comment
key, ok := keyMap[column.Name.String()]
if ok {
field.IsKey = true
field.IsPrimaryKey = key == primary
if field.IsPrimaryKey {
primaryKey.Field = field
if column.Type.Autoincrement {
primaryKey.AutoIncrement = true
}
}
}
fields = append(fields, field)
}
return &Table{
Name: stringx.From(tableName),
PrimaryKey: primaryKey,
Fields: fields,
}, nil
}

View File

@@ -0,0 +1,27 @@
package parser
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestParsePlainText(t *testing.T) {
_, err := Parse("plain text")
assert.NotNil(t, err)
}
func TestParseSelect(t *testing.T) {
_, err := Parse("select * from user")
assert.Equal(t, unSupportDDL, err)
}
func TestParseCreateTable(t *testing.T) {
_, err := Parse("CREATE TABLE `user_snake` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;")
assert.Nil(t, err)
}
func TestParseCreateTable2(t *testing.T) {
_, err := Parse("create table `user_snake` (\n `id` bigint(10) NOT NULL AUTO_INCREMENT,\n `name` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户名称',\n `password` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用户密码',\n `mobile` varchar(255) COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手机号',\n `gender` char(5) COLLATE utf8mb4_general_ci NOT NULL COMMENT '男|女|未公开',\n `nickname` varchar(255) COLLATE utf8mb4_general_ci DEFAULT '' COMMENT '用户昵称',\n `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,\n `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,\n PRIMARY KEY (`id`),\n UNIQUE KEY `name_index` (`name`),\n KEY `mobile_index` (`mobile`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;")
assert.Nil(t, err)
}