开发工具代码生成数据库【免费下载链接】sqlcGenerate type-safe code from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqlc点击查看免费下载本文围绕 sqlc 仓库中的端到端测试用例ddl_alter_type_rename_value展开该用例的 MySQL 侧说明 明确指出 MySQL 不支持CREATE TYPE ... AS ENUM枚举只能以内联ENUM类型直接定义在表列上。文章将解读这条文档背后的完整语义并通过仓库源码与 PostgreSQL 对照组讲清 sqlc 在两种数据库引擎下对枚举 DDL 的不同处理方式帮助你在 schema 设计时正确选择枚举声明语法。这个测试用例在验证什么在 sqlc 的端到端测试目录中ddl_alter_type_rename_value用于验证修改枚举类型取值名这一 DDL 场景对应 PostgreSQL 的ALTER TYPE ... RENAME VALUE。该目录下同时存在两套引擎的测试mysql/只有一份 README.md没有任何 schema.sql、query.sql 或生成代码postgresql/包含完整的 stdlib、pgx/v4、pgx/v5 三套 Go 生成结果以及对应的 schema.sql 与 sqlc.json。这种目录结构本身就是 sqlc 对 MySQL 枚举能力边界的声明MySQL 侧不提供对应的 DDL 测试因为该语句在 MySQL 中根本不存在。原文档核心结论MySQL 没有独立的枚举类型 DDL关联文档 mysql/README.md 的原文结论如下MySQL does not supportCREATE TYPE ... AS ENUM. Instead, enumerations are defined via theENUMtype directly in table columns.翻译并展开为两层事实不存在CREATE TYPE ... AS ENUM与 PostgreSQL 可以先用CREATE TYPE status AS ENUM (...)创建独立类型、再在表中引用不同MySQL 无法单独创建枚举类型对象枚举必须内联定义在表列上MySQL 的枚举形态是列级类型声明例如CREATE TABLE t (status ENUM(open,closed))枚举取值集合是列定义的一部分而不是一个可复用的独立数据库对象。因此ALTER TYPE status RENAME VALUE closed TO shut这类重命名枚举取值的语句在 MySQL 语法体系中不存在对应物sqlc 也就没有为其编写 MySQL 端到端用例而是用一篇简短文档说明原因并把完整的测试覆盖留给 PostgreSQL 对照组。sqlc 如何解析 MySQL 的列内联枚举尽管 MySQL 没有独立枚举类型sqlc 的 MySQL 引擎内部代号 dolphin仍能识别列上的ENUM类型并把它纳入类型系统。可以从三层源码确认这一点1. 引擎方言层enum 被识别为标量字符串类型internal/engine/dolphin/dialect/types.jsonl 中声明了{name: enum, category: S}category: S表示该类型被归入字符串string类目。这意味着在 MySQL 模式下sqlc 首先把ENUM当作一个字符串类型的列来处理。2. 代码生成层MySQL 枚举列默认映射为 Go stringGo 代码生成器在 internal/codegen/golang/mysql_type.go 中处理enum类型case enum: // TODO: Proper Enum support return string也就是说当 schema 中直接出现ENUM(open,closed)这类列定义时只要该枚举没有在 Catalog 中登记为具名枚举类型生成的 Go 字段类型就是string可空场景由上层逻辑处理为sql.NullString等。从源码中的TODO: Proper Enum support注释可以看出MySQL 内联枚举目前走的是退化为字符串的保守策略。3. 目录构建层内联枚举被合成为内部类型名当 MySQL 的列定义携带枚举取值集合Vals时sqlc 会把它们合成为一个内部枚举类型命名规则为表名_列名见 internal/sql/catalog/table.go 中的defineColumnif col.Vals ! nil { typeName : ast.TypeName{ Name: fmt.Sprintf(%s_%s, table.Name, col.Colname), } s : ast.CreateEnumStmt{TypeName: typeName, Vals: col.Vals} if err : c.createEnum(s); err ! nil { return nil, err } tc.Type typeName tc.linkedType true }这段代码揭示了 MySQL 内联枚举在 sqlc 内部的真实表示列上写的ENUM(a,b)会被转换为一次隐式的CREATE ENUM枚举名由表名与列名拼接而成列类型再指向这个合成枚举。这也解释了mysql_type.go中default分支为何还会遍历schema.Enums来匹配列类型——当枚举被登记为具名类型后生成逻辑会尝试输出枚举结构体而非纯字符串。对照组PostgreSQL 如何完整支持RENAME VALUEPostgreSQL 侧才是ddl_alter_type_rename_value真正执行测试的地方其完整链路可以一步步追踪1. 测试 schema 与生成结果postgresql/stdlib/schema.sql 给出了最小可复现 schemaCREATE TYPE status AS ENUM (open, closed); ALTER TYPE status RENAME VALUE closed TO shut;对应的生成结果 postgresql/stdlib/go/models.go 中枚举被生成为 Go 类型与常量type Status string const ( StatusOpen Status open StatusShut Status shut )注意两个关键点旧值closed在生成常量中彻底消失取而代之的是新值shutStatusShut证明 sqlc 的解析结果正确应用了重命名该测试的 query.sql 仅有一条SELECT 1占位查询说明此用例只验证 schema 解析与类型生成不涉及查询编译。pgx/v4 与 pgx/v5 的生成结果与 stdlib 完全一致枚举常量、Scan/Value方法结构相同表明RENAME VALUE的处理不依赖具体驱动。2. 解析器把 PostgreSQL 的 ALTER ENUM 拆成两类语句PostgreSQL 引擎解析器在 internal/engine/postgresql/parse.go 中处理AlterEnumStmt当语句携带旧值n.OldVal ! 时翻译为重命名取值语句否则翻译为追加取值语句case *nodes.Node_AlterEnumStmt: n : inner.AlterEnumStmt rel, err : parseRelationFromNodes(n.TypeName) if err ! nil { return nil, err } if n.OldVal ! { return ast.AlterTypeRenameValueStmt{ Type: rel.TypeName(), OldValue: makeString(n.OldVal), NewValue: makeString(n.NewVal), }, nil } else { return ast.AlterTypeAddValueStmt{ ... }, nil }3. AST 节点结构化表达重命名internal/sql/ast/alter_type_rename_value_stmt.go 定义了对应的 AST 节点携带三个字段type AlterTypeRenameValueStmt struct { Tag NodeTag[AlterTypeRenameValueStmt] json:tag Type *TypeName json:type,omitempty OldValue *string json:old_value,omitempty NewValue *string json:new_value,omitempty }4. Catalog 应用校验并原地替换取值最终落地在 internal/sql/catalog/types.go 的alterTypeRenameValue其行为值得细读for i, val : range enum.Vals { if val *stmt.OldValue { oldIndex i } if val *stmt.NewValue { newIndex i } } if oldIndex 0 { return fmt.Errorf(type %T does not have value %s, stmt.Type, *stmt.OldValue) } if newIndex 0 { return fmt.Errorf(type %T already has value %s, stmt.Type, *stmt.NewValue) } enum.Vals[oldIndex] *stmt.NewValue实现要点保留原位置只替换Vals数组中的对应元素不改变枚举值的顺序这对依赖枚举顺序的代码生成常量顺序、Valid/Values方法很重要双向校验旧值不存在、或新值与已有值冲突时都会报错保证 Catalog 中的枚举取值集合始终合法schema 作用域未显式指定 schema 时使用DefaultSchemans c.DefaultSchema与 sqlc 的默认 schema 解析策略一致。该节点在 internal/sql/catalog/catalog.go 中被分发到上述实现形成解析 → AST → Catalog 更新 → 代码生成的完整链路。实操建议两种引擎下枚举 schema 应该怎么写基于以上源码事实可以给出直接可落地的 schema 编写建议场景推荐写法sqlc 处理结果PostgreSQLCREATE TYPE status AS ENUM (open,closed); 表列引用生成具名 Go 枚举类型与常量ALTER TYPE ... RENAME VALUE可安全用于迁移MySQL列内联status ENUM(open,closed)枚举取值作为列的一部分被解析列类型在代码生成中默认映射为string源码标记为 TODO 的未完成特性若希望获得具名类型行为需依赖 Catalog 中的合成枚举路径具体生成形态受 mysql_type.go 当前实现约束关键结论不要试图在 MySQL schema 里写CREATE TYPE ... AS ENUM或ALTER TYPE ... RENAME VALUE——MySQL 方言没有这些语句dolphin 引擎的转换逻辑internal/engine/dolphin/convert.go只处理ALTER TABLE下的列操作并不会识别独立的类型级 ALTERMySQL 的枚举变更需要写成ALTER TABLE ... MODIFY COLUMN重新声明列上的 ENUM 取值集合而不是类型级语句如果希望让 MySQL 枚举列获得与 PostgreSQL 一致的具名类型生成体验目前需要在 sqlc 的 MySQL 类型映射mysql_type.go层面关注其演进现阶段应默认接受enum → string的映射结果。小结ddl_alter_type_rename_value这个测试用例用一份简短的 README 精准划出了 sqlc 对 MySQL 枚举 DDL 的能力边界MySQL 不提供CREATE TYPE ... AS ENUM枚举内联在表列中而完整的ALTER TYPE ... RENAME VALUE支持链路解析器翻译 → AST 节点 → Catalog 校验与替换 → 具名枚举生成则完整存在于 PostgreSQL 引擎中并有 stdlib、pgx/v4、pgx/v5 三套生成结果佐证。对使用者而言理解这条边界能避免写出在 MySQL 下无法解析的 schema也能在迁移到 PostgreSQL 时放心使用类型级枚举重命名。赞分享开发工具代码生成数据库【免费下载链接】sqlcGenerate type-safe code from SQL项目地址https://gitcode.com/gh_mirrors/sq/sqlc点击查看免费下载相关推荐sqlc 中枚举演进的处理PostgreSQL ALTER TYPE ... ADD VALUE 与 MySQL 列内 ENUM 的差异解析sqlc 中枚举演进的处理PostgreSQL ALTER TYPE ... ADD VALUE 与 MySQL 列内 ENUM 的差异解析 在 sqlc 的开发工具代码生成数据库ModernDive社区与支持如何参与开源项目和获取帮助的完整指南ModernDive社区与支持如何参与开源项目和获取帮助的完整指南 ModernDive是一个专注于R和Tidyverse数据科学统计推断的开源教材项目为学终极NSwag枚举处理指南如何正确配置字符串枚举与数值枚举终极NSwag枚举处理指南如何正确配置字符串枚举与数值枚举 NSwag是一个强大的Swagger/OpenAPI工具链专为.NET开发者设计能够轻松生成A开发工具代码生成API设计上一篇桌面太枯燥让DyberPet用AI桌宠伙伴为你注入温暖与活力下一篇3步搞定React Native性能监控Sentry与Flipper集成指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考