ソースを参照

自动建立表通过-doris

qdy 2ヶ月前
コミット
72d27b4b18

+ 45
- 0
drivers/doris.go ファイルの表示

@@ -0,0 +1,45 @@
1
+package drivers
2
+
3
+import (
4
+	"fmt"
5
+
6
+	_ "github.com/go-sql-driver/mysql"
7
+	"github.com/jmoiron/sqlx"
8
+)
9
+
10
+type DorisDriver struct{}
11
+
12
+func (d *DorisDriver) Name() string {
13
+	return "doris"
14
+}
15
+
16
+func (d *DorisDriver) BuildDSN(config DBConfig) string {
17
+	return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local",
18
+		config.Username,
19
+		config.Password,
20
+		config.Host,
21
+		config.Port,
22
+		config.Database)
23
+}
24
+
25
+func (d *DorisDriver) Open(config DBConfig) (*sqlx.DB, error) {
26
+	dsn := d.BuildDSN(config)
27
+	db, err := sqlx.Open("mysql", dsn)
28
+	if err != nil {
29
+		return nil, err
30
+	}
31
+
32
+	// 使用公共的连接池配置函数
33
+	configureConnectionPool(db, config)
34
+
35
+	// 测试连接
36
+	if err = db.Ping(); err != nil {
37
+		return nil, err
38
+	}
39
+
40
+	return db, nil
41
+}
42
+
43
+func init() {
44
+	Register(&DorisDriver{})
45
+}

+ 2
- 3
factory/database/db_factory.go ファイルの表示

@@ -45,12 +45,11 @@ func GetDBFactory() (*DBFactory, error) {
45 45
 		}
46 46
 
47 47
 		// 显示所支持的数据库驱动
48
-		driversStr := drivers.GetAllDrivers()
49
-		log.Printf("Available database drivers: %v\n", driversStr)
48
+		//driversStr := drivers.GetAllDrivers()
50 49
 
51 50
 		dbConfig := cfg.GetDatabase()
52 51
 		dbType := dbConfig.Type
53
-
52
+		log.Printf("Available database drivers: %v\n", dbType)
54 53
 		// 获取对应的驱动
55 54
 		dbDriver, err := drivers.Get(dbType)
