!109 refactor:ddl生成方式重构,数据类型和长度重构,所有数据库迁移调试

* feat:同步sqlite全量sql
* refactor:ddl生成方式重构,数据类型和长度重构,所有数据库迁移调试
This commit is contained in:
zongyangleo
2024-03-21 03:35:18 +00:00
committed by Coder慌
parent 68e0088016
commit b13d27ccd6
27 changed files with 1309 additions and 1405 deletions

View File

@@ -115,7 +115,7 @@
<el-option
v-for="item in state.targetColumnList"
:key="item.columnName"
:label="item.columnName + ` ${item.columnType}` + (item.columnComment && ' - ' + item.columnComment)"
:label="item.columnName + ` ${item.showDataType}` + (item.columnComment && ' - ' + item.columnComment)"
:value="item.columnName"
/>
</el-select>

View File

@@ -461,7 +461,7 @@ const formatDataValues = (datas: any) => {
for (let data of datas) {
for (let column of props.columns!) {
data[column.columnName] = getFormatTimeValue(dbDialect.getDataType(column.columnType), data[column.columnName]);
data[column.columnName] = getFormatTimeValue(dbDialect.getDataType(column.dataType), data[column.columnName]);
}
}
};
@@ -479,9 +479,9 @@ const setTableColumns = (columns: any) => {
state.columns = columns.map((x: any) => {
const columnName = x.columnName;
// 数据类型
x.dataType = dbDialect.getDataType(x.columnType);
x.dataType = dbDialect.getDataType(x.dataType);
x.dataTypeSubscript = ColumnTypeSubscript[x.dataType];
x.remark = `${x.columnType} ${x.columnComment ? ' | ' + x.columnComment : ''}`;
x.remark = `${x.showDataType} ${x.columnComment ? ' | ' + x.columnComment : ''}`;
return {
...x,

View File

@@ -9,15 +9,15 @@
:required="column.nullable != 'YES' && !column.isPrimaryKey && !column.isIdentity"
>
<template #label>
<span class="pointer" :title="`${column.columnType} | ${column.columnComment}`">
<span class="pointer" :title="`${column.showDataType} | ${column.columnComment}`">
{{ column.columnName }}
</span>
</template>
<ColumnFormItem
v-model="modelValue[`${column.columnName}`]"
:data-type="dbInst.getDialect().getDataType(column.columnType)"
:placeholder="`${column.columnType} ${column.columnComment}`"
:data-type="dbInst.getDialect().getDataType(column.dataType)"
:placeholder="`${column.showDataType} ${column.columnComment}`"
:column-name="column.columnName"
:disabled="column.isIdentity"
/>

View File

@@ -98,7 +98,7 @@
<el-divider direction="vertical" />
<span style="color: var(--el-color-info-light-3)">
{{ item.columnType }}
{{ item.showDataType }}
<template v-if="item.columnComment">
<el-divider direction="vertical" />
@@ -463,7 +463,7 @@ const handlerColumnSelect = (column: any) => {
// 默认拼接上 columnName =
let value = column.columnName + ' = ';
// 不是数字类型默认拼接上''
if (!DbInst.isNumber(column.columnType)) {
if (!DbInst.isNumber(column.dataType)) {
value = `${value} ''`;
}
@@ -507,7 +507,7 @@ const filterCondColumns = computed(() => {
const onConditionRowClick = (event: any) => {
const row = event[0];
state.conditionDialog.title = `请输入 [${row.columnName}] 的值`;
state.conditionDialog.placeholder = `${row.columnType} ${row.columnComment}`;
state.conditionDialog.placeholder = `${row.showDataType} ${row.columnComment}`;
state.conditionDialog.columnRow = row;
state.conditionDialog.visible = true;
setTimeout(() => {
@@ -524,7 +524,7 @@ const onConfirmCondition = () => {
}
const row = conditionDialog.columnRow as any;
condition += `${row.columnName} ${conditionDialog.condition} `;
state.condition = condition + state.dbDialect.wrapValue(row.columnType, conditionDialog.value!);
state.condition = condition + state.dbDialect.wrapValue(row.dataType, conditionDialog.value!);
onCancelCondition();
condInputRef.value.focus();
};

View File

@@ -192,7 +192,7 @@ const state = reactive({
},
{
prop: 'numScale',
label: '小数',
label: '小数精度',
width: 120,
},
{
@@ -412,7 +412,6 @@ const filterChangedData = (oldArr: object[], nowArr: object[], key: string): { d
const genSql = () => {
let data = state.tableData;
console.log(data);
// 创建表
if (!props.data?.edit) {
let createTable = dbDialect.getCreateTableSql(data);
@@ -504,9 +503,6 @@ watch(
// 回显列
if (columns && Array.isArray(columns) && columns.length > 0) {
columns.forEach((a) => {
let typeObj = a.columnType.replace(')', '').split('(');
let type = typeObj[0];
let length = (typeObj.length > 1 && typeObj[1]) || '';
let defaultValue = '';
if (a.columnDefault) {
defaultValue = a.columnDefault.trim().replace(/^'|'$/g, '');
@@ -516,10 +512,10 @@ watch(
let data = {
name: a.columnName,
oldName: a.columnName,
type,
type: a.dataType,
value: defaultValue,
length,
numScale: a.numScale,
length: a.showLength,
numScale: a.showScale,
notNull: a.nullable !== 'YES',
pri: a.isPrimaryKey,
auto_increment: a.isIdentity /*a.extra?.indexOf('auto_increment') > -1*/,

View File

@@ -317,7 +317,7 @@ export class DbInst {
let sql = `UPDATE ${schema}${this.wrapName(table)} SET `;
// 主键列信息
const primaryKey = await this.loadTableColumn(dbName, table);
let primaryKeyType = primaryKey.columnType;
let primaryKeyType = primaryKey.dataType;
let primaryKeyName = primaryKey.columnName;
let primaryKeyValue = rowData[primaryKeyName];
const dialect = this.getDialect();
@@ -325,7 +325,7 @@ export class DbInst {
const v = columnValue[k];
// 更新字段列信息
const updateColumn = await this.loadTableColumn(dbName, table, k);
sql += ` ${this.wrapName(k)} = ${dialect.wrapValue(updateColumn.columnType, v)},`;
sql += ` ${this.wrapName(k)} = ${dialect.wrapValue(updateColumn.dataType, v)},`;
}
sql = sql.substring(0, sql.length - 1);
@@ -341,7 +341,7 @@ export class DbInst {
async genDeleteByPrimaryKeysSql(db: string, table: string, datas: any[]) {
const primaryKey = await this.loadTableColumn(db, table);
const primaryKeyColumnName = primaryKey.columnName;
const ids = datas.map((d: any) => `${this.getDialect().wrapValue(primaryKey.columnType, d[primaryKeyColumnName])}`).join(',');
const ids = datas.map((d: any) => `${this.getDialect().wrapValue(primaryKey.dataType, d[primaryKeyColumnName])}`).join(',');
return `DELETE FROM ${this.wrapName(table)} WHERE ${this.wrapName(primaryKeyColumnName)} IN (${ids})`;
}

View File

@@ -303,7 +303,7 @@ class MssqlDialect implements DbDialect {
}
if (changeData.add.length > 0) {
changeData.add.forEach((a) => {
addArr.push(` ALTER TABLE ${baseTable} ADD COLUMN ${this.genColumnBasicSql(a)}`);
addArr.push(` ALTER TABLE ${baseTable} ADD ${this.genColumnBasicSql(a)}`);
if (a.remark) {
addCommentArr.push(
`EXECUTE sp_addextendedproperty N'MS_Description', N'${a.remark}', N'SCHEMA', N'${schema}', N'TABLE', N'${tableName}', N'COLUMN', N'${a.name}'`

View File

@@ -435,7 +435,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
res[tName] = make([]string, 0)
}
columnName := fmt.Sprintf("%s [%s]", v.ColumnName, v.ColumnType)
columnName := fmt.Sprintf("%s [%s]", v.ColumnName, v.ShowDataType)
comment := v.ColumnComment
// 如果字段备注不为空,则加上备注信息
if comment != "" {

View File

@@ -169,6 +169,8 @@ func (app *dbTransferAppImpl) transferTables(task *entity.DbTransferTask, srcCon
srcDialect.ToCommonColumn(colPtr)
// 公共列转为目标库列
targetDialect.ToColumn(colPtr)
// 初始化列显示类型
colPtr.InitShowNum()
targetCols = append(targetCols, *colPtr)
}
@@ -222,6 +224,8 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string
batchSize := 1000 // 每次查询并迁移1000条数据
var queryColumns []*dbi.QueryColumn
var err error
srcMeta := srcConn.GetMetaData()
srcConverter := srcMeta.GetDataConverter()
// 游标查询源表数据,并批量插入目标表
err = srcConn.WalkTableRows(ctx, tableName, func(row map[string]any, columns []*dbi.QueryColumn) error {
@@ -236,7 +240,13 @@ func (app *dbTransferAppImpl) transferData(ctx context.Context, tableName string
}
total++
result = append(result, row)
rawValue := map[string]any{}
for _, column := range columns {
// 某些情况如oracle需要转换时间类型的字符串为time类型
res := srcConverter.ParseData(row[column.Name], srcConverter.GetDataType(column.Type))
rawValue[column.Name] = res
}
result = append(result, rawValue)
if total%batchSize == 0 {
err = app.transfer2Target(targetConn, queryColumns, result, targetDialect, tableName)
if err != nil {

View File

@@ -34,6 +34,10 @@ type MetaData interface {
// 获取建表ddl
GetTableDDL(tableName string) (string, error)
GenerateTableDDL(columns []Column, tableInfo Table, dropBeforeCreate bool) []string
GenerateIndexDDL(indexs []Index, tableInfo Table) []string
GetSchemas() ([]string, error)
// 获取数据转换器用于解析格式化列数据等
@@ -49,6 +53,7 @@ type DbServer struct {
// 表信息
type Table struct {
TableName string `json:"tableName"` // 表名
TableNewName string `json:"tableNewName"` // 新表名复制表生成ddl时需要传入新表名
TableComment string `json:"tableComment"` // 表备注
CreateTime string `json:"createTime"` // 创建时间
TableRows int `json:"tableRows"`
@@ -60,7 +65,6 @@ type Table struct {
type Column struct {
TableName string `json:"tableName"` // 表名
ColumnName string `json:"columnName"` // 列名
ColumnType string `json:"columnType"` // 列类型,包含类型等描述(后续移除)
DataType ColumnDataType `json:"dataType"` // 数据类型
ColumnComment string `json:"columnComment"` // 列备注
IsPrimaryKey bool `json:"isPrimaryKey"` // 是否为主键
@@ -71,17 +75,38 @@ type Column struct {
NumPrecision int `json:"numPrecision"` // 精度(总数字位数)
NumScale int `json:"numScale"` // 小数点位数
Extra collx.M `json:"extra"` // 其他额外信息
ShowLength int `json:"showLength"`
ShowScale int `json:"showScale"`
ShowDataType string `json:"showDataType"` // 显示数据类型
}
// 获取列类型拼接数据类型与长度等。如varchar(2000)decimal(20,2)
func (c *Column) GetColumnType() string {
// 初始化列显示类型拼接数据类型与长度等。如varchar(2000)decimal(20,2)
func (c *Column) InitShowNum() string {
if c.CharMaxLength > 0 {
return fmt.Sprintf("%s(%d)", c.DataType, c.CharMaxLength)
c.ShowDataType = fmt.Sprintf("%s(%d)", c.DataType, c.CharMaxLength)
c.ShowLength = c.CharMaxLength
c.ShowScale = 0
return c.ShowDataType
}
if c.NumPrecision > 0 {
return fmt.Sprintf("%s(%d,%d)", c.DataType, c.NumPrecision, c.NumScale)
if c.NumScale > 0 {
c.ShowDataType = fmt.Sprintf("%s(%d,%d)", c.DataType, c.NumPrecision, c.NumScale)
c.ShowScale = c.NumScale
} else {
c.ShowDataType = fmt.Sprintf("%s(%d)", c.DataType, c.NumPrecision)
c.ShowScale = 0
}
c.ShowLength = c.NumPrecision
return c.ShowDataType
}
return string(c.DataType)
c.ShowDataType = string(c.DataType)
c.ShowLength = 0
c.ShowScale = 0
return c.ShowDataType
}
// 表索引信息

View File

@@ -34,39 +34,35 @@ WHERE a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
ORDER BY a.object_name
---------------------------------------
--DM_INDEX_INFO 表索引信息
select
a.index_name as INDEX_NAME,
a.index_type as INDEX_TYPE,
case when a.uniqueness = 'UNIQUE' then 1 else 0 end as IS_UNIQUE,
indexdef(b.object_id,1) as INDEX_DEF,
c.column_name as COLUMN_NAME,
c.column_position as SEQ_IN_INDEX,
'' as INDEX_COMMENT
select a.index_name as INDEX_NAME,
a.index_type as INDEX_TYPE,
case when a.uniqueness = 'UNIQUE' then 1 else 0 end as IS_UNIQUE,
indexdef(b.object_id, 1) as INDEX_DEF,
c.column_name as COLUMN_NAME,
c.column_position as SEQ_IN_INDEX,
'' as INDEX_COMMENT
FROM ALL_INDEXES a
LEFT JOIN all_objects b on a.owner = b.owner and b.object_name = a.index_name and b.object_type = 'INDEX'
LEFT JOIN ALL_IND_COLUMNS c
on a.owner = c.table_owner and a.index_name = c.index_name and a.TABLE_NAME = c.table_name
WHERE a.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))
and a.TABLE_NAME = '%s'
and indexdef(b.object_id,1) != '禁止查看系统定义的索引信息'
order by a.TABLE_NAME, a.index_name, c.column_position asc
and a.TABLE_NAME = '%s'
and indexdef(b.object_id, 1) != '禁止查看系统定义的索引信息'
order by a.TABLE_NAME, a.index_name, c.column_position asc
---------------------------------------
--DM_COLUMN_MA 表列信息
select a.table_name as TABLE_NAME,
a.column_name as COLUMN_NAME,
case when a.NULLABLE = 'Y' then 'YES' when a.NULLABLE = 'N' then 'NO' else 'NO' end as NULLABLE,
case
when a.char_col_decl_length > 0 then concat(a.data_type, '(', a.char_col_decl_length, ')')
when a.data_precision > 0 and a.data_scale > 0
then concat(a.data_type, '(', a.data_precision, ',', a.data_scale, ')')
else a.data_type end
as COLUMN_TYPE,
a.data_type as DATA_TYPE,
a.char_col_decl_length as CHAR_MAX_LENGTH,
a.data_precision as NUM_PRECISION,
a.data_scale as NUM_SCALE,
b.comments as COLUMN_COMMENT,
a.data_default as COLUMN_DEFAULT,
a.data_scale as NUM_SCALE,
case when t.COL_NAME = a.column_name then 1 else 0 end as IS_IDENTITY,
case when t2.constraint_type = 'P' then 1 else 0 end as IS_PRIMARY_KEY
case when t.COL_NAME = a.column_name then 1 else 0 end as IS_IDENTITY,
case when t2.constraint_type = 'P' then 1 else 0 end as IS_PRIMARY_KEY
from all_tab_columns a
left join user_col_comments b
on b.owner = (SELECT SF_GET_SCHEMA_NAME_BY_ID(CURRENT_SCHID))

View File

@@ -61,28 +61,16 @@ WHERE ss.name = ?
and t.name = ?
---------------------------------------
--MSSQL_COLUMN_MA 列信息元数据
SELECT t.name AS TABLE_NAME,
c.name AS COLUMN_NAME,
CASE
WHEN c.is_nullable = 1 THEN 'YES'
ELSE 'NO'
END AS NULLABLE,
tp.name +
CASE
WHEN tp.name IN ('char', 'varchar', 'nchar', 'nvarchar') THEN '(' + CASE
WHEN c.max_length = -1 THEN 'max'
ELSE CAST(c.max_length AS NVARCHAR(255)) END +
')'
WHEN tp.name IN ('numeric', 'decimal') THEN '(' + CAST(c.precision AS NVARCHAR(255)) + ',' +
CAST(c.scale AS NVARCHAR(255)) + ')'
ELSE ''
END AS COLUMN_TYPE,
ep.value AS COLUMN_COMMENT,
COLUMN_DEFAULT = CASE
WHEN c.default_object_id IS NOT NULL THEN object_definition(c.default_object_id)
ELSE ''
END,
c.scale AS NUM_SCALE,
SELECT t.name AS TABLE_NAME,
c.name AS COLUMN_NAME,
CASE WHEN c.is_nullable = 1 THEN 'YES' ELSE 'NO' END AS NULLABLE,
tp.name AS DATA_TYPE,
c.max_length AS CHAR_MAX_LENGTH,
c.precision AS NUM_PRECISION,
c.scale AS NUM_SCALE,
ep.value AS COLUMN_COMMENT,
COLUMN_DEFAULT =
CASE WHEN c.default_object_id IS NOT NULL THEN object_definition(c.default_object_id) ELSE '' END,
IS_IDENTITY = COLUMNPROPERTY(c.object_id, c.name, 'IsIdentity'),
IS_PRIMARY_KEY = CASE
WHEN (SELECT COUNT(*)
@@ -105,106 +93,3 @@ FROM sys.tables t
WHERE ss.name = ?
and t.name in (%s)
ORDER BY t.name, c.column_id
---------------------------------------
--MSSQL_TABLE_DDL 建表ddl
declare
@tabname varchar(50)
set @tabname= ? --
if ( object_id('tempdb.dbo.#t') is not null)
begin
DROP TABLE #t
end
select 'create table [' + so.name + '] (' + o.list + ')'
+ CASE
WHEN tc.Constraint_Name IS NULL THEN ''
ELSE 'ALTER TABLE ' + so.Name + ' ADD CONSTRAINT ' + tc.Constraint_Name + ' PRIMARY KEY ' +
' (' + LEFT(j.List, Len(j.List)-1) + ')' END
TABLE_DDL
into #t
from sysobjects so
cross apply
(SELECT
' \n ['+ column_name +'] ' +
data_type + case data_type
when 'sql_variant' then ''
when 'text' then ''
when 'ntext' then ''
when 'xml' then ''
when 'decimal' then '(' + cast (numeric_precision as varchar) + ', ' + cast (numeric_scale as varchar) + ')'
else coalesce ('('+ case when character_maximum_length = -1 then 'MAX' else cast (character_maximum_length as varchar) end +')', '') end + ' ' +
case when exists (
select id from syscolumns
where object_name(id)=so.name
and name = column_name
and columnproperty(id, name, 'IsIdentity') = 1
) then
'IDENTITY(' +
cast (ident_seed(so.name) as varchar) + ',' +
cast (ident_incr(so.name) as varchar) + ')'
else ''
end + ' ' +
(case when IS_NULLABLE = 'No' then 'NOT ' else '' end ) + 'NULL ' +
case when information_schema.columns.COLUMN_DEFAULT IS NOT NULL THEN 'DEFAULT '+ information_schema.columns.COLUMN_DEFAULT ELSE '' END + ', '
from information_schema.columns where table_name = so.name
order by ordinal_position
FOR XML PATH ('')) o (list)
left join
information_schema.table_constraints tc
on tc.Table_name = so.Name
AND tc.Constraint_Type = 'PRIMARY KEY'
cross apply
(select '[' + Column_Name + '], '
FROM information_schema.key_column_usage kcu
WHERE kcu.Constraint_Name = tc.Constraint_Name
ORDER BY
ORDINAL_POSITION
FOR XML PATH ('')) j (list)
where xtype = 'U'
AND name =@tabname
select (
case
when (select count(a.constraint_type)
from information_schema.table_constraints a
inner join information_schema.constraint_column_usage b
on a.constraint_name = b.constraint_name
where a.constraint_type = 'PRIMARY KEY'--
and a.table_name = @tabname) = 1 then replace(table_ddl
, ', )ALTER TABLE'
, ')' + CHAR (13)+'ALTER TABLE')
else SUBSTRING(table_ddl
, 1
, len(table_ddl) - 3) + ')' end
) as TableDDL
from #t
drop table #t
---------------------------------------
--MSSQL_TABLE_INDEX_DDL 建索引ddl
DECLARE
@TableName NVARCHAR(255)
SET @TableName = ?;
SELECT 'CREATE ' +
CASE
WHEN i.is_primary_key = 1 THEN 'CLUSTERED '
WHEN i.type_desc = 'HEAP' THEN ''
ELSE 'NONCLUSTERED '
END +
'INDEX ' + i.name + ' ON ' + t.name + ' (' +
STUFF((SELECT ',' + c.name +
CASE
WHEN ic.is_descending_key = 1 THEN ' DESC'
ELSE ' ASC'
END
FROM sys.index_columns ic
INNER JOIN
sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
WHERE ic.object_id = i.object_id
AND ic.index_id = i.index_id
ORDER BY ic.key_ordinal
FOR XML PATH (''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '') + ');' AS IndexDDL
FROM sys.tables t
INNER JOIN
sys.indexes i ON t.object_id = i.object_id
WHERE t.name = @TableName;

View File

@@ -50,122 +50,21 @@ WHERE a.schemaname = (select current_schema())
AND a.tablename = '%s';
---------------------------------------
--PGSQL_COLUMN_MA 表列信息
SELECT a.table_name AS "tableName",
a.column_name AS "columnName",
a.is_nullable AS "nullable",
case when character_maximum_length > 0 then concat(udt_name, '(',character_maximum_length,')') else udt_name end AS "columnType",
a.column_default as "columnDefault",
a.numeric_scale AS "numScale",
case when a.column_default like 'nextval%%' then 1 else 0 end "isIdentity",
case when b.column_name is not null then 1 else 0 end "isPrimaryKey",
col_description((a.table_schema || '.' || a.table_name)::regclass, a.ordinal_position) AS "columnComment"
SELECT a.*,
a.table_name AS "tableName",
a.column_name AS "columnName",
a.is_nullable AS "nullable",
a.udt_name AS "dataType",
a.character_maximum_length AS "charMaxLength",
a.numeric_precision AS "numPrecision",
a.column_default AS "columnDefault",
a.numeric_scale AS "numScale",
case when a.column_default like 'nextval%%' then 1 else 0 end AS "isIdentity",
case when b.column_name is not null then 1 else 0 end AS "isPrimaryKey",
col_description((a.table_schema || '.' || a.table_name) ::regclass, a.ordinal_position) AS "columnComment"
FROM information_schema.columns a
left join information_schema.key_column_usage b
on a.table_schema = b.table_schema and b.table_name = a.table_name and b.column_name = a.column_name
WHERE a.table_schema = (select current_schema())
and a.table_name in (%s)
order by a.table_name, a.ordinal_position
---------------------------------------
--PGSQL_TABLE_DDL_FUNC 表ddl函数
CREATE OR REPLACE FUNCTION showcreatetable(namespace character varying, tablename character varying)
RETURNS character varying AS
$BODY$
declare
tableScript character varying default '';
begin
-- columns
tableScript:=tableScript || ' CREATE TABLE '|| tablename|| ' ( '|| chr(13)||chr(10) || array_to_string(
array(
select ' ' || concat_ws(' ',fieldName, fieldType, isNullStr ) as column_line
from (
select a.attname as fieldName,format_type(a.atttypid,a.atttypmod) as fieldType,(case when atttypmod-4>0 then
atttypmod-4 else 0 end) as fieldLen,
(case when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and
contype='p')>0 then 'PRI'
when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and contype='u')>0
then 'UNI'
when (select count(*) from pg_constraint where conrelid = a.attrelid and conkey[1]=attnum and contype='f')>0
then 'FRI'
else '' end) as indexType,
(case when a.attnotnull=true then 'not null' else 'null' end) as isNullStr,
' comment ' || col_description(a.attrelid,a.attnum) as fieldComment
from pg_attribute a where attstattarget=-1 and attrelid = (select c.oid from pg_class c,pg_namespace n where
c.relnamespace=n.oid and n.nspname =namespace and relname =tablename)
) as string_columns
),','||chr(13)||chr(10));
-- 约束
tableScript:= tableScript || array_to_string(
array(
select '' union all
select concat(' CONSTRAINT ',conname ,c ,u,p,f) from (
select conname,
case when contype='c' then ' CHECK('|| ( select findattname(namespace,tablename,'c') ) ||')' end as c ,
case when contype='u' then ' UNIQUE('|| ( select findattname(namespace,tablename,'u') ) ||')' end as u ,
case when contype='p' then ' PRIMARY KEY ('|| ( select findattname(namespace,tablename,'p') ) ||')' end as p ,
case when contype='f' then ' FOREIGN KEY('|| ( select findattname(namespace,tablename,'u') ) ||') REFERENCES '||
(select p.relname from pg_class p where p.oid=c.confrelid ) || '('|| ( select
findattname(namespace,tablename,'u') ) ||')' end as f
from pg_constraint c
where contype in('u','c','f','p') and conrelid=(
select oid from pg_class where relname=tablename and relnamespace =(
select oid from pg_namespace where nspname = namespace
)
)
) as t
) ,',' || chr(13)||chr(10) ) || chr(13)||chr(10) ||' ); ';
-- indexs
-- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table pg_language
--
/** **/
--- 获取非约束索引 column
-- CREATE UNIQUE INDEX pg_language_oid_index ON pg_language USING btree (oid); -- table pg_language
tableScript:= tableScript || chr(13)||chr(10) || array_to_string(
array(
select 'CREATE INDEX ' || indexrelname || ' ON ' || tablename || ' USING btree '|| '(' || attname || ');' from (
SELECT
i.relname AS indexrelname , x.indkey,
( select array_to_string (
array(
select a.attname from pg_attribute a where attrelid=c.oid and a.attnum in ( select unnest(x.indkey) )
)
,',' ) )as attname
FROM pg_class c
JOIN pg_index x ON c.oid = x.indrelid
JOIN pg_class i ON i.oid = x.indexrelid
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname=tablename and i.relname not in
( select constraint_name from information_schema.key_column_usage where table_name=tablename )
)as t
) , chr(13)||chr(10));
-- COMMENT ON COLUMN sys_activity.id IS '主键';
tableScript:= tableScript || chr(13)||chr(10) || array_to_string(
array(
SELECT 'COMMENT ON COLUMN ' || tablename || '.' || a.attname ||' IS '|| ''''|| d.description ||''';'
FROM pg_class c
JOIN pg_description d ON c.oid=d.objoid
JOIN pg_attribute a ON c.oid = a.attrelid
WHERE c.relname=tablename
AND a.attnum = d.objsubid), chr(13)||chr(10)) ;
return tableScript;
end
$BODY$ LANGUAGE plpgsql;
CREATE OR REPLACE FUNCTION findattname(namespace character varying, tablename character varying, ctype character
varying)
RETURNS character varying as $BODY$
declare
tt oid ;
aname character varying default '';
begin
tt := oid from pg_class where relname= tablename and relnamespace =(select oid from pg_namespace where
nspname=namespace) ;
aname:= array_to_string(
array(
select a.attname from pg_attribute a
where a.attrelid=tt and a.attnum in (
select unnest(conkey) from pg_constraint c where contype=ctype
and conrelid=tt and array_to_string(conkey,',') is not null
)
),',');
return aname;
end
$BODY$ LANGUAGE plpgsql

View File

@@ -165,157 +165,56 @@ func (dd *DMDialect) CopyTable(copy *dbi.DbCopyTable) error {
return err
}
// func (dd *DMDialect) TransColumns(columns []dbi.Column) []dbi.Column {
// var commonColumns []dbi.Column
// for _, column := range columns {
// // 取出当前数据库类型
// arr := strings.Split(column.ColumnType, "(")
// ctype := arr[0]
// // 翻译为通用数据库类型
// t1 := commonColumnMap[ctype]
// if t1 == "" {
// ctype = "VARCHAR(2000)"
// } else {
// // 回写到列信息
// if len(arr) > 1 {
// ctype = t1 + "(" + arr[1]
// }
// }
// column.ColumnType = ctype
// commonColumns = append(commonColumns, column)
// }
// return commonColumns
// }
func (dd *DMDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
meta := dd.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
tbName := meta.QuoteIdentifier(tableInfo.TableName)
if dropOldTable {
_, _ = dd.dc.Exec(fmt.Sprintf("drop table if exists %s", tbName))
func (dd *DMDialect) ToCommonColumn(dialectColumn *dbi.Column) {
// 翻译为通用数据库类型
dataType := dialectColumn.DataType
t1 := commonColumnTypeMap[string(dataType)]
if t1 == "" {
dialectColumn.DataType = dbi.CommonTypeVarchar
dialectColumn.CharMaxLength = 2000
} else {
dialectColumn.DataType = t1
}
// 组装建表语句
createSql := fmt.Sprintf("create table %s (", tbName)
fields := make([]string, 0)
pks := make([]string, 0)
columnComments := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range commonColumns {
// 取出当前数据库类型
arr := strings.Split(column.ColumnType, "(")
ctype := arr[0]
// 翻译为通用数据库类型
t1 := dmColumnMap[dbi.ColumnDataType(ctype)]
if t1 == "" {
ctype = "VARCHAR(2000)"
} else {
// 回写到列信息
if len(arr) > 1 {
ctype = t1 + "(" + arr[1]
} else {
ctype = t1
}
}
column.ColumnType = ctype
if column.IsPrimaryKey {
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
}
fields = append(fields, dd.genColumnBasicSql(column))
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf("comment on column %s.%s is '%s'", tbName, meta.QuoteIdentifier(column.ColumnName), comment))
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(tableInfo.TableComment)
tableCommentSql = fmt.Sprintf(" comment on table %s is '%s'", tbName, comment)
}
// 达梦需要分开执行sql
var err error
if createSql != "" {
_, err = dd.dc.Exec(createSql)
}
if tableCommentSql != "" {
_, err = dd.dc.Exec(tableCommentSql)
}
if len(columnComments) > 0 {
for _, commentSql := range columnComments {
_, err = dd.dc.Exec(commentSql)
}
}
return 1, err
}
func (dd *DMDialect) genColumnBasicSql(column dbi.Column) string {
meta := dd.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
func (dd *DMDialect) ToColumn(commonColumn *dbi.Column) {
ctype := dmColumnTypeMap[commonColumn.DataType]
incr := ""
if column.IsIdentity {
incr = " IDENTITY"
if ctype == "" {
commonColumn.DataType = "VARCHAR"
commonColumn.CharMaxLength = 2000
} else {
commonColumn.DataType = dbi.ColumnDataType(ctype)
// 如果是date不设长度
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(ctype)) {
commonColumn.CharMaxLength = 0
commonColumn.NumPrecision = 0
} else
// 如果是char且长度未设置则默认长度2000
if collx.ArrayAnyMatches([]string{"char"}, strings.ToLower(ctype)) && commonColumn.CharMaxLength == 0 {
commonColumn.CharMaxLength = 2000
}
}
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
func (dd *DMDialect) CreateTable(columns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(column.ColumnType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
sqlArr := dd.dc.GetMetaData().GenerateTableDDL(columns, tableInfo, dropOldTable)
// 达梦需要分开执行sql
if len(sqlArr) > 0 {
for _, sqlStr := range sqlArr {
_, err := dd.dc.Exec(sqlStr)
if err != nil {
return 0, err
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.ColumnType, incr, nullAble, defVal)
return columnSql
return len(sqlArr), nil
}
func (dd *DMDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
meta := dd.dc.GetMetaData()
sqls := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqls = append(sqls, fmt.Sprintf("create %s index %s on %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
}
sqls := dd.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
_, err := dd.dc.Exec(strings.Join(sqls, ";"))
return err
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
@@ -96,17 +97,23 @@ func (dd *DMMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
columns := make([]dbi.Column, 0)
for _, re := range res {
columns = append(columns, dbi.Column{
column := dbi.Column{
TableName: anyx.ConvString(re["TABLE_NAME"]),
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
DataType: dbi.ColumnDataType(anyx.ToString(re["DATA_TYPE"])),
CharMaxLength: anyx.ConvInt(re["CHAR_MAX_LENGTH"]),
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"]),
NumPrecision: anyx.ConvInt(re["NUM_PRECISION"]),
NumScale: anyx.ConvInt(re["NUM_SCALE"]),
})
}
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
return columns, nil
}
@@ -165,73 +172,151 @@ func (dd *DMMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
return result, nil
}
func (dd *DMMetaData) genColumnBasicSql(column dbi.Column) string {
meta := dd.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
dataType := string(column.DataType)
incr := ""
if column.IsIdentity {
incr = " IDENTITY"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(dataType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.ShowDataType, incr, nullAble, defVal)
return columnSql
}
func (dd *DMMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
meta := dd.dc.GetMetaData()
sqls := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqls = append(sqls, fmt.Sprintf("create %s index %s on %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
}
return sqls
}
func (dd *DMMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
meta := dd.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
tbName := meta.QuoteIdentifier(tableInfo.TableName)
sqlArr := make([]string, 0)
if dropBeforeCreate {
sqlArr = append(sqlArr, fmt.Sprintf("drop table if exists %s", tbName))
}
// 组装建表语句
createSql := fmt.Sprintf("create table %s (", tbName)
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))
}
fields = append(fields, dd.genColumnBasicSql(column))
// 防止注释内含有特殊字符串导致sql出错
if column.ColumnComment != "" {
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf("comment on column %s.%s is '%s'", tbName, meta.QuoteIdentifier(column.ColumnName), comment))
}
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(tableInfo.TableComment)
if comment != "" {
tableCommentSql = fmt.Sprintf(" comment on table %s is '%s'", tbName, comment)
}
}
sqlArr = append(sqlArr, createSql)
if tableCommentSql != "" {
sqlArr = append(sqlArr, tableCommentSql)
}
if len(columnComments) > 0 {
sqlArr = append(sqlArr, columnComments...)
}
return sqlArr
}
// 获取建表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 {
// 1.获取表信息
tbs, err := dd.GetTables(tableName)
tableInfo := &dbi.Table{}
if err != nil && len(tbs) > 0 {
logx.Errorf("获取表信息失败, %s", tableName)
return "", err
}
// 建表ddl
var builder strings.Builder
for _, re := range res {
builder.WriteString(re["COLUMN_VALUE"].(string))
}
tableInfo.TableName = tbs[0].TableName
tableInfo.TableComment = tbs[0].TableComment
// 表注释
_, 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))
// 2.获取列信息
columns, err := dd.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
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)
tableDDLArr := dd.GenerateTableDDL(columns, *tableInfo, false)
// 3.获取索引信息
indexs, err := dd.GetTableIndex(tableName)
if err != nil {
logx.Errorf("获取索引信息失败, %s", tableName)
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
// 组装返回
tableDDLArr = append(tableDDLArr, dd.GenerateIndexDDL(indexs, *tableInfo)...)
return strings.Join(tableDDLArr, ";"), nil
}
// 获取DM当前连接的库可访问的schemaNames
@@ -267,7 +352,7 @@ var (
converter = new(DataConverter)
// 达梦数据类型 对应 公共数据类型
commonColumnMap = map[string]dbi.ColumnDataType{
commonColumnTypeMap = map[string]dbi.ColumnDataType{
"CHAR": dbi.CommonTypeChar, // 字符数据类型
"VARCHAR": dbi.CommonTypeVarchar,
@@ -296,7 +381,7 @@ var (
}
// 公共数据类型 对应 达梦数据类型
dmColumnMap = map[dbi.ColumnDataType]string{
dmColumnTypeMap = map[dbi.ColumnDataType]string{
dbi.CommonTypeVarchar: "VARCHAR",
dbi.CommonTypeChar: "CHAR",
dbi.CommonTypeText: "TEXT",

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"strings"
"time"
@@ -253,200 +252,61 @@ func (md *MssqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
return err
}
// func (md *MssqlDialect) TransColumns(columns []dbi.Column) []dbi.Column {
// var commonColumns []dbi.Column
// for _, column := range columns {
// // 取出当前数据库类型
// arr := strings.Split(column.ColumnType, "(")
// ctype := arr[0]
// // 翻译为通用数据库类型
// t1 := commonColumnTypeMap[ctype]
// if t1 == "" {
// ctype = "nvarchar(2000)"
// } else {
// // 回写到列信息
// if len(arr) > 1 {
// ctype = t1 + "(" + arr[1]
// } else {
// ctype = t1
// }
// }
// column.ColumnType = ctype
// commonColumns = append(commonColumns, column)
// }
// return commonColumns
// }
func (md *MssqlDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
meta := md.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
if dropOldTable {
_, _ = md.dc.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", meta.QuoteIdentifier(tableInfo.TableName)))
func (md *MssqlDialect) ToCommonColumn(dialectColumn *dbi.Column) {
// 翻译为通用数据库类型
dataType := dialectColumn.DataType
t1 := commonColumnTypeMap[string(dataType)]
if t1 == "" {
dialectColumn.DataType = dbi.CommonTypeVarchar
dialectColumn.CharMaxLength = 2000
} else {
dialectColumn.DataType = t1
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tableInfo.TableName))
fields := make([]string, 0)
pks := make([]string, 0)
columnComments := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range commonColumns {
// 取出当前数据库类型
arr := strings.Split(column.ColumnType, "(")
ctype := arr[0]
// 翻译为通用数据库类型
t1 := mssqlColumnTypeMap[dbi.ColumnDataType(ctype)]
if t1 == "" {
ctype = "nvarchar(2000)"
} else {
// 回写到列信息
if len(arr) > 1 {
// 如果是int类型不需要指定长度
if strings.Contains(strings.ToLower(t1), "int") {
ctype = t1
} else if collx.ArrayAnyMatches([]string{"float", "number", "decimal"}, strings.ToLower(t1)) {
// 如果是float最大长度为38
match := bracketsRegexp.FindStringSubmatch(column.ColumnType)
if len(match) > 1 {
// size翻倍 防止数据超长报错
size := anyx.ConvInt(match[1])
if size >= 38 { // 如果长度超过38
ctype = t1 + "(38)"
} else {
ctype = fmt.Sprintf("%s(%d)", t1, size)
}
} else {
ctype = t1 + "(38)"
}
} else if strings.Contains(strings.ToLower(t1), "char") {
// 如果是字符串类型长度最大4000否则修改字段类型为text
match := bracketsRegexp.FindStringSubmatch(column.ColumnType)
if len(match) > 1 {
// size翻倍 防止数据超长报错
size := anyx.ConvInt(match[1]) * 2
if size >= 4000 { // 如果长度超过4000则替换为text类型
ctype = "text"
} else {
ctype = fmt.Sprintf("%s(%d)", t1, size)
}
} else {
ctype = t1 + "(1000)"
}
} else {
ctype = t1 + "(" + arr[1]
}
} else {
ctype = t1
}
}
column.ColumnType = ctype
if column.IsPrimaryKey {
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
}
fields = append(fields, md.genColumnBasicSql(column))
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'COLUMN', N'%s'"
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf(commentTmp, comment, md.currentSchema(), column.TableName, column.ColumnName))
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY CLUSTERED (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s'"
tableCommentSql = fmt.Sprintf(commentTmp, replacer.Replace(tableInfo.TableComment), md.currentSchema(), tableInfo.TableName)
}
columnCommentSql := strings.Join(columnComments, ";")
sqls := make([]string, 0)
if createSql != "" {
sqls = append(sqls, createSql)
}
if tableCommentSql != "" {
sqls = append(sqls, tableCommentSql)
}
if columnCommentSql != "" {
sqls = append(sqls, columnCommentSql)
}
_, err := md.dc.Exec(strings.Join(sqls, ";"))
return 1, err
}
func (md *MssqlDialect) genColumnBasicSql(column dbi.Column) string {
meta := md.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
func (md *MssqlDialect) ToColumn(commonColumn *dbi.Column) {
ctype := mssqlColumnTypeMap[commonColumn.DataType]
incr := ""
if column.IsIdentity {
incr = " IDENTITY(1,1)"
}
if ctype == "" {
commonColumn.DataType = "varchar"
commonColumn.CharMaxLength = 2000
} else {
commonColumn.DataType = dbi.ColumnDataType(ctype)
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(column.ColumnType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
if strings.Contains(strings.ToLower(ctype), "int") {
// 如果类型是数字,类型后不需要带长度
commonColumn.CharMaxLength = 0
commonColumn.NumPrecision = 0
} else if collx.ArrayAnyMatches([]string{"float", "number", "decimal"}, strings.ToLower(ctype)) {
// 如果是float最大长度为38
if commonColumn.CharMaxLength > 38 {
commonColumn.CharMaxLength = 38
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
if commonColumn.NumPrecision > 38 {
commonColumn.NumPrecision = 38
}
} else if strings.Contains(strings.ToLower(ctype), "char") {
// 如果是字符串类型长度最大4000否则修改字段类型为text
if commonColumn.CharMaxLength > 4000 {
commonColumn.DataType = "text"
commonColumn.CharMaxLength = 0
}
} else if strings.Contains(strings.ToLower(ctype), "text") {
// 如果是text取消长度
commonColumn.CharMaxLength = 0
}
}
}
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.ColumnType, incr, nullAble, defVal)
return columnSql
func (md *MssqlDialect) CreateTable(columns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
sqlArr := md.dc.GetMetaData().GenerateTableDDL(columns, tableInfo, dropOldTable)
_, err := md.dc.Exec(strings.Join(sqlArr, ";"))
return len(sqlArr), err
}
func (md *MssqlDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
sqls := make([]string, 0)
comments := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqls = append(sqls, fmt.Sprintf("create %s NONCLUSTERED index %s on %s.%s(%s)", unique, indexName, md.currentSchema(), tableInfo.TableName, index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'INDEX', N'%s'", index.IndexComment, md.currentSchema(), tableInfo.TableName, indexName))
}
}
_, err := md.dc.Exec(strings.Join(sqls, ";"))
// 添加注释
if len(comments) > 0 {
_, err = md.dc.Exec(strings.Join(comments, ";"))
}
sqlArr := md.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
_, err := md.dc.Exec(strings.Join(sqlArr, ";"))
return err
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
@@ -13,14 +14,12 @@ import (
)
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"
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"
)
type MssqlMetaData struct {
@@ -106,17 +105,41 @@ func (md *MssqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
columns := make([]dbi.Column, 0)
for _, re := range res {
columns = append(columns, dbi.Column{
column := dbi.Column{
TableName: anyx.ToString(re["TABLE_NAME"]),
ColumnName: anyx.ToString(re["COLUMN_NAME"]),
ColumnType: anyx.ToString(re["COLUMN_TYPE"]),
DataType: dbi.ColumnDataType(anyx.ToString(re["DATA_TYPE"])),
CharMaxLength: anyx.ConvInt(re["CHAR_MAX_LENGTH"]),
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"]),
ColumnDefault: anyx.ConvString(re["COLUMN_DEFAULT"]),
NumPrecision: anyx.ConvInt(re["NUM_PRECISION"]),
NumScale: anyx.ConvInt(re["NUM_SCALE"]),
})
}
dataType := strings.ToLower(string(column.DataType))
if collx.ArrayAnyMatches([]string{"date", "time"}, dataType) {
// 如果是datetime精度取NumScale字段
column.CharMaxLength = column.NumScale
} else if collx.ArrayAnyMatches([]string{"int", "bit", "real", "text", "xml"}, dataType) {
// 不显示长度的类型
column.NumPrecision = 0
column.CharMaxLength = 0
} else if collx.ArrayAnyMatches([]string{"numeric", "decimal", "float"}, dataType) {
// 如果是num长度取精度和小数位数
column.CharMaxLength = 0
} else if collx.ArrayAnyMatches([]string{"nvarchar", "nchar"}, dataType) {
// 如果是nvarchar可视长度减半
column.CharMaxLength = column.CharMaxLength / 2
}
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
return columns, nil
}
@@ -193,93 +216,204 @@ func (md *MssqlMetaData) CopyTableDDL(tableName string, newTableName string) (st
if newTableName == "" {
newTableName = tableName
}
meta := md.dc.GetMetaData()
// 根据列信息生成建表语句
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 {
tbs, err := md.GetTables(tableName)
if err != nil || len(tbs) < 1 {
logx.Errorf("获取表信息失败, %s", tableName)
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))
}
tabInfo := &dbi.Table{
TableName: tableName,
TableComment: tbs[0].TableComment,
TableNewName: newTableName,
}
baseTable := fmt.Sprintf("%s.%s", meta.QuoteIdentifier(md.dc.Info.CurrentSchema()), meta.QuoteIdentifier(newTableName))
// 查询列信息
columns, err := md.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
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))
}
}
sqlArr := meta.GenerateTableDDL(columns, *tabInfo, true)
// 设置索引
indexs, err := md.GetTableIndex(tableName)
if err != nil {
return "", err
logx.Errorf("获取索引信息失败, %s", tableName)
return strings.Join(sqlArr, ";"), 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))
sqlArr = append(sqlArr, meta.GenerateIndexDDL(indexs, *tabInfo)...)
return strings.Join(sqlArr, ";"), nil
}
// 获取建索引ddl
func (md *MssqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
tbName := tableInfo.TableName
if tableInfo.TableNewName != "" {
tbName = tableInfo.TableNewName
}
sqls := make([]string, 0)
comments := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tbName, colName)
sqls = append(sqls, fmt.Sprintf("create %s NONCLUSTERED index %s on %s.%s(%s)", unique, indexName, md.dc.Info.CurrentSchema(), tbName, index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'INDEX', N'%s'", index.IndexComment, md.dc.Info.CurrentSchema(), tbName, indexName))
}
}
return builder.String() + commentBuilder.String(), nil
return sqls
}
func (md *MssqlMetaData) genColumnBasicSql(column dbi.Column) string {
meta := md.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
dataType := string(column.DataType)
incr := ""
if column.IsIdentity {
incr = " IDENTITY(1,1)"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, dataType) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
columnSql := fmt.Sprintf(" %s %s %s %s %s", colName, column.ShowDataType, incr, nullAble, defVal)
return columnSql
}
// 获取建表ddl
func (md *MssqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
tbName := tableInfo.TableName
if tableInfo.TableNewName != "" {
tbName = tableInfo.TableNewName
}
meta := md.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
sqlArr := make([]string, 0)
// 删除表
if dropBeforeCreate {
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s", meta.QuoteIdentifier(tbName)))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tbName))
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))
}
fields = append(fields, md.genColumnBasicSql(column))
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s', N'COLUMN', N'%s'"
// 防止注释内含有特殊字符串导致sql出错
if column.ColumnComment != "" {
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf(commentTmp, comment, md.dc.Info.CurrentSchema(), tbName, column.ColumnName))
}
}
// create
createSql += strings.Join(fields, ",\n")
if len(pks) > 0 {
createSql += fmt.Sprintf(", \n PRIMARY KEY CLUSTERED (%s)", strings.Join(pks, ","))
}
createSql += "\n)"
// comment
tableCommentSql := ""
if tableInfo.TableComment != "" {
commentTmp := "EXECUTE sp_addextendedproperty N'MS_Description', N'%s', N'SCHEMA', N'%s', N'TABLE', N'%s'"
tableCommentSql = fmt.Sprintf(commentTmp, replacer.Replace(tableInfo.TableComment), md.dc.Info.CurrentSchema(), tbName)
}
sqlArr = append(sqlArr, createSql)
if tableCommentSql != "" {
sqlArr = append(sqlArr, tableCommentSql)
}
if len(columnComments) > 0 {
sqlArr = append(sqlArr, columnComments...)
}
return sqlArr
}
// 获取建表ddl
func (md *MssqlMetaData) GetTableDDL(tableName string) (string, error) {
return md.CopyTableDDL(tableName, "")
// 1.获取表信息
tbs, err := md.GetTables(tableName)
tableInfo := &dbi.Table{}
if err != nil && len(tbs) > 0 {
logx.Errorf("获取表信息失败, %s", tableName)
return "", err
}
tableInfo.TableName = tbs[0].TableName
tableInfo.TableComment = tbs[0].TableComment
// 2.获取列信息
columns, err := md.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
return "", err
}
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, false)
// 3.获取索引信息
indexs, err := md.GetTableIndex(tableName)
if err != nil {
logx.Errorf("获取索引信息失败, %s", tableName)
return "", err
}
// 组装返回
tableDDLArr = append(tableDDLArr, md.GenerateIndexDDL(indexs, *tableInfo)...)
return strings.Join(tableDDLArr, ";\n"), nil
}
func (md *MssqlMetaData) GetSchemas() ([]string, error) {

View File

@@ -4,7 +4,6 @@ import (
"database/sql"
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/utils/collx"
"strings"
"time"
)
@@ -98,99 +97,35 @@ func (md *MysqlDialect) ToColumn(column *dbi.Column) {
column.CharMaxLength = 1000
} else {
column.DataType = dbi.ColumnDataType(ctype)
// 如果是int整型删除精度
if strings.Contains(strings.ToLower(ctype), "int") {
column.NumScale = 0
column.CharMaxLength = 0
} else
// 如果是text删除长度
if strings.Contains(strings.ToLower(ctype), "text") {
column.CharMaxLength = 0
column.NumPrecision = 0
}
}
}
func (md *MysqlDialect) CreateTable(columns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
if dropOldTable {
_, _ = md.dc.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tableInfo.TableName))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", tableInfo.TableName)
fields := make([]string, 0)
pks := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range columns {
if column.IsPrimaryKey {
pks = append(pks, column.ColumnName)
}
fields = append(fields, md.genColumnBasicSql(column))
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 "
if tableInfo.TableComment != "" {
replacer := strings.NewReplacer(";", "", "'", "")
createSql += fmt.Sprintf(" COMMENT '%s'", replacer.Replace(tableInfo.TableComment))
}
_, err := md.dc.Exec(createSql)
return 1, err
}
func (md *MysqlDialect) genColumnBasicSql(column dbi.Column) string {
incr := ""
if column.IsIdentity {
incr = " AUTO_INCREMENT"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(column.ColumnType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
sqlArr := md.dc.GetMetaData().GenerateTableDDL(columns, tableInfo, dropOldTable)
for _, sqlStr := range sqlArr {
_, err := md.dc.Exec(sqlStr)
if err != nil {
return 0, err
}
}
comment := ""
if column.ColumnComment != "" {
// 防止注释内含有特殊字符串导致sql出错
replacer := strings.NewReplacer(";", "", "'", "")
commentStr := replacer.Replace(column.ColumnComment)
comment = fmt.Sprintf(" COMMENT '%s'", commentStr)
}
columnSql := fmt.Sprintf(" %s %s %s %s %s %s", md.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.GetColumnType(), nullAble, incr, defVal, comment)
return columnSql
return len(sqlArr), nil
}
func (md *MysqlDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
meta := md.dc.GetMetaData()
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqlTmp := "ALTER TABLE %s ADD %s INDEX %s(%s) USING BTREE COMMENT '%s'"
replacer := strings.NewReplacer(";", "", "'", "")
_, err := md.dc.Exec(fmt.Sprintf(sqlTmp, meta.QuoteIdentifier(tableInfo.TableName), unique, indexName, index.ColumnName, replacer.Replace(index.IndexComment)))
sqlArr := meta.GenerateIndexDDL(indexs, tableInfo)
for _, sqlStr := range sqlArr {
_, err := md.dc.Exec(sqlStr)
if err != nil {
return err
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
@@ -100,10 +101,10 @@ func (md *MysqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
columns := make([]dbi.Column, 0)
for _, re := range res {
columns = append(columns, dbi.Column{
column := dbi.Column{
TableName: anyx.ConvString(re["tableName"]),
ColumnName: anyx.ConvString(re["columnName"]),
ColumnType: strings.Replace(anyx.ConvString(re["columnType"]), " unsigned", "", 1),
DataType: dbi.ColumnDataType(anyx.ConvString(re["dataType"])),
ColumnComment: anyx.ConvString(re["columnComment"]),
Nullable: anyx.ConvString(re["nullable"]),
@@ -113,7 +114,11 @@ func (md *MysqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
CharMaxLength: anyx.ConvInt(re["charMaxLength"]),
NumPrecision: anyx.ConvInt(re["numPrecision"]),
NumScale: anyx.ConvInt(re["numScale"]),
})
}
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
return columns, nil
}
@@ -174,13 +179,141 @@ func (md *MysqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
return result, nil
}
// 获取建索引ddl
func (md *MysqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
meta := md.dc.GetMetaData()
sqlArr := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqlTmp := "ALTER TABLE %s ADD %s INDEX %s(%s) USING BTREE COMMENT '%s'"
replacer := strings.NewReplacer(";", "", "'", "")
sqlArr = append(sqlArr, fmt.Sprintf(sqlTmp, meta.QuoteIdentifier(tableInfo.TableName), unique, indexName, index.ColumnName, replacer.Replace(index.IndexComment)))
}
return sqlArr
}
func (md *MysqlMetaData) genColumnBasicSql(column dbi.Column) string {
replacer := strings.NewReplacer(";", "", "'", "")
dataType := string(column.DataType)
incr := ""
if column.IsIdentity {
incr = " AUTO_INCREMENT"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(dataType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
comment := ""
if column.ColumnComment != "" {
// 防止注释内含有特殊字符串导致sql出错
commentStr := replacer.Replace(column.ColumnComment)
comment = fmt.Sprintf(" COMMENT '%s'", commentStr)
}
columnSql := fmt.Sprintf(" %s %s %s %s %s %s", md.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.ShowDataType, nullAble, incr, defVal, comment)
return columnSql
}
// 获取建表ddl
func (md *MysqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
meta := md.dc.GetMetaData()
sqlArr := make([]string, 0)
if dropBeforeCreate {
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s;", meta.QuoteIdentifier(tableInfo.TableName)))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tableInfo.TableName))
fields := make([]string, 0)
pks := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range columns {
if column.IsPrimaryKey {
pks = append(pks, column.ColumnName)
}
fields = append(fields, md.genColumnBasicSql(column))
}
// 建表ddl
createSql += strings.Join(fields, ",\n")
if len(pks) > 0 {
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += fmt.Sprintf(") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ")
// 表注释
if tableInfo.TableComment != "" {
replacer := strings.NewReplacer(";", "", "'", "")
createSql += fmt.Sprintf(" COMMENT '%s'", replacer.Replace(tableInfo.TableComment))
}
sqlArr = append(sqlArr, createSql)
return sqlArr
}
// 获取建表ddl
func (md *MysqlMetaData) GetTableDDL(tableName string) (string, error) {
_, res, err := md.dc.Query(fmt.Sprintf("show create table `%s` ", tableName))
if err != nil {
// 1.获取表信息
tbs, err := md.GetTables(tableName)
tableInfo := &dbi.Table{}
if err != nil && len(tbs) > 0 {
logx.Errorf("获取表信息失败, %s", tableName)
return "", err
}
return anyx.ConvString(res[0]["Create Table"]) + ";", nil
tableInfo.TableName = tbs[0].TableName
tableInfo.TableComment = tbs[0].TableComment
// 2.获取列信息
columns, err := md.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
return "", err
}
tableDDLArr := md.GenerateTableDDL(columns, *tableInfo, false)
// 3.获取索引信息
indexs, err := md.GetTableIndex(tableName)
if err != nil {
logx.Errorf("获取索引信息失败, %s", tableName)
return "", err
}
// 组装返回
tableDDLArr = append(tableDDLArr, md.GenerateIndexDDL(indexs, *tableInfo)...)
return strings.Join(tableDDLArr, ";\n"), nil
}
func (md *MysqlMetaData) GetSchemas() ([]string, error) {

View File

@@ -5,7 +5,6 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"strings"
"time"
@@ -156,213 +155,60 @@ func (od *OracleDialect) CopyTable(copy *dbi.DbCopyTable) error {
return err
}
// func (od *OracleDialect) TransColumns(columns []dbi.Column) []dbi.Column {
// var commonColumns []dbi.Column
// for _, column := range columns {
// // 取出当前数据库类型
// arr := strings.Split(column.ColumnType, "(")
// ctype := arr[0]
func (od *OracleDialect) ToCommonColumn(dialectColumn *dbi.Column) {
// 翻译为通用数据库类型
dataType := dialectColumn.DataType
t1 := commonColumnTypeMap[string(dataType)]
if t1 == "" {
dialectColumn.DataType = dbi.CommonTypeVarchar
dialectColumn.CharMaxLength = 2000
} else {
dialectColumn.DataType = t1
// 如果是number类型需要根据公共类型加上长度, 如 bigint 需要转换为number(19,0)
if strings.Contains(string(t1), "NUMBER") {
dialectColumn.CharMaxLength = 19
}
}
}
// // 翻译为通用数据库类型
// t1 := commonColumnTypeMap[ctype]
// if t1 == "" {
// ctype = "NVARCHAR2(2000)"
// } else {
// // 回写到列信息
// if t1 == "NUMBER" {
// // 如果是转number类型需要根据公共类型加上长度, 如 bigint 需要转换为number(19,0)
// if column.ColumnType == dbi.CommonTypeBigint {
// ctype = t1 + "(19, 0)"
// } else {
// ctype = t1
// }
// } else if t1 != "NUMBER" && len(arr) > 1 {
// ctype = t1 + "(" + arr[1]
// } else {
// ctype = t1
// }
// }
// column.ColumnType = ctype
// commonColumns = append(commonColumns, column)
// }
// return commonColumns
// }
func (od *OracleDialect) ToColumn(commonColumn *dbi.Column) {
ctype := oracleColumnTypeMap[commonColumn.DataType]
if ctype == "" {
commonColumn.DataType = "NVARCHAR2"
commonColumn.CharMaxLength = 2000
} else {
commonColumn.DataType = dbi.ColumnDataType(ctype)
// 如果类型是数字,类型后不需要带长度
if strings.Contains(strings.ToLower(ctype), "int") {
commonColumn.CharMaxLength = 0
commonColumn.NumPrecision = 0
} else if strings.Contains(strings.ToLower(ctype), "char") {
// 如果是字符串类型长度最大4000否则修改字段类型为clob
if commonColumn.CharMaxLength > 4000 {
commonColumn.DataType = "CLOB"
commonColumn.CharMaxLength = 0
}
}
}
}
func (od *OracleDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
meta := od.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
quoteTableName := meta.QuoteIdentifier(tableInfo.TableName)
if dropOldTable {
// 如果表存在,先删除表
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;
end;
`
_, _ = od.dc.Exec(fmt.Sprintf(dropSqlTmp, tableInfo.TableName, tableInfo.TableName))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (", quoteTableName)
fields := make([]string, 0)
pks := make([]string, 0)
columnComments := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range commonColumns {
// 取出当前数据库类型
arr := strings.Split(column.ColumnType, "(")
ctype := arr[0]
// 翻译为通用数据库类型
t1 := oracleColumnTypeMap[dbi.ColumnDataType(ctype)]
if t1 == "" {
ctype = "NVARCHAR2(2000)"
} else {
// 回写到列信息
if len(arr) > 1 {
// 如果是字符串类型长度最大4000否则修改字段类型为clob
if strings.Contains(strings.ToLower(t1), "char") {
match := bracketsRegexp.FindStringSubmatch(column.ColumnType)
if len(match) > 1 {
size := anyx.ConvInt(match[1])
if size >= 4000 { // 如果长度超过4000则替换为text类型
ctype = "CLOB"
} else {
ctype = fmt.Sprintf("%s(%d)", t1, size)
}
} else {
ctype = t1 + "(2000)"
}
} else {
ctype = t1 + "(" + arr[1]
}
} else {
ctype = t1
}
}
column.ColumnType = ctype
if column.IsPrimaryKey {
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
}
fields = append(fields, od.genColumnBasicSql(column))
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(column.ColumnComment)
if comment != "" {
columnComments = append(columnComments, fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", quoteTableName, meta.QuoteIdentifier(column.ColumnName), comment))
}
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
tableCommentSql = fmt.Sprintf(" COMMENT ON TABLE %s is '%s'", meta.QuoteIdentifier(tableInfo.TableName), replacer.Replace(tableInfo.TableComment))
}
sqlArr := meta.GenerateTableDDL(commonColumns, tableInfo, dropOldTable)
// 需要分开执行sql
var err error
if createSql != "" {
_, err = od.dc.Exec(createSql)
}
if tableCommentSql != "" {
_, err = od.dc.Exec(tableCommentSql)
}
if len(columnComments) > 0 {
for _, commentSql := range columnComments {
_, err = od.dc.Exec(commentSql)
for _, sqlStr := range sqlArr {
_, err := od.dc.Exec(sqlStr)
if err != nil {
return 0, err
}
}
return 1, err
}
func (od *OracleDialect) 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)
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的
if column.ColumnDefault != "" {
mark := false
// 哪些字段类型默认值需要加引号
if collx.ArrayAnyMatches([]string{"CHAR", "LONG", "DATE", "TIME", "CLOB", "BLOB", "BFILE"}, column.ColumnType) {
// 默认值是时间日期函数的必须要加引号
val := strings.ToUpper(column.ColumnDefault)
if collx.ArrayAnyMatches([]string{"DATE", "TIMESTAMP"}, column.ColumnType) && val == "CURRENT_DATE" || val == "CURRENT_TIMESTAMP" {
mark = false
} else {
mark = true
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
} else {
// 如果是数字,默认值提取数字
if collx.ArrayAnyMatches([]string{"NUM", "INT"}, column.ColumnType) {
match := bracketsRegexp.FindStringSubmatch(column.ColumnType)
if len(match) > 1 {
length := anyx.ConvInt(match[1])
defVal = fmt.Sprintf(" DEFAULT %d", length)
} else {
defVal = fmt.Sprintf(" DEFAULT 0")
}
}
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
columnSql := fmt.Sprintf(" %s %s %s %s", colName, column.ColumnType, defVal, nullAble)
return columnSql
return len(sqlArr), nil
}
func (od *OracleDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
meta := od.dc.GetMetaData()
sqls := make([]string, 0)
comments := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqls = append(sqls, fmt.Sprintf("CREATE %s INDEX %s ON %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s IS '%s'", indexName, index.IndexComment))
}
}
_, err := od.dc.Exec(strings.Join(sqls, ";"))
// 添加注释
if len(comments) > 0 {
_, err = od.dc.Exec(strings.Join(comments, ";"))
}
sqlArr := meta.GenerateIndexDDL(indexs, tableInfo)
_, err := od.dc.Exec(strings.Join(sqlArr, ";"))
return err
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
@@ -122,17 +123,21 @@ func (od *OracleMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
if strings.Contains(defaultVal, ".nextval") {
defaultVal = ""
}
columns = append(columns, dbi.Column{
column := dbi.Column{
TableName: anyx.ConvString(re["TABLE_NAME"]),
ColumnName: anyx.ConvString(re["COLUMN_NAME"]),
ColumnType: anyx.ConvString(re["COLUMN_TYPE"]),
DataType: dbi.ColumnDataType(anyx.ConvString(re["DATA_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.ConvInt(re["NUM_SCALE"]),
})
}
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
return columns, nil
}
@@ -191,71 +196,188 @@ func (od *OracleMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
return result, nil
}
// 获取建索引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 {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqls = append(sqls, fmt.Sprintf("CREATE %s INDEX %s ON %s(%s)", unique, indexName, meta.QuoteIdentifier(tableInfo.TableName), index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s IS '%s'", indexName, index.IndexComment))
}
}
sqlArr := make([]string, 0)
sqlArr = append(sqlArr, sqls...)
if len(comments) > 0 {
sqlArr = append(sqlArr, comments...)
}
return sqlArr
}
func (od *OracleMetaData) genColumnBasicSql(column dbi.Column) string {
meta := od.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
dataType := string(column.DataType)
if column.IsIdentity {
// 如果是自增不需要设置默认值和空值自增列数据类型必须是number
return fmt.Sprintf(" %s NUMBER generated by default as IDENTITY", colName)
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的
if column.ColumnDefault != "" {
mark := false
// 哪些字段类型默认值需要加引号
if collx.ArrayAnyMatches([]string{"CHAR", "LONG", "DATE", "TIME", "CLOB", "BLOB", "BFILE"}, dataType) {
// 默认值是时间日期函数的必须要加引号
val := strings.ToUpper(column.ColumnDefault)
if collx.ArrayAnyMatches([]string{"DATE", "TIMESTAMP"}, dataType) && val == "CURRENT_DATE" || val == "CURRENT_TIMESTAMP" {
mark = false
} else {
mark = true
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
} else {
// 如果是数字,默认值提取数字
if collx.ArrayAnyMatches([]string{"NUM", "INT"}, dataType) {
match := bracketsRegexp.FindStringSubmatch(dataType)
if len(match) > 1 {
length := anyx.ConvInt(match[1])
defVal = fmt.Sprintf(" DEFAULT %d", length)
} else {
defVal = fmt.Sprintf(" DEFAULT 0")
}
}
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
columnSql := fmt.Sprintf(" %s %s %s %s", colName, column.ShowDataType, defVal, nullAble)
return columnSql
}
// 获取建表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
func (od *OracleMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
meta := od.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
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;
end;
`
sqlArr = append(sqlArr, fmt.Sprintf(dropSqlTmp, tableInfo.TableName, tableInfo.TableName))
}
// 建表ddl
var builder strings.Builder
for _, re := range res {
builder.WriteString(anyx.ConvString(re["TABLE_DDL"]))
// 组装建表语句
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))
}
fields = append(fields, od.genColumnBasicSql(column))
// 防止注释内含有特殊字符串导致sql出错
if column.ColumnComment != "" {
comment := replacer.Replace(column.ColumnComment)
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)
// 表注释
_, 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 {
tableCommentSql := ""
if tableInfo.TableComment != "" {
tableCommentSql = fmt.Sprintf("COMMENT ON TABLE %s is '%s'", meta.QuoteIdentifier(tableInfo.TableName), replacer.Replace(tableInfo.TableComment))
sqlArr = append(sqlArr, tableCommentSql)
}
// 列注释
if len(columnComments) > 0 {
sqlArr = append(sqlArr, columnComments...)
}
return sqlArr
}
// 获取建表ddl
func (od *OracleMetaData) GetTableDDL(tableName string) (string, error) {
// 1.获取表信息
tbs, err := od.GetTables(tableName)
tableInfo := &dbi.Table{}
if err != nil && len(tbs) > 0 {
logx.Errorf("获取表信息失败, %s", tableName)
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)
}
}
tableInfo.TableName = tbs[0].TableName
tableInfo.TableComment = tbs[0].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)
// 2.获取列信息
columns, err := od.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
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)
tableDDLArr := od.GenerateTableDDL(columns, *tableInfo, false)
// 3.获取索引信息
indexs, err := od.GetTableIndex(tableName)
if err != nil {
logx.Errorf("获取索引信息失败, %s", tableName)
return "", err
}
for _, re := range res {
builder.WriteString("\n\n" + anyx.ConvString(re["INDEX_DEF"]))
}
return builder.String(), nil
// 组装返回
tableDDLArr = append(tableDDLArr, od.GenerateIndexDDL(indexs, *tableInfo)...)
return strings.Join(tableDDLArr, ";\n"), nil
}
// 获取DM当前连接的库可访问的schemaNames

View File

@@ -66,7 +66,7 @@ func (pd *PgsqlDialect) pgsqlOnDuplicateStrategySql(duplicateStrategy int, table
updateColumns = append(updateColumns, fmt.Sprintf("%s = excluded.%s", col, col))
}
// 查询唯一键名,拼接冲突sql
_, keyRes, _ := pd.dc.Query("SELECT constraint_name FROM information_schema.table_constraints WHERE constraint_schema = $1 AND table_name = $2 AND constraint_type in ('PRIMARY KEY', 'UNIQUE') ", pd.currentSchema(), tableName)
_, keyRes, _ := pd.dc.Query("SELECT constraint_name FROM information_schema.table_constraints WHERE constraint_schema = $1 AND table_name = $2 AND constraint_type in ('PRIMARY KEY', 'UNIQUE') ", pd.dc.Info.CurrentSchema(), tableName)
if len(keyRes) > 0 {
for _, re := range keyRes {
key := anyx.ToString(re["constraint_name"])
@@ -117,11 +117,6 @@ func (pd *PgsqlDialect) gaussOnDuplicateStrategySql(duplicateStrategy int, table
return suffix
}
// 从连接信息中获取数据库和schema信息
func (pd *PgsqlDialect) currentSchema() string {
return pd.dc.Info.CurrentSchema()
}
func (pd *PgsqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
tableName := copy.TableName
// 生成新表名,为老表明+_copy_时间戳
@@ -180,189 +175,48 @@ func (pd *PgsqlDialect) CopyTable(copy *dbi.DbCopyTable) error {
return err
}
func (pd *PgsqlDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
meta := pd.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
if dropOldTable {
_, _ = pd.dc.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tableInfo.TableName))
func (pd *PgsqlDialect) ToCommonColumn(column *dbi.Column) {
// 翻译为通用数据库类型
dataType := column.DataType
t1 := commonColumnTypeMap[string(dataType)]
if t1 == "" {
column.DataType = dbi.CommonTypeVarchar
column.CharMaxLength = 2000
} else {
column.DataType = t1
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tableInfo.TableName))
fields := make([]string, 0)
pks := make([]string, 0)
columnComments := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range commonColumns {
// 取出当前数据库类型
arr := strings.Split(column.ColumnType, "(")
ctype := arr[0]
// 翻译为通用数据库类型
t1 := pgsqlColumnTypeMap[dbi.ColumnDataType(ctype)]
if t1 == "" {
ctype = "varchar(2000)"
} else {
// 回写到列信息
if len(arr) > 1 {
if strings.Contains(strings.ToLower(t1), "int") {
// 如果是数字,类型后不需要带长度
ctype = t1
} else if strings.Contains(strings.ToLower(t1), "char") {
// 如果是字符串,长度翻倍
match := bracketsRegexp.FindStringSubmatch(column.ColumnType)
if len(match) > 1 {
ctype = fmt.Sprintf("%s(%d)", t1, anyx.ConvInt(match[1])*2)
} else {
ctype = t1 + "(1000)"
}
} else {
ctype = t1 + "(" + arr[1]
}
} else {
ctype = t1
}
}
column.ColumnType = ctype
if column.IsPrimaryKey {
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
}
fields = append(fields, pd.genColumnBasicSql(column))
commentTmp := "comment on column %s.%s is '%s'"
// 防止注释内含有特殊字符串导致sql出错
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf(commentTmp, column.TableName, column.ColumnName, comment))
}
createSql += strings.Join(fields, ",")
if len(pks) > 0 {
createSql += fmt.Sprintf(", PRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
commentTmp := "comment on table %s is '%s'"
tableCommentSql = fmt.Sprintf(commentTmp, tableInfo.TableName, replacer.Replace(tableInfo.TableComment))
}
columnCommentSql := strings.Join(columnComments, ";")
sqls := make([]string, 0)
if createSql != "" {
sqls = append(sqls, createSql)
}
if tableCommentSql != "" {
sqls = append(sqls, tableCommentSql)
}
if columnCommentSql != "" {
sqls = append(sqls, columnCommentSql)
}
_, err := pd.dc.Exec(strings.Join(sqls, ";"))
return 1, err
}
func (pd *PgsqlDialect) genColumnBasicSql(column dbi.Column) string {
func (pd *PgsqlDialect) ToColumn(commonColumn *dbi.Column) {
ctype := pgsqlColumnTypeMap[commonColumn.DataType]
if ctype == "" {
commonColumn.DataType = "varchar"
commonColumn.CharMaxLength = 2000
} else {
commonColumn.DataType = dbi.ColumnDataType(ctype)
// 哪些字段可以指定长度
if !collx.ArrayAnyMatches([]string{"char", "time", "bit", "num", "decimal"}, ctype) {
commonColumn.CharMaxLength = 0
commonColumn.NumPrecision = 0
} else if strings.Contains(strings.ToLower(ctype), "char") {
// 如果类型是文本,长度翻倍
commonColumn.CharMaxLength = commonColumn.CharMaxLength * 2
}
}
}
func (pd *PgsqlDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
meta := pd.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
// 如果是自增类型需要转换为serial
if column.IsIdentity {
if column.ColumnType == "int4" {
column.ColumnType = "serial"
} else if column.ColumnType == "int2" {
column.ColumnType = "smallserial"
} else if column.ColumnType == "int8" {
column.ColumnType = "bigserial"
} else {
column.ColumnType = "bigserial"
}
return fmt.Sprintf(" %s %s NOT NULL", colName, column.ColumnType)
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
// 如果字段不能为空,则设置默认值
if column.ColumnDefault == "" {
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, strings.ToLower(column.ColumnType)) {
// 文本默认值为空字符串
column.ColumnDefault = " "
} else if collx.ArrayAnyMatches([]string{"int", "num"}, strings.ToLower(column.ColumnType)) {
// 数字默认值为0
column.ColumnDefault = "0"
}
}
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(column.ColumnType)) {
// 如果是文本类型,则默认值不能带括号
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, strings.ToLower(column.ColumnType)) {
column.ColumnDefault = ""
}
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
// 如果数据类型是日期时间,则写死默认值函数
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) {
column.ColumnDefault = "CURRENT_TIMESTAMP"
}
if column.ColumnDefault != "" {
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
}
columnSql := fmt.Sprintf(" %s %s %s %s ", colName, column.ColumnType, nullAble, defVal)
return columnSql
sqlArr := meta.GenerateTableDDL(commonColumns, tableInfo, dropOldTable)
_, err := pd.dc.Exec(strings.Join(sqlArr, ";"))
return len(sqlArr), err
}
func (pd *PgsqlDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
sqls := make([]string, 0)
comments := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
// 如果索引名存在,先删除索引
sqls = append(sqls, fmt.Sprintf("drop index if exists %s.%s", pd.currentSchema(), indexName))
// 创建索引
sqls = append(sqls, fmt.Sprintf("CREATE %s INDEX %s on %s.%s(%s)", unique, indexName, pd.currentSchema(), tableInfo.TableName, index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s.%s IS '%s'", pd.currentSchema(), indexName, index.IndexComment))
}
}
_, err := pd.dc.Exec(strings.Join(sqls, ";"))
// 添加注释
if len(comments) > 0 {
_, err = pd.dc.Exec(strings.Join(comments, ";"))
}
sqlArr := pd.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
_, err := pd.dc.Exec(strings.Join(sqlArr, ";"))
return err
}

View File

@@ -30,7 +30,7 @@ type PostgresMeta struct {
Param string
}
func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
func (pm *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
driverName := "postgres"
// SSH Conect
if d.SshTunnelMachineId > 0 {
@@ -74,8 +74,8 @@ func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
dsn = fmt.Sprintf("%s %s", dsn, strings.Join(strings.Split(d.Params, "&"), " "))
}
if md.Param != "" && !strings.Contains(dsn, "dbtype") {
dsn = fmt.Sprintf("%s %s", dsn, md.Param)
if pm.Param != "" && !strings.Contains(dsn, "dbtype") {
dsn = fmt.Sprintf("%s %s", dsn, pm.Param)
}
return sql.Open(driverName, dsn)
@@ -94,8 +94,8 @@ type PqSqlDialer struct {
sshTunnelMachineId int
}
func (d *PqSqlDialer) Open(name string) (driver.Conn, error) {
return pq.DialOpen(d, name)
func (pd *PqSqlDialer) Open(name string) (driver.Conn, error) {
return pq.DialOpen(pd, name)
}
func (pd *PqSqlDialer) Dial(network, address string) (net.Conn, error) {

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/anyx"
"mayfly-go/pkg/utils/collx"
"mayfly-go/pkg/utils/stringx"
@@ -18,7 +19,6 @@ const (
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 {
@@ -99,17 +99,23 @@ func (pd *PgsqlMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
columns := make([]dbi.Column, 0)
for _, re := range res {
columns = append(columns, dbi.Column{
column := dbi.Column{
TableName: anyx.ConvString(re["tableName"]),
ColumnName: anyx.ConvString(re["columnName"]),
ColumnType: anyx.ConvString(re["columnType"]),
DataType: dbi.ColumnDataType(anyx.ConvString(re["dataType"])),
CharMaxLength: anyx.ConvInt(re["charMaxLength"]),
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"]),
NumPrecision: anyx.ConvInt(re["numPrecision"]),
NumScale: anyx.ConvInt(re["numScale"]),
})
}
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
return columns, nil
}
@@ -168,20 +174,207 @@ func (pd *PgsqlMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
return result, nil
}
func (pd *PgsqlMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
creates := make([]string, 0)
drops := make([]string, 0)
comments := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
// 如果索引名存在,先删除索引
drops = append(drops, fmt.Sprintf("drop index if exists %s.%s", pd.dc.Info.CurrentSchema(), indexName))
// 创建索引
creates = append(creates, fmt.Sprintf("CREATE %s INDEX %s on %s.%s(%s)", unique, indexName, pd.dc.Info.CurrentSchema(), tableInfo.TableName, index.ColumnName))
if index.IndexComment != "" {
comments = append(comments, fmt.Sprintf("COMMENT ON INDEX %s.%s IS '%s'", pd.dc.Info.CurrentSchema(), indexName, index.IndexComment))
}
}
sqlArr := make([]string, 0)
if len(drops) > 0 {
sqlArr = append(sqlArr, drops...)
}
if len(creates) > 0 {
sqlArr = append(sqlArr, creates...)
}
if len(comments) > 0 {
sqlArr = append(sqlArr, comments...)
}
return sqlArr
}
func (pd *PgsqlMetaData) genColumnBasicSql(column dbi.Column) string {
meta := pd.dc.GetMetaData()
colName := meta.QuoteIdentifier(column.ColumnName)
dataType := string(column.DataType)
// 如果是自增类型需要转换为serial
if column.IsIdentity {
if dataType == "int4" {
column.DataType = "serial"
} else if dataType == "int2" {
column.DataType = "smallserial"
} else if dataType == "int8" {
column.DataType = "bigserial"
} else {
column.DataType = "bigserial"
}
return fmt.Sprintf(" %s %s NOT NULL", colName, column.ShowDataType)
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
// 如果字段不能为空,则设置默认值
if column.ColumnDefault == "" {
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, strings.ToLower(dataType)) {
// 文本默认值为空字符串
column.ColumnDefault = " "
} else if collx.ArrayAnyMatches([]string{"int", "num"}, strings.ToLower(dataType)) {
// 数字默认值为0
column.ColumnDefault = "0"
}
}
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, dataType) {
// 如果是文本类型,则默认值不能带括号
if collx.ArrayAnyMatches([]string{"char", "text", "lob"}, dataType) {
column.ColumnDefault = ""
}
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
// 如果数据类型是日期时间,则写死默认值函数
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(dataType)) {
column.ColumnDefault = "CURRENT_TIMESTAMP"
}
if column.ColumnDefault != "" {
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
}
columnSql := fmt.Sprintf(" %s %s %s %s ", colName, column.ShowDataType, nullAble, defVal)
return columnSql
}
func (pd *PgsqlMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
meta := pd.dc.GetMetaData()
replacer := strings.NewReplacer(";", "", "'", "")
sqlArr := make([]string, 0)
if dropBeforeCreate {
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s", meta.QuoteIdentifier(tableInfo.TableName)))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", meta.QuoteIdentifier(tableInfo.TableName))
fields := make([]string, 0)
pks := make([]string, 0)
columnComments := make([]string, 0)
commentTmp := "comment on column %s.%s is '%s'"
for _, column := range columns {
if column.IsPrimaryKey {
pks = append(pks, meta.QuoteIdentifier(column.ColumnName))
}
fields = append(fields, pd.genColumnBasicSql(column))
// 防止注释内含有特殊字符串导致sql出错
if column.ColumnComment != "" {
comment := replacer.Replace(column.ColumnComment)
columnComments = append(columnComments, fmt.Sprintf(commentTmp, column.TableName, column.ColumnName, comment))
}
}
createSql += strings.Join(fields, ",\n")
if len(pks) > 0 {
createSql += fmt.Sprintf(", \nPRIMARY KEY (%s)", strings.Join(pks, ","))
}
createSql += ")"
tableCommentSql := ""
if tableInfo.TableComment != "" {
commentTmp := "comment on table %s is '%s'"
tableCommentSql = fmt.Sprintf(commentTmp, tableInfo.TableName, replacer.Replace(tableInfo.TableComment))
}
// create
sqlArr = append(sqlArr, createSql)
// table comment
if tableCommentSql != "" {
sqlArr = append(sqlArr, tableCommentSql)
}
// column comment
if len(columnComments) > 0 {
sqlArr = append(sqlArr, columnComments...)
}
return sqlArr
}
// 获取建表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 {
// 1.获取表信息
tbs, err := pd.GetTables(tableName)
tableInfo := &dbi.Table{}
if err != nil && len(tbs) > 0 {
logx.Errorf("获取表信息失败, %s", tableName)
return "", err
}
tableInfo.TableName = tbs[0].TableName
tableInfo.TableComment = tbs[0].TableComment
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", pd.dc.Info.CurrentSchema(), tableName)
_, res, err := pd.dc.Query(ddlSql)
// 2.获取列信息
columns, err := pd.GetColumns(tableName)
if err != nil {
logx.Errorf("获取列信息失败, %s", tableName)
return "", err
}
return res[0]["sql"].(string), nil
tableDDLArr := pd.GenerateTableDDL(columns, *tableInfo, false)
// 3.获取索引信息
indexs, err := pd.GetTableIndex(tableName)
if err != nil {
logx.Errorf("获取索引信息失败, %s", tableName)
return "", err
}
// 组装返回
tableDDLArr = append(tableDDLArr, pd.GenerateIndexDDL(indexs, *tableInfo)...)
return strings.Join(tableDDLArr, ";\n"), nil
}
// 获取pgsql当前连接的库可访问的schemaNames
@@ -247,7 +440,7 @@ var (
"nchar": dbi.CommonTypeChar,
"varchar": dbi.CommonTypeVarchar,
"text": dbi.CommonTypeText,
"bytea": dbi.CommonTypeBinary,
"bytea": dbi.CommonTypeText,
"date": dbi.CommonTypeDate,
"time": dbi.CommonTypeTime,
"timestamp": dbi.CommonTypeTimestamp,
@@ -260,10 +453,10 @@ var (
dbi.CommonTypeBlob: "text",
dbi.CommonTypeLongblob: "text",
dbi.CommonTypeLongtext: "text",
dbi.CommonTypeBinary: "bytea",
dbi.CommonTypeBinary: "text",
dbi.CommonTypeMediumblob: "text",
dbi.CommonTypeMediumtext: "text",
dbi.CommonTypeVarbinary: "bytea",
dbi.CommonTypeVarbinary: "text",
dbi.CommonTypeInt: "int4",
dbi.CommonTypeSmallint: "int2",
dbi.CommonTypeTinyint: "int2",

View File

@@ -4,8 +4,6 @@ import (
"database/sql"
"fmt"
"mayfly-go/internal/db/dbm/dbi"
"mayfly-go/pkg/logx"
"mayfly-go/pkg/utils/collx"
"strings"
"time"
)
@@ -88,127 +86,46 @@ func (sd *SqliteDialect) CopyTable(copy *dbi.DbCopyTable) error {
return err
}
// func (sd *SqliteDialect) TransColumns(columns []dbi.Column) []dbi.Column {
// var commonColumns []dbi.Column
// for _, column := range columns {
// // 取出当前数据库类型
// arr := strings.Split(column.ColumnType, "(")
// ctype := arr[0]
// // 翻译为通用数据库类型
// t1 := commonColumnTypeMap[ctype]
// if t1 == "" {
// ctype = "varchar(2000)"
// } else {
// // 回写到列信息
// if len(arr) > 1 {
// ctype = t1 + "(" + arr[1]
// } else {
// ctype = t1
// }
// }
// column.ColumnType = ctype
// commonColumns = append(commonColumns, column)
// }
// return commonColumns
// }
func (sd *SqliteDialect) ToCommonColumn(dialectColumn *dbi.Column) {
// 翻译为通用数据库类型
dataType := dialectColumn.DataType
t1 := commonColumnTypeMap[string(dataType)]
if t1 == "" {
dialectColumn.DataType = dbi.CommonTypeVarchar
dialectColumn.CharMaxLength = 2000
} else {
dialectColumn.DataType = t1
}
}
func (sd *SqliteDialect) CreateTable(commonColumns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
tbName := sd.dc.GetMetaData().QuoteIdentifier(tableInfo.TableName)
if dropOldTable {
_, err := sd.dc.Exec(fmt.Sprintf("DROP TABLE IF EXISTS %s", tbName))
func (sd *SqliteDialect) ToColumn(commonColumn *dbi.Column) {
ctype := sqliteColumnTypeMap[commonColumn.DataType]
if ctype == "" {
commonColumn.DataType = "nvarchar"
commonColumn.CharMaxLength = 2000
}
}
func (sd *SqliteDialect) CreateTable(columns []dbi.Column, tableInfo dbi.Table, dropOldTable bool) (int, error) {
sqlArr := sd.dc.GetMetaData().GenerateTableDDL(columns, tableInfo, dropOldTable)
for _, sqlStr := range sqlArr {
_, err := sd.dc.Exec(sqlStr)
if err != nil {
logx.Error("删除表失败", err)
return 0, err
}
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", tbName)
fields := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range commonColumns {
// 取出当前数据库类型
arr := strings.Split(column.ColumnType, "(")
ctype := arr[0]
// 翻译为通用数据库类型
t1 := sqliteColumnTypeMap[dbi.ColumnDataType(ctype)]
if t1 == "" {
ctype = "nvarchar(2000)"
} else {
// 回写到列信息
if len(arr) > 1 {
ctype = t1 + "(" + arr[1]
}
}
column.ColumnType = ctype
fields = append(fields, sd.genColumnBasicSql(column))
}
createSql += strings.Join(fields, ",")
createSql += fmt.Sprintf(") ")
_, err := sd.dc.Exec(createSql)
return 1, err
return len(sqlArr), nil
}
func (sd *SqliteDialect) CreateIndex(tableInfo dbi.Table, indexs []dbi.Index) error {
sqls := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqlTmp := "CREATE %s INDEX %s ON \"%s\" (%s) "
sqls = append(sqls, fmt.Sprintf(sqlTmp, unique, indexName, tableInfo.TableName, index.ColumnName))
}
_, err := sd.dc.Exec(strings.Join(sqls, ";"))
return err
}
func (sd *SqliteDialect) genColumnBasicSql(column dbi.Column) string {
incr := ""
if column.IsIdentity {
incr = " AUTOINCREMENT"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
// 如果是主键,则直接返回,不判断默认值
if column.IsPrimaryKey {
return fmt.Sprintf(" %s integer PRIMARY KEY %s %s", column.ColumnName, incr, nullAble)
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(column.ColumnType)) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(column.ColumnType)) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
sqlArr := sd.dc.GetMetaData().GenerateIndexDDL(indexs, tableInfo)
for _, sqlStr := range sqlArr {
_, err := sd.dc.Exec(sqlStr)
if err != nil {
return err
}
}
return fmt.Sprintf(" %s %s %s %s", sd.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.ColumnType, nullAble, defVal)
return nil
}
func (sd *SqliteDialect) UpdateSequence(tableName string, columns []dbi.Column) {

View File

@@ -82,6 +82,18 @@ func (sd *SqliteMetaData) GetTables(tableNames ...string) ([]dbi.Table, error) {
return tables, nil
}
// GetDataTypes 正则提取字段类型中的关键字,
// 如 decimal(10,2) 提取decimal, 10 ,2
// 如:text 提取text,null,null
// 如:varchar(100) 提取varchar, 100
func (sd *SqliteMetaData) getDataTypes(dataType string) (string, string, string) {
matches := dataTypeRegexp.FindStringSubmatch(dataType)
if len(matches) == 0 {
return "", "", ""
}
return matches[1], matches[2], matches[3]
}
// 获取列元信息, 如列名等
func (sd *SqliteMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error) {
@@ -104,17 +116,33 @@ func (sd *SqliteMetaData) GetColumns(tableNames ...string) ([]dbi.Column, error)
if strings.Contains(defaultValue, "'") {
defaultValue = strings.ReplaceAll(defaultValue, "'", "")
}
columns = append(columns, dbi.Column{
column := 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,
})
}
// 切割类型和长度如果长度内有逗号则说明是decimal类型
columnType := anyx.ConvString(re["type"])
dataType, length, scale := sd.getDataTypes(columnType)
if scale != "0" && scale != "" {
column.NumPrecision = anyx.ConvInt(length)
column.NumScale = anyx.ConvInt(scale)
column.CharMaxLength = 0
} else {
column.CharMaxLength = anyx.ConvInt(length)
}
column.DataType = dbi.ColumnDataType(dataType)
// 初始化列展示的长度,精度
column.InitShowNum()
columns = append(columns, column)
}
}
return columns, nil
@@ -175,6 +203,91 @@ func (sd *SqliteMetaData) GetTableIndex(tableName string) ([]dbi.Index, error) {
return indexs, nil
}
// 获取建索引ddl
func (sd *SqliteMetaData) GenerateIndexDDL(indexs []dbi.Index, tableInfo dbi.Table) []string {
sqls := make([]string, 0)
for _, index := range indexs {
// 通过字段、表名拼接索引名
columnName := strings.ReplaceAll(index.ColumnName, "-", "")
columnName = strings.ReplaceAll(columnName, "_", "")
colName := strings.ReplaceAll(columnName, ",", "_")
keyType := "normal"
unique := ""
if index.IsUnique {
keyType = "unique"
unique = "unique"
}
indexName := fmt.Sprintf("%s_key_%s_%s", keyType, tableInfo.TableName, colName)
sqlTmp := "CREATE %s INDEX %s ON \"%s\" (%s) "
sqls = append(sqls, fmt.Sprintf(sqlTmp, unique, indexName, tableInfo.TableName, index.ColumnName))
}
return sqls
}
func (sd *SqliteMetaData) genColumnBasicSql(column dbi.Column) string {
incr := ""
if column.IsIdentity {
incr = " AUTOINCREMENT"
}
nullAble := ""
if column.Nullable == "NO" {
nullAble = " NOT NULL"
}
// 如果是主键,则直接返回,不判断默认值
if column.IsPrimaryKey {
return fmt.Sprintf(" %s integer PRIMARY KEY %s %s", column.ColumnName, incr, nullAble)
}
defVal := "" // 默认值需要判断引号,如函数是不需要引号的 // 为了防止跨源函数不支持 当默认值是函数时,不需要设置默认值
if column.ColumnDefault != "" && !strings.Contains(column.ColumnDefault, "(") {
// 哪些字段类型默认值需要加引号
mark := false
if collx.ArrayAnyMatches([]string{"char", "text", "date", "time", "lob"}, strings.ToLower(string(column.DataType))) {
// 当数据类型是日期时间,默认值是日期时间函数时,默认值不需要引号
if collx.ArrayAnyMatches([]string{"date", "time"}, strings.ToLower(string(column.DataType))) &&
collx.ArrayAnyMatches([]string{"DATE", "TIME"}, strings.ToUpper(column.ColumnDefault)) {
mark = false
} else {
mark = true
}
}
if mark {
defVal = fmt.Sprintf(" DEFAULT '%s'", column.ColumnDefault)
} else {
defVal = fmt.Sprintf(" DEFAULT %s", column.ColumnDefault)
}
}
return fmt.Sprintf(" %s %s %s %s", sd.dc.GetMetaData().QuoteIdentifier(column.ColumnName), column.ShowDataType, nullAble, defVal)
}
// 获取建表ddl
func (sd *SqliteMetaData) GenerateTableDDL(columns []dbi.Column, tableInfo dbi.Table, dropBeforeCreate bool) []string {
sqlArr := make([]string, 0)
tbName := sd.dc.GetMetaData().QuoteIdentifier(tableInfo.TableName)
if dropBeforeCreate {
sqlArr = append(sqlArr, fmt.Sprintf("DROP TABLE IF EXISTS %s", tbName))
}
// 组装建表语句
createSql := fmt.Sprintf("CREATE TABLE %s (\n", tbName)
fields := make([]string, 0)
// 把通用类型转换为达梦类型
for _, column := range columns {
fields = append(fields, sd.genColumnBasicSql(column))
}
createSql += strings.Join(fields, ",")
createSql += fmt.Sprintf(") ")
sqlArr = append(sqlArr, createSql)
return sqlArr
}
// 获取建表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)
@@ -203,6 +316,8 @@ var (
// 日期时间类型
datetimeRegexp = regexp.MustCompile(`(?i)datetime`)
dataTypeRegexp = regexp.MustCompile(`(\w+)\((\d*),?(\d*)\)`)
converter = new(DataConverter)
// sqlite数据类型 映射 公共数据类型