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",
|
"countup.js": "^2.8.0",
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.6.0",
|
"element-plus": "^2.6.1",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.46.0",
|
"monaco-editor": "^0.47.0",
|
||||||
"monaco-sql-languages": "^0.11.0",
|
"monaco-sql-languages": "^0.11.0",
|
||||||
"monaco-themes": "^0.4.4",
|
"monaco-themes": "^0.4.4",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
:zIndex="10000000"
|
:zIndex="10000000"
|
||||||
:width="210"
|
:width="210"
|
||||||
v-if="themeConfig.isWatermark"
|
v-if="themeConfig.isWatermark"
|
||||||
:font="{ color: 'rgba(180, 180, 180, 0.5)' }"
|
:font="{ color: 'rgba(180, 180, 180, 0.3)' }"
|
||||||
:content="themeConfig.watermarkText"
|
:content="themeConfig.watermarkText"
|
||||||
class="h100"
|
class="h100"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -50,11 +50,17 @@ import { FlowBizType, ProcinstBizStatus, ProcinstStatus } from './enums';
|
|||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { formatTime } from '@/common/utils/format';
|
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 = [
|
const columns = [
|
||||||
TableColumn.new('bizType', '业务').typeTag(FlowBizType),
|
TableColumn.new('bizType', '业务').typeTag(FlowBizType),
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
TableColumn.new('creator', '发起人'),
|
TableColumn.new('creator', '发起人'),
|
||||||
|
TableColumn.new('bizKey', '业务key'),
|
||||||
TableColumn.new('procdefName', '流程名'),
|
TableColumn.new('procdefName', '流程名'),
|
||||||
TableColumn.new('status', '流程状态').typeTag(ProcinstStatus),
|
TableColumn.new('status', '流程状态').typeTag(ProcinstStatus),
|
||||||
TableColumn.new('bizStatus', '业务状态').typeTag(ProcinstBizStatus),
|
TableColumn.new('bizStatus', '业务状态').typeTag(ProcinstBizStatus),
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ const columns = [
|
|||||||
TableColumn.new('procinst.creator', '发起人'),
|
TableColumn.new('procinst.creator', '发起人'),
|
||||||
TableColumn.new('procinst.status', '流程状态').typeTag(ProcinstStatus),
|
TableColumn.new('procinst.status', '流程状态').typeTag(ProcinstStatus),
|
||||||
TableColumn.new('status', '任务状态').typeTag(ProcinstTaskStatus),
|
TableColumn.new('status', '任务状态').typeTag(ProcinstTaskStatus),
|
||||||
|
TableColumn.new('procinst.bizKey', '业务key'),
|
||||||
TableColumn.new('procinst.procdefName', '流程名'),
|
TableColumn.new('procinst.procdefName', '流程名'),
|
||||||
TableColumn.new('taskName', '当前节点'),
|
TableColumn.new('taskName', '当前节点'),
|
||||||
TableColumn.new('procinst.createTime', '发起时间').isTime(),
|
TableColumn.new('procinst.createTime', '发起时间').isTime(),
|
||||||
|
|||||||
@@ -332,7 +332,7 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
|||||||
|
|
||||||
dbMeta := dbConn.GetDialect()
|
dbMeta := dbConn.GetDialect()
|
||||||
if len(tables) == 0 {
|
if len(tables) == 0 {
|
||||||
ti, err := dbMeta.GetTables()
|
ti, err := dbMeta.GetMetaData().GetTables()
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
tables = make([]string, len(ti))
|
tables = make([]string, len(ti))
|
||||||
for i, table := range ti {
|
for i, table := range ti {
|
||||||
@@ -346,7 +346,7 @@ func (d *Db) dumpDb(ctx context.Context, writer *gzipWriter, dbId uint64, dbName
|
|||||||
if needStruct {
|
if needStruct {
|
||||||
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
|
writer.WriteString(fmt.Sprintf("\n-- ----------------------------\n-- 表结构: %s \n-- ----------------------------\n", table))
|
||||||
writer.WriteString(fmt.Sprintf("DROP TABLE IF EXISTS %s;\n", quotedTable))
|
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)
|
biz.ErrIsNil(err)
|
||||||
writer.WriteString(ddl + "\n")
|
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) {
|
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")
|
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -394,7 +394,7 @@ func (d *Db) TableInfos(rc *req.Ctx) {
|
|||||||
func (d *Db) TableIndex(rc *req.Ctx) {
|
func (d *Db) TableIndex(rc *req.Ctx) {
|
||||||
tn := rc.Query("tableName")
|
tn := rc.Query("tableName")
|
||||||
biz.NotEmpty(tn, "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")
|
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -405,7 +405,7 @@ func (d *Db) ColumnMA(rc *req.Ctx) {
|
|||||||
biz.NotEmpty(tn, "tableName不能为空")
|
biz.NotEmpty(tn, "tableName不能为空")
|
||||||
|
|
||||||
dbi := d.getDbConn(rc)
|
dbi := d.getDbConn(rc)
|
||||||
res, err := dbi.GetDialect().GetColumns(tn)
|
res, err := dbi.GetDialect().GetMetaData().GetColumns(tn)
|
||||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -416,7 +416,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
|||||||
|
|
||||||
dm := dbi.GetDialect()
|
dm := dbi.GetDialect()
|
||||||
// 获取所有表
|
// 获取所有表
|
||||||
tables, err := dm.GetTables()
|
tables, err := dm.GetMetaData().GetTables()
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
tableNames := make([]string, 0)
|
tableNames := make([]string, 0)
|
||||||
for _, v := range tables {
|
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)
|
biz.ErrIsNil(err)
|
||||||
for _, v := range columnMds {
|
for _, v := range columnMds {
|
||||||
tName := v.TableName
|
tName := v.TableName
|
||||||
@@ -455,13 +455,13 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
|||||||
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
func (d *Db) GetTableDDL(rc *req.Ctx) {
|
||||||
tn := rc.Query("tableName")
|
tn := rc.Query("tableName")
|
||||||
biz.NotEmpty(tn, "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")
|
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Db) GetSchemas(rc *req.Ctx) {
|
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")
|
biz.ErrIsNilAppendErr(err, "获取schemas失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ func (d *Instance) GetDbServer(rc *req.Ctx) {
|
|||||||
instanceId := getInstanceId(rc)
|
instanceId := getInstanceId(rc)
|
||||||
conn, err := d.DbApp.GetDbConnByInstanceId(instanceId)
|
conn, err := d.DbApp.GetDbConnByInstanceId(instanceId)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
res, err := conn.GetDialect().GetDbServer()
|
res, err := conn.GetDialect().GetMetaData().GetDbServer()
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package application
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"gorm.io/gorm"
|
|
||||||
"mayfly-go/internal/db/dbm"
|
"mayfly-go/internal/db/dbm"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/internal/db/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
@@ -12,6 +11,8 @@ import (
|
|||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Instance interface {
|
type Instance interface {
|
||||||
@@ -159,5 +160,5 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error
|
|||||||
}
|
}
|
||||||
defer dbConn.Close()
|
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 {
|
if err != nil {
|
||||||
return nil, errorx.NewBiz("获取表主键信息失败")
|
return nil, errorx.NewBiz("获取表主键信息失败")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,12 +2,6 @@ package dbi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"embed"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"mayfly-go/pkg/biz"
|
|
||||||
"mayfly-go/pkg/utils/collx"
|
|
||||||
"mayfly-go/pkg/utils/stringx"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type DataType string
|
type DataType string
|
||||||
@@ -29,46 +23,6 @@ const (
|
|||||||
DuplicateStrategyUpdate = 2
|
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 {
|
type DbCopyTable struct {
|
||||||
Id uint64 `json:"id"`
|
Id uint64 `json:"id"`
|
||||||
Db string `json:"db" `
|
Db string `json:"db" `
|
||||||
@@ -90,31 +44,11 @@ type DataConverter interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------元数据接口定义------------------------------------------
|
// -----------------------------------元数据接口定义------------------------------------------
|
||||||
// 数据库方言、元信息接口(表、列、获取表数据等元信息)
|
// 数据库方言 用于获取元信息接口、批量插入等各个数据库方言不一致的实现方式
|
||||||
type Dialect interface {
|
type Dialect interface {
|
||||||
|
|
||||||
// 获取数据库服务实例信息
|
// 获取元数据信息接口
|
||||||
GetDbServer() (*DbServer, error)
|
GetMetaData() MetaData
|
||||||
|
|
||||||
// 获取数据库名称列表
|
|
||||||
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)
|
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||||
GetDbProgram() (DbProgram, error)
|
GetDbProgram() (DbProgram, error)
|
||||||
@@ -127,40 +61,3 @@ type Dialect interface {
|
|||||||
|
|
||||||
CopyTable(copy *DbCopyTable) error
|
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/internal/machine/mcm"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DbInfo struct {
|
type DbInfo struct {
|
||||||
@@ -89,6 +90,17 @@ func (di *DbInfo) IfUseSshTunnelChangeIpPort() error {
|
|||||||
return nil
|
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
|
// 根据ssh tunnel机器id返回ssh tunnel
|
||||||
func GetSshTunnel(sshTunnelMachineId int) (*mcm.SshTunnelMachine, error) {
|
func GetSshTunnel(sshTunnelMachineId int) (*mcm.SshTunnelMachine, error) {
|
||||||
return machineapp.GetMachineApp().GetSshTunnelMachine(sshTunnelMachineId)
|
return machineapp.GetMachineApp().GetSshTunnelMachine(sshTunnelMachineId)
|
||||||
|
|||||||
@@ -16,11 +16,11 @@ func GetMeta(dt DbType) Meta {
|
|||||||
return metas[dt]
|
return metas[dt]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 数据库元信息获取,如获取sql.DB、Dialect等
|
// 数据库元信息,如获取sql.DB、Dialect等
|
||||||
type Meta interface {
|
type Meta interface {
|
||||||
// 根据数据库信息获取sql.DB
|
// 根据数据库信息获取sql.DB
|
||||||
GetSqlDb(*DbInfo) (*sql.DB, error)
|
GetSqlDb(*DbInfo) (*sql.DB, error)
|
||||||
|
|
||||||
// 获取数据库方言,用于获取表结构等信息
|
// 获取数据库方言
|
||||||
GetDialect(*DbConn) Dialect
|
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"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
@@ -18,233 +17,14 @@ import (
|
|||||||
_ "gitee.com/chunanyong/dm"
|
_ "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 {
|
type DMDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dd *DMDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (dd *DMDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := dd.dc.Query("select * from v$instance")
|
return &DMMetaData{
|
||||||
if err != nil {
|
dc: dd.dc,
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
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 获取数据库程序模块,用于数据库备份与恢复
|
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||||
@@ -304,7 +84,8 @@ func (dd *DMDialect) batchInsertMerge(dbType dbi.DbType, tx *sql.Tx, tableName s
|
|||||||
// 查询主键字段
|
// 查询主键字段
|
||||||
uniqueCols := make([]string, 0)
|
uniqueCols := make([]string, 0)
|
||||||
caseSqls := make([]string, 0)
|
caseSqls := make([]string, 0)
|
||||||
tableCols, _ := dd.GetColumns(tableName)
|
metadata := dd.GetMetaData()
|
||||||
|
tableCols, _ := metadata.GetColumns(tableName)
|
||||||
identityCols := make([]string, 0)
|
identityCols := make([]string, 0)
|
||||||
for _, col := range tableCols {
|
for _, col := range tableCols {
|
||||||
if col.IsPrimaryKey {
|
if col.IsPrimaryKey {
|
||||||
@@ -317,7 +98,7 @@ func (dd *DMDialect) batchInsertMerge(dbType dbi.DbType, tx *sql.Tx, tableName s
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 查询唯一索引涉及到的字段,并组装到match条件内
|
// 查询唯一索引涉及到的字段,并组装到match条件内
|
||||||
indexs, _ := dd.GetTableIndex(tableName)
|
indexs, _ := metadata.GetTableIndex(tableName)
|
||||||
if indexs != nil {
|
if indexs != nil {
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
if index.IsUnique {
|
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 {
|
func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||||
tableName := copy.TableName
|
tableName := copy.TableName
|
||||||
ddl, err := dd.GetTableDDL(tableName)
|
metadata := dd.GetMetaData()
|
||||||
|
ddl, err := metadata.GetTableDDL(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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))
|
_, _ = dd.dc.Exec(fmt.Sprintf("set identity_insert \"%s\" on", newTableName))
|
||||||
// 获取列名
|
// 获取列名
|
||||||
columns, _ := dd.GetColumns(tableName)
|
columns, _ := metadata.GetColumns(tableName)
|
||||||
columnArr := make([]string, 0)
|
columnArr := make([]string, 0)
|
||||||
for _, column := range columns {
|
for _, column := range columns {
|
||||||
columnArr = append(columnArr, fmt.Sprintf("\"%s\"", column.ColumnName))
|
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
|
package mssql
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
@@ -14,282 +12,12 @@ import (
|
|||||||
"time"
|
"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 {
|
type MssqlDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *MssqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (md *MssqlDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := md.dc.Query("SELECT @@VERSION as version")
|
return &MssqlMetaData{dc: md.dc}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// 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) {
|
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 := ""
|
ignoreDupSql := ""
|
||||||
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
||||||
// ALTER TABLE dbo.TEST ADD CONSTRAINT uniqueRows UNIQUE (ColA, ColB, ColC, ColD) WITH (IGNORE_DUP_KEY = ON)
|
// 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)
|
uniqueColumns := make([]string, 0)
|
||||||
for _, index := range indexs {
|
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) {
|
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
|
dbType := md.dc.Info.Type
|
||||||
|
|
||||||
// 收集MERGE 语句的 ON 子句条件
|
// 收集MERGE 语句的 ON 子句条件
|
||||||
@@ -374,7 +104,7 @@ func (md *MssqlDialect) batchInsertMerge(tx *sql.Tx, tableName string, columns [
|
|||||||
|
|
||||||
// 查询取出自增列字段, merge update不能修改自增列
|
// 查询取出自增列字段, merge update不能修改自增列
|
||||||
identityCols := make([]string, 0)
|
identityCols := make([]string, 0)
|
||||||
cols, err := md.GetColumns(tableName)
|
cols, err := msMetadata.GetColumns(tableName)
|
||||||
for _, col := range cols {
|
for _, col := range cols {
|
||||||
if col.IsIdentity {
|
if col.IsIdentity {
|
||||||
identityCols = append(identityCols, col.ColumnName)
|
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 {
|
func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
||||||
|
msMetadata := md.GetMetaData().(*MssqlMetaData)
|
||||||
schema := md.currentSchema()
|
schema := md.dc.Info.CurrentSchema()
|
||||||
|
|
||||||
// 生成新表名,为老表明+_copy_时间戳
|
// 生成新表名,为老表明+_copy_时间戳
|
||||||
newTableName := copy.TableName + "_copy_" + time.Now().Format("20060102150405")
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -512,7 +242,7 @@ func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
|||||||
if copy.CopyData {
|
if copy.CopyData {
|
||||||
go func() {
|
go func() {
|
||||||
// 查询所有的列
|
// 查询所有的列
|
||||||
columns, err := md.GetColumns(copy.TableName)
|
columns, err := msMetadata.GetColumns(copy.TableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logx.Warnf("复制表[%s]数据失败: %s", copy.TableName, err.Error())
|
logx.Warnf("复制表[%s]数据失败: %s", copy.TableName, err.Error())
|
||||||
return
|
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"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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 {
|
type MysqlDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (md *MysqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (md *MysqlDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := md.dc.Query("SELECT VERSION() version")
|
return &MysqlMetaData{dc: md.dc}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// 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"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
@@ -15,255 +14,12 @@ import (
|
|||||||
_ "gitee.com/chunanyong/dm"
|
_ "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 {
|
type OracleDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (od *OracleDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (od *OracleDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := od.dc.Query("select * from v$instance")
|
return &OracleMetaData{dc: od.dc}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||||
@@ -296,7 +52,7 @@ func (od *OracleDialect) batchInsertSimple(dbType dbi.DbType, tableName string,
|
|||||||
ignore := ""
|
ignore := ""
|
||||||
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
if duplicateStrategy == dbi.DuplicateStrategyIgnore {
|
||||||
// 查出唯一索引涉及的字段
|
// 查出唯一索引涉及的字段
|
||||||
indexs, _ := od.GetTableIndex(tableName)
|
indexs, _ := od.GetMetaData().GetTableIndex(tableName)
|
||||||
if indexs != nil {
|
if indexs != nil {
|
||||||
arr := make([]string, 0)
|
arr := make([]string, 0)
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
@@ -336,7 +92,7 @@ func (od *OracleDialect) batchInsertMergeSql(dbType dbi.DbType, tableName string
|
|||||||
uniqueCols := make([]string, 0)
|
uniqueCols := make([]string, 0)
|
||||||
caseSqls := make([]string, 0)
|
caseSqls := make([]string, 0)
|
||||||
// 查询唯一索引涉及到的字段,并组装到match条件内
|
// 查询唯一索引涉及到的字段,并组装到match条件内
|
||||||
indexs, _ := od.GetTableIndex(tableName)
|
indexs, _ := od.GetMetaData().GetTableIndex(tableName)
|
||||||
if indexs != nil {
|
if indexs != nil {
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
if index.IsUnique {
|
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"
|
"database/sql"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/errorx"
|
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -12,176 +11,12 @@ import (
|
|||||||
"time"
|
"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 {
|
type PgsqlDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pd *PgsqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (pd *PgsqlDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := pd.dc.Query("SELECT version() as server_version")
|
return &PgsqlMetaData{dc: pd.dc}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||||
@@ -261,7 +96,7 @@ func (pd PgsqlDialect) gaussOnDuplicateStrategySql(duplicateStrategy int, tableN
|
|||||||
|
|
||||||
// 查出表里的唯一键涉及的字段
|
// 查出表里的唯一键涉及的字段
|
||||||
var uniqueColumns []string
|
var uniqueColumns []string
|
||||||
indexs, err := pd.GetTableIndex(tableName)
|
indexs, err := pd.GetMetaData().GetTableIndex(tableName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
for _, index := range indexs {
|
for _, index := range indexs {
|
||||||
if index.IsUnique {
|
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 (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/pkg/logx"
|
|
||||||
"mayfly-go/pkg/utils/anyx"
|
"mayfly-go/pkg/utils/anyx"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"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 {
|
type SqliteDialect struct {
|
||||||
dc *dbi.DbConn
|
dc *dbi.DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd *SqliteDialect) GetDbServer() (*dbi.DbServer, error) {
|
func (sd *SqliteDialect) GetMetaData() dbi.MetaData {
|
||||||
_, res, err := sd.dc.Query("SELECT SQLITE_VERSION() as version")
|
return &SqliteMetaData{dc: sd.dc}
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
// GetDbProgram 获取数据库程序模块,用于数据库备份与恢复
|
||||||
@@ -267,7 +106,7 @@ func (sd *SqliteDialect) CopyTable(copy *dbi.DbCopyTable) error {
|
|||||||
|
|
||||||
// 生成新表名,为老表明+_copy_时间戳
|
// 生成新表名,为老表明+_copy_时间戳
|
||||||
newTableName := tableName + "_copy_" + time.Now().Format("20060102150405")
|
newTableName := tableName + "_copy_" + time.Now().Format("20060102150405")
|
||||||
ddl, err := sd.GetTableDDL(tableName)
|
ddl, err := sd.GetMetaData().GetTableDDL(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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"` // 流程定义名称
|
ProcdefName string `json:"procdefName"` // 流程定义名称
|
||||||
|
|
||||||
BizType string `json:"bizType" form:"bizType"` // 业务类型
|
BizType string `json:"bizType" form:"bizType"` // 业务类型
|
||||||
BizKey string `json:"bizKey"` // 业务key
|
BizKey string `json:"bizKey" form:"bizKey"` // 业务key
|
||||||
Status ProcinstStatus `json:"status" form:"status"` // 状态
|
Status ProcinstStatus `json:"status" form:"status"` // 状态
|
||||||
|
|
||||||
CreatorId uint64
|
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 '数据库实例名称',
|
`name` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '数据库实例名称',
|
||||||
`host` varchar(100) COLLATE utf8mb4_bin NOT NULL,
|
`host` varchar(100) COLLATE utf8mb4_bin NOT NULL,
|
||||||
`port` int(8) 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,
|
`username` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL,
|
||||||
`password` 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...)',
|
`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('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('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`, `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('机器相关配置', '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('数据库备份恢复', '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('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('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;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user