不能直接用sqlparser解析go struct生成迁移sql,因其仅处理字符串而不理解struct定义;需依赖gorm.io/gorm/schema等代码生成方案,从struct推导ddl,并避开约束顺序、default语法、外键索引等方言兼容性坑。

为什么不能直接用 sqlparser 解析 Go struct 生成迁移 SQL
Go 原生没有运行时反射导出字段完整类型元信息(比如是否为 `NOT NULL`、默认值表达式、索引类型),sqlparser 这类 SQL 解析库只处理字符串输入,不理解 Go struct 定义。你真正需要的不是“解析 SQL”,而是“从 struct 定义推导 DDL”——这属于代码生成范畴,不是 SQL 解析。
gorm.io/gorm/schema 是最靠谱的起点
GORM v2 的 schema 包暴露了完整的 struct 映射逻辑,包括字段标签解析、类型映射、约束推导。它不依赖数据库连接,纯内存建模,适合复用。
- 用
gorm.Schema构造器传入 struct 指针,调用Parse后可拿到*schema.Schema -
schema.Fields里每个*schema.Field包含DBName、DataType、IsPrimaryKey、IsUnique、HasDefaultValue、DefaultValue等关键属性 - 注意:GORM 默认把
int映射为BIGINT,string为VARCHAR(255),如需调整,得提前注册自定义dialector或改field.DataType - 别直接读
field.Tag——GORM 已解析并归一化到field字段,重复解析标签易出错
手写 DDL 生成器要绕开三个坑
生成 CREATE TABLE 不是拼字符串,关键是约束顺序和方言兼容性。
- 主键必须在列定义后单独声明(PostgreSQL 要求;MySQL 允许内联但不推荐混用)
-
DEFAULT值不能带引号包裹字面量:对bool写DEFAULT true,不是DEFAULT 'true';对string才用单引号,且需转义 - 外键、索引必须延迟到表定义之后用
ADD CONSTRAINT/CREATE INDEX单独生成,否则 SQLite 和 MySQL 5.7+ 会报错 - 示例片段:
func generateCreateTable(s *schema.Schema, dialect string) string { var b strings.Builder b.WriteString("CREATE TABLE `" + s.Table + "` (") for i, f := range s.Fields { if i > 0 { b.WriteString(", ") } b.WriteString("`" + f.DBName + "` " + mapType(f.DataType, dialect)) if f.HasDefaultValue { b.WriteString(" DEFAULT " + defaultValueSQL(f.DefaultValue, f.DataType)) } if !f.NotNull { b.WriteString(" NULL") } } b.WriteString(")") if len(s.PrimaryFields) > 0 { b.WriteString(", PRIMARY KEY (" + strings.Join(quoteNames(s.PrimaryFields), ", ") + ")") } return b.String() }
什么时候该放弃自动生成,改用 migrate + 手写 SQL
自动生成只能覆盖 CREATE TABLE 和基础字段增删。一旦涉及以下操作,硬编码生成器会迅速失控:
- 列类型变更(如
VARCHAR(255) → TEXT),不同数据库语法差异大(PostgreSQL 用ALTER COLUMN ... TYPE,MySQL 用MODIFY COLUMN) - 添加唯一约束但已有重复数据,需先清理再建约束
- JSON 字段索引(MySQL 5.7+ 的
GENERATED COLUMN+INDEX组合) - 迁移中需执行数据修复逻辑(例如拆分字段、合并表)
这些场景下,老老实实用 github.com/golang-migrate/migrate 管理手写 .sql 文件更稳——生成器只用来 scaffold 初始 migration,别让它承担运行时决策。











