mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-02 23:40:24 +08:00
!99 feat: DBMS新增kingbaseES、vastbase,还有一些优化
* refactor: 重构机器列表展示 * fix:修复编辑表问题 * refactor: 优化下拉实例显示 * feat: DBMS新增kingbaseES(已测试postgres、oracle兼容模式) 、vastbase
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -88,6 +88,20 @@
|
||||
"font_class": "gauss",
|
||||
"unicode": "e683",
|
||||
"unicode_decimal": 59011
|
||||
},
|
||||
{
|
||||
"icon_id": "34836637",
|
||||
"name": "kingbase",
|
||||
"font_class": "kingbase",
|
||||
"unicode": "e882",
|
||||
"unicode_decimal": 59522
|
||||
},
|
||||
{
|
||||
"icon_id": "33047500",
|
||||
"name": "vastbase",
|
||||
"font_class": "vastbase",
|
||||
"unicode": "e62b",
|
||||
"unicode_decimal": 58923
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -189,7 +189,7 @@ const emit = defineEmits(['update:queryForm', 'update:selectionData', 'pageChang
|
||||
|
||||
export interface PageTableProps {
|
||||
size?: string;
|
||||
pageApi: Api; // 请求表格数据的 api
|
||||
pageApi?: Api; // 请求表格数据的 api
|
||||
columns: TableColumn[]; // 列配置项 ==> 必传
|
||||
showSelection?: boolean;
|
||||
selectable?: (row: any) => boolean; // 是否可选
|
||||
@@ -257,7 +257,7 @@ const changeSimpleFormItem = (searchItem: SearchItem) => {
|
||||
nowSearchItem.value = searchItem;
|
||||
};
|
||||
|
||||
const { tableData, total, loading, search, reset, getTableData, handlePageNumChange, handlePageSizeChange } = usePageTable(
|
||||
let { tableData, total, loading, search, reset, getTableData, handlePageNumChange, handlePageSizeChange } = usePageTable(
|
||||
props.pageable,
|
||||
props.pageApi,
|
||||
queryForm,
|
||||
@@ -288,6 +288,13 @@ watch(isShowSearch, () => {
|
||||
calcuTableHeight();
|
||||
});
|
||||
|
||||
watch(
|
||||
() => props.data,
|
||||
(newValue: any) => {
|
||||
tableData = newValue;
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(async () => {
|
||||
calcuTableHeight();
|
||||
useEventListener(window, 'resize', calcuTableHeight);
|
||||
|
||||
@@ -104,7 +104,11 @@ const loadTags = async () => {
|
||||
const tags = await tagApi.getResourceTagPaths.request({ resourceType: props.resourceType });
|
||||
const tagNodes = [];
|
||||
for (let tagPath of tags) {
|
||||
tagNodes.push(new TagTreeNode(tagPath, tagPath, props.tagPathNodeType));
|
||||
let isLeaf = false;
|
||||
if (!props.tagPathNodeType?.hasLoadNodesFunc) {
|
||||
isLeaf = true;
|
||||
}
|
||||
tagNodes.push(new TagTreeNode(tagPath, tagPath, props.tagPathNodeType).withIsLeaf(isLeaf));
|
||||
}
|
||||
return tagNodes;
|
||||
};
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
v-model="modelValue"
|
||||
@change="changeNode"
|
||||
>
|
||||
<template #prefix="{ node, data }">
|
||||
<slot name="iconPrefix" :node="node" :data="data" />
|
||||
</template>
|
||||
<template #default="{ node, data }">
|
||||
<span>
|
||||
<span v-if="data.type.value == TagTreeNode.TagPath">
|
||||
@@ -33,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref, watch, toRefs } from 'vue';
|
||||
import { onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||
import { NodeType, TagTreeNode } from './tag';
|
||||
import TagInfo from './TagInfo.vue';
|
||||
import { tagApi } from '../tag/api';
|
||||
|
||||
@@ -89,6 +89,8 @@ export class NodeType {
|
||||
|
||||
contextMenuItems: ContextmenuItem[];
|
||||
|
||||
hasLoadNodesFunc: boolean;
|
||||
|
||||
loadNodesFunc: (parentNode: TagTreeNode) => Promise<TagTreeNode[]>;
|
||||
|
||||
nodeClickFunc: (node: TagTreeNode) => void;
|
||||
@@ -104,6 +106,7 @@ export class NodeType {
|
||||
*/
|
||||
withLoadNodesFunc(func: (parentNode: TagTreeNode) => Promise<TagTreeNode[]>) {
|
||||
this.loadNodesFunc = func;
|
||||
this.hasLoadNodesFunc = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</template>
|
||||
|
||||
<template #type="{ data }">
|
||||
<el-tooltip :content="data.type" placement="top">
|
||||
<el-tooltip :content="getDbDialect(data.type).getInfo().name" placement="top">
|
||||
<SvgIcon :name="getDbDialect(data.type).getInfo().icon" :size="20" />
|
||||
</el-tooltip>
|
||||
</template>
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
<db-select-tree
|
||||
placeholder="请选择源数据库"
|
||||
v-model:db-id="form.srcDbId"
|
||||
v-model:inst-name="form.srcInstName"
|
||||
v-model:db-name="form.srcDbName"
|
||||
v-model:tag-path="form.srcTagPath"
|
||||
v-model:db-type="form.srcDbType"
|
||||
@@ -56,8 +57,10 @@
|
||||
<db-select-tree
|
||||
placeholder="请选择目标数据库"
|
||||
v-model:db-id="form.targetDbId"
|
||||
v-model:inst-name="form.targetInstName"
|
||||
v-model:db-name="form.targetDbName"
|
||||
v-model:tag-path="form.targetTagPath"
|
||||
v-model:db-type="form.targetDbType"
|
||||
@select-db="onSelectTargetDb"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -182,7 +185,7 @@ import { ElMessage } from 'element-plus';
|
||||
import DbSelectTree from '@/views/ops/db/component/DbSelectTree.vue';
|
||||
import MonacoEditor from '@/components/monaco/MonacoEditor.vue';
|
||||
import { DbInst, registerDbCompletionItemProvider } from '@/views/ops/db/db';
|
||||
import {DbType, getDbDialect} from '@/views/ops/db/dialect'
|
||||
import { DbType, getDbDialect } from '@/views/ops/db/dialect';
|
||||
import CrontabInput from '@/components/crontab/CrontabInput.vue';
|
||||
|
||||
const props = defineProps({
|
||||
@@ -227,13 +230,16 @@ type FormData = {
|
||||
taskName?: string;
|
||||
taskCron: string;
|
||||
srcDbId?: number;
|
||||
srcInstName?: string;
|
||||
srcDbName?: string;
|
||||
srcDbType?: string;
|
||||
srcTagPath?: string;
|
||||
targetDbId?: number;
|
||||
targetInstName?: string;
|
||||
targetDbName?: string;
|
||||
targetTagPath?: string;
|
||||
targetTableName?: string;
|
||||
targetDbType?: string;
|
||||
dataSql?: string;
|
||||
pageSize?: number;
|
||||
updField?: string;
|
||||
@@ -304,7 +310,8 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
// 初始化实例
|
||||
db.databases = db.database?.split(' ').sort() || [];
|
||||
state.srcDbInst = DbInst.getOrNewInst(db);
|
||||
state.form.srcDbType = state.srcDbInst.type
|
||||
state.form.srcDbType = state.srcDbInst.type;
|
||||
state.form.srcInstName = db.instanceName;
|
||||
}
|
||||
|
||||
// 初始化target数据源
|
||||
@@ -315,6 +322,8 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
// 初始化实例
|
||||
db.databases = db.database?.split(' ').sort() || [];
|
||||
state.targetDbInst = DbInst.getOrNewInst(db);
|
||||
state.form.targetDbType = state.targetDbInst.type;
|
||||
state.form.targetInstName = db.instanceName;
|
||||
}
|
||||
|
||||
if (targetDbId && state.form.targetDbName) {
|
||||
@@ -414,15 +423,15 @@ const handleGetSrcFields = async () => {
|
||||
|
||||
// 执行sql
|
||||
// oracle的分页关键字不一样
|
||||
let limit = ' limit 1'
|
||||
if(state.form.srcDbType === DbType.oracle){
|
||||
limit = ' where rownum <= 1'
|
||||
let limit = ' limit 1';
|
||||
if (state.form.srcDbType === DbType.oracle) {
|
||||
limit = ' where rownum <= 1';
|
||||
}
|
||||
|
||||
|
||||
const res = await dbApi.sqlExec.request({
|
||||
id: state.form.srcDbId,
|
||||
db: state.form.srcDbName,
|
||||
sql: `select * from (${state.form.dataSql}) t ${limit}`
|
||||
sql: `select * from (${state.form.dataSql}) t ${limit}`,
|
||||
});
|
||||
|
||||
if (!res.columns) {
|
||||
|
||||
@@ -6,6 +6,9 @@
|
||||
:resource-type="TagResourceTypeEnum.Db.value"
|
||||
:tag-path-node-type="NodeTypeTagPath"
|
||||
>
|
||||
<template #iconPrefix>
|
||||
<SvgIcon v-if="dbType && getDbDialect(dbType)" :name="getDbDialect(dbType).getInfo().icon" :size="18" />
|
||||
</template>
|
||||
<template #prefix="{ data }">
|
||||
<SvgIcon v-if="data.type.value == SqlExecNodeType.DbInst" :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
|
||||
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
|
||||
@@ -27,6 +30,9 @@ const props = defineProps({
|
||||
dbId: {
|
||||
type: Number,
|
||||
},
|
||||
instName: {
|
||||
type: String,
|
||||
},
|
||||
dbName: {
|
||||
type: String,
|
||||
},
|
||||
@@ -38,7 +44,7 @@ const props = defineProps({
|
||||
},
|
||||
});
|
||||
|
||||
const emits = defineEmits(['update:dbName', 'update:tagPath', 'update:dbId', 'update:dbType', 'selectDb']);
|
||||
const emits = defineEmits(['update:dbName', 'update:tagPath', 'update:instName', 'update:dbId', 'update:dbType', 'selectDb']);
|
||||
|
||||
/**
|
||||
* 树节点类型
|
||||
@@ -56,7 +62,7 @@ class SqlExecNodeType {
|
||||
|
||||
const selectNode = computed({
|
||||
get: () => {
|
||||
return props.dbName ? `${props.tagPath} - ${props.dbId} - ${props.dbName}` : '';
|
||||
return props.dbName ? `${props.tagPath} > ${props.instName} > ${props.dbName}` : '';
|
||||
},
|
||||
set: () => {
|
||||
//
|
||||
@@ -151,6 +157,7 @@ const changeNode = (nodeData: TagTreeNode) => {
|
||||
const params = nodeData.params;
|
||||
// postgres
|
||||
emits('update:dbName', params.db);
|
||||
emits('update:instName', params.name);
|
||||
emits('update:dbId', params.id);
|
||||
emits('update:tagPath', params.tagPath);
|
||||
emits('update:dbType', params.type);
|
||||
|
||||
@@ -272,6 +272,18 @@ watch(props, async (newValue) => {
|
||||
dbDialect = getDbDialect(newValue.dbType);
|
||||
});
|
||||
|
||||
// 切换到索引tab时,刷新索引字段下拉选项
|
||||
watch(
|
||||
() => state.activeName,
|
||||
(newValue) => {
|
||||
if (newValue === '2') {
|
||||
state.tableData.indexs.columns = state.tableData.fields.res.map((a) => {
|
||||
return { name: a.name, remark: a.remark };
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const cancel = () => {
|
||||
emit('update:visible', false);
|
||||
reset();
|
||||
@@ -391,22 +403,22 @@ const genSql = () => {
|
||||
let data = state.tableData;
|
||||
// 创建表
|
||||
if (!props.data?.edit) {
|
||||
if (state.activeName === '1') {
|
||||
return dbDialect.getCreateTableSql(data);
|
||||
} else if (state.activeName === '2' && data.indexs.res.length > 0) {
|
||||
return dbDialect.getCreateIndexSql(data);
|
||||
let createTable = dbDialect.getCreateTableSql(data);
|
||||
let createIndex = '';
|
||||
if (data.indexs.res.length > 0) {
|
||||
createIndex = dbDialect.getCreateIndexSql(data);
|
||||
}
|
||||
return createTable + ';' + createIndex;
|
||||
} else {
|
||||
// 修改
|
||||
if (state.activeName === '1') {
|
||||
// 修改列
|
||||
let changeData = filterChangedData(state.tableData.fields.oldFields, state.tableData.fields.res, 'name');
|
||||
return dbDialect.getModifyColumnSql(data, data.tableName, changeData);
|
||||
} else if (state.activeName === '2') {
|
||||
// 修改索引
|
||||
let changeData = filterChangedData(state.tableData.indexs.oldIndexs, state.tableData.indexs.res, 'indexName');
|
||||
return dbDialect.getModifyIndexSql(data, data.tableName, changeData);
|
||||
}
|
||||
// 修改列
|
||||
let changeColData = filterChangedData(state.tableData.fields.oldFields, state.tableData.fields.res, 'name');
|
||||
let colSql = dbDialect.getModifyColumnSql(data, data.tableName, changeColData);
|
||||
// 修改索引
|
||||
let changeIdxData = filterChangedData(state.tableData.indexs.oldIndexs, state.tableData.indexs.res, 'indexName');
|
||||
let idxSql = dbDialect.getModifyIndexSql(data, data.tableName, changeIdxData);
|
||||
// 修改表名
|
||||
|
||||
return colSql + ';' + idxSql;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import { MariadbDialect } from '@/views/ops/db/dialect/mariadb_dialect';
|
||||
import { SqliteDialect } from '@/views/ops/db/dialect/sqlite_dialect';
|
||||
import { MssqlDialect } from '@/views/ops/db/dialect/mssql_dialect';
|
||||
import { GaussDialect } from '@/views/ops/db/dialect/gauss_dialect';
|
||||
import { KingbaseEsDialect } from '@/views/ops/db/dialect/kingbaseES_dialect';
|
||||
import { VastbaseDialect } from '@/views/ops/db/dialect/vastbase_dialect';
|
||||
|
||||
export interface sqlColumnType {
|
||||
udtName: string;
|
||||
@@ -122,13 +124,15 @@ export const DbType = {
|
||||
oracle: 'oracle',
|
||||
sqlite: 'sqlite',
|
||||
mssql: 'mssql', // ms sqlserver
|
||||
kingbaseEs: 'kingbaseEs', // 人大金仓 pgsql模式 https://help.kingbase.com.cn/v8/index.html
|
||||
vastbase: 'vastbase', // https://docs.vastdata.com.cn/zh/docs/VastbaseG100Ver2.2.5/doc/%E5%BC%80%E5%8F%91%E8%80%85%E6%8C%87%E5%8D%97/SQL%E5%8F%82%E8%80%83/SQL%E5%8F%82%E8%80%83.html
|
||||
};
|
||||
|
||||
// mysql兼容的数据库
|
||||
export const noSchemaTypes = [DbType.mysql, DbType.mariadb, DbType.sqlite];
|
||||
|
||||
// 有schema层的数据库
|
||||
export const schemaDbTypes = [DbType.postgresql, DbType.gauss, DbType.dm, DbType.oracle, DbType.mssql];
|
||||
export const schemaDbTypes = [DbType.postgresql, DbType.gauss, DbType.dm, DbType.oracle, DbType.mssql, DbType.kingbaseEs, DbType.vastbase];
|
||||
|
||||
export const editDbTypes = [...noSchemaTypes, ...schemaDbTypes];
|
||||
|
||||
@@ -218,8 +222,8 @@ export const getDbDialectMap = () => {
|
||||
return dbType2DialectMap;
|
||||
};
|
||||
|
||||
export const getDbDialect = (dbType: string): DbDialect => {
|
||||
return dbType2DialectMap.get(dbType) || mysqlDialect;
|
||||
export const getDbDialect = (dbType?: string): DbDialect => {
|
||||
return dbType2DialectMap.get(dbType!) || mysqlDialect;
|
||||
};
|
||||
|
||||
(function () {
|
||||
@@ -232,4 +236,6 @@ export const getDbDialect = (dbType: string): DbDialect => {
|
||||
registerDbDialect(DbType.oracle, new OracleDialect());
|
||||
registerDbDialect(DbType.sqlite, new SqliteDialect());
|
||||
registerDbDialect(DbType.mssql, new MssqlDialect());
|
||||
registerDbDialect(DbType.kingbaseEs, new KingbaseEsDialect());
|
||||
registerDbDialect(DbType.vastbase, new VastbaseDialect());
|
||||
})();
|
||||
|
||||
18
mayfly_go_web/src/views/ops/db/dialect/kingbaseES_dialect.ts
Normal file
18
mayfly_go_web/src/views/ops/db/dialect/kingbaseES_dialect.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { DialectInfo } from './index';
|
||||
import { PostgresqlDialect } from '@/views/ops/db/dialect/postgres_dialect';
|
||||
|
||||
let kbpgDialectInfo: DialectInfo;
|
||||
|
||||
export class KingbaseEsDialect extends PostgresqlDialect {
|
||||
getInfo(): DialectInfo {
|
||||
if (kbpgDialectInfo) {
|
||||
return kbpgDialectInfo;
|
||||
}
|
||||
|
||||
kbpgDialectInfo = {} as DialectInfo;
|
||||
Object.assign(kbpgDialectInfo, super.getInfo());
|
||||
kbpgDialectInfo.name = 'KingbaseES';
|
||||
kbpgDialectInfo.icon = 'iconfont icon-kingbase';
|
||||
return kbpgDialectInfo;
|
||||
}
|
||||
}
|
||||
@@ -303,11 +303,15 @@ class PostgresqlDialect implements DbDialect {
|
||||
// CREATE UNIQUE INDEX idx_column_name ON your_table (column1, column2);
|
||||
// COMMENT ON INDEX idx_column_name IS 'Your index comment here';
|
||||
// 创建索引
|
||||
let schema = tableData.db.split('/')[1];
|
||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableData.tableName)}`;
|
||||
let sql: string[] = [];
|
||||
tableData.indexs.res.forEach((a: any) => {
|
||||
sql.push(` create ${a.unique ? 'UNIQUE' : ''} index ${a.indexName} ("${a.columnNames.join('","')})"`);
|
||||
// 字段名用双引号包裹
|
||||
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
||||
if (a.indexComment) {
|
||||
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
||||
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${a.indexComment}'`);
|
||||
}
|
||||
});
|
||||
return sql.join(';');
|
||||
@@ -367,6 +371,9 @@ class PostgresqlDialect implements DbDialect {
|
||||
}
|
||||
|
||||
getModifyIndexSql(tableData: any, tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
||||
let schema = tableData.db.split('/')[1];
|
||||
let dbTable = `${this.quoteIdentifier(schema)}.${this.quoteIdentifier(tableName)}`;
|
||||
|
||||
// 不能直接修改索引名或字段、需要先删后加
|
||||
let dropIndexNames: string[] = [];
|
||||
let addIndexs: any[] = [];
|
||||
@@ -400,9 +407,11 @@ class PostgresqlDialect implements DbDialect {
|
||||
|
||||
if (addIndexs.length > 0) {
|
||||
addIndexs.forEach((a) => {
|
||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')})`);
|
||||
// 字段名用双引号包裹
|
||||
let colArr = a.columnNames.map((a: string) => `${this.quoteIdentifier(a)}`);
|
||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${this.quoteIdentifier(a.indexName)} on ${dbTable} (${colArr.join(',')})`);
|
||||
if (a.indexComment) {
|
||||
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
||||
sql.push(`COMMENT ON INDEX ${schema}.${this.quoteIdentifier(a.indexName)} IS '${a.indexComment}'`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
18
mayfly_go_web/src/views/ops/db/dialect/vastbase_dialect.ts
Normal file
18
mayfly_go_web/src/views/ops/db/dialect/vastbase_dialect.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { DialectInfo } from './index';
|
||||
import { PostgresqlDialect } from '@/views/ops/db/dialect/postgres_dialect';
|
||||
|
||||
let vastDialectInfo: DialectInfo;
|
||||
|
||||
export class VastbaseDialect extends PostgresqlDialect {
|
||||
getInfo(): DialectInfo {
|
||||
if (vastDialectInfo) {
|
||||
return vastDialectInfo;
|
||||
}
|
||||
|
||||
vastDialectInfo = {} as DialectInfo;
|
||||
Object.assign(vastDialectInfo, super.getInfo());
|
||||
vastDialectInfo.name = 'VastbaseG100';
|
||||
vastDialectInfo.icon = 'iconfont icon-vastbase';
|
||||
return vastDialectInfo;
|
||||
}
|
||||
}
|
||||
@@ -1,191 +1,207 @@
|
||||
<template>
|
||||
<div>
|
||||
<page-table
|
||||
ref="pageTableRef"
|
||||
:page-api="machineApi.list"
|
||||
:before-query-fn="checkRouteTagPath"
|
||||
:search-items="searchItems"
|
||||
v-model:query-form="params"
|
||||
:show-selection="true"
|
||||
v-model:selection-data="state.selectionData"
|
||||
:columns="columns"
|
||||
>
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="perms.addMachine" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加 </el-button>
|
||||
<el-button v-auth="perms.delMachine" :disabled="selectionData.length < 1" @click="deleteMachine()" type="danger" icon="delete">删除</el-button>
|
||||
</template>
|
||||
<div class="flex-all-center">
|
||||
<Splitpanes class="default-theme">
|
||||
<Pane size="20" max-size="30">
|
||||
<tag-tree :resource-type="TagResourceTypeEnum.Machine.value" :tag-path-node-type="NodeTypeTagPath" />
|
||||
</Pane>
|
||||
|
||||
<template #ipPort="{ data }">
|
||||
<el-link :disabled="data.status == -1" @click="showMachineStats(data)" type="primary" :underline="false">
|
||||
{{ `${data.ip}:${data.port}` }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
||||
<template #stat="{ data }">
|
||||
<span v-if="!data.stat">-</span>
|
||||
<div v-else>
|
||||
<el-row>
|
||||
<el-text size="small" style="font-size: 10px">
|
||||
内存(可用/总):
|
||||
<span :class="getStatsFontClass(data.stat.memAvailable, data.stat.memTotal)"
|
||||
>{{ formatByteSize(data.stat.memAvailable, 1) }}/{{ formatByteSize(data.stat.memTotal, 1) }}
|
||||
</span>
|
||||
</el-text>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-text style="font-size: 10px" size="small">
|
||||
CPU(空闲): <span :class="getStatsFontClass(data.stat.cpuIdle, 100)">{{ data.stat.cpuIdle.toFixed(0) }}%</span>
|
||||
</el-text>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #fs="{ data }">
|
||||
<span v-if="!data.stat?.fsInfos">-</span>
|
||||
<div v-else>
|
||||
<el-row v-for="(i, idx) in data.stat.fsInfos.slice(0, 2)" :key="i.mountPoint">
|
||||
<el-text style="font-size: 10px" size="small" :class="getStatsFontClass(i.free, i.used + i.free)">
|
||||
{{ i.mountPoint }} => {{ formatByteSize(i.free, 0) }}/{{ formatByteSize(i.used + i.free, 0) }}
|
||||
</el-text>
|
||||
|
||||
<!-- 展示剩余的磁盘信息 -->
|
||||
<el-popover :show-after="300" v-if="data.stat.fsInfos.length > 2 && idx == 1" placement="top-start" width="230" trigger="hover">
|
||||
<template #reference>
|
||||
<SvgIcon class="mt5 ml5" color="var(--el-color-primary)" name="MoreFilled" />
|
||||
</template>
|
||||
|
||||
<el-row v-for="i in data.stat.fsInfos.slice(2)" :key="i.mountPoint">
|
||||
<el-text style="font-size: 10px" size="small" :class="getStatsFontClass(i.free, i.used + i.free)">
|
||||
{{ i.mountPoint }} => {{ formatByteSize(i.free, 0) }}/{{ formatByteSize(i.used + i.free, 0) }}
|
||||
</el-text>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #status="{ data }">
|
||||
<el-switch
|
||||
v-auth:disabled="'machine:update'"
|
||||
:width="52"
|
||||
v-model="data.status"
|
||||
:active-value="1"
|
||||
:inactive-value="-1"
|
||||
inline-prompt
|
||||
active-text="启用"
|
||||
inactive-text="停用"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
@change="changeStatus(data)"
|
||||
></el-switch>
|
||||
</template>
|
||||
|
||||
<template #tagPath="{ data }">
|
||||
<resource-tag :resource-code="data.code" :resource-type="TagResourceTypeEnum.Machine.value" />
|
||||
</template>
|
||||
|
||||
<template #action="{ data }">
|
||||
<span v-auth="'machine:terminal'">
|
||||
<el-tooltip :show-after="500" content="按住ctrl则为新标签打开" placement="top">
|
||||
<el-button :disabled="data.status == -1" type="primary" @click="showTerminal(data, $event)" link>终端</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
|
||||
<span v-auth="'machine:file'">
|
||||
<el-button type="success" :disabled="data.status == -1" @click="showFileManage(data)" link>文件</el-button>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
|
||||
<el-button :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link>脚本</el-button>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span class="el-dropdown-link-machine-list">
|
||||
更多
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'edit', data }" v-if="actionBtns[perms.updateMachine]"> 编辑 </el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'process', data }" :disabled="data.status == -1"> 进程 </el-dropdown-item>
|
||||
|
||||
<el-dropdown-item :command="{ type: 'terminalRec', data }" v-if="actionBtns[perms.updateMachine] && data.enableRecorder == 1">
|
||||
终端回放
|
||||
</el-dropdown-item>
|
||||
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'closeCli', data }"
|
||||
v-if="actionBtns[perms.closeCli]"
|
||||
:disabled="!data.hasCli || data.status == -1"
|
||||
<Pane>
|
||||
<div>
|
||||
<page-table :data="state.pageData" :pageable="false" :show-selection="true" v-model:selection-data="state.selectionData" :columns="columns">
|
||||
<template #tableHeader>
|
||||
<el-button v-auth="perms.addMachine" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加 </el-button>
|
||||
<el-button v-auth="perms.delMachine" :disabled="selectionData.length < 1" @click="deleteMachine()" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
关闭连接
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</page-table>
|
||||
</template>
|
||||
|
||||
<el-dialog v-model="infoDialog.visible">
|
||||
<el-descriptions title="详情" :column="3" border>
|
||||
<el-descriptions-item :span="1.5" label="机器id">{{ infoDialog.data.id }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1.5" label="名称">{{ infoDialog.data.name }}</el-descriptions-item>
|
||||
<template #ipPort="{ data }">
|
||||
<el-link :disabled="data.status == -1" @click="showMachineStats(data)" type="primary" :underline="false">
|
||||
{{ `${data.ip}:${data.port}` }}
|
||||
</el-link>
|
||||
</template>
|
||||
|
||||
<el-descriptions-item :span="3" label="标签路径">{{ infoDialog.data.tagPath }}</el-descriptions-item>
|
||||
<template #stat="{ data }">
|
||||
<span v-if="!data.stat">-</span>
|
||||
<div v-else>
|
||||
<el-row>
|
||||
<el-text size="small" style="font-size: 10px">
|
||||
内存(可用/总):
|
||||
<span :class="getStatsFontClass(data.stat.memAvailable, data.stat.memTotal)"
|
||||
>{{ formatByteSize(data.stat.memAvailable, 1) }}/{{ formatByteSize(data.stat.memTotal, 1) }}
|
||||
</span>
|
||||
</el-text>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-text style="font-size: 10px" size="small">
|
||||
CPU(空闲): <span :class="getStatsFontClass(data.stat.cpuIdle, 100)">{{ data.stat.cpuIdle.toFixed(0) }}%</span>
|
||||
</el-text>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-descriptions-item :span="2" label="IP">{{ infoDialog.data.ip }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
||||
<template #fs="{ data }">
|
||||
<span v-if="!data.stat?.fsInfos">-</span>
|
||||
<div v-else>
|
||||
<el-row v-for="(i, idx) in data.stat.fsInfos.slice(0, 2)" :key="i.mountPoint">
|
||||
<el-text style="font-size: 10px" size="small" :class="getStatsFontClass(i.free, i.used + i.free)">
|
||||
{{ i.mountPoint }} => {{ formatByteSize(i.free, 0) }}/{{ formatByteSize(i.used + i.free, 0) }}
|
||||
</el-text>
|
||||
|
||||
<el-descriptions-item :span="2" label="用户名">{{ infoDialog.data.username }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="认证方式">
|
||||
{{ infoDialog.data.authCertId > 1 ? '授权凭证' : '密码' }}
|
||||
</el-descriptions-item>
|
||||
<!-- 展示剩余的磁盘信息 -->
|
||||
<el-popover
|
||||
:show-after="300"
|
||||
v-if="data.stat.fsInfos.length > 2 && idx == 1"
|
||||
placement="top-start"
|
||||
width="230"
|
||||
trigger="hover"
|
||||
>
|
||||
<template #reference>
|
||||
<SvgIcon class="mt5 ml5" color="var(--el-color-primary)" name="MoreFilled" />
|
||||
</template>
|
||||
|
||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||
<el-row v-for="i in data.stat.fsInfos.slice(2)" :key="i.mountPoint">
|
||||
<el-text style="font-size: 10px" size="small" :class="getStatsFontClass(i.free, i.used + i.free)">
|
||||
{{ i.mountPoint }} => {{ formatByteSize(i.free, 0) }}/{{ formatByteSize(i.used + i.free, 0) }}
|
||||
</el-text>
|
||||
</el-row>
|
||||
</el-popover>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
|
||||
<template #status="{ data }">
|
||||
<el-switch
|
||||
v-auth:disabled="'machine:update'"
|
||||
:width="52"
|
||||
v-model="data.status"
|
||||
:active-value="1"
|
||||
:inactive-value="-1"
|
||||
inline-prompt
|
||||
active-text="启用"
|
||||
inactive-text="停用"
|
||||
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
|
||||
@change="changeStatus(data)"
|
||||
></el-switch>
|
||||
</template>
|
||||
|
||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
|
||||
<template #action="{ data }">
|
||||
<span v-auth="'machine:terminal'">
|
||||
<el-tooltip :show-after="500" content="按住ctrl则为新标签打开" placement="top">
|
||||
<el-button :disabled="data.status == -1" type="primary" @click="showTerminal(data, $event)" link>终端</el-button>
|
||||
</el-tooltip>
|
||||
|
||||
<el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
|
||||
<terminal-dialog ref="terminalDialogRef" :visibleMinimize="true">
|
||||
<template #headerTitle="{ terminalInfo }">
|
||||
{{ `${(terminalInfo.terminalId + '').slice(-2)}` }}
|
||||
<el-divider direction="vertical" />
|
||||
{{ `${terminalInfo.meta.username}@${terminalInfo.meta.ip}:${terminalInfo.meta.port}` }}
|
||||
<el-divider direction="vertical" />
|
||||
{{ terminalInfo.meta.name }}
|
||||
</template>
|
||||
</terminal-dialog>
|
||||
<span v-auth="'machine:file'">
|
||||
<el-button type="success" :disabled="data.status == -1" @click="showFileManage(data)" link>文件</el-button>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
</span>
|
||||
|
||||
<machine-edit
|
||||
:title="machineEditDialog.title"
|
||||
v-model:visible="machineEditDialog.visible"
|
||||
v-model:machine="machineEditDialog.data"
|
||||
@valChange="submitSuccess"
|
||||
></machine-edit>
|
||||
<el-button :disabled="data.status == -1" type="warning" @click="serviceManager(data)" link>脚本</el-button>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
|
||||
<el-dropdown @command="handleCommand">
|
||||
<span class="el-dropdown-link-machine-list">
|
||||
更多
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'detail', data }"> 详情 </el-dropdown-item>
|
||||
|
||||
<script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
|
||||
<el-dropdown-item :command="{ type: 'edit', data }" v-if="actionBtns[perms.updateMachine]"> 编辑 </el-dropdown-item>
|
||||
|
||||
<file-conf-list :title="fileDialog.title" v-model:visible="fileDialog.visible" v-model:machineId="fileDialog.machineId" />
|
||||
<el-dropdown-item :command="{ type: 'process', data }" :disabled="data.status == -1"> 进程 </el-dropdown-item>
|
||||
|
||||
<machine-stats v-model:visible="machineStatsDialog.visible" :machineId="machineStatsDialog.machineId" :title="machineStatsDialog.title"></machine-stats>
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'terminalRec', data }"
|
||||
v-if="actionBtns[perms.updateMachine] && data.enableRecorder == 1"
|
||||
>
|
||||
终端回放
|
||||
</el-dropdown-item>
|
||||
|
||||
<machine-rec v-model:visible="machineRecDialog.visible" :machineId="machineRecDialog.machineId" :title="machineRecDialog.title"></machine-rec>
|
||||
<el-dropdown-item
|
||||
:command="{ type: 'closeCli', data }"
|
||||
v-if="actionBtns[perms.closeCli]"
|
||||
:disabled="!data.hasCli || data.status == -1"
|
||||
>
|
||||
关闭连接
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
</page-table>
|
||||
|
||||
<el-dialog v-model="infoDialog.visible">
|
||||
<el-descriptions title="详情" :column="3" border>
|
||||
<el-descriptions-item :span="1.5" label="机器id">{{ infoDialog.data.id }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1.5" label="名称">{{ infoDialog.data.name }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="标签路径">{{ infoDialog.data.tagPath }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="IP">{{ infoDialog.data.ip }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="端口">{{ infoDialog.data.port }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="用户名">{{ infoDialog.data.username }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="认证方式">
|
||||
{{ infoDialog.data.authCertId > 1 ? '授权凭证' : '密码' }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="1.5" label="SSH隧道">{{ infoDialog.data.sshTunnelMachineId > 0 ? '是' : '否' }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1.5" label="终端回放">{{ infoDialog.data.enableRecorder == 1 ? '是' : '否' }} </el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="创建时间">{{ dateFormat(infoDialog.data.createTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
|
||||
<terminal-dialog ref="terminalDialogRef" :visibleMinimize="true">
|
||||
<template #headerTitle="{ terminalInfo }">
|
||||
{{ `${(terminalInfo.terminalId + '').slice(-2)}` }}
|
||||
<el-divider direction="vertical" />
|
||||
{{ `${terminalInfo.meta.username}@${terminalInfo.meta.ip}:${terminalInfo.meta.port}` }}
|
||||
<el-divider direction="vertical" />
|
||||
{{ terminalInfo.meta.name }}
|
||||
</template>
|
||||
</terminal-dialog>
|
||||
|
||||
<machine-edit
|
||||
:title="machineEditDialog.title"
|
||||
v-model:visible="machineEditDialog.visible"
|
||||
v-model:machine="machineEditDialog.data"
|
||||
@valChange="submitSuccess"
|
||||
></machine-edit>
|
||||
|
||||
<process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
|
||||
|
||||
<script-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
|
||||
|
||||
<file-conf-list :title="fileDialog.title" v-model:visible="fileDialog.visible" v-model:machineId="fileDialog.machineId" />
|
||||
|
||||
<machine-stats
|
||||
v-model:visible="machineStatsDialog.visible"
|
||||
:machineId="machineStatsDialog.machineId"
|
||||
:title="machineStatsDialog.title"
|
||||
></machine-stats>
|
||||
|
||||
<machine-rec
|
||||
v-model:visible="machineRecDialog.visible"
|
||||
:machineId="machineRecDialog.machineId"
|
||||
:title="machineRecDialog.title"
|
||||
></machine-rec>
|
||||
</div>
|
||||
</Pane>
|
||||
</Splitpanes>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -202,8 +218,9 @@ import { hasPerms } from '@/components/auth/auth';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import { SearchItem } from '@/components/SearchForm';
|
||||
import { getTagPathSearchItem } from '../component/tag';
|
||||
|
||||
import { getTagPathSearchItem, NodeType, TagTreeNode } from '../component/tag';
|
||||
import TagTree from '../component/TagTree.vue';
|
||||
import { Splitpanes, Pane } from 'splitpanes';
|
||||
// 组件
|
||||
const TerminalDialog = defineAsyncComponent(() => import('@/components/terminal/TerminalDialog.vue'));
|
||||
const MachineEdit = defineAsyncComponent(() => import('./MachineEdit.vue'));
|
||||
@@ -235,7 +252,6 @@ const columns = [
|
||||
TableColumn.new('fs', '磁盘(挂载点=>可用/总)').isSlot().setAddWidth(20),
|
||||
TableColumn.new('username', '用户名'),
|
||||
TableColumn.new('status', '状态').isSlot().setMinWidth(85),
|
||||
TableColumn.new('tagPath', '关联标签').isSlot().setAddWidth(10).alignCenter(),
|
||||
TableColumn.new('remark', '备注'),
|
||||
TableColumn.new('action', '操作').isSlot().setMinWidth(238).fixedRight().alignCenter(),
|
||||
];
|
||||
@@ -243,6 +259,10 @@ const columns = [
|
||||
// 该用户拥有的的操作列按钮权限,使用v-if进行判断,v-auth对el-dropdown-item无效
|
||||
const actionBtns = hasPerms([perms.updateMachine, perms.closeCli]);
|
||||
|
||||
class MachineNodeType {
|
||||
static Machine = 1;
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
params: {
|
||||
pageNum: 1,
|
||||
@@ -251,6 +271,7 @@ const state = reactive({
|
||||
name: null,
|
||||
tagPath: '',
|
||||
},
|
||||
pageData: [] as any[],
|
||||
infoDialog: {
|
||||
visible: false,
|
||||
data: null as any,
|
||||
@@ -291,7 +312,21 @@ const state = reactive({
|
||||
|
||||
const { params, infoDialog, selectionData, serviceDialog, processDialog, fileDialog, machineStatsDialog, machineEditDialog, machineRecDialog } = toRefs(state);
|
||||
|
||||
onMounted(async () => {});
|
||||
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (node: any) => {
|
||||
// 加载标签树下的机器列表
|
||||
state.params.tagPath = node.key;
|
||||
state.params.pageNum = 1;
|
||||
state.params.pageSize = 1000;
|
||||
const res = await search();
|
||||
// 把list 根据name字段排序
|
||||
res.list = res.list.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||
state.pageData = res.list;
|
||||
return res.list.map((x: any) => new TagTreeNode(x.id, x.name, NodeTypeMachine).withParams(x).withIsLeaf(true));
|
||||
});
|
||||
|
||||
const NodeTypeMachine = new NodeType(MachineNodeType.Machine).withNodeClickFunc((node: any) => {
|
||||
state.pageData = [node.params];
|
||||
});
|
||||
|
||||
const checkRouteTagPath = (query: any) => {
|
||||
if (route.query.tagPath) {
|
||||
@@ -421,7 +456,9 @@ const showMachineStats = async (machine: any) => {
|
||||
};
|
||||
|
||||
const search = async () => {
|
||||
pageTableRef.value.search();
|
||||
const res = await machineApi.list.request(state.params);
|
||||
state.pageData = res.list;
|
||||
return res;
|
||||
};
|
||||
|
||||
const submitSuccess = () => {
|
||||
|
||||
@@ -154,7 +154,7 @@ func (d *dbAppImpl) GetDbConn(dbId uint64, dbName string) (*dbi.DbConn, error) {
|
||||
|
||||
checkDb := dbName
|
||||
// 兼容pgsql/dm db/schema模式
|
||||
if dbi.DbTypePostgres.Equal(instance.Type) || dbi.DbTypeGauss.Equal(instance.Type) || dbi.DbTypeDM.Equal(instance.Type) || dbi.DbTypeOracle.Equal(instance.Type) || dbi.DbTypeMssql.Equal(instance.Type) {
|
||||
if dbi.DbTypePostgres.Equal(instance.Type) || dbi.DbTypeGauss.Equal(instance.Type) || dbi.DbTypeDM.Equal(instance.Type) || dbi.DbTypeOracle.Equal(instance.Type) || dbi.DbTypeMssql.Equal(instance.Type) || dbi.DbTypeKingbaseEs.Equal(instance.Type) || dbi.DbTypeVastbase.Equal(instance.Type) {
|
||||
ss := strings.Split(dbName, "/")
|
||||
if len(ss) > 1 {
|
||||
checkDb = ss[0]
|
||||
|
||||
@@ -11,14 +11,16 @@ import (
|
||||
type DbType string
|
||||
|
||||
const (
|
||||
DbTypeMysql DbType = "mysql"
|
||||
DbTypeMariadb DbType = "mariadb"
|
||||
DbTypePostgres DbType = "postgres"
|
||||
DbTypeGauss DbType = "gauss"
|
||||
DbTypeDM DbType = "dm"
|
||||
DbTypeOracle DbType = "oracle"
|
||||
DbTypeSqlite DbType = "sqlite"
|
||||
DbTypeMssql DbType = "mssql"
|
||||
DbTypeMysql DbType = "mysql"
|
||||
DbTypeMariadb DbType = "mariadb"
|
||||
DbTypePostgres DbType = "postgres"
|
||||
DbTypeGauss DbType = "gauss"
|
||||
DbTypeDM DbType = "dm"
|
||||
DbTypeOracle DbType = "oracle"
|
||||
DbTypeSqlite DbType = "sqlite"
|
||||
DbTypeMssql DbType = "mssql"
|
||||
DbTypeKingbaseEs DbType = "kingbaseEs"
|
||||
DbTypeVastbase DbType = "vastbase"
|
||||
)
|
||||
|
||||
func ToDbType(dbType string) DbType {
|
||||
@@ -44,7 +46,7 @@ func (dbType DbType) QuoteIdentifier(name string) string {
|
||||
switch dbType {
|
||||
case DbTypeMysql, DbTypeMariadb:
|
||||
return quoteIdentifier(name, "`")
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
return quoteIdentifier(name, `"`)
|
||||
case DbTypeMssql:
|
||||
return fmt.Sprintf("[%s]", name)
|
||||
@@ -57,7 +59,7 @@ func (dbType DbType) RemoveQuote(name string) string {
|
||||
switch dbType {
|
||||
case DbTypeMysql, DbTypeMariadb:
|
||||
return removeQuote(name, "`")
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
return removeQuote(name, `"`)
|
||||
default:
|
||||
return removeQuote(name, `"`)
|
||||
@@ -70,7 +72,7 @@ func (dbType DbType) QuoteLiteral(literal string) string {
|
||||
literal = strings.ReplaceAll(literal, `\`, `\\`)
|
||||
literal = strings.ReplaceAll(literal, `'`, `''`)
|
||||
return "'" + literal + "'"
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
return pq.QuoteLiteral(literal)
|
||||
default:
|
||||
return pq.QuoteLiteral(literal)
|
||||
@@ -85,6 +87,10 @@ func (dbType DbType) MetaDbName() string {
|
||||
return "postgres"
|
||||
case DbTypeDM:
|
||||
return ""
|
||||
case DbTypeKingbaseEs:
|
||||
return "security"
|
||||
case DbTypeVastbase:
|
||||
return "vastbase"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
@@ -94,7 +100,7 @@ func (dbType DbType) Dialect() sqlparser.Dialect {
|
||||
switch dbType {
|
||||
case DbTypeMysql, DbTypeMariadb:
|
||||
return sqlparser.MysqlDialect{}
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
return sqlparser.PostgresDialect{}
|
||||
default:
|
||||
return sqlparser.PostgresDialect{}
|
||||
@@ -122,7 +128,7 @@ func (dbType DbType) StmtSetForeignKeyChecks(check bool) string {
|
||||
} else {
|
||||
return "SET FOREIGN_KEY_CHECKS = 0;\n"
|
||||
}
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
// not currently supported postgres
|
||||
return ""
|
||||
default:
|
||||
@@ -134,7 +140,7 @@ func (dbType DbType) StmtUseDatabase(dbName string) string {
|
||||
switch dbType {
|
||||
case DbTypeMysql, DbTypeMariadb:
|
||||
return fmt.Sprintf("USE %s;\n", dbType.QuoteIdentifier(dbName))
|
||||
case DbTypePostgres, DbTypeGauss:
|
||||
case DbTypePostgres, DbTypeGauss, DbTypeKingbaseEs, DbTypeVastbase:
|
||||
// not currently supported postgres
|
||||
return ""
|
||||
default:
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
--MSSQL_DBS 数据库名信息
|
||||
SELECT name AS dbname
|
||||
FROM sys.databases
|
||||
WHERE owner_sid = SUSER_SID()
|
||||
and name not in ('master', 'tempdb', 'model', 'msdb')
|
||||
---------------------------------------
|
||||
--MSSQL_TABLE_DETAIL 查询表名和表注释
|
||||
SELECT t.name AS tableName,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
--ORACLE_DB_SCHEMAS 库schemas
|
||||
select distinct owner as SCHEMA_NAME
|
||||
from all_objects
|
||||
order by owner
|
||||
select USERNAME
|
||||
from sys.all_users
|
||||
order by USERNAME
|
||||
---------------------------------------
|
||||
--ORACLE_TABLE_INFO 表详细信息
|
||||
select a.TABLE_NAME,
|
||||
@@ -10,9 +10,9 @@ select a.TABLE_NAME,
|
||||
d.BYTES as DATA_LENGTH,
|
||||
0 as INDEX_LENGTH,
|
||||
a.NUM_ROWS as TABLE_ROWS
|
||||
from all_tables a
|
||||
from ALL_TABLES a
|
||||
left join ALL_TAB_COMMENTS b on b.TABLE_NAME = a.TABLE_NAME AND b.OWNER = a.OWNER
|
||||
left join all_objects c on c.OBJECT_TYPE = 'TABLE' AND c.OWNER = a.OWNER AND c.OBJECT_NAME = a.TABLE_NAME
|
||||
left join ALL_OBJECTS c on c.OBJECT_TYPE = 'TABLE' AND c.OWNER = a.OWNER AND c.OBJECT_NAME = a.TABLE_NAME
|
||||
left join dba_segments d on d.SEGMENT_TYPE = 'TABLE' AND d.OWNER = a.OWNER AND d.SEGMENT_NAME = a.TABLE_NAME
|
||||
where a.owner = (SELECT sys_context('USERENV', 'CURRENT_SCHEMA') FROM dual)
|
||||
ORDER BY a.TABLE_NAME
|
||||
@@ -55,12 +55,12 @@ SELECT a.TABLE_NAME as TABLE_NAME,
|
||||
a.DATA_SCALE as NUM_SCALE,
|
||||
CASE WHEN d.pri IS NOT NULL THEN 1 ELSE 0 END as IS_PRIMARY_KEY,
|
||||
CASE WHEN a.IDENTITY_COLUMN = 'YES' THEN 1 ELSE 0 END as IS_IDENTITY
|
||||
FROM all_tab_columns a
|
||||
LEFT JOIN all_col_comments b
|
||||
FROM ALL_TAB_COLUMNS a
|
||||
LEFT JOIN ALL_COL_COMMENTS b
|
||||
on a.OWNER = b.OWNER AND a.TABLE_NAME = b.TABLE_NAME AND a.COLUMN_NAME = b.COLUMN_NAME
|
||||
LEFT JOIN (select ac.TABLE_NAME, ac.OWNER, cc.COLUMN_NAME, 1 as pri
|
||||
from all_constraints ac
|
||||
join all_cons_columns cc on cc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME AND cc.OWNER = ac.OWNER
|
||||
from ALL_CONSTRAINTS ac
|
||||
join ALL_CONS_COLUMNS cc on cc.CONSTRAINT_NAME = ac.CONSTRAINT_NAME AND cc.OWNER = ac.OWNER
|
||||
where cc.CONSTRAINT_NAME IS NOT NULL
|
||||
AND ac.CONSTRAINT_TYPE = 'P') d
|
||||
on d.OWNER = a.OWNER AND d.TABLE_NAME = a.TABLE_NAME AND d.COLUMN_NAME = a.COLUMN_NAME
|
||||
|
||||
@@ -38,8 +38,17 @@ func (md *Meta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
|
||||
query.Add("database", d.Database)
|
||||
}
|
||||
}
|
||||
params := query.Encode()
|
||||
if d.Params != "" {
|
||||
if !strings.HasPrefix(d.Params, "&") {
|
||||
params = params + "&" + d.Params
|
||||
} else {
|
||||
params = params + d.Params
|
||||
}
|
||||
}
|
||||
|
||||
const driverName = "mssql"
|
||||
dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%d?%s", url.PathEscape(d.Username), url.PathEscape(d.Password), d.Host, d.Port, query.Encode())
|
||||
dsn := fmt.Sprintf("sqlserver://%s:%s@%s:%d?%s", url.PathEscape(d.Username), url.PathEscape(d.Password), d.Host, d.Port, params)
|
||||
return sql.Open(driverName, dsn)
|
||||
}
|
||||
|
||||
|
||||
@@ -242,7 +242,7 @@ func (od *OracleDialect) GetSchemas() ([]string, error) {
|
||||
}
|
||||
schemaNames := make([]string, 0)
|
||||
for _, re := range res {
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["SCHEMA_NAME"]))
|
||||
schemaNames = append(schemaNames, anyx.ConvString(re["USERNAME"]))
|
||||
}
|
||||
return schemaNames, nil
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ type PgsqlDialect struct {
|
||||
}
|
||||
|
||||
func (md *PgsqlDialect) GetDbServer() (*dbi.DbServer, error) {
|
||||
_, res, err := md.dc.Query("SHOW server_version")
|
||||
_, res, err := md.dc.Query("SELECT version() as server_version")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@ import (
|
||||
)
|
||||
|
||||
func init() {
|
||||
dbi.Register(dbi.DbTypePostgres, new(PostgresMeta))
|
||||
meta := new(PostgresMeta)
|
||||
dbi.Register(dbi.DbTypePostgres, meta)
|
||||
dbi.Register(dbi.DbTypeKingbaseEs, meta)
|
||||
dbi.Register(dbi.DbTypeVastbase, meta)
|
||||
|
||||
gauss := new(PostgresMeta)
|
||||
gauss.Param = "dbtype=gauss"
|
||||
@@ -40,16 +43,17 @@ func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
|
||||
|
||||
db := d.Database
|
||||
var dbParam string
|
||||
exsitSchema := false
|
||||
if db != "" {
|
||||
// postgres database可以使用db/schema表示,方便连接指定schema, 若不存在schema则使用默认schema
|
||||
ss := strings.Split(db, "/")
|
||||
if len(ss) > 1 {
|
||||
exsitSchema = true
|
||||
dbParam = fmt.Sprintf("dbname=%s search_path=%s", ss[0], ss[len(ss)-1])
|
||||
} else {
|
||||
dbParam = "dbname=" + db
|
||||
}
|
||||
existSchema := false
|
||||
if db == "" {
|
||||
db = d.Type.MetaDbName()
|
||||
}
|
||||
// postgres database可以使用db/schema表示,方便连接指定schema, 若不存在schema则使用默认schema
|
||||
ss := strings.Split(db, "/")
|
||||
if len(ss) > 1 {
|
||||
existSchema = true
|
||||
dbParam = fmt.Sprintf("dbname=%s search_path=%s", ss[0], ss[len(ss)-1])
|
||||
} else {
|
||||
dbParam = "dbname=" + db
|
||||
}
|
||||
|
||||
dsn := fmt.Sprintf("host=%s port=%d user=%s password=%s %s sslmode=disable connect_timeout=8", d.Host, d.Port, d.Username, d.Password, dbParam)
|
||||
@@ -62,7 +66,7 @@ func (md *PostgresMeta) GetSqlDb(d *dbi.DbInfo) (*sql.DB, error) {
|
||||
if strings.HasPrefix(param, "dbname=") {
|
||||
return true
|
||||
}
|
||||
if exsitSchema && strings.HasPrefix(param, "search_path") {
|
||||
if existSchema && strings.HasPrefix(param, "search_path") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user