56 55
 		if err != nil {

+ 188
- 0
sqldef/generators/doris.go ファイルの表示

@@ -0,0 +1,188 @@
1
+package generators
2
+
3
+import (
4
+	"fmt"
5
+	"strings"
6
+)
7
+
8
+// DorisGenerator Doris SQL生成器
9
+type DorisGenerator struct{}
10
+
11
+// NewDorisGenerator 创建Doris生成器实例
12
+func NewDorisGenerator() *DorisGenerator {
13
+	return &DorisGenerator{}
14
+}
15
+
16
+func (dg *DorisGenerator) DBType() string {
17
+	return "doris"
18
+}
19
+
20
+func (dg *DorisGenerator) TableExistsSQL(tableName string) string {
21
+	return fmt.Sprintf(
22
+		"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = DATABASE() AND table_name = '%s'",
23
+		tableName,
24
+	)
25
+}
26
+
27
+func (dg *DorisGenerator) DropTableSQL(tableName string) string {
28
+	return fmt.Sprintf("DROP TABLE IF EXISTS %s", tableName)
29
+}
30
+
31
+func (dg *DorisGenerator) GenerateCreateTable(table TableDDL) string {
32
+	if table.Schema == nil {
33
+		return ""
34
+	}
35
+
36
+	var sql strings.Builder
37
+	var primaryKeyColumns []string
38
+
39
+	// 表头
40
+	sql.WriteString(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n", table.Name))
41
+
42
+	// 列定义 - 第一遍收集主键列
43
+	for _, col := range table.Schema.Columns {
44
+		for _, opt := range col.Options {
45
+			if strings.ToUpper(opt) == "PRIMARY KEY" {
46
+				primaryKeyColumns = append(primaryKeyColumns, col.Name)
47
+				break
48
+			}
49
+		}
50
+	}
51
+
52
+	// 列定义 - 第二遍生成列定义
53
+	for i, col := range table.Schema.Columns {
54
+		colDef := fmt.Sprintf("  %s %s", col.Name, dg.getDorisType(col))
55
+
56
+		// 处理列选项,过滤掉 PRIMARY KEY(不在列级定义)
57
+		for _, opt := range col.Options {
58
+			upperOpt := strings.ToUpper(opt)
59
+			if upperOpt != "PRIMARY KEY" && upperOpt != "UNIQUE" {
60
+				colDef += " " + opt
61
+			}
62
+		}
63
+
64
+		// 如果是主键列,确保有 NOT NULL
65
+		isPrimaryKey := false
66
+		for _, pk := range primaryKeyColumns {
67
+			if pk == col.Name {
68
+				isPrimaryKey = true
69
+				break
70
+			}
71
+		}
72
+
73
+		if isPrimaryKey {
74
+			hasNotNull := false
75
+			for _, opt := range col.Options {
76
+				if strings.ToUpper(opt) == "NOT NULL" {
77
+					hasNotNull = true
78
+					break
79
+				}
80
+			}
81
+			if !hasNotNull {
82
+				colDef += " NOT NULL"
83
+			}
84
+		}
85
+
86
+		// 添加默认值
87
+		if col.Default != "" {
88
+			upperDefault := strings.ToUpper(col.Default)
89
+			if strings.Contains(upperDefault, "CURRENT_TIMESTAMP") ||
90
+				strings.Contains(upperDefault, "NOW()") ||
91
+				strings.Contains(upperDefault, "UUID()") ||
92
+				strings.Contains(upperDefault, "NULL") {
93
+				colDef += " DEFAULT " + col.Default
94
+			} else {
95
+				colDef += fmt.Sprintf(" DEFAULT '%s'", col.Default)
96
+			}
97
+		}
98
+
99
+		// 添加注释
100
+		if col.Comment != "" {
101
+			colDef += fmt.Sprintf(` COMMENT "%s"`, col.Comment)
102
+		}
103
+
104
+		if i < len(table.Schema.Columns)-1 {
105
+			sql.WriteString(colDef + ",\n")
106
+		} else {
107
+			sql.WriteString(colDef + "\n")
108
+		}
109
+	}
110
+
111
+	// 关闭列定义括号
112
+	sql.WriteString(")")
113
+
114
+	// 添加 UNIQUE KEY(如果有主键列)
115
+	if len(primaryKeyColumns) > 0 {
116
+		sql.WriteString(fmt.Sprintf("\nUNIQUE KEY(%s)", strings.Join(primaryKeyColumns, ", ")))
117
+	}
118
+
119
+	// 分布式配置 - 使用第一个主键列,如果没有主键则使用第一个列
120
+	distKey := ""
121
+	if len(primaryKeyColumns) > 0 {
122
+		distKey = primaryKeyColumns[0]
123
+	} else if len(table.Schema.Columns) > 0 {
124
+		distKey = table.Schema.Columns[0].Name
125
+	}
126
+
127
+	if distKey != "" {
128
+		sql.WriteString(fmt.Sprintf("\nDISTRIBUTED BY HASH(%s) BUCKETS 10", distKey))
129
+	}
130
+
131
+	// 副本数配置
132
+	sql.WriteString("\nPROPERTIES (\"replication_num\" = \"1\");")
133
+
134
+	return sql.String()
135
+}
136
+
137
+// getDorisType 获取Doris数据类型
138
+func (dg *DorisGenerator) getDorisType(col ColumnSchema) string {
139
+	switch col.Type {
140
+	case "DECIMAL":
141
+		if col.Precision > 0 && col.Scale > 0 {
142
+			return fmt.Sprintf("DECIMAL(%d,%d)", col.Precision, col.Scale)
143
+		}
144
+		return "DECIMAL"
145
+	case "VARCHAR":
146
+		if col.Length > 0 {
147
+			return fmt.Sprintf("VARCHAR(%d)", col.Length)
148
+		}
149
+		return "VARCHAR(255)"
150
+	case "CHAR":
151
+		if col.Length > 0 {
152
+			return fmt.Sprintf("CHAR(%d)", col.Length)
153
+		}
154
+		return "CHAR(1)"
155
+	case "INT":
156
+		return "INT"
157
+	case "BIGINT":
158
+		return "BIGINT"
159
+	case "TINYINT":
160
+		return "TINYINT"
161
+	case "BOOL":
162
+		return "BOOLEAN"
163
+	case "DATETIME":
164
+		return "DATETIME"
165
+	case "TIMESTAMP":
166
+		return "DATETIME" // Doris 中通常用 DATETIME
167
+	case "DATE":
168
+		return "DATE"
169
+	case "TIME":
170
+		return "TIME"
171
+	case "TEXT":
172
+		return "STRING"
173
+	case "JSON":
174
+		return "STRING"
175
+	case "BLOB":
176
+		return "STRING"
177
+	case "FLOAT":
178
+		return "FLOAT"
179
+	case "DOUBLE":
180
+		return "DOUBLE"
181
+	default:
182
+		return col.Type
183
+	}
184
+}
185
+
186
+func init() {
187
+	RegisterGenerator(NewDorisGenerator())
188
+}

+ 75
- 23
sqldef/generators/mysql.go ファイルの表示

@@ -1,4 +1,3 @@
1
-// mysql.go 根据//table_defintion.go 定义的表结构,编写mysql建立表和索引的代码。
2 1
 package generators
3 2
 
4 3
 import (
@@ -35,60 +34,108 @@ func (mg *MySQLGenerator) GenerateCreateTable(table TableDDL) string {
35 34
 	}
36 35
 
37 36
 	var sql strings.Builder
37
+	var pkColumns []string
38 38
 
39 39
 	// 表头
40 40
 	sql.WriteString(fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n", table.Name))
41 41
 
42
-	// 列定义
43
-	columns := table.Schema.Columns
44
-	for i, col := range columns {
45
-		sql.WriteString(fmt.Sprintf("  %s %s", col.Name, mg.getMySQLType(col)))
42
+	// 第一遍:收集主键列
43
+	for _, col := range table.Schema.Columns {
44
+		for _, opt := range col.Options {
45
+			if strings.ToUpper(opt) == "PRIMARY KEY" {
46
+				pkColumns = append(pkColumns, col.Name)
47
+				break
48
+			}
49
+		}
50
+	}
51
+
52
+	// 第二遍:构建列定义
53
+	columnDefs := make([]string, 0, len(table.Schema.Columns))
54
+	for _, col := range table.Schema.Columns {
55
+		colDef := fmt.Sprintf("  %s %s", col.Name, mg.getMySQLType(col))
46 56
 
47
-		// 添加选项
57
+		// 处理列选项
48 58
 		for _, opt := range col.Options {
49
-			sql.WriteString(" " + opt)
59
+			upperOpt := strings.ToUpper(opt)
60
+			// PRIMARY KEY 选项不在列级定义
61
+			if upperOpt != "PRIMARY KEY" {
62
+				colDef += " " + opt
63
+			}
64
+		}
65
+
66
+		// 如果是主键列,确保有 NOT NULL
67
+		isPrimaryKey := false
68
+		for _, pk := range pkColumns {
69
+			if pk == col.Name {
70
+				isPrimaryKey = true
71
+				break
72
+			}
73
+		}
74
+
75
+		if isPrimaryKey {
76
+			hasNotNull := false
77
+			for _, opt := range col.Options {
78
+				if strings.ToUpper(opt) == "NOT NULL" {
79
+					hasNotNull = true
80
+					break
81
+				}
82
+			}
83
+			if !hasNotNull {
84
+				colDef += " NOT NULL"
85
+			}
50 86
 		}
51 87
 
52 88
 		// 添加默认值
53 89
 		if col.Default != "" {
54
-			// 检查是否是函数调用(如CURRENT_TIMESTAMP)
55
-			if strings.Contains(strings.ToUpper(col.Default), "CURRENT_TIMESTAMP") ||
56
-				strings.Contains(strings.ToUpper(col.Default), "NOW()") {
57
-				sql.WriteString(" DEFAULT " + col.Default)
90
+			upperDefault := strings.ToUpper(col.Default)
91
+			if strings.Contains(upperDefault, "CURRENT_TIMESTAMP") ||
92
+				strings.Contains(upperDefault, "NOW()") ||
93
+				strings.Contains(upperDefault, "UUID()") ||
94
+				strings.Contains(upperDefault, "NULL") {
95
+				colDef += " DEFAULT " + col.Default
58 96
 			} else {
59
-				sql.WriteString(fmt.Sprintf(" DEFAULT '%s'", col.Default))
97
+				colDef += fmt.Sprintf(" DEFAULT '%s'", col.Default)
60 98
 			}
61 99
 		}
62 100
 
63 101
 		// 添加注释
64 102
 		if col.Comment != "" {
65
-			sql.WriteString(fmt.Sprintf(" COMMENT '%s'", col.Comment))
103
+			colDef += fmt.Sprintf(" COMMENT '%s'", escapeSingleQuote(col.Comment))
66 104
 		}
67 105
 
68
-		if i < len(columns)-1 {
69
-			sql.WriteString(",")
70
-		}
71
-		sql.WriteString("\n")
106
+		columnDefs = append(columnDefs, colDef)
72 107
 	}
73 108
 
74
-	// 添加索引(在MySQL中,索引可以在CREATE TABLE语句中定义)
109
+	// 将所有定义部分合并
110
+	allParts := make([]string, 0)
111
+	allParts = append(allParts, columnDefs...)
112
+
113
+	// 添加主键
114
+	if len(pkColumns) > 0 {
115
+		allParts = append(allParts, fmt.Sprintf("  PRIMARY KEY (%s)", strings.Join(pkColumns, ", ")))
116
+	}
117
+
118
+	// 添加索引
75 119
 	for _, idx := range table.Schema.Indexes {
76 120
 		if idx.Unique {
77
-			sql.WriteString(fmt.Sprintf("  ,UNIQUE KEY %s (%s)\n",
121
+			allParts = append(allParts, fmt.Sprintf("  UNIQUE KEY %s (%s)",
78 122
 				idx.Name, strings.Join(idx.Columns, ", ")))
79 123
 		} else {
80
-			sql.WriteString(fmt.Sprintf("  ,KEY %s (%s)\n",
124
+			allParts = append(allParts, fmt.Sprintf("  KEY %s (%s)",
81 125
 				idx.Name, strings.Join(idx.Columns, ", ")))
82 126
 		}
83 127
 	}
84 128
 
85
-	sql.WriteString(")")
129
+	// 用逗号连接所有部分
130
+	sql.WriteString(strings.Join(allParts, ",\n"))
131
+	sql.WriteString("\n)")
86 132
 
87
-	// 表选项
133
+	// 表注释
88 134
 	if table.Schema.Comment != "" {
89
-		sql.WriteString(fmt.Sprintf(" COMMENT='%s'", table.Schema.Comment))
135
+		sql.WriteString(fmt.Sprintf(" COMMENT='%s'", escapeSingleQuote(table.Schema.Comment)))
90 136
 	}
91 137
 
138
+	// 引擎和字符集
92 139
 	sql.WriteString(" ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;")
93 140
 
94 141
 	return sql.String()
@@ -143,6 +190,11 @@ func (mg *MySQLGenerator) getMySQLType(col ColumnSchema) string {
143 190
 	}
144 191
 }
145 192
 
193
+// escapeSingleQuote 转义单引号
194
+func escapeSingleQuote(str string) string {
195
+	return strings.ReplaceAll(str, "'", "''")
196
+}
197
+
146 198
 func init() {
147 199
 	RegisterGenerator(NewMySQLGenerator())
148 200
 }

+ 1
- 0
sqldef/generators/sql_generator.go ファイルの表示

@@ -40,6 +40,7 @@ type TableDDL struct {
40 40
 	SQL     string
41 41
 	Comment string
42 42
 	Schema  *TableSchema // 新增Schema字段
43
+
43 44
 }
44 45
 
45 46
 // SQLGenerator SQL生成器接口

+ 1
- 0
sqldef/table_create.go ファイルの表示

@@ -18,6 +18,7 @@ type TableSyncer struct {
18 18
 
19 19
 // NewTableSyncer 创建表同步器
20 20
 func NewTableSyncer(db *sqlx.DB, dbType string) (*TableSyncer, error) {
21
+
21 22
 	// 获取SQL生成器
22 23
 	generator, err := generators.GetGenerator(dbType)
23 24
 	if err != nil {

読み込み中…
キャンセル
保存