2024-03-11 20:04:20 +08:00
package oracle
import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
2024-03-21 03:35:18 +00:00
"mayfly-go/pkg/logx"
2024-03-11 20:04:20 +08:00
"mayfly-go/pkg/utils/collx"
2024-03-18 12:25:40 +08:00
"mayfly-go/pkg/utils/stringx"
2024-03-11 20:04:20 +08:00
"strings"
2024-03-21 17:15:52 +08:00
"github.com/may-fly/cast"
2024-03-11 20:04:20 +08:00
)
// ---------------------------------- DM元数据 -----------------------------------
const (
2024-03-18 12:25:40 +08:00
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"
2024-03-11 20:04:20 +08:00
)
type OracleMetaData struct {
2024-03-15 13:31:53 +08:00
dbi . DefaultMetaData
2024-03-11 20:04:20 +08:00
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 {
2024-03-21 17:15:52 +08:00
Version : cast . ToString ( res [ 0 ] [ "VERSION" ] ) ,
2024-03-11 20:04:20 +08:00
}
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 {
2024-03-21 17:15:52 +08:00
databases = append ( databases , cast . ToString ( re [ "DBNAME" ] ) )
2024-03-11 20:04:20 +08:00
}
return databases , nil
}
2024-03-15 09:01:51 +00:00
func ( od * OracleMetaData ) GetTables ( tableNames ... string ) ( [ ] dbi . Table , error ) {
meta := od . dc . GetMetaData ( )
names := strings . Join ( collx . ArrayMap [ string , string ] ( tableNames , func ( val string ) string {
return fmt . Sprintf ( "'%s'" , meta . RemoveQuote ( val ) )
} ) , "," )
2024-03-11 20:04:20 +08:00
2024-03-15 09:01:51 +00:00
var res [ ] map [ string ] any
var err error
2024-03-11 20:04:20 +08:00
2024-03-18 12:25:40 +08:00
sql , err := stringx . TemplateParse ( dbi . GetLocalSql ( ORACLE_META_FILE , ORACLE_TABLE_INFO_KEY ) , collx . M { "tableNames" : names } )
if err != nil {
return nil , err
2024-03-15 09:01:51 +00:00
}
2024-03-18 12:25:40 +08:00
_ , res , err = od . dc . Query ( sql )
2024-03-11 20:04:20 +08:00
if err != nil {
return nil , err
}
tables := make ( [ ] dbi . Table , 0 )
for _ , re := range res {
tables = append ( tables , dbi . Table {
2024-03-21 17:15:52 +08:00
TableName : cast . ToString ( re [ "TABLE_NAME" ] ) ,
TableComment : cast . ToString ( re [ "TABLE_COMMENT" ] ) ,
CreateTime : cast . ToString ( re [ "CREATE_TIME" ] ) ,
TableRows : cast . ToInt ( re [ "TABLE_ROWS" ] ) ,
DataLength : cast . ToInt64 ( re [ "DATA_LENGTH" ] ) ,
IndexLength : cast . ToInt64 ( re [ "INDEX_LENGTH" ] ) ,
2024-03-11 20:04:20 +08:00
} )
}
return tables , nil
}
// 获取列元信息, 如列名等
func ( od * OracleMetaData ) GetColumns ( tableNames ... string ) ( [ ] dbi . Column , error ) {
2024-03-15 09:01:51 +00:00
meta := od . dc . GetMetaData ( )
2024-03-11 20:04:20 +08:00
tableName := strings . Join ( collx . ArrayMap [ string , string ] ( tableNames , func ( val string ) string {
2024-03-15 09:01:51 +00:00
return fmt . Sprintf ( "'%s'" , meta . RemoveQuote ( val ) )
2024-03-11 20:04:20 +08:00
} ) , "," )
// 如果表数量超过了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
}
2024-03-26 21:46:03 +08:00
columnHelper := meta . GetColumnHelper ( )
2024-03-11 20:04:20 +08:00
columns := make ( [ ] dbi . Column , 0 )
for _ , re := range res {
2024-03-21 03:35:18 +00:00
column := dbi . Column {
2024-03-21 17:15:52 +08:00
TableName : cast . ToString ( re [ "TABLE_NAME" ] ) ,
ColumnName : cast . ToString ( re [ "COLUMN_NAME" ] ) ,
DataType : dbi . ColumnDataType ( cast . ToString ( re [ "DATA_TYPE" ] ) ) ,
2024-03-26 09:05:28 +00:00
CharMaxLength : cast . ToInt ( re [ "CHAR_MAX_LENGTH" ] ) ,
2024-03-21 17:15:52 +08:00
ColumnComment : cast . ToString ( re [ "COLUMN_COMMENT" ] ) ,
2024-03-21 20:28:24 +08:00
Nullable : cast . ToString ( re [ "NULLABLE" ] ) == "YES" ,
2024-03-21 17:15:52 +08:00
IsPrimaryKey : cast . ToInt ( re [ "IS_PRIMARY_KEY" ] ) == 1 ,
IsIdentity : cast . ToInt ( re [ "IS_IDENTITY" ] ) == 1 ,
2024-03-26 09:05:28 +00:00
ColumnDefault : cast . ToString ( re [ "COLUMN_DEFAULT" ] ) ,
NumPrecision : cast . ToInt ( re [ "NUM_PRECISION" ] ) ,
2024-03-21 17:15:52 +08:00
NumScale : cast . ToInt ( re [ "NUM_SCALE" ] ) ,
2024-03-21 03:35:18 +00:00
}
2024-03-26 21:46:03 +08:00
columnHelper . FixColumn ( & column )
2024-03-21 03:35:18 +00:00
columns = append ( columns , column )
2024-03-11 20:04:20 +08:00
}
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 {
2024-03-21 17:15:52 +08:00
IndexName : cast . ToString ( re [ "INDEX_NAME" ] ) ,
ColumnName : cast . ToString ( re [ "COLUMN_NAME" ] ) ,
IndexType : cast . ToString ( re [ "INDEX_TYPE" ] ) ,
IndexComment : cast . ToString ( re [ "INDEX_COMMENT" ] ) ,
IsUnique : cast . ToInt ( re [ "IS_UNIQUE" ] ) == 1 ,
SeqInIndex : cast . ToInt ( re [ "SEQ_IN_INDEX" ] ) ,
2024-03-26 09:05:28 +00:00
IsPrimaryKey : cast . ToInt ( re [ "IS_PRIMARY" ] ) == 1 ,
2024-03-11 20:04:20 +08:00
} )
}
// 把查询结果以索引名分组,索引字段以逗号连接
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
}
2024-03-21 03:35:18 +00:00
// 获取建索引ddl
func ( od * OracleMetaData ) GenerateIndexDDL ( indexs [ ] dbi . Index , tableInfo dbi . Table ) [ ] string {
meta := od . dc . GetMetaData ( )
sqls := make ( [ ] string , 0 )
comments := make ( [ ] string , 0 )
for _ , index := range indexs {
unique := ""
if index . IsUnique {
unique = "unique"
}
2024-03-26 09:05:28 +00:00
// 取出列名,添加引号
cols := strings . Split ( index . ColumnName , "," )
colNames := make ( [ ] string , len ( cols ) )
for i , name := range cols {
colNames [ i ] = meta . QuoteIdentifier ( name )
2024-03-21 03:35:18 +00:00
}
2024-03-26 09:05:28 +00:00
sqls = append ( sqls , fmt . Sprintf ( "CREATE %s INDEX %s ON %s(%s)" , unique , index . IndexName , meta . QuoteIdentifier ( tableInfo . TableName ) , strings . Join ( colNames , "," ) ) )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
sqlArr := make ( [ ] string , 0 )
sqlArr = append ( sqlArr , sqls ... )
if len ( comments ) > 0 {
sqlArr = append ( sqlArr , comments ... )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
return sqlArr
}
func ( od * OracleMetaData ) genColumnBasicSql ( column dbi . Column ) string {
meta := od . dc . GetMetaData ( )
colName := meta . QuoteIdentifier ( column . ColumnName )
if column . IsIdentity {
// 如果是自增, 不需要设置默认值和空值, 自增列数据类型必须是number
return fmt . Sprintf ( " %s NUMBER generated by default as IDENTITY" , colName )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
nullAble := ""
2024-03-21 20:28:24 +08:00
if ! column . Nullable {
2024-03-21 03:35:18 +00:00
nullAble = " NOT NULL"
}
2024-03-26 09:05:28 +00:00
defVal := ""
2024-03-21 03:35:18 +00:00
if column . ColumnDefault != "" {
2024-03-26 09:05:28 +00:00
defVal = fmt . Sprintf ( " DEFAULT %v" , column . ColumnDefault )
2024-03-11 20:04:20 +08:00
}
2024-03-26 09:05:28 +00:00
columnSql := fmt . Sprintf ( " %s %s%s%s" , colName , column . GetColumnType ( ) , defVal , nullAble )
2024-03-21 03:35:18 +00:00
return columnSql
}
// 获取建表ddl
func ( od * OracleMetaData ) GenerateTableDDL ( columns [ ] dbi . Column , tableInfo dbi . Table , dropBeforeCreate bool ) [ ] string {
meta := od . dc . GetMetaData ( )
quoteTableName := meta . QuoteIdentifier ( tableInfo . TableName )
sqlArr := make ( [ ] string , 0 )
if dropBeforeCreate {
dropSqlTmp := `
declare
num number ;
begin
select count ( 1 ) into num from user_tables where table_name = ' % s ' and owner = ( SELECT sys_context ( ' USERENV ' , ' CURRENT_SCHEMA ' ) FROM dual ) ;
if num > 0 then
execute immediate ' drop table "%s" ' ;
end if ;
2024-03-26 09:05:28 +00:00
end `
2024-03-21 03:35:18 +00:00
sqlArr = append ( sqlArr , fmt . Sprintf ( dropSqlTmp , tableInfo . TableName , tableInfo . TableName ) )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
// 组装建表语句
createSql := fmt . Sprintf ( "CREATE TABLE %s ( \n" , quoteTableName )
fields := make ( [ ] string , 0 )
pks := make ( [ ] string , 0 )
columnComments := make ( [ ] string , 0 )
// 把通用类型转换为达梦类型
for _ , column := range columns {
if column . IsPrimaryKey {
pks = append ( pks , meta . QuoteIdentifier ( column . ColumnName ) )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
fields = append ( fields , od . genColumnBasicSql ( column ) )
// 防止注释内含有特殊字符串导致sql出错
if column . ColumnComment != "" {
2024-03-26 09:05:28 +00:00
comment := meta . QuoteEscape ( column . ColumnComment )
2024-03-21 03:35:18 +00:00
columnComments = append ( columnComments , fmt . Sprintf ( "COMMENT ON COLUMN %s.%s IS '%s'" , quoteTableName , meta . QuoteIdentifier ( column . ColumnName ) , comment ) )
}
}
// 建表
createSql += strings . Join ( fields , ",\n" )
if len ( pks ) > 0 {
createSql += fmt . Sprintf ( ", \nPRIMARY KEY (%s)" , strings . Join ( pks , "," ) )
}
createSql += "\n)"
sqlArr = append ( sqlArr , createSql )
// 表注释
tableCommentSql := ""
if tableInfo . TableComment != "" {
2024-03-26 09:05:28 +00:00
tableCommentSql = fmt . Sprintf ( "COMMENT ON TABLE %s is '%s'" , meta . QuoteIdentifier ( tableInfo . TableName ) , meta . QuoteEscape ( tableInfo . TableComment ) )
2024-03-21 03:35:18 +00:00
sqlArr = append ( sqlArr , tableCommentSql )
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
// 列注释
if len ( columnComments ) > 0 {
sqlArr = append ( sqlArr , columnComments ... )
}
return sqlArr
}
// 获取建表ddl
2024-03-26 09:05:28 +00:00
func ( od * OracleMetaData ) GetTableDDL ( tableName string , dropBeforeCreate bool ) ( string , error ) {
2024-03-21 03:35:18 +00:00
// 1.获取表信息
tbs , err := od . GetTables ( tableName )
tableInfo := & dbi . Table { }
2024-03-26 09:05:28 +00:00
if err != nil || tbs == nil || len ( tbs ) <= 0 {
2024-03-21 03:35:18 +00:00
logx . Errorf ( "获取表信息失败, %s" , tableName )
return "" , err
}
tableInfo . TableName = tbs [ 0 ] . TableName
tableInfo . TableComment = tbs [ 0 ] . TableComment
// 2.获取列信息
columns , err := od . GetColumns ( tableName )
2024-03-11 20:04:20 +08:00
if err != nil {
2024-03-21 03:35:18 +00:00
logx . Errorf ( "获取列信息失败, %s" , tableName )
2024-03-11 20:04:20 +08:00
return "" , err
}
2024-03-26 09:05:28 +00:00
tableDDLArr := od . GenerateTableDDL ( columns , * tableInfo , dropBeforeCreate )
2024-03-21 03:35:18 +00:00
// 3.获取索引信息
indexs , err := od . GetTableIndex ( tableName )
if err != nil {
logx . Errorf ( "获取索引信息失败, %s" , tableName )
return "" , err
2024-03-11 20:04:20 +08:00
}
2024-03-21 03:35:18 +00:00
// 组装返回
tableDDLArr = append ( tableDDLArr , od . GenerateIndexDDL ( indexs , * tableInfo ) ... )
return strings . Join ( tableDDLArr , ";\n" ) , nil
2024-03-11 20:04:20 +08:00
}
// 获取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 {
2024-03-21 17:15:52 +08:00
schemaNames = append ( schemaNames , cast . ToString ( re [ "USERNAME" ] ) )
2024-03-11 20:04:20 +08:00
}
return schemaNames , nil
}
2024-03-15 13:31:53 +08:00
2024-03-26 21:46:03 +08:00
func ( od * OracleMetaData ) GetDataHelper ( ) dbi . DataHelper {
return new ( DataHelper )
2024-03-15 13:31:53 +08:00
}
2024-03-26 21:46:03 +08:00
func ( od * OracleMetaData ) GetColumnHelper ( ) dbi . ColumnHelper {
return new ( ColumnHelper )
2024-03-26 09:05:28 +00:00
}