mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-04 00:10:25 +08:00
refactor: dbm包重构
This commit is contained in:
@@ -17,12 +17,12 @@
|
||||
"countup.js": "^2.8.0",
|
||||
"cropperjs": "^1.6.1",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.6.0",
|
||||
"element-plus": "^2.6.1",
|
||||
"js-base64": "^3.7.7",
|
||||
"jsencrypt": "^3.3.2",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"monaco-editor": "^0.46.0",
|
||||
"monaco-editor": "^0.47.0",
|
||||
"monaco-sql-languages": "^0.11.0",
|
||||
"monaco-themes": "^0.4.4",
|
||||
"nprogress": "^0.2.0",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:zIndex="10000000"
|
||||
:width="210"
|
||||
v-if="themeConfig.isWatermark"
|
||||
:font="{ color: 'rgba(180, 180, 180, 0.5)' }"
|
||||
:font="{ color: 'rgba(180, 180, 180, 0.3)' }"
|
||||
:content="themeConfig.watermarkText"
|
||||
class="h100"
|
||||
>
|
||||
|
||||
@@ -50,11 +50,17 @@ import { FlowBizType, ProcinstBizStatus, ProcinstStatus } from './enums';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { formatTime } from '@/common/utils/format';
|
||||
|
||||
const searchItems = [SearchItem.select('status', '流程状态').withEnum(ProcinstStatus), SearchItem.select('bizType', '业务类型').withEnum(FlowBizType)];
|
||||
const searchItems = [
|
||||
SearchItem.select('status', '流程状态').withEnum(ProcinstStatus),
|
||||
SearchItem.select('bizType', '业务类型').withEnum(FlowBizType),
|
||||
SearchItem.input('bizKey', '业务key'),
|
||||
];
|
||||
|
||||
const columns = [
|
||||
TableColumn.new('bizType', '业务').typeTag(FlowBizType),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('creator', '发起人'),
|
||||
TableColumn.new('bizKey', '业务key'),
|
||||
TableColumn.new('procdefName', '流程名'),
|
||||
TableColumn.new('status', '流程状态').typeTag(ProcinstStatus),
|
||||
TableColumn.new('bizStatus', '业务状态').typeTag(ProcinstBizStatus),
|
||||
|
||||
@@ -46,6 +46,7 @@ const columns = [
|
||||
TableColumn.new('procinst.creator', '发起人'),
|
||||
TableColumn.new('procinst.status', '流程状态').typeTag(ProcinstStatus),
|
||||
TableColumn.new('status', '任务状态').typeTag(ProcinstTaskStatus),
|
||||
TableColumn.new('procinst.bizKey', '业务key'),
|
||||
TableColumn.new('procinst.procdefName', '流程名'),
|
||||
TableColumn.new('taskName', '当前节点'),
|
||||
TableColumn.new('procinst.createTime', '发起时间').isTime(),
|
||||
|
||||
@@ -332,7 +332,7 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
||||
|
||||
dbMeta := dbConn.GetDialect()
|
||||
if len(tables) == 0 {
|
||||
ti, err := dbMeta.GetTables()
|
||||
ti, err := dbMeta.GetMetaData().GetTables()
|
||||
biz.ErrIsNil(err)
|
||||
tables = make([]string, len(ti))
|
||||
for i, table := range ti {
|
||||
@@ -346,7 +346,7 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
||||
if needStruct {
|
||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
|
||||
writer.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS %s;\n", quotedTable))
|
||||
ddl, err := dbMeta.GetTableDDL(table)
|
||||
ddl, err := dbMeta.GetMetaData().GetTableDDL(table)
|
||||
biz.ErrIsNil(err)
|
||||
writer.WriteString(ddl + "\n")
|
||||
}
|
||||
@@ -386,7 +386,7 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
||||
}
|
||||
|
||||
func (d *Db) TableInfos(rc *req.Ctx) {
|
||||
res, err := d.getDbConn(rc).GetDialect().GetTables()
|
||||
res, err := d.getDbConn(rc).GetDialect().GetMetaData().GetTables()
|
||||
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -394,7 +394,7 @@ func (d *Db) TableInfos(rc *req.Ctx) {
|
||||
func (d *Db) TableIndex(rc *req.Ctx) {
|
||||
tn := rc.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
res, err := d.getDbConn(rc).GetDialect().GetTableIndex(tn)
|
||||
res, err := d.getDbConn(rc).GetDialect().GetMetaData().GetTableIndex(tn)
|
||||
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -405,7 +405,7 @@ func (d *Db) ColumnMA(rc *req.Ctx) {
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
|
||||
dbi := d.getDbConn(rc)
|
||||
res, err := dbi.GetDialect().GetColumns(tn)
|
||||
res, err := dbi.GetDialect().GetMetaData().GetColumns(tn)
|
||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
@@ -416,7 +416,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
||||
|
||||
dm := dbi.GetDialect()
|
||||
// 获取所有表
|
||||
tables, err := dm.GetTables()
|
||||
tables, err := dm.GetMetaData().GetTables()
|
||||
biz.ErrIsNil(err)
|
||||
tableNames := make([]string, 0)
|
||||
for _, v := range tables {
|
||||
@@ -432,7 +432,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
||||
}
|
||||
|
||||
// 获取所有表下的所有列信息
|
||||
columnMds, err := dm.GetColumns(tableNames...)
|
||||
columnMds, err := dm.GetMetaData().GetColumns(tableNames...)
|
||||
biz.ErrIsNil(err)
|
||||
for _, v := range columnMds {
|
||||
tName := v.TableName
|
||||
@@ -455,13 +455,13 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
||||
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
||||
tn := rc.Query("tableName")
|
||||
biz.NotEmpty(tn, "tableName不能为空")
|
||||
res, err := d.getDbConn(rc).GetDialect().GetTableDDL(tn)
|
||||
res, err := d.getDbConn(rc).GetDialect().GetMetaData().GetTableDDL(tn)
|
||||
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
func (d *Db) GetSchemas(rc *req.Ctx) {
|
||||
res, err := d.getDbConn(rc).GetDialect().GetSchemas()
|
||||
res, err := d.getDbConn(rc).GetDialect().GetMetaData().GetSchemas()
|
||||
biz.ErrIsNilAppendErr(err, "获取schemas失败: %s")
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ func (d *Instance) GetDbServer(rc *req.Ctx) {
|
||||
instanceId := getInstanceId(rc)
|
||||
conn, err := d.DbApp.GetDbConnByInstanceId(instanceId)
|
||||
biz.ErrIsNil(err)
|
||||
res, err := conn.GetDialect().GetDbServer()
|
||||
res, err := conn.GetDialect().GetMetaData().GetDbServer()
|
||||
biz.ErrIsNil(err)
|
||||
rc.ResData = res
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package application
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"gorm.io/gorm"
|
||||
"mayfly-go/internal/db/dbm"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/internal/db/domain/entity"
|
||||
@@ -12,6 +11,8 @@ import (
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/model"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type Instance interface {
|
||||
@@ -159,5 +160,5 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error
|
||||
}
|
||||
defer dbConn.Close()
|
||||
|
||||
return dbConn.GetDialect().GetDbNames()
|
||||
return dbConn.GetDialect().GetMetaData().GetDbNames()
|
||||
}
|
||||
|
||||
@@ -278,7 +278,7 @@ func (d *dbSqlExecAppImpl) doUpdate(ctx context.Context, update *sqlparser.Updat
|
||||
}
|
||||
|
||||
// 获取表主键列名,排除使用别名
|
||||
primaryKey, err := dbConn.GetDialect().GetPrimaryKey(tableName)
|
||||
primaryKey, err := dbConn.GetDialect().GetMetaData().GetPrimaryKey(tableName)
|
||||
if err != nil {
|
||||
return nil, errorx.NewBiz("获取表主键信息失败")
|
||||
}
|
||||
|
||||
@@ -2,12 +2,6 @@ package dbi
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"embed"
|
||||
"strings"
|
||||
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
)
|
||||
|
||||
type DataType string
|
||||
@@ -29,46 +23,6 @@ const (
|
||||
DuplicateStrategyUpdate = 2
|
||||
)
|
||||
|
||||
// 数据库服务实例信息
|
||||
type DbServer struct {
|
||||
Version string `json:"version"` // 版本信息
|
||||
Extra collx.M `json:"extra"` // 其他额外信息
|
||||
}
|
||||
|
||||
// 表信息
|
||||
type Table struct {
|
||||
TableName string `json:"tableName"` // 表名
|
||||
TableComment string `json:"tableComment"` // 表备注
|
||||
CreateTime string `json:"createTime"` // 创建时间
|
||||
TableRows int `json:"tableRows"`
|
||||
DataLength int64 `json:"dataLength"`
|
||||
IndexLength int64 `json:"indexLength"`
|
||||
}
|
||||
|
||||
// 表的列信息
|
||||
type Column struct {
|
||||
TableName string `json:"tableName"` // 表名
|
||||
ColumnName string `json:"columnName"` // 列名
|
||||
ColumnType string `json:"columnType"` // 列类型
|
||||
ColumnComment string `json:"columnComment"` // 列备注
|
||||
IsPrimaryKey bool `json:"isPrimaryKey"` // 是否为主键
|
||||
IsIdentity bool `json:"isIdentity"` // 是否自增
|
||||
ColumnDefault string `json:"columnDefault"` // 默认值
|
||||
Nullable string `json:"nullable"` // 是否可为null
|
||||
NumScale string `json:"numScale"` // 小数点
|
||||
Extra collx.M `json:"extra"` // 其他额外信息
|
||||
}
|
||||
|
||||
// 表索引信息
|
||||
type Index struct {
|
||||
IndexName string `json:"indexName"` // 索引名
|
||||
ColumnName string `json:"columnName"` // 列名
|
||||
IndexType string `json:"indexType"` // 索引类型
|
||||
IndexComment string `json:"indexComment"` // 备注
|
||||
SeqInIndex int `json:"seqInIndex"`
|
||||
IsUnique bool `json:"isUnique"`
|
||||
}
|
||||
|
||||
type DbCopyTable struct {
|
||||
Id uint64 `json:"id"`
|
||||
Db string `json:"db" `
|
||||
@@ -90,31 +44,11 @@ type DataConverter interface {
|
||||
}
|
||||
|
||||
// -----------------------------------元数据接口定义------------------------------------------
|
||||
// 数据库方言、元信息接口(表、列、获取表数据等元信息)
|
||||
// 数据库方言 用于获取元信息接口、批量插入等各个数据库方言不一致的实现方式
|
||||
type Dialect interface {
|
||||
|
||||
// 获取数据库服务实例信息
|
||||
GetDbServer() (*DbServer, error)
|
||||
|
||||
// 获取数据库名称列表
|
||||
GetDbNames() ([]string, error)
|
||||
|
||||
// 获取表信息
|
||||
GetTables() ([]Table, error)
|
||||
|
||||
// 获取指定表名的所有列元信息
|
||||
GetColumns(tableNames ...string) ([]Column, error)
|
||||
|
||||
// 获取表主键字段名,没有主键标识则默认第一个字段
|
||||
GetPrimaryKey(tableName string) (string, error)
|
||||
|
||||
// 获取表索引信息
|
||||
GetTableIndex(tableName string) ([]Index, error)
|
||||
|
||||
// 获取建表ddl
|
||||
GetTableDDL(tableName string) (string, error)
|
||||
|
||||
GetSchemas() ([]string, error)
|
||||
// 获取元数据信息接口
|
||||
GetMetaData() MetaData
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
GetDbProgram() (DbProgram, error)
|
||||
@@ -127,40 +61,3 @@ type Dialect interface {
|
||||
|
||||
CopyTable(copy *DbCopyTable) error
|
||||
}
|
||||
|
||||
// ------------------------- 元数据sql操作 -------------------------
|
||||
//
|
||||
//go:embed metasql/*
|
||||
var metasql embed.FS
|
||||
|
||||
// sql缓存 key: sql备注的key 如:MYSQL_TABLE_MA value: sql内容
|
||||
var sqlCache = make(map[string]string, 20)
|
||||
|
||||
// 获取本地文件的sql内容,并进行解析,获取对应key的sql内容
|
||||
func GetLocalSql(file, key string) string {
|
||||
sql := sqlCache[key]
|
||||
if sql != "" {
|
||||
return sql
|
||||
}
|
||||
|
||||
bytes, err := metasql.ReadFile(file)
|
||||
biz.ErrIsNilAppendErr(err, "获取sql meta文件内容失败: %s")
|
||||
allSql := string(bytes)
|
||||
|
||||
sqls := strings.Split(allSql, "---------------------------------------")
|
||||
var resSql string
|
||||
for _, sql := range sqls {
|
||||
sql = stringx.TrimSpaceAndBr(sql)
|
||||
// 获取sql第一行的sql备注信息如:--MYSQL_TABLE_MA 表信息元数据
|
||||
info := strings.SplitN(sql, "\n", 2)
|
||||
// 原始sql,即去除第一行的key与备注信息
|
||||
rowSql := info[1]
|
||||
// 获取sql key;如:MYSQL_TABLE_MA
|
||||
sqlKey := strings.Split(strings.Split(info[0], " ")[0], "--")[1]
|
||||
if key == sqlKey {
|
||||
resSql = rowSql
|
||||
}
|
||||
sqlCache[sqlKey] = rowSql
|
||||
}
|
||||
return resSql
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"mayfly-go/internal/machine/mcm"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type DbInfo struct {
|
||||
@@ -89,6 +90,17 @@ func (di *DbInfo) IfUseSshTunnelChangeIpPort() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 获取当前库的schema
|
||||
func (di *DbInfo) CurrentSchema() string {
|
||||
dbName := di.Database
|
||||
schema := ""
|
||||
arr := strings.Split(dbName, "/")
|
||||
if len(arr) == 2 {
|
||||
schema = arr[1]
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
// 根据ssh tunnel机器id返回ssh tunnel
|
||||
func GetSshTunnel(sshTunnelMachineId int) (*mcm.SshTunnelMachine, error) {
|
||||
return machineapp.GetMachineApp().GetSshTunnelMachine(sshTunnelMachineId)
|
||||
|
||||
@@ -16,11 +16,11 @@ func GetMeta(dt DbType) Meta {
|
||||
return metas[dt]
|
||||
}
|
||||
|
||||
// 数据库元信息获取,如获取sql.DB、Dialect等
|
||||
// 数据库元信息,如获取sql.DB、Dialect等
|
||||
type Meta interface {
|
||||
// 根据数据库信息获取sql.DB
|
||||
GetSqlDb(*DbInfo) (*sql.DB, error)
|
||||
|
||||
// 获取数据库方言,用于获取表结构等信息
|
||||
// 获取数据库方言
|
||||
GetDialect(*DbConn) Dialect
|
||||
}
|
||||
|
||||
112
server/internal/db/dbm/dbi/metadata.go
Normal file
112
server/internal/db/dbm/dbi/metadata.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package dbi
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"mayfly-go/pkg/utils/stringx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 元数据接口(表、列、等元信息)
|
||||
type MetaData interface {
|
||||
// 获取数据库服务实例信息
|
||||
GetDbServer() (*DbServer, error)
|
||||
|
||||
// 获取数据库名称列表
|
||||
GetDbNames() ([]string, error)
|
||||
|
||||
// 获取表信息
|
||||
GetTables() ([]Table, error)
|
||||
|
||||
// 获取指定表名的所有列元信息
|
||||
GetColumns(tableNames ...string) ([]Column, error)
|
||||
|
||||
// 获取表主键字段名,没有主键标识则默认第一个字段
|
||||
GetPrimaryKey(tableName string) (string, error)
|
||||
|
||||
// 获取表索引信息
|
||||
GetTableIndex(tableName string) ([]Index, error)
|
||||
|
||||
// 获取建表ddl
|
||||
GetTableDDL(tableName string) (string, error)
|
||||
|
||||
GetSchemas() ([]string, error)
|
||||
}
|
||||
|
||||
// 数据库服务实例信息
|
||||
type DbServer struct {
|
||||
Version string `json:"version"` // 版本信息
|
||||
Extra collx.M `json:"extra"` // 其他额外信息
|
||||
}
|
||||
|
||||
// 表信息
|
||||
type Table struct {
|
||||
TableName string `json:"tableName"` // 表名
|
||||
TableComment string `json:"tableComment"` // 表备注
|
||||
CreateTime string `json:"createTime"` // 创建时间
|
||||
TableRows int `json:"tableRows"`
|
||||
DataLength int64 `json:"dataLength"`
|
||||
IndexLength int64 `json:"indexLength"`
|
||||
}
|
||||
|
||||
// 表的列信息
|
||||
type Column struct {
|
||||
TableName string `json:"tableName"` // 表名
|
||||
ColumnName string `json:"columnName"` // 列名
|
||||
ColumnType string `json:"columnType"` // 列类型
|
||||
ColumnComment string `json:"columnComment"` // 列备注
|
||||
IsPrimaryKey bool `json:"isPrimaryKey"` // 是否为主键
|
||||
IsIdentity bool `json:"isIdentity"` // 是否自增
|
||||
ColumnDefault string `json:"columnDefault"` // 默认值
|
||||
Nullable string `json:"nullable"` // 是否可为null
|
||||
NumScale string `json:"numScale"` // 小数点
|
||||
Extra collx.M `json:"extra"` // 其他额外信息
|
||||
}
|
||||
|
||||
// 表索引信息
|
||||
type Index struct {
|
||||
IndexName string `json:"indexName"` // 索引名
|
||||
ColumnName string `json:"columnName"` // 列名
|
||||
IndexType string `json:"indexType"` // 索引类型
|
||||
IndexComment string `json:"indexComment"` // 备注
|
||||
SeqInIndex int `json:"seqInIndex"`
|
||||
IsUnique bool `json:"isUnique"`
|
||||
}
|
||||
|
||||
// ------------------------- 元数据sql操作 -------------------------
|
||||
//
|
||||
//go:embed metasql/*
|
||||
var metasql embed.FS
|
||||
|
||||
// sql缓存 key: sql备注的key 如:MYSQL_TABLE_MA value: sql内容
|
||||
var sqlCache = make(map[string]string, 20)
|
||||
|
||||
// 获取本地文件的sql内容,并进行解析,获取对应key的sql内容
|
||||
func GetLocalSql(file, key string) string {
|
||||
sql := sqlCache[key]
|
||||
if sql != "" {
|
||||
return sql
|
||||
}
|
||||
|
||||
bytes, err := metasql.ReadFile(file)
|
||||
biz.ErrIsNilAppendErr(err, "获取sql meta文件内容失败: %s")
|
||||
allSql := string(bytes)
|
||||
|
||||
sqls := strings.Split(allSql, "---------------------------------------")
|
||||
var resSql string
|
||||
for _, sql := range sqls {
|
||||
sql = stringx.TrimSpaceAndBr(sql)
|
||||
// 获取sql第一行的sql备注信息如:--MYSQL_TABLE_MA 表信息元数据
|
||||
info := strings.SplitN(sql, "\n", 2)
|
||||
// 原始sql,即去除第一行的key与备注信息
|
||||
rowSql := info[1]
|
||||
// 获取sql key;如:MYSQL_TABLE_MA
|
||||
sqlKey := strings.Split(strings.Split(info[0], " ")[0], "--")[1]
|
||||
if key == sqlKey {
|
||||
resSql = rowSql
|
||||
}
|
||||
sqlCache[sqlKey] = rowSql
|
||||
}
|
||||
return resSql
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -18,233 +17,14 @@ import (
|
||||
_ "gitee.com/chunanyong/dm"
|
||||
)
|
||||
|
||||
const (
|
||||
DM_META_FILE = "metasql/dm_meta.sql"
|
||||
DM_DB_SCHEMAS = "DM_DB_SCHEMAS"
|
||||
DM_TABLE_INFO_KEY = "DM_TABLE_INFO"
|
||||
DM_INDEX_INFO_KEY = "DM_INDEX_INFO"
|
||||
DM_COLUMN_MA_KEY = "DM_COLUMN_MA"
|
||||
)
|
||||
|
||||
type DMDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (dd *DMDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := dd.dc.Query("select * from v$instance")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (dd *DMDialect) GetMetaData() dbi.MetaData {
|
||||
return &DMMetaData{
|
||||
dc: dd.dc,
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["SVR_VERSION"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (dd *DMDialect) GetDbNames() ([]string, error) {
|
||||
_, res, err := dd.dc.Query("SELECT name AS DBNAME FROM v$database")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["DBNAME"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (dd *DMDialect) GetTables() ([]dbi.Table, error) {
|
||||
|
||||
// 首先执行更新统计信息sql 这个统计信息在数据量比较大的时候就比较耗时,所以最好定时执行
|
||||
// _, _, err := pd.dc.Query("dbms_stats.GATHER_SCHEMA_stats(SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))")
|
||||
|
||||
// 查询表信息
|
||||
_, res, err := dd.dc.Query(dbi.GetLocalSql(DM_META_FILE, DM_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
TableComment: anyx.ConvString(re["TABLE_COMMENT"]),
|
||||
CreateTime: anyx.ConvString(re["CREATE_TIME"]),
|
||||
TableRows: anyx.ConvInt(re["TABLE_ROWS"]),
|
||||
DataLength: anyx.ConvInt64(re["DATA_LENGTH"]),
|
||||
IndexLength: anyx.ConvInt64(re["INDEX_LENGTH"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (dd *DMDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := dd.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := dd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(DM_META_FILE, DM_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ConvString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ConvString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["COLUMN_DEFAULT"]),
|
||||
NumScale: anyx.ConvString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (dd *DMDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := dd.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (dd *DMDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := dd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(DM_META_FILE, DM_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["INDEX_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
IndexType: anyx.ConvString(re["INDEX_TYPE"]),
|
||||
IndexComment: anyx.ConvString(re["INDEX_COMMENT"]),
|
||||
IsUnique: anyx.ConvInt(re["IS_UNIQUE"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["SEQ_IN_INDEX"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (dd *DMDialect) GetTableDDL(tableName string) (string, error) {
|
||||
ddlSql := fmt.Sprintf("CALL SP_TABLEDEF((SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID)), '%s')", tableName)
|
||||
_, res, err := dd.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 建表ddl
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(re["COLUMN_VALUE"].(string))
|
||||
}
|
||||
|
||||
// 表注释
|
||||
_, res, err = dd.dc.Query(fmt.Sprintf(`
|
||||
select OWNER, COMMENTS from ALL_TAB_COMMENTS where TABLE_TYPE='TABLE' and TABLE_NAME = '%s'
|
||||
and owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
`, tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
// COMMENT ON TABLE "SYS_MENU" IS '菜单表';
|
||||
if re["COMMENTS"] != nil {
|
||||
tableComment := fmt.Sprintf("\n\nCOMMENT ON TABLE \"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COMMENTS"].(string))
|
||||
builder.WriteString(tableComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
fieldSql := fmt.Sprintf(`
|
||||
SELECT OWNER, COLUMN_NAME, COMMENTS
|
||||
FROM USER_COL_COMMENTS
|
||||
WHERE OWNER = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
AND TABLE_NAME = '%s'
|
||||
`, tableName)
|
||||
_, res, err = dd.dc.Query(fieldSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString("\n")
|
||||
for _, re := range res {
|
||||
// COMMENT ON COLUMN "SYS_MENU"."BIZ_CODE" IS '业务编码,应用编码1';
|
||||
if re["COMMENTS"] != nil {
|
||||
fieldComment := fmt.Sprintf("\nCOMMENT ON COLUMN \"%s\".\"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COLUMN_NAME"].(string), re["COMMENTS"].(string))
|
||||
builder.WriteString(fieldComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 索引信息
|
||||
indexSql := fmt.Sprintf(`
|
||||
select indexdef(b.object_id,1) as INDEX_DEF from ALL_INDEXES a
|
||||
join ALL_objects b on a.owner = b.owner and b.object_name = a.index_name and b.object_type = 'INDEX'
|
||||
where a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
and a.table_name = '%s'
|
||||
and indexdef(b.object_id,1) != '禁止查看系统定义的索引信息'
|
||||
`, tableName)
|
||||
_, res, err = dd.dc.Query(indexSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
builder.WriteString("\n\n" + re["INDEX_DEF"].(string))
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// 获取DM当前连接的库可访问的schemaNames
|
||||
func (dd *DMDialect) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(DM_META_FILE, DM_DB_SCHEMAS)
|
||||
_, res, err := dd.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["SCHEMA_NAME"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
@@ -304,7 +84,8 @@ func (dd *DMDialect) batchInsertMerge(dbType dbi.DbType, tx *sql.Tx, tableName s
|
||||
// 查询主键字段
|
||||
uniqueCols := make([]string, 0)
|
||||
caseSqls := make([]string, 0)
|
||||
tableCols, _ := dd.GetColumns(tableName)
|
||||
metadata := dd.GetMetaData()
|
||||
tableCols, _ := metadata.GetColumns(tableName)
|
||||
identityCols := make([]string, 0)
|
||||
for _, col := range tableCols {
|
||||
if col.IsPrimaryKey {
|
||||
@@ -317,7 +98,7 @@ func (dd *DMDialect) batchInsertMerge(dbType dbi.DbType, tx *sql.Tx, tableName s
|
||||
}
|
||||
}
|
||||
// 查询唯一索引涉及到的字段,并组装到match条件内
|
||||
indexs, _ := dd.GetTableIndex(tableName)
|
||||
indexs, _ := metadata.GetTableIndex(tableName)
|
||||
if indexs != nil {
|
||||
for _, index := range indexs {
|
||||
if index.IsUnique {
|
||||
@@ -428,7 +209,8 @@ func (dd *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
||||
|
||||
func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
tableName := copy.TableName
|
||||
ddl, err := dd.GetTableDDL(tableName)
|
||||
metadata := dd.GetMetaData()
|
||||
ddl, err := metadata.GetTableDDL(tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -450,7 +232,7 @@ func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
// 设置允许填充自增列之后,显示指定列名可以插入自增列
|
||||
_, _ = dd.dc.Exec(fmt.Sprintf("set identity_insert \"%s\" on", newTableName))
|
||||
// 获取列名
|
||||
columns, _ := dd.GetColumns(tableName)
|
||||
columns, _ := metadata.GetColumns(tableName)
|
||||
columnArr := make([]string, 0)
|
||||
for _, column := range columns {
|
||||
columnArr = append(columnArr, fmt.Sprintf("\"%s\"", column.ColumnName))
|
||||
|
||||
239
server/internal/db/dbm/dm/metadata.go
Normal file
239
server/internal/db/dbm/dm/metadata.go
Normal file
@@ -0,0 +1,239 @@
|
||||
package dm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
DM_META_FILE = "metasql/dm_meta.sql"
|
||||
DM_DB_SCHEMAS = "DM_DB_SCHEMAS"
|
||||
DM_TABLE_INFO_KEY = "DM_TABLE_INFO"
|
||||
DM_INDEX_INFO_KEY = "DM_INDEX_INFO"
|
||||
DM_COLUMN_MA_KEY = "DM_COLUMN_MA"
|
||||
)
|
||||
|
||||
type DMMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (dd *DMMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := dd.dc.Query("select * from v$instance")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["SVR_VERSION"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (dd *DMMetaData) GetDbNames() ([]string, error) {
|
||||
_, res, err := dd.dc.Query("SELECT name AS DBNAME FROM v$database")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["DBNAME"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (dd *DMMetaData) GetTables() ([]dbi.Table, error) {
|
||||
|
||||
// 首先执行更新统计信息sql 这个统计信息在数据量比较大的时候就比较耗时,所以最好定时执行
|
||||
// _, _, err := pd.dc.Query("dbms_stats.GATHER_SCHEMA_stats(SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))")
|
||||
|
||||
// 查询表信息
|
||||
_, res, err := dd.dc.Query(dbi.GetLocalSql(DM_META_FILE, DM_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
TableComment: anyx.ConvString(re["TABLE_COMMENT"]),
|
||||
CreateTime: anyx.ConvString(re["CREATE_TIME"]),
|
||||
TableRows: anyx.ConvInt(re["TABLE_ROWS"]),
|
||||
DataLength: anyx.ConvInt64(re["DATA_LENGTH"]),
|
||||
IndexLength: anyx.ConvInt64(re["INDEX_LENGTH"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (dd *DMMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := dd.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := dd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(DM_META_FILE, DM_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ConvString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ConvString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["COLUMN_DEFAULT"]),
|
||||
NumScale: anyx.ConvString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (dd *DMMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := dd.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (dd *DMMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := dd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(DM_META_FILE, DM_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["INDEX_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
IndexType: anyx.ConvString(re["INDEX_TYPE"]),
|
||||
IndexComment: anyx.ConvString(re["INDEX_COMMENT"]),
|
||||
IsUnique: anyx.ConvInt(re["IS_UNIQUE"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["SEQ_IN_INDEX"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (dd *DMMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
ddlSql := fmt.Sprintf("CALL SP_TABLEDEF((SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID)), '%s')", tableName)
|
||||
_, res, err := dd.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 建表ddl
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(re["COLUMN_VALUE"].(string))
|
||||
}
|
||||
|
||||
// 表注释
|
||||
_, res, err = dd.dc.Query(fmt.Sprintf(`
|
||||
select OWNER, COMMENTS from ALL_TAB_COMMENTS where TABLE_TYPE='TABLE' and TABLE_NAME = '%s'
|
||||
and owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
`, tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
// COMMENT ON TABLE "SYS_MENU" IS '菜单表';
|
||||
if re["COMMENTS"] != nil {
|
||||
tableComment := fmt.Sprintf("\n\nCOMMENT ON TABLE \"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COMMENTS"].(string))
|
||||
builder.WriteString(tableComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
fieldSql := fmt.Sprintf(`
|
||||
SELECT OWNER, COLUMN_NAME, COMMENTS
|
||||
FROM USER_COL_COMMENTS
|
||||
WHERE OWNER = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
AND TABLE_NAME = '%s'
|
||||
`, tableName)
|
||||
_, res, err = dd.dc.Query(fieldSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString("\n")
|
||||
for _, re := range res {
|
||||
// COMMENT ON COLUMN "SYS_MENU"."BIZ_CODE" IS '业务编码,应用编码1';
|
||||
if re["COMMENTS"] != nil {
|
||||
fieldComment := fmt.Sprintf("\nCOMMENT ON COLUMN \"%s\".\"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COLUMN_NAME"].(string), re["COMMENTS"].(string))
|
||||
builder.WriteString(fieldComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 索引信息
|
||||
indexSql := fmt.Sprintf(`
|
||||
select indexdef(b.object_id,1) as INDEX_DEF from ALL_INDEXES a
|
||||
join ALL_objects b on a.owner = b.owner and b.object_name = a.index_name and b.object_type = 'INDEX'
|
||||
where a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
|
||||
and a.table_name = '%s'
|
||||
and indexdef(b.object_id,1) != '禁止查看系统定义的索引信息'
|
||||
`, tableName)
|
||||
_, res, err = dd.dc.Query(indexSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
builder.WriteString("\n\n" + re["INDEX_DEF"].(string))
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// 获取DM当前连接的库可访问的schemaNames
|
||||
func (dd *DMMetaData) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(DM_META_FILE, DM_DB_SCHEMAS)
|
||||
_, res, err := dd.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["SCHEMA_NAME"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
}
|
||||
@@ -1,11 +1,9 @@
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -14,282 +12,12 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MSSQL_META_FILE = "metasql/mssql_meta.sql"
|
||||
MSSQL_DBS_KEY = "MSSQL_DBS"
|
||||
MSSQL_DB_SCHEMAS_KEY = "MSSQL_DB_SCHEMAS"
|
||||
MSSQL_TABLE_INFO_KEY = "MSSQL_TABLE_INFO"
|
||||
MSSQL_INDEX_INFO_KEY = "MSSQL_INDEX_INFO"
|
||||
MSSQL_COLUMN_MA_KEY = "MSSQL_COLUMN_MA"
|
||||
MSSQL_TABLE_DETAIL_KEY = "MSSQL_TABLE_DETAIL"
|
||||
MSSQL_TABLE_INDEX_DDL_KEY = "MSSQL_TABLE_INDEX_DDL"
|
||||
)
|
||||
|
||||
type MssqlDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := md.dc.Query("SELECT @@VERSION as version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) GetDbNames() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_DBS_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 从连接信息中获取数据库和schema信息
|
||||
func (md *MssqlDialect) currentSchema() string {
|
||||
dbName := md.dc.Info.Database
|
||||
schema := ""
|
||||
arr := strings.Split(dbName, "/")
|
||||
if len(arr) == 2 {
|
||||
schema = arr[1]
|
||||
}
|
||||
return schema
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (md *MssqlDialect) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_TABLE_INFO_KEY), md.currentSchema())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (md *MssqlDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := md.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := md.dc.Query(fmt.Sprintf(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_COLUMN_MA_KEY), tableName), md.currentSchema())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ToString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ToString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ToString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ToString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ToString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: anyx.ToString(re["COLUMN_DEFAULT"]),
|
||||
NumScale: anyx.ToString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||
func (md *MssqlDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := md.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) getTableIndexWithPK(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_INDEX_INFO_KEY), md.currentSchema(), tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,多个索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和字段顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return indexs, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (md *MssqlDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
indexs, _ := md.getTableIndexWithPK(tableName)
|
||||
result := make([]dbi.Index, 0)
|
||||
// 过滤掉主键索引,主键索引名为PK__开头的
|
||||
for _, v := range indexs {
|
||||
in := v.IndexName
|
||||
if strings.HasPrefix(in, "PK__") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (md MssqlDialect) CopyTableDDL(tableName string, newTableName string) (string, error) {
|
||||
if newTableName == "" {
|
||||
newTableName = tableName
|
||||
}
|
||||
|
||||
// 根据列信息生成建表语句
|
||||
var builder strings.Builder
|
||||
var commentBuilder strings.Builder
|
||||
|
||||
// 查询表名和表注释, 设置表注释
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_TABLE_DETAIL_KEY), md.currentSchema(), tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tableComment := ""
|
||||
if len(res) > 0 {
|
||||
tableComment = anyx.ToString(res[0]["tableComment"])
|
||||
if tableComment != "" {
|
||||
// 注释转义单引号
|
||||
tableComment = strings.ReplaceAll(tableComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s';\n", tableComment, md.currentSchema(), newTableName))
|
||||
}
|
||||
}
|
||||
|
||||
baseTable := fmt.Sprintf("%s.%s", md.dc.Info.Type.QuoteIdentifier(md.currentSchema()), md.dc.Info.Type.QuoteIdentifier(newTableName))
|
||||
|
||||
// 查询列信息
|
||||
columns, err := md.GetColumns(tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString(fmt.Sprintf("CREATE TABLE %s (\n", baseTable))
|
||||
pks := make([]string, 0)
|
||||
for i, v := range columns {
|
||||
nullAble := "NULL"
|
||||
if v.Nullable == "NO" {
|
||||
nullAble = "NOT NULL"
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("\t[%s] %s %s", v.ColumnName, v.ColumnType, nullAble))
|
||||
if v.IsIdentity {
|
||||
builder.WriteString(" IDENTITY(1,11)")
|
||||
}
|
||||
if v.ColumnDefault != "" {
|
||||
builder.WriteString(fmt.Sprintf(" DEFAULT %s", v.ColumnDefault))
|
||||
}
|
||||
if v.IsPrimaryKey {
|
||||
pks = append(pks, fmt.Sprintf("[%s]", v.ColumnName))
|
||||
}
|
||||
if i < len(columns)-1 {
|
||||
builder.WriteString(",")
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
// 设置主键
|
||||
if len(pks) > 0 {
|
||||
builder.WriteString(fmt.Sprintf("\tCONSTRAINT PK_%s PRIMARY KEY ( %s )", newTableName, strings.Join(pks, ",")))
|
||||
}
|
||||
builder.WriteString("\n);\n")
|
||||
|
||||
// 设置字段注释
|
||||
for _, v := range columns {
|
||||
if v.ColumnComment != "" {
|
||||
// 注释转义单引号
|
||||
v.ColumnComment = strings.ReplaceAll(v.ColumnComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s', N'COLUMN', N'%s';\n", v.ColumnComment, md.currentSchema(), newTableName, v.ColumnName))
|
||||
}
|
||||
}
|
||||
|
||||
// 设置索引
|
||||
indexs, err := md.GetTableIndex(tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, v := range indexs {
|
||||
builder.WriteString(fmt.Sprintf("\nCREATE NONCLUSTERED INDEX [%s] ON %s (%s);\n", v.IndexName, baseTable, v.ColumnName))
|
||||
// 设置索引注释
|
||||
if v.IndexComment != "" {
|
||||
// 注释转义单引号
|
||||
v.IndexComment = strings.ReplaceAll(v.IndexComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s', N'INDEX', N'%s';\n", v.IndexComment, md.currentSchema(), newTableName, v.IndexName))
|
||||
}
|
||||
}
|
||||
return builder.String() + commentBuilder.String(), nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (md *MssqlDialect) GetTableDDL(tableName string) (string, error) {
|
||||
return md.CopyTableDDL(tableName, "")
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) WalkTableRecord(tableName string, walkFn dbi.WalkQueryRowsFunc) error {
|
||||
return md.dc.WalkQueryRows(context.Background(), fmt.Sprintf("SELECT * FROM %s", tableName), walkFn)
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) GetSchemas() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_DB_SCHEMAS_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schemas := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemas = append(schemas, anyx.ConvString(re["SCHEMA_NAME"]))
|
||||
}
|
||||
return schemas, nil
|
||||
func (md *MssqlDialect) GetMetaData() dbi.MetaData {
|
||||
return &MssqlMetaData{dc: md.dc}
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
@@ -307,11 +35,12 @@ func (md *MssqlDialect) BatchInsert(tx *sql.Tx, tableName string, columns []stri
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) batchInsertSimple(tx *sql.Tx, tableName string, columns []string, values [][]any, duplicateStrategy int) (int64, error) {
|
||||
schema := md.currentSchema()
|
||||
msMetadata := md.GetMetaData().(*MssqlMetaData)
|
||||
schema := md.dc.Info.CurrentSchema()
|
||||
ignoreDupSql := ""
|
||||
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
||||
// ALTER TABLE dbo.TEST ADD CONSTRAINT uniqueRows UNIQUE (ColA, ColB, ColC, ColD) WITH (IGNORE_DUP_KEY = ON)
|
||||
indexs, _ := md.getTableIndexWithPK(tableName)
|
||||
indexs, _ := msMetadata.getTableIndexWithPK(tableName)
|
||||
// 收集唯一索引涉及到的字段
|
||||
uniqueColumns := make([]string, 0)
|
||||
for _, index := range indexs {
|
||||
@@ -365,7 +94,8 @@ func (md *MssqlDialect) batchInsertSimple(tx *sql.Tx, tableName string, columns
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) batchInsertMerge(tx *sql.Tx, tableName string, columns []string, values [][]any, duplicateStrategy int) (int64, error) {
|
||||
schema := md.currentSchema()
|
||||
msMetadata := md.GetMetaData().(*MssqlMetaData)
|
||||
schema := md.dc.Info.CurrentSchema()
|
||||
dbType := md.dc.Info.Type
|
||||
|
||||
// 收集MERGE 语句的 ON 子句条件
|
||||
@@ -374,7 +104,7 @@ func (md *MssqlDialect) batchInsertMerge(tx *sql.Tx, tableName string, columns [
|
||||
|
||||
// 查询取出自增列字段, merge update不能修改自增列
|
||||
identityCols := make([]string, 0)
|
||||
cols, err := md.GetColumns(tableName)
|
||||
cols, err := msMetadata.GetColumns(tableName)
|
||||
for _, col := range cols {
|
||||
if col.IsIdentity {
|
||||
identityCols = append(identityCols, col.ColumnName)
|
||||
@@ -491,14 +221,14 @@ func (dc *DataConverter) ParseData(dbColumnValue any, dataType dbi.DataType) any
|
||||
}
|
||||
|
||||
func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
schema := md.currentSchema()
|
||||
msMetadata := md.GetMetaData().(*MssqlMetaData)
|
||||
schema := md.dc.Info.CurrentSchema()
|
||||
|
||||
// 生成新表名,为老表明+_copy_时间戳
|
||||
newTableName := copy.TableName + "_copy_" + time.Now().Format("20060102150405")
|
||||
|
||||
// 复制建表语句
|
||||
ddl, err := md.CopyTableDDL(copy.TableName, newTableName)
|
||||
ddl, err := msMetadata.CopyTableDDL(copy.TableName, newTableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -512,7 +242,7 @@ func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
if copy.CopyData {
|
||||
go func() {
|
||||
// 查询所有的列
|
||||
columns, err := md.GetColumns(copy.TableName)
|
||||
columns, err := msMetadata.GetColumns(copy.TableName)
|
||||
if err != nil {
|
||||
logx.Warnf("复制表[%s]数据失败: %s", copy.TableName, err.Error())
|
||||
return
|
||||
|
||||
273
server/internal/db/dbm/mssql/metadata.go
Normal file
273
server/internal/db/dbm/mssql/metadata.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package mssql
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
MSSQL_META_FILE = "metasql/mssql_meta.sql"
|
||||
MSSQL_DBS_KEY = "MSSQL_DBS"
|
||||
MSSQL_DB_SCHEMAS_KEY = "MSSQL_DB_SCHEMAS"
|
||||
MSSQL_TABLE_INFO_KEY = "MSSQL_TABLE_INFO"
|
||||
MSSQL_INDEX_INFO_KEY = "MSSQL_INDEX_INFO"
|
||||
MSSQL_COLUMN_MA_KEY = "MSSQL_COLUMN_MA"
|
||||
MSSQL_TABLE_DETAIL_KEY = "MSSQL_TABLE_DETAIL"
|
||||
MSSQL_TABLE_INDEX_DDL_KEY = "MSSQL_TABLE_INDEX_DDL"
|
||||
)
|
||||
|
||||
type MssqlMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (md *MssqlMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := md.dc.Query("SELECT @@VERSION as version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (md *MssqlMetaData) GetDbNames() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_DBS_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (md *MssqlMetaData) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_TABLE_INFO_KEY), md.dc.Info.CurrentSchema())
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (md *MssqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := md.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := md.dc.Query(fmt.Sprintf(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_COLUMN_MA_KEY), tableName), md.dc.Info.CurrentSchema())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ToString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ToString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ToString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ToString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ToString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: anyx.ToString(re["COLUMN_DEFAULT"]),
|
||||
NumScale: anyx.ToString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||
func (md *MssqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := md.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
func (md *MssqlMetaData) getTableIndexWithPK(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_INDEX_INFO_KEY), md.dc.Info.CurrentSchema(), tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,多个索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和字段顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return indexs, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (md *MssqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
indexs, _ := md.getTableIndexWithPK(tableName)
|
||||
result := make([]dbi.Index, 0)
|
||||
// 过滤掉主键索引,主键索引名为PK__开头的
|
||||
for _, v := range indexs {
|
||||
in := v.IndexName
|
||||
if strings.HasPrefix(in, "PK__") {
|
||||
continue
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (md MssqlMetaData) CopyTableDDL(tableName string, newTableName string) (string, error) {
|
||||
if newTableName == "" {
|
||||
newTableName = tableName
|
||||
}
|
||||
|
||||
// 根据列信息生成建表语句
|
||||
var builder strings.Builder
|
||||
var commentBuilder strings.Builder
|
||||
|
||||
// 查询表名和表注释, 设置表注释
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_TABLE_DETAIL_KEY), md.dc.Info.CurrentSchema(), tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
tableComment := ""
|
||||
if len(res) > 0 {
|
||||
tableComment = anyx.ToString(res[0]["tableComment"])
|
||||
if tableComment != "" {
|
||||
// 注释转义单引号
|
||||
tableComment = strings.ReplaceAll(tableComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s';\n", tableComment, md.dc.Info.CurrentSchema(), newTableName))
|
||||
}
|
||||
}
|
||||
|
||||
baseTable := fmt.Sprintf("%s.%s", md.dc.Info.Type.QuoteIdentifier(md.dc.Info.CurrentSchema()), md.dc.Info.Type.QuoteIdentifier(newTableName))
|
||||
|
||||
// 查询列信息
|
||||
columns, err := md.GetColumns(tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString(fmt.Sprintf("CREATE TABLE %s (\n", baseTable))
|
||||
pks := make([]string, 0)
|
||||
for i, v := range columns {
|
||||
nullAble := "NULL"
|
||||
if v.Nullable == "NO" {
|
||||
nullAble = "NOT NULL"
|
||||
}
|
||||
builder.WriteString(fmt.Sprintf("\t[%s] %s %s", v.ColumnName, v.ColumnType, nullAble))
|
||||
if v.IsIdentity {
|
||||
builder.WriteString(" IDENTITY(1,11)")
|
||||
}
|
||||
if v.ColumnDefault != "" {
|
||||
builder.WriteString(fmt.Sprintf(" DEFAULT %s", v.ColumnDefault))
|
||||
}
|
||||
if v.IsPrimaryKey {
|
||||
pks = append(pks, fmt.Sprintf("[%s]", v.ColumnName))
|
||||
}
|
||||
if i < len(columns)-1 {
|
||||
builder.WriteString(",")
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
// 设置主键
|
||||
if len(pks) > 0 {
|
||||
builder.WriteString(fmt.Sprintf("\tCONSTRAINT PK_%s PRIMARY KEY ( %s )", newTableName, strings.Join(pks, ",")))
|
||||
}
|
||||
builder.WriteString("\n);\n")
|
||||
|
||||
// 设置字段注释
|
||||
for _, v := range columns {
|
||||
if v.ColumnComment != "" {
|
||||
// 注释转义单引号
|
||||
v.ColumnComment = strings.ReplaceAll(v.ColumnComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s', N'COLUMN', N'%s';\n", v.ColumnComment, md.dc.Info.CurrentSchema(), newTableName, v.ColumnName))
|
||||
}
|
||||
}
|
||||
|
||||
// 设置索引
|
||||
indexs, err := md.GetTableIndex(tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, v := range indexs {
|
||||
builder.WriteString(fmt.Sprintf("\nCREATE NONCLUSTERED INDEX [%s] ON %s (%s);\n", v.IndexName, baseTable, v.ColumnName))
|
||||
// 设置索引注释
|
||||
if v.IndexComment != "" {
|
||||
// 注释转义单引号
|
||||
v.IndexComment = strings.ReplaceAll(v.IndexComment, "'", "\\'")
|
||||
commentBuilder.WriteString(fmt.Sprintf("\nEXEC sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE',N'%s', N'INDEX', N'%s';\n", v.IndexComment, md.dc.Info.CurrentSchema(), newTableName, v.IndexName))
|
||||
}
|
||||
}
|
||||
return builder.String() + commentBuilder.String(), nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (md *MssqlMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
return md.CopyTableDDL(tableName, "")
|
||||
}
|
||||
|
||||
func (md *MssqlMetaData) GetSchemas() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MSSQL_META_FILE, MSSQL_DB_SCHEMAS_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
schemas := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemas = append(schemas, anyx.ConvString(re["SCHEMA_NAME"]))
|
||||
}
|
||||
return schemas, nil
|
||||
}
|
||||
@@ -4,167 +4,18 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
MYSQL_META_FILE = "metasql/mysql_meta.sql"
|
||||
MYSQL_DBS = "MYSQL_DBS"
|
||||
MYSQL_TABLE_INFO_KEY = "MYSQL_TABLE_INFO"
|
||||
MYSQL_INDEX_INFO_KEY = "MYSQL_INDEX_INFO"
|
||||
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
|
||||
)
|
||||
|
||||
type MysqlDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (md *MysqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := md.dc.Query("SELECT VERSION() version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (md *MysqlDialect) GetDbNames() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_DBS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (md *MysqlDialect) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (md *MysqlDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := md.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := md.dc.Query(fmt.Sprintf(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
ColumnType: anyx.ConvString(re["columnType"]),
|
||||
ColumnComment: anyx.ConvString(re["columnComment"]),
|
||||
Nullable: anyx.ConvString(re["nullable"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["isPrimaryKey"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["isIdentity"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["columnDefault"]),
|
||||
NumScale: anyx.ConvString(re["numScale"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||
func (md *MysqlDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := md.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (md *MysqlDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (md *MysqlDialect) GetTableDDL(tableName string) (string, error) {
|
||||
_, res, err := md.dc.Query(fmt.Sprintf("show create table `%s` ", tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return anyx.ConvString(res[0]["Create Table"]) + ";", nil
|
||||
}
|
||||
|
||||
func (md *MysqlDialect) GetSchemas() ([]string, error) {
|
||||
return nil, nil
|
||||
func (md *MysqlDialect) GetMetaData() dbi.MetaData {
|
||||
return &MysqlMetaData{dc: md.dc}
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
|
||||
166
server/internal/db/dbm/mysql/metadata.go
Normal file
166
server/internal/db/dbm/mysql/metadata.go
Normal file
@@ -0,0 +1,166 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
MYSQL_META_FILE = "metasql/mysql_meta.sql"
|
||||
MYSQL_DBS = "MYSQL_DBS"
|
||||
MYSQL_TABLE_INFO_KEY = "MYSQL_TABLE_INFO"
|
||||
MYSQL_INDEX_INFO_KEY = "MYSQL_INDEX_INFO"
|
||||
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
|
||||
)
|
||||
|
||||
type MysqlMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (md *MysqlMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := md.dc.Query("SELECT VERSION() version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (md *MysqlMetaData) GetDbNames() ([]string, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_DBS))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (md *MysqlMetaData) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (md *MysqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := md.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := md.dc.Query(fmt.Sprintf(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
ColumnType: anyx.ConvString(re["columnType"]),
|
||||
ColumnComment: anyx.ConvString(re["columnComment"]),
|
||||
Nullable: anyx.ConvString(re["nullable"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["isPrimaryKey"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["isIdentity"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["columnDefault"]),
|
||||
NumScale: anyx.ConvString(re["numScale"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||
func (md *MysqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := md.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (md *MysqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := md.dc.Query(dbi.GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (md *MysqlMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
_, res, err := md.dc.Query(fmt.Sprintf("show create table `%s` ", tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return anyx.ConvString(res[0]["Create Table"]) + ";", nil
|
||||
}
|
||||
|
||||
func (md *MysqlMetaData) GetSchemas() ([]string, error) {
|
||||
return nil, errors.New("不支持schema")
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
@@ -15,255 +14,12 @@ import (
|
||||
_ "gitee.com/chunanyong/dm"
|
||||
)
|
||||
|
||||
// ---------------------------------- DM元数据 -----------------------------------
|
||||
const (
|
||||
ORACLE_META_FILE = "metasql/oracle_meta.sql"
|
||||
ORACLE_DB_SCHEMAS = "ORACLE_DB_SCHEMAS"
|
||||
ORACLE_TABLE_INFO_KEY = "ORACLE_TABLE_INFO"
|
||||
ORACLE_INDEX_INFO_KEY = "ORACLE_INDEX_INFO"
|
||||
ORACLE_COLUMN_MA_KEY = "ORACLE_COLUMN_MA"
|
||||
)
|
||||
|
||||
type OracleDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (od *OracleDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := od.dc.Query("select * from v$instance")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["VERSION"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (od *OracleDialect) GetDbNames() ([]string, error) {
|
||||
_, res, err := od.dc.Query("SELECT name AS DBNAME FROM v$database")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["DBNAME"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (od *OracleDialect) GetTables() ([]dbi.Table, error) {
|
||||
|
||||
// 首先执行更新统计信息sql 这个统计信息在数据量比较大的时候就比较耗时,所以最好定时执行
|
||||
// _, _, err := pd.dc.Query("dbms_stats.GATHER_SCHEMA_stats(SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))")
|
||||
|
||||
// 查询表信息
|
||||
_, res, err := od.dc.Query(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
TableComment: anyx.ConvString(re["TABLE_COMMENT"]),
|
||||
CreateTime: anyx.ConvString(re["CREATE_TIME"]),
|
||||
TableRows: anyx.ConvInt(re["TABLE_ROWS"]),
|
||||
DataLength: anyx.ConvInt64(re["DATA_LENGTH"]),
|
||||
IndexLength: anyx.ConvInt64(re["INDEX_LENGTH"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (od *OracleDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := od.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
// 如果表数量超过了1000,需要分批查询
|
||||
if len(tableNames) > 1000 {
|
||||
columns := make([]dbi.Column, 0)
|
||||
for i := 0; i < len(tableNames); i += 1000 {
|
||||
end := i + 1000
|
||||
if end > len(tableNames) {
|
||||
end = len(tableNames)
|
||||
}
|
||||
tables := tableNames[i:end]
|
||||
cols, err := od.GetColumns(tables...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns = append(columns, cols...)
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
_, res, err := od.dc.Query(fmt.Sprintf(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
defaultVal := anyx.ConvString(re["COLUMN_DEFAULT"])
|
||||
// 如果默认值包含.nextval,说明是序列,默认值为null
|
||||
if strings.Contains(defaultVal, ".nextval") {
|
||||
defaultVal = ""
|
||||
}
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ConvString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ConvString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: defaultVal,
|
||||
NumScale: anyx.ConvString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (od *OracleDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := od.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (od *OracleDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := od.dc.Query(fmt.Sprintf(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["INDEX_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
IndexType: anyx.ConvString(re["INDEX_TYPE"]),
|
||||
IndexComment: anyx.ConvString(re["INDEX_COMMENT"]),
|
||||
IsUnique: anyx.ConvInt(re["IS_UNIQUE"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["SEQ_IN_INDEX"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (od *OracleDialect) GetTableDDL(tableName string) (string, error) {
|
||||
ddlSql := fmt.Sprintf("SELECT DBMS_METADATA.GET_DDL('TABLE', '%s', (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)) AS TABLE_DDL FROM DUAL", tableName)
|
||||
_, res, err := od.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 建表ddl
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(anyx.ConvString(re["TABLE_DDL"]))
|
||||
}
|
||||
|
||||
// 表注释
|
||||
_, res, err = od.dc.Query(fmt.Sprintf(`
|
||||
select OWNER, COMMENTS from ALL_TAB_COMMENTS where TABLE_TYPE='TABLE' and TABLE_NAME = '%s'
|
||||
and owner = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual) `, tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
// COMMENT ON TABLE "SYS_MENU" IS '菜单表';
|
||||
if re["COMMENTS"] != nil {
|
||||
tableComment := fmt.Sprintf("\n\nCOMMENT ON TABLE \"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COMMENTS"].(string))
|
||||
builder.WriteString(tableComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
fieldSql := fmt.Sprintf(`
|
||||
SELECT OWNER, COLUMN_NAME, COMMENTS
|
||||
FROM ALL_COL_COMMENTS
|
||||
WHERE OWNER = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)
|
||||
AND TABLE_NAME = '%s'
|
||||
`, tableName)
|
||||
_, res, err = od.dc.Query(fieldSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString("\n")
|
||||
for _, re := range res {
|
||||
// COMMENT ON COLUMN "SYS_MENU"."BIZ_CODE" IS '业务编码,应用编码1';
|
||||
if re["COMMENTS"] != nil {
|
||||
fieldComment := fmt.Sprintf("\nCOMMENT ON COLUMN \"%s\".\"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COLUMN_NAME"].(string), re["COMMENTS"].(string))
|
||||
builder.WriteString(fieldComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 索引信息
|
||||
indexSql := fmt.Sprintf(`
|
||||
select DBMS_METADATA.GET_DDL('INDEX', a.INDEX_NAME, a.OWNER) AS INDEX_DEF from ALL_INDEXES a
|
||||
join ALL_objects b on a.owner = b.owner and b.object_name = a.index_name and b.object_type = 'INDEX'
|
||||
where a.owner = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)
|
||||
and a.table_name = '%s'
|
||||
`, tableName)
|
||||
_, res, err = od.dc.Query(indexSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
builder.WriteString("\n\n" + anyx.ConvString(re["INDEX_DEF"]))
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// 获取DM当前连接的库可访问的schemaNames
|
||||
func (od *OracleDialect) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_DB_SCHEMAS)
|
||||
_, res, err := od.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["USERNAME"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
func (od *OracleDialect) GetMetaData() dbi.MetaData {
|
||||
return &OracleMetaData{dc: od.dc}
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
@@ -296,7 +52,7 @@ func (od *OracleDialect) batchInsertSimple(dbType dbi.DbType, tableName string,
|
||||
ignore := ""
|
||||
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
||||
// 查出唯一索引涉及的字段
|
||||
indexs, _ := od.GetTableIndex(tableName)
|
||||
indexs, _ := od.GetMetaData().GetTableIndex(tableName)
|
||||
if indexs != nil {
|
||||
arr := make([]string, 0)
|
||||
for _, index := range indexs {
|
||||
@@ -336,7 +92,7 @@ func (od *OracleDialect) batchInsertMergeSql(dbType dbi.DbType, tableName string
|
||||
uniqueCols := make([]string, 0)
|
||||
caseSqls := make([]string, 0)
|
||||
// 查询唯一索引涉及到的字段,并组装到match条件内
|
||||
indexs, _ := od.GetTableIndex(tableName)
|
||||
indexs, _ := od.GetMetaData().GetTableIndex(tableName)
|
||||
if indexs != nil {
|
||||
for _, index := range indexs {
|
||||
if index.IsUnique {
|
||||
|
||||
261
server/internal/db/dbm/oracle/metadata.go
Normal file
261
server/internal/db/dbm/oracle/metadata.go
Normal file
@@ -0,0 +1,261 @@
|
||||
package oracle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// ---------------------------------- DM元数据 -----------------------------------
|
||||
const (
|
||||
ORACLE_META_FILE = "metasql/oracle_meta.sql"
|
||||
ORACLE_DB_SCHEMAS = "ORACLE_DB_SCHEMAS"
|
||||
ORACLE_TABLE_INFO_KEY = "ORACLE_TABLE_INFO"
|
||||
ORACLE_INDEX_INFO_KEY = "ORACLE_INDEX_INFO"
|
||||
ORACLE_COLUMN_MA_KEY = "ORACLE_COLUMN_MA"
|
||||
)
|
||||
|
||||
type OracleMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (od *OracleMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := od.dc.Query("select * from v$instance")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["VERSION"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (od *OracleMetaData) GetDbNames() ([]string, error) {
|
||||
_, res, err := od.dc.Query("SELECT name AS DBNAME FROM v$database")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["DBNAME"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (od *OracleMetaData) GetTables() ([]dbi.Table, error) {
|
||||
|
||||
// 首先执行更新统计信息sql 这个统计信息在数据量比较大的时候就比较耗时,所以最好定时执行
|
||||
// _, _, err := pd.dc.Query("dbms_stats.GATHER_SCHEMA_stats(SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))")
|
||||
|
||||
// 查询表信息
|
||||
_, res, err := od.dc.Query(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
TableComment: anyx.ConvString(re["TABLE_COMMENT"]),
|
||||
CreateTime: anyx.ConvString(re["CREATE_TIME"]),
|
||||
TableRows: anyx.ConvInt(re["TABLE_ROWS"]),
|
||||
DataLength: anyx.ConvInt64(re["DATA_LENGTH"]),
|
||||
IndexLength: anyx.ConvInt64(re["INDEX_LENGTH"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (od *OracleMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := od.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
// 如果表数量超过了1000,需要分批查询
|
||||
if len(tableNames) > 1000 {
|
||||
columns := make([]dbi.Column, 0)
|
||||
for i := 0; i < len(tableNames); i += 1000 {
|
||||
end := i + 1000
|
||||
if end > len(tableNames) {
|
||||
end = len(tableNames)
|
||||
}
|
||||
tables := tableNames[i:end]
|
||||
cols, err := od.GetColumns(tables...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
columns = append(columns, cols...)
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
_, res, err := od.dc.Query(fmt.Sprintf(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
defaultVal := anyx.ConvString(re["COLUMN_DEFAULT"])
|
||||
// 如果默认值包含.nextval,说明是序列,默认值为null
|
||||
if strings.Contains(defaultVal, ".nextval") {
|
||||
defaultVal = ""
|
||||
}
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["TABLE_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
|
||||
ColumnComment: anyx.ConvString(re["COLUMN_COMMENT"]),
|
||||
Nullable: anyx.ConvString(re["NULLABLE"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["IS_PRIMARY_KEY"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["IS_IDENTITY"]) == 1,
|
||||
ColumnDefault: defaultVal,
|
||||
NumScale: anyx.ConvString(re["NUM_SCALE"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (od *OracleMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := od.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (od *OracleMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := od.dc.Query(fmt.Sprintf(dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["INDEX_NAME"]),
|
||||
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
|
||||
IndexType: anyx.ConvString(re["INDEX_TYPE"]),
|
||||
IndexComment: anyx.ConvString(re["INDEX_COMMENT"]),
|
||||
IsUnique: anyx.ConvInt(re["IS_UNIQUE"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["SEQ_IN_INDEX"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (od *OracleMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
ddlSql := fmt.Sprintf("SELECT DBMS_METADATA.GET_DDL('TABLE', '%s', (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)) AS TABLE_DDL FROM DUAL", tableName)
|
||||
_, res, err := od.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
// 建表ddl
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(anyx.ConvString(re["TABLE_DDL"]))
|
||||
}
|
||||
|
||||
// 表注释
|
||||
_, res, err = od.dc.Query(fmt.Sprintf(`
|
||||
select OWNER, COMMENTS from ALL_TAB_COMMENTS where TABLE_TYPE='TABLE' and TABLE_NAME = '%s'
|
||||
and owner = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual) `, tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
// COMMENT ON TABLE "SYS_MENU" IS '菜单表';
|
||||
if re["COMMENTS"] != nil {
|
||||
tableComment := fmt.Sprintf("\n\nCOMMENT ON TABLE \"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COMMENTS"].(string))
|
||||
builder.WriteString(tableComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
fieldSql := fmt.Sprintf(`
|
||||
SELECT OWNER, COLUMN_NAME, COMMENTS
|
||||
FROM ALL_COL_COMMENTS
|
||||
WHERE OWNER = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)
|
||||
AND TABLE_NAME = '%s'
|
||||
`, tableName)
|
||||
_, res, err = od.dc.Query(fieldSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
builder.WriteString("\n")
|
||||
for _, re := range res {
|
||||
// COMMENT ON COLUMN "SYS_MENU"."BIZ_CODE" IS '业务编码,应用编码1';
|
||||
if re["COMMENTS"] != nil {
|
||||
fieldComment := fmt.Sprintf("\nCOMMENT ON COLUMN \"%s\".\"%s\".\"%s\" IS '%s';", re["OWNER"].(string), tableName, re["COLUMN_NAME"].(string), re["COMMENTS"].(string))
|
||||
builder.WriteString(fieldComment)
|
||||
}
|
||||
}
|
||||
|
||||
// 索引信息
|
||||
indexSql := fmt.Sprintf(`
|
||||
select DBMS_METADATA.GET_DDL('INDEX', a.INDEX_NAME, a.OWNER) AS INDEX_DEF from ALL_INDEXES a
|
||||
join ALL_objects b on a.owner = b.owner and b.object_name = a.index_name and b.object_type = 'INDEX'
|
||||
where a.owner = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)
|
||||
and a.table_name = '%s'
|
||||
`, tableName)
|
||||
_, res, err = od.dc.Query(indexSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
builder.WriteString("\n\n" + anyx.ConvString(re["INDEX_DEF"]))
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// 获取DM当前连接的库可访问的schemaNames
|
||||
func (od *OracleMetaData) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(ORACLE_META_FILE, ORACLE_DB_SCHEMAS)
|
||||
_, res, err := od.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["USERNAME"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"regexp"
|
||||
@@ -12,176 +11,12 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
PGSQL_META_FILE = "metasql/pgsql_meta.sql"
|
||||
PGSQL_DB_SCHEMAS = "PGSQL_DB_SCHEMAS"
|
||||
PGSQL_TABLE_INFO_KEY = "PGSQL_TABLE_INFO"
|
||||
PGSQL_INDEX_INFO_KEY = "PGSQL_INDEX_INFO"
|
||||
PGSQL_COLUMN_MA_KEY = "PGSQL_COLUMN_MA"
|
||||
PGSQL_TABLE_DDL_KEY = "PGSQL_TABLE_DDL_FUNC"
|
||||
)
|
||||
|
||||
type PgsqlDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (pd *PgsqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := pd.dc.Query("SELECT version() as server_version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["server_version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (pd *PgsqlDialect) GetDbNames() ([]string, error) {
|
||||
_, res, err := pd.dc.Query("SELECT datname AS dbname FROM pg_database WHERE datistemplate = false AND has_database_privilege(datname, 'CONNECT')")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (pd *PgsqlDialect) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := pd.dc.Query(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: re["tableName"].(string),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (pd *PgsqlDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := pd.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := pd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
ColumnType: anyx.ConvString(re["columnType"]),
|
||||
ColumnComment: anyx.ConvString(re["columnComment"]),
|
||||
Nullable: anyx.ConvString(re["nullable"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["isPrimaryKey"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["isIdentity"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["columnDefault"]),
|
||||
NumScale: anyx.ConvString(re["numScale"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (pd *PgsqlDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := pd.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (pd *PgsqlDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := pd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["IndexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (pd *PgsqlDialect) GetTableDDL(tableName string) (string, error) {
|
||||
_, err := pd.dc.Exec(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_DDL_KEY))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", pd.currentSchema(), tableName)
|
||||
_, res, err := pd.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res[0]["sql"].(string), nil
|
||||
}
|
||||
|
||||
// 获取pgsql当前连接的库可访问的schemaNames
|
||||
func (pd *PgsqlDialect) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_DB_SCHEMAS)
|
||||
_, res, err := pd.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["schemaName"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
func (pd *PgsqlDialect) GetMetaData() dbi.MetaData {
|
||||
return &PgsqlMetaData{dc: pd.dc}
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
@@ -261,7 +96,7 @@ func (pd PgsqlDialect) gaussOnDuplicateStrategySql(duplicateStrategy int, tableN
|
||||
|
||||
// 查出表里的唯一键涉及的字段
|
||||
var uniqueColumns []string
|
||||
indexs, err := pd.GetTableIndex(tableName)
|
||||
indexs, err := pd.GetMetaData().GetTableIndex(tableName)
|
||||
if err == nil {
|
||||
for _, index := range indexs {
|
||||
if index.IsUnique {
|
||||
|
||||
182
server/internal/db/dbm/postgres/metadata.go
Normal file
182
server/internal/db/dbm/postgres/metadata.go
Normal file
@@ -0,0 +1,182 @@
|
||||
package postgres
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/errorx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"mayfly-go/pkg/utils/collx"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PGSQL_META_FILE = "metasql/pgsql_meta.sql"
|
||||
PGSQL_DB_SCHEMAS = "PGSQL_DB_SCHEMAS"
|
||||
PGSQL_TABLE_INFO_KEY = "PGSQL_TABLE_INFO"
|
||||
PGSQL_INDEX_INFO_KEY = "PGSQL_INDEX_INFO"
|
||||
PGSQL_COLUMN_MA_KEY = "PGSQL_COLUMN_MA"
|
||||
PGSQL_TABLE_DDL_KEY = "PGSQL_TABLE_DDL_FUNC"
|
||||
)
|
||||
|
||||
type PgsqlMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (pd *PgsqlMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := pd.dc.Query("SELECT version() as server_version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["server_version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (pd *PgsqlMetaData) GetDbNames() ([]string, error) {
|
||||
_, res, err := pd.dc.Query("SELECT datname AS dbname FROM pg_database WHERE datistemplate = false AND has_database_privilege(datname, 'CONNECT')")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databases := make([]string, 0)
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (pd *PgsqlMetaData) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := pd.dc.Query(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: re["tableName"].(string),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (pd *PgsqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
dbType := pd.dc.Info.Type
|
||||
tableName := strings.Join(collx.ArrayMap[string, string](tableNames, func(val string) string {
|
||||
return fmt.Sprintf("'%s'", dbType.RemoveQuote(val))
|
||||
}), ",")
|
||||
|
||||
_, res, err := pd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_COLUMN_MA_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
for _, re := range res {
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
ColumnType: anyx.ConvString(re["columnType"]),
|
||||
ColumnComment: anyx.ConvString(re["columnComment"]),
|
||||
Nullable: anyx.ConvString(re["nullable"]),
|
||||
IsPrimaryKey: anyx.ConvInt(re["isPrimaryKey"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["isIdentity"]) == 1,
|
||||
ColumnDefault: anyx.ConvString(re["columnDefault"]),
|
||||
NumScale: anyx.ConvString(re["numScale"]),
|
||||
})
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (pd *PgsqlMetaData) GetPrimaryKey(tablename string) (string, error) {
|
||||
columns, err := pd.GetColumns(tablename)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(columns) == 0 {
|
||||
return "", errorx.NewBiz("[%s] 表不存在", tablename)
|
||||
}
|
||||
for _, v := range columns {
|
||||
if v.IsPrimaryKey {
|
||||
return v.ColumnName, nil
|
||||
}
|
||||
}
|
||||
|
||||
return columns[0].ColumnName, nil
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (pd *PgsqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := pd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: anyx.ConvString(re["columnName"]),
|
||||
IndexType: anyx.ConvString(re["IndexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: anyx.ConvInt(re["isUnique"]) == 1,
|
||||
SeqInIndex: anyx.ConvInt(re["seqInIndex"]),
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
result := make([]dbi.Index, 0)
|
||||
key := ""
|
||||
for _, v := range indexs {
|
||||
// 当前的索引名
|
||||
in := v.IndexName
|
||||
if key == in {
|
||||
// 索引字段已根据名称和顺序排序,故取最后一个即可
|
||||
i := len(result) - 1
|
||||
// 同索引字段以逗号连接
|
||||
result[i].ColumnName = result[i].ColumnName + "," + v.ColumnName
|
||||
} else {
|
||||
key = in
|
||||
result = append(result, v)
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (pd *PgsqlMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
_, err := pd.dc.Exec(dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_DDL_KEY))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", pd.dc.Info.CurrentSchema(), tableName)
|
||||
_, res, err := pd.dc.Query(ddlSql)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return res[0]["sql"].(string), nil
|
||||
}
|
||||
|
||||
// 获取pgsql当前连接的库可访问的schemaNames
|
||||
func (pd *PgsqlMetaData) GetSchemas() ([]string, error) {
|
||||
sql := dbi.GetLocalSql(PGSQL_META_FILE, PGSQL_DB_SCHEMAS)
|
||||
_, res, err := pd.dc.Query(sql)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["schemaName"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
}
|
||||
@@ -2,181 +2,20 @@ package sqlite
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
SQLITE_META_FILE = "metasql/sqlite_meta.sql"
|
||||
SQLITE_TABLE_INFO_KEY = "SQLITE_TABLE_INFO"
|
||||
SQLITE_INDEX_INFO_KEY = "SQLITE_INDEX_INFO"
|
||||
)
|
||||
|
||||
type SqliteDialect struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (sd *SqliteDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := sd.dc.Query("SELECT SQLITE_VERSION() as version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (sd *SqliteDialect) GetDbNames() ([]string, error) {
|
||||
databases := make([]string, 0)
|
||||
_, res, err := sd.dc.Query("PRAGMA database_list")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["name"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (sd *SqliteDialect) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := sd.dc.Query(dbi.GetLocalSql(SQLITE_META_FILE, SQLITE_TABLE_INFO_KEY))
|
||||
//cols, res, err := sd.dc.Query("SELECT datetime(1092941466, 'unixepoch')")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (sd *SqliteDialect) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
|
||||
for i := 0; i < len(tableNames); i++ {
|
||||
tableName := tableNames[i]
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
||||
if err != nil {
|
||||
logx.Error("获取数据库表字段结构出错", err.Error())
|
||||
continue
|
||||
}
|
||||
for _, re := range res {
|
||||
nullable := "YES"
|
||||
if anyx.ConvInt(re["notnull"]) == 1 {
|
||||
nullable = "NO"
|
||||
}
|
||||
// 去掉默认值的引号
|
||||
defaultValue := anyx.ConvString(re["dflt_value"])
|
||||
if strings.Contains(defaultValue, "'") {
|
||||
defaultValue = strings.ReplaceAll(defaultValue, "'", "")
|
||||
}
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: tableName,
|
||||
ColumnName: anyx.ConvString(re["name"]),
|
||||
ColumnType: strings.ToLower(anyx.ConvString(re["type"])),
|
||||
ColumnComment: "",
|
||||
Nullable: nullable,
|
||||
IsPrimaryKey: anyx.ConvInt(re["pk"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["pk"]) == 1,
|
||||
ColumnDefault: defaultValue,
|
||||
NumScale: "0",
|
||||
})
|
||||
}
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (sd *SqliteDialect) GetPrimaryKey(tableName string) (string, error) {
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
if anyx.ConvInt(re["pk"]) == 1 {
|
||||
return anyx.ConvString(re["name"]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("不存在主键")
|
||||
}
|
||||
|
||||
// 解析索引创建语句以获取字段信息
|
||||
func extractIndexFields(indexSQL string) string {
|
||||
// 使用正则表达式提取字段信息
|
||||
re := regexp.MustCompile(`\((.*?)\)`)
|
||||
match := re.FindStringSubmatch(indexSQL)
|
||||
if len(match) > 1 {
|
||||
fields := strings.Split(match[1], ",")
|
||||
for i, field := range fields {
|
||||
// 去除空格
|
||||
fields[i] = strings.TrimSpace(field)
|
||||
}
|
||||
return strings.Join(fields, ",")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (sd *SqliteDialect) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(SQLITE_META_FILE, SQLITE_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexSql := anyx.ConvString(re["indexSql"])
|
||||
isUnique := strings.Contains(indexSql, "CREATE UNIQUE INDEX")
|
||||
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: extractIndexFields(indexSql),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: isUnique,
|
||||
SeqInIndex: 1,
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
return indexs, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (sd *SqliteDialect) GetTableDDL(tableName string) (string, error) {
|
||||
_, res, err := sd.dc.Query("select sql from sqlite_master WHERE tbl_name=? order by type desc", tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(anyx.ConvString(re["sql"]) + "; \n\n")
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func (sd *SqliteDialect) GetSchemas() ([]string, error) {
|
||||
return nil, nil
|
||||
func (sd *SqliteDialect) GetMetaData() dbi.MetaData {
|
||||
return &SqliteMetaData{dc: sd.dc}
|
||||
}
|
||||
|
||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||
@@ -267,7 +106,7 @@ func (sd *SqliteDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||
|
||||
// 生成新表名,为老表明+_copy_时间戳
|
||||
newTableName := tableName + "_copy_" + time.Now().Format("20060102150405")
|
||||
ddl, err := sd.GetTableDDL(tableName)
|
||||
ddl, err := sd.GetMetaData().GetTableDDL(tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
178
server/internal/db/dbm/sqlite/metadata.go
Normal file
178
server/internal/db/dbm/sqlite/metadata.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package sqlite
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"mayfly-go/internal/db/dbm/dbi"
|
||||
"mayfly-go/pkg/logx"
|
||||
"mayfly-go/pkg/utils/anyx"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
SQLITE_META_FILE = "metasql/sqlite_meta.sql"
|
||||
SQLITE_TABLE_INFO_KEY = "SQLITE_TABLE_INFO"
|
||||
SQLITE_INDEX_INFO_KEY = "SQLITE_INDEX_INFO"
|
||||
)
|
||||
|
||||
type SqliteMetaData struct {
|
||||
dc *dbi.DbConn
|
||||
}
|
||||
|
||||
func (sd *SqliteMetaData) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := sd.dc.Query("SELECT SQLITE_VERSION() as version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ds := &dbi.DbServer{
|
||||
Version: anyx.ConvString(res[0]["version"]),
|
||||
}
|
||||
return ds, nil
|
||||
}
|
||||
|
||||
func (sd *SqliteMetaData) GetDbNames() ([]string, error) {
|
||||
databases := make([]string, 0)
|
||||
_, res, err := sd.dc.Query("PRAGMA database_list")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, re := range res {
|
||||
databases = append(databases, anyx.ConvString(re["name"]))
|
||||
}
|
||||
|
||||
return databases, nil
|
||||
}
|
||||
|
||||
// 获取表基础元信息, 如表名等
|
||||
func (sd *SqliteMetaData) GetTables() ([]dbi.Table, error) {
|
||||
_, res, err := sd.dc.Query(dbi.GetLocalSql(SQLITE_META_FILE, SQLITE_TABLE_INFO_KEY))
|
||||
//cols, res, err := sd.dc.Query("SELECT datetime(1092941466, 'unixepoch')")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tables := make([]dbi.Table, 0)
|
||||
for _, re := range res {
|
||||
tables = append(tables, dbi.Table{
|
||||
TableName: anyx.ConvString(re["tableName"]),
|
||||
TableComment: anyx.ConvString(re["tableComment"]),
|
||||
CreateTime: anyx.ConvString(re["createTime"]),
|
||||
TableRows: anyx.ConvInt(re["tableRows"]),
|
||||
DataLength: anyx.ConvInt64(re["dataLength"]),
|
||||
IndexLength: anyx.ConvInt64(re["indexLength"]),
|
||||
})
|
||||
}
|
||||
return tables, nil
|
||||
}
|
||||
|
||||
// 获取列元信息, 如列名等
|
||||
func (sd *SqliteMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
|
||||
|
||||
columns := make([]dbi.Column, 0)
|
||||
|
||||
for i := 0; i < len(tableNames); i++ {
|
||||
tableName := tableNames[i]
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
||||
if err != nil {
|
||||
logx.Error("获取数据库表字段结构出错", err.Error())
|
||||
continue
|
||||
}
|
||||
for _, re := range res {
|
||||
nullable := "YES"
|
||||
if anyx.ConvInt(re["notnull"]) == 1 {
|
||||
nullable = "NO"
|
||||
}
|
||||
// 去掉默认值的引号
|
||||
defaultValue := anyx.ConvString(re["dflt_value"])
|
||||
if strings.Contains(defaultValue, "'") {
|
||||
defaultValue = strings.ReplaceAll(defaultValue, "'", "")
|
||||
}
|
||||
columns = append(columns, dbi.Column{
|
||||
TableName: tableName,
|
||||
ColumnName: anyx.ConvString(re["name"]),
|
||||
ColumnType: strings.ToLower(anyx.ConvString(re["type"])),
|
||||
ColumnComment: "",
|
||||
Nullable: nullable,
|
||||
IsPrimaryKey: anyx.ConvInt(re["pk"]) == 1,
|
||||
IsIdentity: anyx.ConvInt(re["pk"]) == 1,
|
||||
ColumnDefault: defaultValue,
|
||||
NumScale: "0",
|
||||
})
|
||||
}
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
func (sd *SqliteMetaData) GetPrimaryKey(tableName string) (string, error) {
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf("PRAGMA table_info(%s)", tableName))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
for _, re := range res {
|
||||
if anyx.ConvInt(re["pk"]) == 1 {
|
||||
return anyx.ConvString(re["name"]), nil
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("不存在主键")
|
||||
}
|
||||
|
||||
// 解析索引创建语句以获取字段信息
|
||||
func extractIndexFields(indexSQL string) string {
|
||||
// 使用正则表达式提取字段信息
|
||||
re := regexp.MustCompile(`\((.*?)\)`)
|
||||
match := re.FindStringSubmatch(indexSQL)
|
||||
if len(match) > 1 {
|
||||
fields := strings.Split(match[1], ",")
|
||||
for i, field := range fields {
|
||||
// 去除空格
|
||||
fields[i] = strings.TrimSpace(field)
|
||||
}
|
||||
return strings.Join(fields, ",")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// 获取表索引信息
|
||||
func (sd *SqliteMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
|
||||
_, res, err := sd.dc.Query(fmt.Sprintf(dbi.GetLocalSql(SQLITE_META_FILE, SQLITE_INDEX_INFO_KEY), tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
indexs := make([]dbi.Index, 0)
|
||||
for _, re := range res {
|
||||
indexSql := anyx.ConvString(re["indexSql"])
|
||||
isUnique := strings.Contains(indexSql, "CREATE UNIQUE INDEX")
|
||||
|
||||
indexs = append(indexs, dbi.Index{
|
||||
IndexName: anyx.ConvString(re["indexName"]),
|
||||
ColumnName: extractIndexFields(indexSql),
|
||||
IndexType: anyx.ConvString(re["indexType"]),
|
||||
IndexComment: anyx.ConvString(re["indexComment"]),
|
||||
IsUnique: isUnique,
|
||||
SeqInIndex: 1,
|
||||
})
|
||||
}
|
||||
// 把查询结果以索引名分组,索引字段以逗号连接
|
||||
return indexs, nil
|
||||
}
|
||||
|
||||
// 获取建表ddl
|
||||
func (sd *SqliteMetaData) GetTableDDL(tableName string) (string, error) {
|
||||
_, res, err := sd.dc.Query("select sql from sqlite_master WHERE tbl_name=? order by type desc", tableName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var builder strings.Builder
|
||||
for _, re := range res {
|
||||
builder.WriteString(anyx.ConvString(re["sql"]) + "; \n\n")
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
func (sd *SqliteMetaData) GetSchemas() ([]string, error) {
|
||||
return nil, nil
|
||||
}
|
||||
@@ -5,7 +5,7 @@ type ProcinstQuery struct {
|
||||
ProcdefName string `json:"procdefName"` // 流程定义名称
|
||||
|
||||
BizType string `json:"bizType" form:"bizType"` // 业务类型
|
||||
BizKey string `json:"bizKey"` // 业务key
|
||||
BizKey string `json:"bizKey" form:"bizKey"` // 业务key
|
||||
Status ProcinstStatus `json:"status" form:"status"` // 状态
|
||||
|
||||
CreatorId uint64
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -10,7 +10,7 @@ CREATE TABLE `t_db_instance` (
|
||||
`name` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库实例名称',
|
||||
`host` varchar(100) COLLATE utf8mb4_bin NOT NULL,
|
||||
`port` int(8) NULL,
|
||||
`sid` varchar(255) NULL COMMENT 'oracle数据库需要sid',
|
||||
`extra` varchar(255) NULL COMMENT '连接需要的额外参数,如oracle数据库需要sid等',
|
||||
`username` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||
`type` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '数据库实例类型(mysql...)',
|
||||
@@ -634,12 +634,11 @@ INSERT INTO `t_sys_config` (name, `key`, params, value, remark, create_time, cre
|
||||
INSERT INTO `t_sys_config` (name, `key`, params, value, remark, permission, create_time, creator_id, creator, update_time, modifier_id, modifier, is_deleted, delete_time) VALUES('oauth2登录配置', 'Oauth2Login', '[{"name":"是否启用","model":"enable","placeholder":"是否启用oauth2登录","options":"true,false"},{"name":"名称","model":"name","placeholder":"oauth2名称"},{"name":"Client ID","model":"clientId","placeholder":"Client ID"},{"name":"Client Secret","model":"clientSecret","placeholder":"Client Secret"},{"name":"Authorization URL","model":"authorizationURL","placeholder":"Authorization URL"},{"name":"AccessToken URL","model":"accessTokenURL","placeholder":"AccessToken URL"},{"name":"Redirect URL","model":"redirectURL","placeholder":"本系统地址"},{"name":"Scopes","model":"scopes","placeholder":"Scopes"},{"name":"Resource URL","model":"resourceURL","placeholder":"获取用户信息资源地址"},{"name":"UserIdentifier","model":"userIdentifier","placeholder":"用户唯一标识字段;格式为type:fieldPath(string:username)"},{"name":"是否自动注册","model":"autoRegister","placeholder":"","options":"true,false"}]', '', 'oauth2登录相关配置信息', 'admin,', '2023-07-22 13:58:51', 1, 'admin', '2023-07-22 19:34:37', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (name, `key`, params, value, remark, permission, create_time, creator_id, creator, update_time, modifier_id, modifier, is_deleted, delete_time) VALUES('ldap登录配置', 'LdapLogin', '[{"name":"是否启用","model":"enable","placeholder":"是否启用","options":"true,false"},{"name":"host","model":"host","placeholder":"host"},{"name":"port","model":"port","placeholder":"port"},{"name":"bindDN","model":"bindDN","placeholder":"LDAP 服务的管理员账号,如: \\"cn=admin,dc=example,dc=com\\""},{"name":"bindPwd","model":"bindPwd","placeholder":"LDAP 服务的管理员密码"},{"name":"baseDN","model":"baseDN","placeholder":"用户所在的 base DN, 如: \\"ou=users,dc=example,dc=com\\""},{"name":"userFilter","model":"userFilter","placeholder":"过滤用户的方式, 如: \\"(uid=%s)、(&(objectClass=organizationalPerson)(uid=%s))\\""},{"name":"uidMap","model":"uidMap","placeholder":"用户id和 LDAP 字段名之间的映射关系,如: cn"},{"name":"udnMap","model":"udnMap","placeholder":"用户姓名(dispalyName)和 LDAP 字段名之间的映射关系,如: displayName"},{"name":"emailMap","model":"emailMap","placeholder":"用户email和 LDAP 字段名之间的映射关系"},{"name":"skipTLSVerify","model":"skipTLSVerify","placeholder":"客户端是否跳过 TLS 证书验证","options":"true,false"},{"name":"安全协议","model":"securityProtocol","placeholder":"安全协议(为Null不使用安全协议),如: StartTLS, LDAPS","options":"Null,StartTLS,LDAPS"}]', '', 'ldap登录相关配置', 'admin,', '2023-08-25 21:47:20', 1, 'admin', '2023-08-25 22:56:07', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('系统全局样式设置', 'SysStyleConfig', '[{"model":"logoIcon","name":"logo图标","placeholder":"系统logo图标(base64编码, 建议svg格式,不超过10k)","required":false},{"model":"title","name":"菜单栏标题","placeholder":"系统菜单栏标题展示","required":false},{"model":"viceTitle","name":"登录页标题","placeholder":"登录页标题展示","required":false},{"model":"useWatermark","name":"是否启用水印","placeholder":"是否启用系统水印","options":"true,false","required":false},{"model":"watermarkContent","name":"水印补充信息","placeholder":"额外水印信息","required":false}]', '{"title":"mayfly-go","viceTitle":"mayfly-go","logoIcon":"","useWatermark":"true","watermarkContent":""}', '系统icon、标题、水印信息等配置', 'all', '2024-01-04 15:17:18', 1, 'admin', '2024-01-05 09:40:44', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (name, `key`, params, value, remark, create_time, creator_id, creator, update_time, modifier_id, modifier)VALUES ('数据库查询最大结果集', 'DbQueryMaxCount', '[]', '0', '允许sql查询的最大结果集数。注: 0=不限制', '2023-02-11 14:29:03', 1, 'admin', '2023-02-11 14:40:56', 1, 'admin');
|
||||
INSERT INTO `t_sys_config` (name, `key`, params, value, remark, create_time, creator_id, creator, update_time, modifier_id, modifier)VALUES ('数据库是否记录查询SQL', 'DbSaveQuerySQL', '[]', '0', '1: 记录、0:不记录', '2023-02-11 16:07:14', 1, 'admin', '2023-02-11 16:44:17', 1, 'admin');
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('机器相关配置', 'MachineConfig', '[{"name":"终端回放存储路径","model":"terminalRecPath","placeholder":"终端回放存储路径"},{"name":"uploadMaxFileSize","model":"uploadMaxFileSize","placeholder":"允许上传的最大文件大小(1MB\\2GB等)"},{"model":"termOpSaveDays","name":"终端记录保存时间","placeholder":"终端记录保存时间(单位天)"}]', '{"terminalRecPath":"./rec","uploadMaxFileSize":"100MB"}', '机器相关配置,如终端回放路径等', 'all', '2023-07-13 16:26:44', 1, 'admin', '2024-01-15 16:30:22', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('数据库备份恢复', 'DbBackupRestore', '[{"model":"backupPath","name":"备份路径","placeholder":"备份文件存储路径"}]', '{"backupPath":"./db/backup"}', '', 'admin,', '2023-12-29 09:55:26', 1, 'admin', '2023-12-29 15:45:24', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('Mysql可执行文件', 'MysqlBin', '[{"model":"path","name":"路径","placeholder":"可执行文件路径","required":true},{"model":"mysql","name":"mysql","placeholder":"mysql命令路径(空则为 路径/mysql)","required":false},{"model":"mysqldump","name":"mysqldump","placeholder":"mysqldump命令路径(空则为 路径/mysqldump)","required":false},{"model":"mysqlbinlog","name":"mysqlbinlog","placeholder":"mysqlbinlog命令路径(空则为 路径/mysqlbinlog)","required":false}]', '{"mysql":"","mysqldump":"","mysqlbinlog":"","path":"./db/mysql/bin"}', '', 'admin,', '2023-12-29 10:01:33', 1, 'admin', '2023-12-29 13:34:40', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('MariaDB可执行文件', 'MariadbBin', '[{"model":"path","name":"路径","placeholder":"可执行文件路径","required":true},{"model":"mysql","name":"mysql","placeholder":"mysql命令路径(空则为 路径/mysql)","required":false},{"model":"mysqldump","name":"mysqldump","placeholder":"mysqldump命令路径(空则为 路径/mysqldump)","required":false},{"model":"mysqlbinlog","name":"mysqlbinlog","placeholder":"mysqlbinlog命令路径(空则为 路径/mysqlbinlog)","required":false}]', '{"mysql":"","mysqldump":"","mysqlbinlog":"","path":"./db/mariadb/bin"}', '', 'admin,', '2023-12-29 10:01:33', 1, 'admin', '2023-12-29 13:34:40', 1, 'admin', 0, NULL);
|
||||
INSERT INTO `t_sys_config` (`name`, `key`, `params`, `value`, `remark`, `permission`, `create_time`, `creator_id`, `creator`, `update_time`, `modifier_id`, `modifier`, `is_deleted`, `delete_time`) VALUES('DBMS配置', 'DbmsConfig', '[{"model":"querySqlSave","name":"记录查询sql","placeholder":"是否记录查询类sql","options":"true,false"},{"model":"maxResultSet","name":"最大结果集","placeholder":"允许sql查询的最大结果集数。注: 0=不限制","options":""},{"model":"sqlExecTl","name":"sql执行时间限制","placeholder":"超过该时间(单位:秒),执行将被取消"}]', '{"querySqlSave":"false","maxResultSet":"0","sqlExecTl":"60"}', 'DBMS相关配置', 'admin,', '2024-03-06 13:30:51', 1, 'admin', '2024-03-06 14:07:16', 1, 'admin', 0, NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
||||
Reference in New Issue
Block a user