package db import ( "context" "database/sql" "fmt" "strings" ) // GetDDL 返回表/视图的建表语句。mysql 族走 SHOW CREATE TABLE,sqlite 读 sqlite_master, // pg/mssql/oracle 用 ListColumns 元数据在 Go 侧拼装 CREATE TABLE。 func GetDDL(ctx context.Context, c ConnReq) (string, error) { ctx = reqCtx(ctx) if c.Table == "" { return "", fmt.Errorf("缺少表名") } if isRedis(c.Driver) { return "", fmt.Errorf("该驱动不支持 DDL 查看") } // pg/mssql/oracle:复用列元数据拼装,避免再查一次库 switch { case isPostgresFamily(c.Driver), isMSSQL(c.Driver), driverKey(c.Driver) == "oracle": cols, err := ListColumns(ctx, c) if err != nil { return "", err } return buildDDLFromColumns(c.Driver, c.Table, cols) } var out string err := withDB(c, func(db *sql.DB) error { var err error switch { case isMySQLFamily(c.Driver): out, err = getDDLMySQL(ctx, db, c) case driverKey(c.Driver) == "sqlite": out, err = getDDLSQLite(ctx, db, c.Table) default: err = unsupportedDriverErr(c) } return err }) if err != nil { return "", err } return out, nil } // getDDLMySQL SHOW CREATE TABLE 第二列即 DDL(列名随对象类型变化:Create Table/Create View)。 func getDDLMySQL(ctx context.Context, dbh *sql.DB, c ConnReq) (string, error) { ident := quoteMySQLIdent(c.Table) if c.Database != "" { ident = quoteMySQLIdent(c.Database) + "." + ident } rows, err := dbh.QueryContext(ctx, "SHOW CREATE TABLE "+ident) if err != nil { return "", err } defer rows.Close() if rows.Next() { var name string var ddl sql.NullString if err := rows.Scan(&name, &ddl); err != nil { return "", err } if !ddl.Valid { return "", fmt.Errorf("无法读取 %s 的 DDL", c.Table) } return ddl.String, nil } if err := rows.Err(); err != nil { return "", err } return "", fmt.Errorf("表 %s 不存在", c.Table) } // getDDLSQLite sqlite_master 的 sql 列即建表语句。 func getDDLSQLite(ctx context.Context, dbh *sql.DB, table string) (string, error) { var ddl sql.NullString err := dbh.QueryRowContext(ctx, `SELECT sql FROM sqlite_master WHERE name = ?`, table).Scan(&ddl) if err == sql.ErrNoRows { return "", fmt.Errorf("表 %s 不存在", table) } if err != nil { return "", err } if !ddl.Valid { return "", fmt.Errorf("表 %s 无 DDL 记录", table) } return ddl.String, nil } // buildDDLFromColumns 用列元数据拼装 CREATE TABLE(类型直接用 ColumnInfo.Type)。 func buildDDLFromColumns(driver, table string, cols []ColumnInfo) (string, error) { if len(cols) == 0 { return "", fmt.Errorf("表 %s 不存在或无列", table) } var pks []string for _, col := range cols { if col.Key == "PRI" { pks = append(pks, quoteIdentFor(driver, col.Name)) } } var b strings.Builder fmt.Fprintf(&b, "CREATE TABLE %s (\n", quoteTableNameFor(driver, table)) for i, col := range cols { line := " " + quoteIdentFor(driver, col.Name) + " " + col.Type if !col.Nullable { line += " NOT NULL" } // 非最后一列、或后面还有 PRIMARY KEY 子句时需要逗号 if i < len(cols)-1 || len(pks) > 0 { line += "," } b.WriteString(line + "\n") } if len(pks) > 0 { b.WriteString(" PRIMARY KEY (" + strings.Join(pks, ", ") + ")\n") } b.WriteString(");\n") return b.String(), nil }