mirror of
				https://gitee.com/dromara/mayfly-go
				synced 2025-11-04 08:20: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