Files
EasyGoAdmin_GoFrame_EleVue/internal/migrate/ddl.go
T
2026-10-20 11:37:25 +08:00

806 lines
22 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package migrate
import (
"context"
"fmt"
"strings"
"github.com/gogf/gf/v2/database/gdb"
)
// ============================== 目标方言 DDL 渲染 ==============================
// Renderer 目标方言 DDL 渲染器
// 负责标识符引用、列定义、建表、建索引、清表与自增序列修正
type Renderer interface {
Quote(name string) string
Column(col ColumnMeta) string
CreateTable(t *TableMeta) string
CreateIndex(t *TableMeta, idx IndexMeta) string
DropAllTables(ctx context.Context, db gdb.DB) error
FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error
}
// NewRenderer 按目标方言创建渲染器
func NewRenderer(dialect string) (Renderer, error) {
switch dialect {
case "mysql":
return &mysqlRenderer{dialect: "mysql"}, nil
case "pgsql":
return &pgsqlRenderer{dialect: "pgsql"}, nil
case "sqlite":
return &sqliteRenderer{dialect: "sqlite"}, nil
case "mssql":
return &mssqlRenderer{dialect: "mssql"}, nil
case "oracle":
return &oracleRenderer{dialect: "oracle"}, nil
}
return nil, fmt.Errorf("不支持的目标数据库类型: %s", dialect)
}
// quoteStd 双引号引用
// 适用于 PostgreSQL、Oracle、SQLite
func quoteStd(name string) string {
return `"` + strings.ReplaceAll(name, `"`, `""`) + `"`
}
// quoteTick 反引号引用
// 适用于 MySQL
func quoteTick(name string) string {
return "`" + strings.ReplaceAll(name, "`", "``") + "`"
}
// quoteBracket 方括号引用
// 适用于 SQL Server
func quoteBracket(name string) string {
return "[" + strings.ReplaceAll(name, "]", "]]") + "]"
}
// defaultLiteral 渲染默认值字面量
//
// 数值原样输出,其余加引号;
// 时间函数按目标方言归一转换
func defaultLiteral(dialect, v string) string {
if v == "" {
return ""
}
u := strings.ToUpper(v)
// 时间函数按目标方言归一(NOW/CURRENT_TIMESTAMP → 各库原生)
if u == "CURRENT_TIMESTAMP" || u == "NOW()" || u == "NOW" {
switch dialect {
case "mssql":
return "GETDATE()"
case "oracle":
return "SYSDATE"
default:
return "CURRENT_TIMESTAMP"
}
}
if u == "NULL" {
return "NULL"
}
// 纯数值/负数原样
if _, err := fmt.Sscanf(v, "%f", new(float64)); err == nil {
return v
}
return "'" + strings.ReplaceAll(v, "'", "''") + "'"
}
// hasAutoIncCol 判断表中是否存在自增列
func hasAutoIncCol(t *TableMeta) bool {
for _, c := range t.Columns {
if c.AutoInc {
return true
}
}
return false
}
// pkColumns 返回表的主键列清单
func pkColumns(t *TableMeta) []string {
var cols []string
for _, c := range t.Columns {
if c.IsPK {
cols = append(cols, c.Name)
}
}
return cols
}
// ============================================================
// MySQL
// ============================================================
// mysqlRenderer MySQL 方言渲染器
type mysqlRenderer struct{ dialect string }
// Quote 引用标识符
func (r *mysqlRenderer) Quote(name string) string { return quoteTick(name) }
// Column 渲染列定义
func (r *mysqlRenderer) Column(col ColumnMeta) string {
var b strings.Builder
b.WriteString(quoteTick(col.Name) + " " + r.colType(col))
if col.AutoInc {
b.WriteString(" NOT NULL AUTO_INCREMENT")
} else {
if !col.Nullable {
b.WriteString(" NOT NULL")
}
if d := defaultLiteral(r.dialect, col.Default); d != "" {
b.WriteString(" DEFAULT " + d)
}
}
if col.Comment != "" {
b.WriteString(" COMMENT '" + strings.ReplaceAll(col.Comment, "'", "''") + "'")
}
return b.String()
}
// colType 列类型映射
func (r *mysqlRenderer) colType(col ColumnMeta) string {
switch col.Type {
case TypeBool:
return "tinyint(1)"
case TypeTinyint:
return "tinyint"
case TypeSmallint:
return "smallint"
case TypeInt:
return "int"
case TypeBigint:
return "bigint"
case TypeFloat:
return "float"
case TypeDouble:
return "double"
case TypeDecimal:
if col.Length > 0 {
return fmt.Sprintf("decimal(%d,%d)", col.Length, col.Scale)
}
return "decimal"
case TypeVarchar:
if col.Length > 0 {
return fmt.Sprintf("varchar(%d)", col.Length)
}
return "varchar(255)"
case TypeChar:
if col.Length > 0 {
return fmt.Sprintf("char(%d)", col.Length)
}
return "char(36)"
case TypeTinytext:
return "tinytext"
case TypeMediumText:
return "mediumtext"
case TypeLongText:
return "longtext"
case TypeText:
return "text"
case TypeDate:
return "date"
case TypeTime:
return "time"
case TypeDateTime:
return "datetime"
case TypeBlob:
return "longblob"
case TypeJson:
return "json"
case TypeEnum:
return "enum('" + strings.Join(col.EnumVals, "','") + "')"
case TypeSet:
return "set('" + strings.Join(col.EnumVals, "','") + "')"
}
return "text"
}
// CreateTable 渲染建表语句
func (r *mysqlRenderer) CreateTable(t *TableMeta) string {
var lines []string
for _, c := range t.Columns {
lines = append(lines, " "+r.Column(c))
}
if pk := pkColumns(t); len(pk) > 0 {
quoted := make([]string, len(pk))
for i, c := range pk {
quoted[i] = quoteTick(c)
}
lines = append(lines, " PRIMARY KEY ("+strings.Join(quoted, ",")+")")
}
sql := "CREATE TABLE IF NOT EXISTS " + quoteTick(t.Name) + " (\n" + strings.Join(lines, ",\n") + "\n)"
if t.Comment != "" {
sql += " COMMENT='" + strings.ReplaceAll(t.Comment, "'", "''") + "'"
}
return sql + ";"
}
// CreateIndex 渲染索引语句
func (r *mysqlRenderer) CreateIndex(t *TableMeta, idx IndexMeta) string {
unique := ""
if idx.Unique {
unique = "UNIQUE "
}
cols := make([]string, len(idx.Columns))
for i, c := range idx.Columns {
cols[i] = quoteTick(c)
}
return fmt.Sprintf("CREATE %sINDEX %s ON %s (%s);",
unique, quoteTick(idx.Name), quoteTick(t.Name), strings.Join(cols, ","))
}
// DropAllTables 清空全部表
func (r *mysqlRenderer) DropAllTables(ctx context.Context, db gdb.DB) error {
if _, err := db.Exec(ctx, "SET FOREIGN_KEY_CHECKS=0"); err != nil {
return err
}
defer db.Exec(ctx, "SET FOREIGN_KEY_CHECKS=1")
tables, err := db.Tables(ctx)
if err != nil {
return err
}
for _, t := range tables {
if _, err = db.Exec(ctx, "DROP TABLE IF EXISTS "+quoteTick(t)); err != nil {
return err
}
}
return nil
}
// FixSequence 修正自增序列
// MySQL 自增自动续接,无需处理
func (r *mysqlRenderer) FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error { return nil }
// ============================================================
// PostgreSQL
// ============================================================
// pgsqlRenderer PostgreSQL 方言渲染器
type pgsqlRenderer struct{ dialect string }
// Quote 引用标识符
func (r *pgsqlRenderer) Quote(name string) string { return quoteStd(name) }
// Column 渲染列定义
func (r *pgsqlRenderer) Column(col ColumnMeta) string {
var b strings.Builder
b.WriteString(quoteStd(col.Name) + " " + r.colType(col))
if col.AutoInc {
b.WriteString(" GENERATED BY DEFAULT AS IDENTITY")
}
if !col.Nullable && !col.AutoInc {
b.WriteString(" NOT NULL")
}
if !col.AutoInc {
if d := defaultLiteral(r.dialect, col.Default); d != "" {
b.WriteString(" DEFAULT " + d)
}
}
return b.String()
}
// colType 列类型映射
func (r *pgsqlRenderer) colType(col ColumnMeta) string {
switch col.Type {
case TypeBool:
return "BOOLEAN"
case TypeTinyint, TypeSmallint:
return "SMALLINT"
case TypeInt:
return "INTEGER"
case TypeBigint:
return "BIGINT"
case TypeFloat:
return "REAL"
case TypeDouble:
return "DOUBLE PRECISION"
case TypeDecimal:
if col.Length > 0 {
return fmt.Sprintf("NUMERIC(%d,%d)", col.Length, col.Scale)
}
return "NUMERIC"
case TypeVarchar:
if col.Length > 0 {
return fmt.Sprintf("VARCHAR(%d)", col.Length)
}
return "VARCHAR"
case TypeChar:
if col.Length > 0 {
return fmt.Sprintf("CHAR(%d)", col.Length)
}
return "CHAR(36)"
case TypeText, TypeTinytext, TypeMediumText, TypeLongText:
return "TEXT"
case TypeDate:
return "DATE"
case TypeTime:
return "TIME"
case TypeDateTime:
return "TIMESTAMP"
case TypeBlob:
return "BYTEA"
case TypeJson:
return "JSONB"
case TypeEnum, TypeSet:
return "VARCHAR(255)" // pgsql 无内建 enum/set,降级为 varchar(值域由应用层保证)
}
return "TEXT"
}
// CreateTable 渲染建表语句
func (r *pgsqlRenderer) CreateTable(t *TableMeta) string {
var lines []string
for _, c := range t.Columns {
lines = append(lines, " "+r.Column(c))
}
if pk := pkColumns(t); len(pk) > 0 {
quoted := make([]string, len(pk))
for i, c := range pk {
quoted[i] = quoteStd(c)
}
lines = append(lines, " PRIMARY KEY ("+strings.Join(quoted, ",")+")")
}
sql := "CREATE TABLE IF NOT EXISTS " + quoteStd(t.Name) + " (\n" + strings.Join(lines, ",\n") + "\n);"
if t.Comment != "" {
sql += "\nCOMMENT ON TABLE " + quoteStd(t.Name) + " IS '" + strings.ReplaceAll(t.Comment, "'", "''") + "';"
}
// 列注释随建表语句一并输出
for _, c := range t.Columns {
if c.Comment != "" {
sql += "\nCOMMENT ON COLUMN " + quoteStd(t.Name) + "." + quoteStd(c.Name) +
" IS '" + strings.ReplaceAll(c.Comment, "'", "''") + "';"
}
}
return sql
}
// CreateIndex 渲染索引语句
func (r *pgsqlRenderer) CreateIndex(t *TableMeta, idx IndexMeta) string {
unique := ""
if idx.Unique {
unique = "UNIQUE "
}
cols := make([]string, len(idx.Columns))
for i, c := range idx.Columns {
cols[i] = quoteStd(c)
}
return fmt.Sprintf("CREATE %sINDEX IF NOT EXISTS %s ON %s (%s);",
unique, quoteStd(idx.Name), quoteStd(t.Name), strings.Join(cols, ","))
}
// DropAllTables 清空全部表
func (r *pgsqlRenderer) DropAllTables(ctx context.Context, db gdb.DB) error {
rows, err := db.Ctx(ctx).Raw(
"SELECT tablename FROM pg_tables WHERE schemaname = current_schema()").All()
if err != nil {
return err
}
for _, row := range rows {
if _, err = db.Exec(ctx, "DROP TABLE IF EXISTS "+quoteStd(row["tablename"].String())+" CASCADE"); err != nil {
return err
}
}
return nil
}
// FixSequence 修正自增序列
//
// 将序列设置到当前 max(id),下次插入取 max+1;
// 空表不处理,IDENTITY 从 1 开始
func (r *pgsqlRenderer) FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error {
for _, c := range t.Columns {
if !c.AutoInc {
continue
}
// setval 到当前 max(id),下次插入取 max+1;空表不动(IDENTITY 从1开始)
_, err := db.Exec(ctx, fmt.Sprintf(
`SELECT setval(pg_get_serial_sequence('%s', '%s'), COALESCE((SELECT MAX(%s) FROM %s), 0) + 1, false)`,
t.Name, c.Name, quoteStd(c.Name), quoteStd(t.Name)))
if err != nil {
return err
}
}
return nil
}
// ============================================================
// SQLite
// ============================================================
// sqliteRenderer SQLite 方言渲染器
type sqliteRenderer struct{ dialect string }
// Quote 引用标识符
func (r *sqliteRenderer) Quote(name string) string { return quoteStd(name) }
// Column 渲染列定义
func (r *sqliteRenderer) Column(col ColumnMeta) string {
var b strings.Builder
b.WriteString(quoteStd(col.Name) + " " + r.colType(col))
if col.IsPK && col.AutoInc {
b.WriteString(" PRIMARY KEY AUTOINCREMENT")
} else {
if col.IsPK {
b.WriteString(" PRIMARY KEY")
}
if !col.Nullable {
b.WriteString(" NOT NULL")
}
if d := defaultLiteral(r.dialect, col.Default); d != "" {
b.WriteString(" DEFAULT " + d)
}
}
return b.String()
}
// colType 列类型映射
func (r *sqliteRenderer) colType(col ColumnMeta) string {
switch col.Type {
case TypeBool, TypeTinyint, TypeSmallint, TypeInt, TypeBigint, TypeDate, TypeTime, TypeDateTime:
return "INTEGER"
case TypeFloat, TypeDouble, TypeDecimal:
return "REAL"
case TypeBlob:
return "BLOB"
default:
return "TEXT"
}
}
// CreateTable 渲染建表语句
func (r *sqliteRenderer) CreateTable(t *TableMeta) string {
var lines []string
for _, c := range t.Columns {
lines = append(lines, " "+r.Column(c))
}
// 复合主键走表级约束(列级 PRIMARY KEY 仅单列生效)
if pk := pkColumns(t); len(pk) > 1 || !hasAutoIncCol(t) {
if pk := pkColumns(t); len(pk) > 0 {
quoted := make([]string, len(pk))
for i, c := range pk {
quoted[i] = quoteStd(c)
}
lines = append(lines, " PRIMARY KEY ("+strings.Join(quoted, ",")+")")
}
}
// sqlite 无表/列注释
return "CREATE TABLE IF NOT EXISTS " + quoteStd(t.Name) + " (\n" + strings.Join(lines, ",\n") + "\n);"
}
// CreateIndex 渲染索引语句
func (r *sqliteRenderer) CreateIndex(t *TableMeta, idx IndexMeta) string {
unique := ""
if idx.Unique {
unique = "UNIQUE "
}
cols := make([]string, len(idx.Columns))
for i, c := range idx.Columns {
cols[i] = quoteStd(c)
}
return fmt.Sprintf("CREATE %sINDEX IF NOT EXISTS %s ON %s (%s);",
unique, quoteStd(idx.Name), quoteStd(t.Name), strings.Join(cols, ","))
}
// DropAllTables 清空全部表
func (r *sqliteRenderer) DropAllTables(ctx context.Context, db gdb.DB) error {
rows, err := db.Ctx(ctx).Raw(
"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'").All()
if err != nil {
return err
}
if _, err = db.Exec(ctx, "PRAGMA foreign_keys = OFF"); err != nil {
return err
}
defer db.Exec(ctx, "PRAGMA foreign_keys = ON")
for _, row := range rows {
if _, err = db.Exec(ctx, "DROP TABLE IF EXISTS "+quoteStd(row["name"].String())); err != nil {
return err
}
}
return nil
}
// FixSequence 修正自增序列
// AUTOINCREMENT 自动续接,无需处理
func (r *sqliteRenderer) FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error { return nil }
// ============================================================
// SQL Server(首期实测链路为 mysql→pgsql,mssql/oracle 按定稿方言约定实现,待人工校对)
// ============================================================
// mssqlRenderer SQL Server 方言渲染器
type mssqlRenderer struct{ dialect string }
// Quote 引用标识符
func (r *mssqlRenderer) Quote(name string) string { return quoteBracket(name) }
// Column 渲染列定义
func (r *mssqlRenderer) Column(col ColumnMeta) string {
var b strings.Builder
b.WriteString(quoteBracket(col.Name) + " " + r.colType(col))
if col.AutoInc {
b.WriteString(" IDENTITY(1,1)")
}
if !col.Nullable {
b.WriteString(" NOT NULL")
}
if !col.AutoInc {
if d := defaultLiteral(r.dialect, col.Default); d != "" {
b.WriteString(" DEFAULT " + d)
}
}
return b.String()
}
// colType 列类型映射
func (r *mssqlRenderer) colType(col ColumnMeta) string {
switch col.Type {
case TypeBool:
return "BIT"
case TypeTinyint:
return "TINYINT"
case TypeSmallint:
return "SMALLINT"
case TypeInt:
return "INT"
case TypeBigint:
return "BIGINT"
case TypeFloat:
return "FLOAT"
case TypeDouble:
return "FLOAT"
case TypeDecimal:
if col.Length > 0 {
return fmt.Sprintf("DECIMAL(%d,%d)", col.Length, col.Scale)
}
return "DECIMAL"
case TypeVarchar:
if col.Length > 0 {
return fmt.Sprintf("NVARCHAR(%d)", col.Length)
}
return "NVARCHAR(255)"
case TypeChar:
if col.Length > 0 {
return fmt.Sprintf("NCHAR(%d)", col.Length)
}
return "NCHAR(36)"
case TypeText, TypeTinytext, TypeMediumText, TypeLongText, TypeJson, TypeEnum, TypeSet:
return "NVARCHAR(MAX)"
case TypeDate:
return "DATE"
case TypeTime:
return "TIME"
case TypeDateTime:
return "DATETIME2"
case TypeBlob:
return "VARBINARY(MAX)"
}
return "NVARCHAR(MAX)"
}
// CreateTable 渲染建表语句
func (r *mssqlRenderer) CreateTable(t *TableMeta) string {
var lines []string
for _, c := range t.Columns {
lines = append(lines, " "+r.Column(c))
}
if pk := pkColumns(t); len(pk) > 0 {
quoted := make([]string, len(pk))
for i, c := range pk {
quoted[i] = quoteBracket(c)
}
lines = append(lines, " PRIMARY KEY ("+strings.Join(quoted, ",")+")")
}
sql := "IF OBJECT_ID(N'" + t.Name + "', N'U') IS NULL CREATE TABLE " + quoteBracket(t.Name) +
" (\n" + strings.Join(lines, ",\n") + "\n);"
// 表注释(扩展属性 MS_Description)
if t.Comment != "" {
sql += "\nEXEC sp_addextendedproperty N'MS_Description', N'" +
strings.ReplaceAll(t.Comment, "'", "''") +
"', N'SCHEMA', N'dbo', N'TABLE', N'" + t.Name + "';"
}
// 列注释(扩展属性 MS_Description)
for _, c := range t.Columns {
if c.Comment != "" {
sql += "\nEXEC sp_addextendedproperty N'MS_Description', N'" +
strings.ReplaceAll(c.Comment, "'", "''") +
"', N'SCHEMA', N'dbo', N'TABLE', N'" + t.Name + "', N'COLUMN', N'" + c.Name + "';"
}
}
return sql
}
// CreateIndex 渲染索引语句
func (r *mssqlRenderer) CreateIndex(t *TableMeta, idx IndexMeta) string {
unique := ""
if idx.Unique {
unique = "UNIQUE "
}
cols := make([]string, len(idx.Columns))
for i, c := range idx.Columns {
cols[i] = quoteBracket(c)
}
// IF NOT EXISTS 保护:重复建索引跳过
return fmt.Sprintf("IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name = N'%s' AND object_id = OBJECT_ID(N'%s')) "+
"CREATE %sINDEX %s ON %s (%s);",
idx.Name, t.Name, unique, quoteBracket(idx.Name), quoteBracket(t.Name), strings.Join(cols, ","))
}
// DropAllTables 清空全部表
func (r *mssqlRenderer) DropAllTables(ctx context.Context, db gdb.DB) error {
// 先禁用全部外键约束,再逐表 DROP
fks, err := db.Ctx(ctx).Raw(
`SELECT OBJECT_NAME(parent_object_id) AS tbl, name AS fk FROM sys.foreign_keys`).All()
if err != nil {
return err
}
for _, fk := range fks {
if _, err = db.Exec(ctx, fmt.Sprintf("ALTER TABLE %s DROP CONSTRAINT %s",
quoteBracket(fk["tbl"].String()), quoteBracket(fk["fk"].String()))); err != nil {
return err
}
}
tables, err := db.Tables(ctx)
if err != nil {
return err
}
for _, t := range tables {
if _, err = db.Exec(ctx, "DROP TABLE "+quoteBracket(t)); err != nil {
return err
}
}
return nil
}
// FixSequence 修正自增序列
func (r *mssqlRenderer) FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error {
for _, c := range t.Columns {
if !c.AutoInc {
continue
}
if _, err := db.Exec(ctx, fmt.Sprintf(
"DBCC CHECKIDENT ('%s', RESEED)", t.Name)); err != nil {
return err
}
}
return nil
}
// ============================================================
// Oracle
// ============================================================
// oracleRenderer Oracle 方言渲染器
type oracleRenderer struct{ dialect string }
// Quote 引用标识符
func (r *oracleRenderer) Quote(name string) string { return quoteStd(name) }
// Column 渲染列定义
func (r *oracleRenderer) Column(col ColumnMeta) string {
var b strings.Builder
b.WriteString(quoteStd(col.Name) + " " + r.colType(col))
if col.AutoInc {
b.WriteString(" GENERATED BY DEFAULT AS IDENTITY")
}
if !col.Nullable && !col.AutoInc {
b.WriteString(" NOT NULL")
}
if !col.AutoInc {
if d := defaultLiteral(r.dialect, col.Default); d != "" {
b.WriteString(" DEFAULT " + d)
}
}
return b.String()
}
// colType 列类型映射
func (r *oracleRenderer) colType(col ColumnMeta) string {
switch col.Type {
case TypeBool, TypeTinyint, TypeSmallint:
return "NUMBER(3)"
case TypeInt:
return "NUMBER(10)"
case TypeBigint:
return "NUMBER(19)"
case TypeFloat:
return "BINARY_FLOAT"
case TypeDouble:
return "BINARY_DOUBLE"
case TypeDecimal:
if col.Length > 0 {
return fmt.Sprintf("NUMBER(%d,%d)", col.Length, col.Scale)
}
return "NUMBER"
case TypeVarchar:
if col.Length > 0 {
return fmt.Sprintf("NVARCHAR2(%d)", col.Length)
}
return "NVARCHAR2(255)"
case TypeChar:
if col.Length > 0 {
return fmt.Sprintf("NCHAR(%d)", col.Length)
}
return "NCHAR(36)"
case TypeText, TypeTinytext, TypeMediumText, TypeLongText, TypeJson, TypeEnum, TypeSet:
return "NCLOB"
case TypeDate:
return "DATE"
case TypeTime, TypeDateTime:
return "TIMESTAMP"
case TypeBlob:
return "BLOB"
}
return "NCLOB"
}
// CreateTable 渲染建表语句
func (r *oracleRenderer) CreateTable(t *TableMeta) string {
var lines []string
for _, c := range t.Columns {
lines = append(lines, " "+r.Column(c))
}
if pk := pkColumns(t); len(pk) > 0 {
quoted := make([]string, len(pk))
for i, c := range pk {
quoted[i] = quoteStd(c)
}
lines = append(lines, " PRIMARY KEY ("+strings.Join(quoted, ",")+")")
}
sql := "CREATE TABLE " + quoteStd(t.Name) + " (\n" + strings.Join(lines, ",\n") + "\n)"
if t.Comment != "" {
sql += "\nCOMMENT ON TABLE " + quoteStd(t.Name) + " IS '" + strings.ReplaceAll(t.Comment, "'", "''") + "'"
}
for _, c := range t.Columns {
if c.Comment != "" {
sql += "\nCOMMENT ON COLUMN " + quoteStd(t.Name) + "." + quoteStd(c.Name) +
" IS '" + strings.ReplaceAll(c.Comment, "'", "''") + "'"
}
}
return sql + ";"
}
// CreateIndex 渲染索引语句
//
// Oracle 无 IF NOT EXISTS,由编排层预检索引存在性后决定是否执行
func (r *oracleRenderer) CreateIndex(t *TableMeta, idx IndexMeta) string {
unique := ""
if idx.Unique {
unique = "UNIQUE "
}
cols := make([]string, len(idx.Columns))
for i, c := range idx.Columns {
cols[i] = quoteStd(c)
}
// oracle 无 IF NOT EXISTS,由编排层预检索引存在性后决定是否执行
return fmt.Sprintf("CREATE %sINDEX %s ON %s (%s);",
unique, quoteStd(idx.Name), quoteStd(t.Name), strings.Join(cols, ","))
}
// DropAllTables 清空全部表
func (r *oracleRenderer) DropAllTables(ctx context.Context, db gdb.DB) error {
rows, err := db.Ctx(ctx).Raw("SELECT table_name FROM user_tables").All()
if err != nil {
return err
}
for _, row := range rows {
if _, err = db.Exec(ctx, "DROP TABLE "+quoteStd(row["table_name"].String())+" CASCADE CONSTRAINTS PURGE"); err != nil {
return err
}
}
return nil
}
// FixSequence 修正自增序列
//
// Oracle 的 IDENTITY 由缓存失效自动续接,无需显式修序
func (r *oracleRenderer) FixSequence(ctx context.Context, db gdb.DB, t *TableMeta) error {
for _, c := range t.Columns {
if !c.AutoInc {
continue
}
// oracle identity 由缓存失效自动续接,无需显式修序
}
return nil
}