refactor: 新增达梦图标、调整前端DbDialect接口

This commit is contained in:
meilin.huang
2023-12-07 11:48:17 +08:00
parent 2430c4f6aa
commit 1db990b554
14 changed files with 117 additions and 74 deletions

File diff suppressed because one or more lines are too long

View File

@@ -26,6 +26,13 @@
"unicode": "e8b7",
"unicode_decimal": 59575
},
{
"icon_id": "12295203",
"name": "达梦数据库",
"font_class": "db-dm",
"unicode": "e6f0",
"unicode_decimal": 59120
},
{
"icon_id": "10055634",
"name": "云数据库MongoDB",

View File

@@ -8,10 +8,11 @@
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="type" label="类型" required>
<el-select style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
<el-option key="item.id" label="mysql" value="mysql"> </el-option>
<el-option key="item.id" label="postgres" value="postgres"> </el-option>
<el-option key="item.id" label="达梦(暂不支持ssh)" value="dm"> </el-option>
<el-select @change="changeDbType" style="width: 100%" v-model="form.type" placeholder="请选择数据库类型">
<el-option v-for="dt in dbTypes" :key="dt.type" :value="dt.type">
<SvgIcon :name="getDbDialect(dt.type).getInfo().icon" :size="18" />
{{ dt.label }}
</el-option>
</el-select>
</el-form-item>
<el-form-item prop="host" label="host" required>
@@ -86,6 +87,8 @@ import { ElMessage } from 'element-plus';
import { notBlank } from '@/common/assert';
import { RsaEncrypt } from '@/common/rsa';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
import { getDbDialect } from './dialect';
import SvgIcon from '@/components/svgIcon/index.vue';
const props = defineProps({
visible: {
@@ -121,7 +124,7 @@ const rules = {
{
required: true,
message: '请输入主机ip和port',
trigger: ['change', 'blur'],
trigger: ['blur'],
},
],
username: [
@@ -135,6 +138,21 @@ const rules = {
const dbForm: any = ref(null);
const dbTypes = [
{
type: 'mysql',
label: 'mysql',
},
{
type: 'postgres',
label: 'postgres',
},
{
type: 'dm',
label: '达梦(暂不支持ssh)',
},
];
const state = reactive({
dialogVisible: false,
tabActiveName: 'basic',
@@ -143,7 +161,7 @@ const state = reactive({
type: null,
name: null,
host: '',
port: 3306,
port: null,
username: null,
password: null,
params: null,
@@ -170,11 +188,17 @@ watch(props, (newValue: any) => {
state.form = { ...newValue.data };
state.oldUserName = state.form.username;
} else {
state.form = { port: 3306 } as any;
state.form = { port: null } as any;
state.oldUserName = null;
}
});
const changeDbType = (val: string) => {
if (!state.form.id) {
state.form.port = getDbDialect(val).getInfo().defaultPort as any;
}
};
const getDbPwd = async () => {
state.pwd = await dbApi.getInstancePwd.request({ id: state.form.id });
};

View File

@@ -7,7 +7,7 @@
<span v-if="data.type.value == SqlExecNodeType.DbInst">
<el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="250">
<template #reference>
<SvgIcon :name="getDbDialect(data.params.type).getIcon()" :size="18" />
<SvgIcon :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
</template>
<template #default>
<el-descriptions :column="1" size="small">
@@ -66,7 +66,7 @@
<el-descriptions-item label-align="right">
<template #label>
<div>
<SvgIcon :name="getDbDialect(nowDbInst.type).getIcon()" :size="18" />
<SvgIcon :name="getDbDialect(nowDbInst.type).getInfo().icon" :size="18" />
实例
</div>
</template>
@@ -104,7 +104,7 @@
{{ dt.params.name }}
</el-descriptions-item>
<el-descriptions-item label="host">
<SvgIcon :name="getDbDialect(dt.params.type).getIcon()" :size="18" />
<SvgIcon :name="getDbDialect(dt.params.type).getInfo().icon" :size="18" />
{{ dt.params.host }}
</el-descriptions-item>
<el-descriptions-item label="库名">

View File

@@ -332,7 +332,7 @@ const selectData = async () => {
const table = props.tableName;
try {
const countRes = await dbInst.runSql(db, dbInst.getDefaultCountSql(table, state.condition));
state.count = countRes.res[0].count || countRes.res[0].COUNT;
state.count = countRes.res[0].count || countRes.res[0].COUNT || 0;
let sql = dbInst.getDefaultSelectSql(table, state.condition, state.orderBy, state.pageNum, state.pageSize);
state.sql = sql;
if (state.count > 0) {

View File

@@ -167,7 +167,7 @@ const state = reactive({
dialogVisible: false,
btnloading: false,
activeName: '1',
columnTypeList: dbDialect.getColumnTypes(),
columnTypeList: dbDialect.getInfo().columnTypes,
indexTypeList: ['BTREE'], // mysql索引类型详解 http://c.biancheng.net/view/7897.html
tableData: {
fields: {
@@ -425,7 +425,7 @@ const submit = async () => {
sql: sql,
dbId: props.dbId as any,
db: props.db as any,
dbType: dbDialect.getFormatDialect(),
dbType: dbDialect.getInfo().formatSqlDialect,
runSuccessCallback: () => {
emit('submit-sql', { tableName: state.tableData.tableName });
// cancel();

View File

@@ -1,5 +1,4 @@
import { DbDialect, sqlColumnType } from './index';
import { SqlLanguage } from 'sql-formatter/lib/src/sqlFormatter';
import { DbDialect, sqlColumnType, DialectInfo } from './index';
export { DMDialect, GAUSS_TYPE_LIST };
@@ -83,13 +82,16 @@ const GAUSS_TYPE_LIST: sqlColumnType[] = [
{ udtName: 'macaddr', dataType: 'macaddr', desc: 'MAC地址', space: '6字节' },
];
class DMDialect implements DbDialect {
getFormatDialect(): SqlLanguage {
return 'postgresql';
}
const dmDialectInfo: DialectInfo = {
icon: 'iconfont icon-db-dm',
defaultPort: 5236,
formatSqlDialect: 'postgresql',
columnTypes: GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)),
};
getIcon() {
return 'iconfont icon-op-postgres';
class DMDialect implements DbDialect {
getInfo() {
return dmDialectInfo;
}
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
@@ -102,10 +104,6 @@ class DMDialect implements DbDialect {
return name;
};
getColumnTypes(): sqlColumnType[] {
return GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName));
}
matchType(text: string, arr: string[]): boolean {
if (!text || !arr || arr.length === 0) {
return false;

View File

@@ -11,6 +11,29 @@ export interface sqlColumnType {
range?: string;
}
// 数据库基础信息
export interface DialectInfo {
/**
* 图标
*/
icon: string;
/**
* 默认端口
*/
defaultPort: number;
/**
* 格式化sql的方言
*/
formatSqlDialect: SqlLanguage;
/**
* 列字段类型
*/
columnTypes: sqlColumnType[];
}
export const DbType = {
mysql: 'mysql',
postgresql: 'postgres',
@@ -19,14 +42,9 @@ export const DbType = {
export interface DbDialect {
/**
* 获取格式化sql对应的dialect名称
* 获取一些数据库默认信息
*/
getFormatDialect(): SqlLanguage;
/**
* 获取图标信息
*/
getIcon(): string;
getInfo(): DialectInfo;
/**
* 获取默认查询sql
@@ -44,11 +62,6 @@ export interface DbDialect {
*/
wrapName(name: string): string;
/**
* 生成字段类型列表
* */
getColumnTypes(): sqlColumnType[];
/**
* 生成创建表sql
* @param tableData 建表数据

View File

@@ -1,5 +1,4 @@
import { DbDialect, sqlColumnType } from './index';
import { SqlLanguage } from 'sql-formatter/lib/src/sqlFormatter';
import { DbDialect, DialectInfo } from './index';
export { MYSQL_TYPE_LIST, MysqlDialect, language };
@@ -30,13 +29,16 @@ const MYSQL_TYPE_LIST = [
'varchar',
];
class MysqlDialect implements DbDialect {
getFormatDialect(): SqlLanguage {
return 'mysql';
}
const mysqlDialectInfo: DialectInfo = {
icon: 'iconfont icon-op-mysql',
defaultPort: 3306,
formatSqlDialect: 'mysql',
columnTypes: MYSQL_TYPE_LIST.map((a) => ({ udtName: a, dataType: a, desc: '', space: '' })),
};
getIcon() {
return 'iconfont icon-op-mysql';
class MysqlDialect implements DbDialect {
getInfo(): DialectInfo {
return mysqlDialectInfo;
}
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
@@ -49,10 +51,6 @@ class MysqlDialect implements DbDialect {
return `\`${name}\``;
};
getColumnTypes(): sqlColumnType[] {
return MYSQL_TYPE_LIST.map((a) => ({ udtName: a, dataType: a, desc: '', space: '' }));
}
genColumnBasicSql(cl: any): string {
let val = cl.value ? (cl.value === 'CURRENT_TIMESTAMP' ? cl.value : `'${cl.value}'`) : '';
let defVal = val ? `DEFAULT ${val}` : '';

View File

@@ -1,5 +1,4 @@
import { DbDialect, sqlColumnType } from './index';
import { SqlLanguage } from 'sql-formatter/lib/src/sqlFormatter';
import { DbDialect, DialectInfo, sqlColumnType } from './index';
export { PostgresqlDialect, GAUSS_TYPE_LIST };
@@ -83,13 +82,16 @@ const GAUSS_TYPE_LIST: sqlColumnType[] = [
{ udtName: 'macaddr', dataType: 'macaddr', desc: 'MAC地址', space: '6字节' },
];
class PostgresqlDialect implements DbDialect {
getFormatDialect(): SqlLanguage {
return 'postgresql';
}
const postgresDialectInfo: DialectInfo = {
icon: 'iconfont icon-op-postgres',
defaultPort: 5432,
formatSqlDialect: 'postgresql',
columnTypes: GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName)),
};
getIcon() {
return 'iconfont icon-op-postgres';
class PostgresqlDialect implements DbDialect {
getInfo(): DialectInfo {
return postgresDialectInfo;
}
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
@@ -102,10 +104,6 @@ class PostgresqlDialect implements DbDialect {
return name;
};
getColumnTypes(): sqlColumnType[] {
return GAUSS_TYPE_LIST.sort((a, b) => a.udtName.localeCompare(b.udtName));
}
matchType(text: string, arr: string[]): boolean {
if (!text || !arr || arr.length === 0) {
return false;

View File

@@ -34,8 +34,6 @@ require (
gorm.io/gorm v1.25.5
)
require golang.org/x/exp v0.0.0-20230519143937-03e91628a987
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc // indirect
@@ -79,6 +77,7 @@ require (
github.com/xdg-go/stringprep v1.0.4 // indirect
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20230519143937-03e91628a987
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect
golang.org/x/net v0.18.0 // indirect
golang.org/x/sync v0.1.0 // indirect

View File

@@ -115,7 +115,7 @@ func (d *Db) ExecSql(rc *req.Ctx) {
ctx := rc.MetaCtx
// 如果存在执行id则保存取消函数用于后续可能的取消操作
if form.ExecId != "" {
cancelCtx, cancel := context.WithCancel(rc.MetaCtx)
cancelCtx, cancel := context.WithTimeout(rc.MetaCtx, 55*time.Second)
ctx = cancelCtx
cancelExecSqlMap.Store(form.ExecId, cancel)
defer cancelExecSqlMap.Delete(form.ExecId)

View File

@@ -33,10 +33,7 @@ func (d *DbConn) QueryContext(ctx context.Context, querySql string) ([]string, [
result = append(result, record)
})
if err != nil {
if err == context.Canceled {
return nil, nil, errorx.NewBiz("取消执行")
}
return nil, nil, err
return nil, nil, wrapSqlError(err)
}
return columns, result, nil
}
@@ -74,10 +71,7 @@ func (d *DbConn) Exec(sql string) (int64, error) {
func (d *DbConn) ExecContext(ctx context.Context, sql string) (int64, error) {
res, err := d.db.ExecContext(ctx, sql)
if err != nil {
if err == context.Canceled {
return 0, errorx.NewBiz("取消执行")
}
return 0, err
return 0, wrapSqlError(err)
}
return res.RowsAffected()
}
@@ -202,3 +196,14 @@ func valueConvert(data []byte, colType *sql.ColumnType) any {
return stringV
}
// 包装sql执行相关错误
func wrapSqlError(err error) error {
if err == context.Canceled {
return errorx.NewBiz("取消执行")
}
if err == context.DeadlineExceeded {
return errorx.NewBiz("执行超时")
}
return err
}

View File

@@ -4,10 +4,11 @@ import (
"context"
"database/sql"
"fmt"
_ "gitee.com/chunanyong/dm"
"mayfly-go/pkg/errorx"
"mayfly-go/pkg/utils/anyx"
"strings"
_ "gitee.com/chunanyong/dm"
)
func getDmDB(d *DbInfo) (*sql.DB, error) {