mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-12-25 09:06:34 +08:00
refactor: 数据同步编辑页优化等
This commit is contained in:
@@ -33,7 +33,7 @@
|
|||||||
"splitpanes": "^3.1.5",
|
"splitpanes": "^3.1.5",
|
||||||
"sql-formatter": "^14.0.0",
|
"sql-formatter": "^14.0.0",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue": "^3.4.7",
|
"vue": "^3.4.8",
|
||||||
"vue-router": "^4.2.5",
|
"vue-router": "^4.2.5",
|
||||||
"xterm": "^5.3.0",
|
"xterm": "^5.3.0",
|
||||||
"xterm-addon-fit": "^0.8.0",
|
"xterm-addon-fit": "^0.8.0",
|
||||||
@@ -47,8 +47,8 @@
|
|||||||
"@types/sortablejs": "^1.15.3",
|
"@types/sortablejs": "^1.15.3",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
"@typescript-eslint/eslint-plugin": "^6.7.4",
|
||||||
"@typescript-eslint/parser": "^6.7.4",
|
"@typescript-eslint/parser": "^6.7.4",
|
||||||
"@vitejs/plugin-vue": "^5.0.2",
|
"@vitejs/plugin-vue": "^5.0.3",
|
||||||
"@vue/compiler-sfc": "^3.4.7",
|
"@vue/compiler-sfc": "^3.4.8",
|
||||||
"dotenv": "^16.3.1",
|
"dotenv": "^16.3.1",
|
||||||
"eslint": "^8.35.0",
|
"eslint": "^8.35.0",
|
||||||
"eslint-plugin-vue": "^9.19.2",
|
"eslint-plugin-vue": "^9.19.2",
|
||||||
|
|||||||
@@ -255,6 +255,7 @@ const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((p
|
|||||||
|
|
||||||
// 数据库节点
|
// 数据库节点
|
||||||
const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
|
const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
|
||||||
|
.withContextMenuItems([new ContextmenuItem('reloadTables', '刷新').withIcon('RefreshRight').withOnClick((data: any) => reloadNode(data.key))])
|
||||||
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||||
const params = parentNode.params;
|
const params = parentNode.params;
|
||||||
// pg类数据库会多一层schema
|
// pg类数据库会多一层schema
|
||||||
@@ -280,6 +281,7 @@ const NodeTypeDb = new NodeType(SqlExecNodeType.Db)
|
|||||||
|
|
||||||
// postgres schema模式
|
// postgres schema模式
|
||||||
const NodeTypePostgresScheam = new NodeType(SqlExecNodeType.PgSchema)
|
const NodeTypePostgresScheam = new NodeType(SqlExecNodeType.PgSchema)
|
||||||
|
.withContextMenuItems([new ContextmenuItem('reloadTables', '刷新').withIcon('RefreshRight').withOnClick((data: any) => reloadNode(data.key))])
|
||||||
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
.withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||||
const params = parentNode.params;
|
const params = parentNode.params;
|
||||||
return [
|
return [
|
||||||
@@ -292,7 +294,7 @@ const NodeTypePostgresScheam = new NodeType(SqlExecNodeType.PgSchema)
|
|||||||
// 数据库表菜单节点
|
// 数据库表菜单节点
|
||||||
const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
|
const NodeTypeTableMenu = new NodeType(SqlExecNodeType.TableMenu)
|
||||||
.withContextMenuItems([
|
.withContextMenuItems([
|
||||||
new ContextmenuItem('reloadTables', '刷新').withIcon('RefreshRight').withOnClick((data: any) => reloadTables(data.key)),
|
new ContextmenuItem('reloadTables', '刷新').withIcon('RefreshRight').withOnClick((data: any) => reloadNode(data.key)),
|
||||||
|
|
||||||
new ContextmenuItem('tablesOp', '表操作').withIcon('Setting').withOnClick((data: any) => {
|
new ContextmenuItem('tablesOp', '表操作').withIcon('Setting').withOnClick((data: any) => {
|
||||||
const params = data.params;
|
const params = data.params;
|
||||||
@@ -596,7 +598,7 @@ const getSqlMenuNodeKey = (dbId: number, db: string) => {
|
|||||||
return `${dbId}.${db}.sql-menu`;
|
return `${dbId}.${db}.sql-menu`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const reloadTables = (nodeKey: string) => {
|
const reloadNode = (nodeKey: string) => {
|
||||||
state.reloadStatus = true;
|
state.reloadStatus = true;
|
||||||
tagTreeRef.value.reloadNode(nodeKey);
|
tagTreeRef.value.reloadNode(nodeKey);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
>
|
>
|
||||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
<el-form :model="form" ref="dbForm" :rules="rules" label-width="auto">
|
||||||
<el-tabs v-model="tabActiveName" style="height: 450px">
|
<el-tabs v-model="tabActiveName" style="height: 450px">
|
||||||
<el-tab-pane label="基本信息" name="basic">
|
<el-tab-pane label="基本信息" :name="basicTab">
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-row>
|
<el-row>
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
@@ -108,7 +108,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="字段映射" name="field">
|
<el-tab-pane label="字段映射" :name="fieldTab" :disabled="!baseFieldCompleted">
|
||||||
<el-form-item prop="fieldMap" label="字段映射" required>
|
<el-form-item prop="fieldMap" label="字段映射" required>
|
||||||
<el-table :data="form.fieldMap" :max-height="400" size="small">
|
<el-table :data="form.fieldMap" :max-height="400" size="small">
|
||||||
<el-table-column prop="src" label="源字段" :width="200" />
|
<el-table-column prop="src" label="源字段" :width="200" />
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="sql预览" name="sqlPreview">
|
<el-tab-pane label="sql预览" :name="sqlPreviewTab" :disabled="!baseFieldCompleted">
|
||||||
<el-form-item prop="fieldMap" label="查询sql">
|
<el-form-item prop="fieldMap" label="查询sql">
|
||||||
<el-input type="textarea" v-model="state.previewDataSql" readonly :input-style="{ height: '190px' }" />
|
<el-input type="textarea" v-model="state.previewDataSql" readonly :input-style="{ height: '190px' }" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -140,7 +140,41 @@
|
|||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div class="dialog-footer">
|
<div>
|
||||||
|
<el-button
|
||||||
|
v-if="tabActiveName != basicTab"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
switch (tabActiveName) {
|
||||||
|
case fieldTab:
|
||||||
|
tabActiveName = basicTab;
|
||||||
|
break;
|
||||||
|
case sqlPreviewTab:
|
||||||
|
tabActiveName = fieldTab;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>上一步</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
v-if="tabActiveName != sqlPreviewTab"
|
||||||
|
:disabled="!baseFieldCompleted"
|
||||||
|
@click="
|
||||||
|
() => {
|
||||||
|
switch (tabActiveName) {
|
||||||
|
case basicTab:
|
||||||
|
tabActiveName = fieldTab;
|
||||||
|
break;
|
||||||
|
case fieldTab:
|
||||||
|
tabActiveName = sqlPreviewTab;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"
|
||||||
|
>下一步</el-button
|
||||||
|
>
|
||||||
|
|
||||||
<el-button @click="cancel()">取 消</el-button>
|
<el-button @click="cancel()">取 消</el-button>
|
||||||
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
|
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
|
||||||
</div>
|
</div>
|
||||||
@@ -150,7 +184,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, toRefs, watch } from 'vue';
|
import { reactive, ref, toRefs, watch, computed } from 'vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import DbSelectTree from '@/views/ops/db/component/DbSelectTree.vue';
|
import DbSelectTree from '@/views/ops/db/component/DbSelectTree.vue';
|
||||||
@@ -191,6 +225,10 @@ const rules = {
|
|||||||
|
|
||||||
const dbForm: any = ref(null);
|
const dbForm: any = ref(null);
|
||||||
|
|
||||||
|
const basicTab = 'basic';
|
||||||
|
const fieldTab = 'field';
|
||||||
|
const sqlPreviewTab = 'sqlPreview';
|
||||||
|
|
||||||
type FormData = {
|
type FormData = {
|
||||||
id?: number;
|
id?: number;
|
||||||
taskName?: string;
|
taskName?: string;
|
||||||
@@ -235,34 +273,15 @@ const state = reactive({
|
|||||||
previewInsertSql: '',
|
previewInsertSql: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectSrcDb = async (params: any) => {
|
|
||||||
// 初始化数据源
|
|
||||||
params.databases = params.dbs; // 数据源里需要这个值
|
|
||||||
state.srcDbInst = DbInst.getOrNewInst(params);
|
|
||||||
registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSelectTargetDb = async (params: any) => {
|
|
||||||
state.targetDbInst = DbInst.getOrNewInst(params);
|
|
||||||
await loadDbTables(params.id, params.db);
|
|
||||||
};
|
|
||||||
|
|
||||||
const loadDbTables = async (dbId: number, db: string) => {
|
|
||||||
// 加载db下的表
|
|
||||||
let data = await dbApi.tableInfos.request({ id: dbId, db });
|
|
||||||
state.targetTableList = data;
|
|
||||||
if (data && data.length > 0) {
|
|
||||||
let names = data.map((a: any) => a.tableName);
|
|
||||||
if (!names.includes(state.form.targetTableName)) {
|
|
||||||
state.form.targetTableName = data[0].tableName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const { tabActiveName, form, submitForm } = toRefs(state);
|
const { tabActiveName, form, submitForm } = toRefs(state);
|
||||||
|
|
||||||
const { isFetching: saveBtnLoading, execute: saveExec } = dbApi.saveDatasyncTask.useApi(submitForm);
|
const { isFetching: saveBtnLoading, execute: saveExec } = dbApi.saveDatasyncTask.useApi(submitForm);
|
||||||
|
|
||||||
|
// 基础字段信息是否填写完整
|
||||||
|
const baseFieldCompleted = computed(() => {
|
||||||
|
return state.form.srcDbId && state.form.srcDbName && state.form.targetDbId && state.form.targetDbName && state.form.targetTableName;
|
||||||
|
});
|
||||||
|
|
||||||
watch(dialogVisible, async (newValue: boolean) => {
|
watch(dialogVisible, async (newValue: boolean) => {
|
||||||
if (!newValue) {
|
if (!newValue) {
|
||||||
return;
|
return;
|
||||||
@@ -303,6 +322,10 @@ watch(dialogVisible, async (newValue: boolean) => {
|
|||||||
state.targetDbInst = DbInst.getOrNewInst(db);
|
state.targetDbInst = DbInst.getOrNewInst(db);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (targetDbId && state.form.targetDbName) {
|
||||||
|
await loadDbTables(targetDbId, state.form.targetDbName);
|
||||||
|
}
|
||||||
|
|
||||||
// 注册sql代码提示
|
// 注册sql代码提示
|
||||||
if (srcDbId && srcDbName) {
|
if (srcDbId && srcDbName) {
|
||||||
registerDbCompletionItemProvider(srcDbId, srcDbName, state.srcDbInst.databases, state.srcDbInst.type);
|
registerDbCompletionItemProvider(srcDbId, srcDbName, state.srcDbInst.databases, state.srcDbInst.type);
|
||||||
@@ -311,21 +334,15 @@ watch(dialogVisible, async (newValue: boolean) => {
|
|||||||
|
|
||||||
watch(tabActiveName, async (newValue: string) => {
|
watch(tabActiveName, async (newValue: string) => {
|
||||||
switch (newValue) {
|
switch (newValue) {
|
||||||
case 'field':
|
case fieldTab:
|
||||||
await handleGetSrcFields();
|
await handleGetSrcFields();
|
||||||
await handleGetTargetFields();
|
await handleGetTargetFields();
|
||||||
break;
|
break;
|
||||||
case 'dbConf':
|
case sqlPreviewTab:
|
||||||
await handleGetTargetFields();
|
|
||||||
if (state.form.targetDbId && state.form.targetDbName) {
|
|
||||||
await loadDbTables(state.form.targetDbId, state.form.targetDbName);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'sqlPreview':
|
|
||||||
let srcDbDialect = getDbDialect(state.srcDbInst.type);
|
let srcDbDialect = getDbDialect(state.srcDbInst.type);
|
||||||
let targetDbDialect = getDbDialect(state.targetDbInst.type);
|
let targetDbDialect = getDbDialect(state.targetDbInst.type);
|
||||||
|
|
||||||
let updField = srcDbDialect.wrapName(state.form.updField!);
|
let updField = srcDbDialect.quoteIdentifier(state.form.updField!);
|
||||||
state.previewDataSql = `SELECT * FROM (\n ${state.form.dataSql?.trim() || '请输入数据sql'} \n ) t \n where ${updField} > '${
|
state.previewDataSql = `SELECT * FROM (\n ${state.form.dataSql?.trim() || '请输入数据sql'} \n ) t \n where ${updField} > '${
|
||||||
state.form.updFieldVal || ''
|
state.form.updFieldVal || ''
|
||||||
}'`;
|
}'`;
|
||||||
@@ -339,19 +356,46 @@ watch(tabActiveName, async (newValue: string) => {
|
|||||||
});
|
});
|
||||||
if (fields.size < (state.form.fieldMap?.length || 0)) {
|
if (fields.size < (state.form.fieldMap?.length || 0)) {
|
||||||
ElMessage.warning('字段映射中存在重复的目标字段,请检查');
|
ElMessage.warning('字段映射中存在重复的目标字段,请检查');
|
||||||
|
state.previewInsertSql = '';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let fieldArr = state.form.fieldMap?.map((a: any) => targetDbDialect.wrapName(a.target)) || [];
|
let fieldArr = state.form.fieldMap?.map((a: any) => targetDbDialect.quoteIdentifier(a.target)) || [];
|
||||||
let placeholder = '?'.repeat(fieldArr.length).split('').join(',');
|
let placeholder = '?'.repeat(fieldArr.length).split('').join(',');
|
||||||
|
|
||||||
state.previewInsertSql = ` insert into ${targetDbDialect.wrapName(state.form.targetTableName!)}(${fieldArr.join(',')}) values (${placeholder});`;
|
state.previewInsertSql = ` insert into ${targetDbDialect.quoteIdentifier(state.form.targetTableName!)}(${fieldArr.join(
|
||||||
|
','
|
||||||
|
)}) values (${placeholder});`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const onSelectSrcDb = async (params: any) => {
|
||||||
|
// 初始化数据源
|
||||||
|
params.databases = params.dbs; // 数据源里需要这个值
|
||||||
|
state.srcDbInst = DbInst.getOrNewInst(params);
|
||||||
|
registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelectTargetDb = async (params: any) => {
|
||||||
|
state.targetDbInst = DbInst.getOrNewInst(params);
|
||||||
|
await loadDbTables(params.id, params.db);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadDbTables = async (dbId: number, db: string) => {
|
||||||
|
// 加载db下的表
|
||||||
|
let data = await dbApi.tableInfos.request({ id: dbId, db });
|
||||||
|
state.targetTableList = data;
|
||||||
|
if (data && data.length > 0) {
|
||||||
|
let names = data.map((a: any) => a.tableName);
|
||||||
|
if (!names.includes(state.form.targetTableName)) {
|
||||||
|
state.form.targetTableName = data[0].tableName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleGetSrcFields = async () => {
|
const handleGetSrcFields = async () => {
|
||||||
// 执行sql,获取字段信息
|
// 执行sql,获取字段信息
|
||||||
if (!state.form.dataSql || !state.form.dataSql.trim()) {
|
if (!state.form.dataSql || !state.form.dataSql.trim()) {
|
||||||
@@ -456,12 +500,8 @@ const cancel = () => {
|
|||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.sync-task-edit {
|
.sync-task-edit {
|
||||||
.el-select {
|
.el-select {
|
||||||
// width: 360px;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
// .el-input__inner {
|
|
||||||
// width: 100%; /* 将el-select内部输入框的宽度设置为100% */
|
|
||||||
// }
|
|
||||||
.task-sql {
|
.task-sql {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
|||||||
import { editor, languages, Position } from 'monaco-editor';
|
import { editor, languages, Position } from 'monaco-editor';
|
||||||
|
|
||||||
import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
import { registerCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
||||||
import {DbDialect, EditorCompletionItem, getDbDialect} from './dialect'
|
import { DbDialect, EditorCompletionItem, getDbDialect } from './dialect';
|
||||||
|
|
||||||
const dbInstCache: Map<number, DbInst> = new Map();
|
const dbInstCache: Map<number, DbInst> = new Map();
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ export class DbInst {
|
|||||||
},
|
},
|
||||||
kind: monaco.languages.CompletionItemKind.File,
|
kind: monaco.languages.CompletionItemKind.File,
|
||||||
detail: tableComment,
|
detail: tableComment,
|
||||||
insertText: dbDialect.wrapName(tableName) + ' ',
|
insertText: dbDialect.quoteIdentifier(tableName) + ' ',
|
||||||
range,
|
range,
|
||||||
sortText: 300 + index + '',
|
sortText: 300 + index + '',
|
||||||
});
|
});
|
||||||
@@ -113,7 +113,7 @@ export class DbInst {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 加载列信息提示 */
|
/** 加载列信息提示 */
|
||||||
async loadTableColumnSuggestions(dbDialect: DbDialect,db: string, tableName: string, range: any) {
|
async loadTableColumnSuggestions(dbDialect: DbDialect, db: string, tableName: string, range: any) {
|
||||||
let dbHits = await this.loadDbHints(db);
|
let dbHits = await this.loadDbHints(db);
|
||||||
let columns = dbHits[tableName];
|
let columns = dbHits[tableName];
|
||||||
let suggestions: languages.CompletionItem[] = [];
|
let suggestions: languages.CompletionItem[] = [];
|
||||||
@@ -128,7 +128,7 @@ export class DbInst {
|
|||||||
},
|
},
|
||||||
kind: monaco.languages.CompletionItemKind.Property,
|
kind: monaco.languages.CompletionItemKind.Property,
|
||||||
detail: '', // 不显示detail, 否则选中时备注等会被遮挡
|
detail: '', // 不显示detail, 否则选中时备注等会被遮挡
|
||||||
insertText: dbDialect.wrapName(fieldName)+ ' ', // create_time
|
insertText: dbDialect.quoteIdentifier(fieldName) + ' ', // create_time
|
||||||
range,
|
range,
|
||||||
sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
|
sortText: 100 + index + '', // 使用表字段声明顺序排序,排序需为字符串类型
|
||||||
});
|
});
|
||||||
@@ -287,7 +287,7 @@ export class DbInst {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
wrapName = (name: string) => {
|
wrapName = (name: string) => {
|
||||||
return getDbDialect(this.type).wrapName(name);
|
return getDbDialect(this.type).quoteIdentifier(name);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -618,7 +618,7 @@ export function registerDbCompletionItemProvider(dbId: number, db: string, dbs:
|
|||||||
description: 'schema',
|
description: 'schema',
|
||||||
},
|
},
|
||||||
kind: monaco.languages.CompletionItemKind.Folder,
|
kind: monaco.languages.CompletionItemKind.Folder,
|
||||||
insertText: dbDialect.wrapName(a),
|
insertText: dbDialect.quoteIdentifier(a),
|
||||||
range,
|
range,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -679,7 +679,7 @@ export function registerDbCompletionItemProvider(dbId: number, db: string, dbs:
|
|||||||
},
|
},
|
||||||
kind: monaco.languages.CompletionItemKind.File,
|
kind: monaco.languages.CompletionItemKind.File,
|
||||||
detail: tableComment,
|
detail: tableComment,
|
||||||
insertText: dbDialect.wrapName(tableName) + ' ',
|
insertText: dbDialect.quoteIdentifier(tableName) + ' ',
|
||||||
range,
|
range,
|
||||||
sortText: 300 + index + '',
|
sortText: 300 + index + '',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -444,8 +444,8 @@ class DMDialect implements DbDialect {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapName = (name: string) => {
|
quoteIdentifier = (name: string) => {
|
||||||
return name;
|
return `"${name}"`;
|
||||||
};
|
};
|
||||||
|
|
||||||
matchType(text: string, arr: string[]): boolean {
|
matchType(text: string, arr: string[]): boolean {
|
||||||
|
|||||||
@@ -143,10 +143,10 @@ export interface DbDialect {
|
|||||||
getDefaultIndex(): IndexDefinition;
|
getDefaultIndex(): IndexDefinition;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 包裹数据库表名、字段名等,避免使用关键字为字段名或表名时报错
|
* 引用标识符,包裹数据库表名、字段名等,避免使用关键字为字段名或表名时报错
|
||||||
* @param name 名称
|
* @param name 名称
|
||||||
*/
|
*/
|
||||||
wrapName(name: string): string;
|
quoteIdentifier(name: string): string;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 生成创建表sql
|
* 生成创建表sql
|
||||||
|
|||||||
@@ -112,7 +112,10 @@ class MysqlDialect implements DbDialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
||||||
return `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} ${this.getPageSql(pageNum, limit)};`;
|
return `SELECT * FROM ${this.quoteIdentifier(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} ${this.getPageSql(
|
||||||
|
pageNum,
|
||||||
|
limit
|
||||||
|
)};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageSql(pageNum: number, limit: number) {
|
getPageSql(pageNum: number, limit: number) {
|
||||||
@@ -181,7 +184,7 @@ class MysqlDialect implements DbDialect {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapName = (name: string) => {
|
quoteIdentifier = (name: string) => {
|
||||||
return `\`${name}\``;
|
return `\`${name}\``;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -133,7 +133,10 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
||||||
return `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} ${this.getPageSql(pageNum, limit)};`;
|
return `SELECT * FROM ${this.quoteIdentifier(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} ${this.getPageSql(
|
||||||
|
pageNum,
|
||||||
|
limit
|
||||||
|
)};`;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPageSql(pageNum: number, limit: number) {
|
getPageSql(pageNum: number, limit: number) {
|
||||||
@@ -202,7 +205,7 @@ class PostgresqlDialect implements DbDialect {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapName = (name: string) => {
|
quoteIdentifier = (name: string) => {
|
||||||
// 后端sql解析器暂不支持pgsql
|
// 后端sql解析器暂不支持pgsql
|
||||||
return name;
|
return name;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ require (
|
|||||||
gitee.com/chunanyong/dm v1.8.13
|
gitee.com/chunanyong/dm v1.8.13
|
||||||
gitee.com/liuzongyang/libpq v1.0.9
|
gitee.com/liuzongyang/libpq v1.0.9
|
||||||
github.com/buger/jsonparser v1.1.1
|
github.com/buger/jsonparser v1.1.1
|
||||||
|
github.com/emirpasic/gods v1.18.1
|
||||||
github.com/gin-gonic/gin v1.9.1
|
github.com/gin-gonic/gin v1.9.1
|
||||||
github.com/glebarez/sqlite v1.10.0
|
github.com/glebarez/sqlite v1.10.0
|
||||||
github.com/go-gormigrate/gormigrate/v2 v2.1.0
|
github.com/go-gormigrate/gormigrate/v2 v2.1.0
|
||||||
@@ -93,5 +94,3 @@ require (
|
|||||||
modernc.org/sqlite v1.23.1 // indirect
|
modernc.org/sqlite v1.23.1 // indirect
|
||||||
vitess.io/vitess v0.17.3 // indirect
|
vitess.io/vitess v0.17.3 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/emirpasic/gods v1.18.1
|
|
||||||
|
|||||||
@@ -277,7 +277,7 @@ func (app *dataSyncAppImpl) srcData2TargetDb(srcRes []map[string]any, fieldMap [
|
|||||||
for _, item := range fieldMap {
|
for _, item := range fieldMap {
|
||||||
targetField := item["target"]
|
targetField := item["target"]
|
||||||
srcField := item["target"]
|
srcField := item["target"]
|
||||||
targetWrapColumns = append(targetWrapColumns, targetDbConn.Info.Type.WrapName(targetField))
|
targetWrapColumns = append(targetWrapColumns, targetDbConn.Info.Type.QuoteIdentifier(targetField))
|
||||||
srcColumns = append(srcColumns, srcField)
|
srcColumns = append(srcColumns, srcField)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,37 +25,25 @@ func (dbType DbType) Equal(typ string) bool {
|
|||||||
return ToDbType(typ) == dbType
|
return ToDbType(typ) == dbType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
|
||||||
|
// used as part of an SQL statement. For example:
|
||||||
|
//
|
||||||
|
// tblname := "my_table"
|
||||||
|
// data := "my_data"
|
||||||
|
// quoted := quoteIdentifier(tblname, '"')
|
||||||
|
// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data)
|
||||||
|
//
|
||||||
|
// Any double quotes in name will be escaped. The quoted identifier will be
|
||||||
|
// case sensitive when used in a query. If the input string contains a zero
|
||||||
|
// byte, the result will be truncated immediately before it.
|
||||||
func (dbType DbType) QuoteIdentifier(name string) string {
|
func (dbType DbType) QuoteIdentifier(name string) string {
|
||||||
switch dbType {
|
switch dbType {
|
||||||
case DbTypeMysql, DbTypeMariadb:
|
case DbTypeMysql, DbTypeMariadb:
|
||||||
return quoteIdentifier(name, "`")
|
return quoteIdentifier(name, "`")
|
||||||
case DbTypePostgres:
|
case DbTypePostgres:
|
||||||
return pq.QuoteIdentifier(name)
|
return quoteIdentifier(name, `"`)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
return quoteIdentifier(name, `"`)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbType DbType) MetaDbName() string {
|
|
||||||
switch dbType {
|
|
||||||
case DbTypeMysql, DbTypeMariadb:
|
|
||||||
return ""
|
|
||||||
case DbTypePostgres:
|
|
||||||
return "postgres"
|
|
||||||
case DbTypeDM:
|
|
||||||
return ""
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 包装字段名,防止使用了数据库保留关键字
|
|
||||||
func (dbType DbType) WrapName(name string) string {
|
|
||||||
switch dbType {
|
|
||||||
case DbTypeMysql, DbTypeMariadb:
|
|
||||||
return fmt.Sprintf("`%s`", name)
|
|
||||||
default:
|
|
||||||
return fmt.Sprintf(`"%s"`, name)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +56,20 @@ func (dbType DbType) QuoteLiteral(literal string) string {
|
|||||||
case DbTypePostgres:
|
case DbTypePostgres:
|
||||||
return pq.QuoteLiteral(literal)
|
return pq.QuoteLiteral(literal)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
return pq.QuoteLiteral(literal)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dbType DbType) MetaDbName() string {
|
||||||
|
switch dbType {
|
||||||
|
case DbTypeMysql, DbTypeMariadb:
|
||||||
|
return ""
|
||||||
|
case DbTypePostgres:
|
||||||
|
return "postgres"
|
||||||
|
case DbTypeDM:
|
||||||
|
return ""
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,24 +79,11 @@ func (dbType DbType) Dialect() sqlparser.Dialect {
|
|||||||
return sqlparser.MysqlDialect{}
|
return sqlparser.MysqlDialect{}
|
||||||
case DbTypePostgres:
|
case DbTypePostgres:
|
||||||
return sqlparser.PostgresDialect{}
|
return sqlparser.PostgresDialect{}
|
||||||
case DbTypeDM:
|
|
||||||
return sqlparser.PostgresDialect{}
|
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
return sqlparser.PostgresDialect{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// QuoteIdentifier quotes an "identifier" (e.g. a table or a column name) to be
|
|
||||||
// used as part of an SQL statement. For example:
|
|
||||||
//
|
|
||||||
// tblname := "my_table"
|
|
||||||
// data := "my_data"
|
|
||||||
// quoted := pq.QuoteIdentifier(tblname)
|
|
||||||
// err := db.Exec(fmt.Sprintf("INSERT INTO %s VALUES ($1)", quoted), data)
|
|
||||||
//
|
|
||||||
// Any double quotes in name will be escaped. The quoted identifier will be
|
|
||||||
// case sensitive when used in a query. If the input string contains a zero
|
|
||||||
// byte, the result will be truncated immediately before it.
|
|
||||||
func quoteIdentifier(name, quoter string) string {
|
func quoteIdentifier(name, quoter string) string {
|
||||||
end := strings.IndexRune(name, 0)
|
end := strings.IndexRune(name, 0)
|
||||||
if end > -1 {
|
if end > -1 {
|
||||||
@@ -116,7 +104,7 @@ func (dbType DbType) StmtSetForeignKeyChecks(check bool) string {
|
|||||||
// not currently supported postgres
|
// not currently supported postgres
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -128,6 +116,6 @@ func (dbType DbType) StmtUseDatabase(dbName string) string {
|
|||||||
// not currently supported postgres
|
// not currently supported postgres
|
||||||
return ""
|
return ""
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,3 +50,34 @@ func Test_QuoteLiteral(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_quoteIdentifier(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
dbType DbType
|
||||||
|
sql string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
dbType: DbTypeMysql,
|
||||||
|
sql: "`a`",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dbType: DbTypeMysql,
|
||||||
|
sql: "select table",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dbType: DbTypePostgres,
|
||||||
|
sql: "a",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dbType: DbTypePostgres,
|
||||||
|
sql: "table",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(string(tt.dbType)+"_"+tt.sql, func(t *testing.T) {
|
||||||
|
got := tt.dbType.QuoteIdentifier(tt.sql)
|
||||||
|
require.Equal(t, tt.want, got)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -307,7 +307,7 @@ func (dd *DMDialect) BatchInsert(tx *sql.Tx, tableName string, columns []string,
|
|||||||
// 去除最后一个逗号,占位符由括号包裹
|
// 去除最后一个逗号,占位符由括号包裹
|
||||||
placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
|
placeholder := fmt.Sprintf("(%s)", strings.TrimSuffix(repeated, ","))
|
||||||
|
|
||||||
sqlTemp := fmt.Sprintf("insert into %s (%s) values %s", dd.dc.Info.Type.WrapName(tableName), strings.Join(columns, ","), placeholder)
|
sqlTemp := fmt.Sprintf("insert into %s (%s) values %s", dd.dc.Info.Type.QuoteIdentifier(tableName), strings.Join(columns, ","), placeholder)
|
||||||
effRows := 0
|
effRows := 0
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
// 达梦数据库只能一条条的执行insert
|
// 达梦数据库只能一条条的执行insert
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ func (md *MysqlDialect) BatchInsert(tx *sql.Tx, tableName string, columns []stri
|
|||||||
// 去除最后一个逗号
|
// 去除最后一个逗号
|
||||||
placeholder = strings.TrimSuffix(repeated, ",")
|
placeholder = strings.TrimSuffix(repeated, ",")
|
||||||
|
|
||||||
sqlStr := fmt.Sprintf("insert into %s (%s) values %s", md.dc.Info.Type.WrapName(tableName), strings.Join(columns, ","), placeholder)
|
sqlStr := fmt.Sprintf("insert into %s (%s) values %s", md.dc.Info.Type.QuoteIdentifier(tableName), strings.Join(columns, ","), placeholder)
|
||||||
// 执行批量insert sql
|
// 执行批量insert sql
|
||||||
// 把二维数组转为一维数组
|
// 把二维数组转为一维数组
|
||||||
var args []any
|
var args []any
|
||||||
|
|||||||
@@ -319,7 +319,7 @@ func (pd *PgsqlDialect) BatchInsert(tx *sql.Tx, tableName string, columns []stri
|
|||||||
placeholders = append(placeholders, "("+strings.Join(placeholder, ", ")+")")
|
placeholders = append(placeholders, "("+strings.Join(placeholder, ", ")+")")
|
||||||
}
|
}
|
||||||
|
|
||||||
sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.dc.Info.Type.WrapName(tableName), strings.Join(columns, ","), strings.Join(placeholders, ", "))
|
sqlStr := fmt.Sprintf("insert into %s (%s) values %s", pd.dc.Info.Type.QuoteIdentifier(tableName), strings.Join(columns, ","), strings.Join(placeholders, ", "))
|
||||||
// 执行批量insert sql
|
// 执行批量insert sql
|
||||||
|
|
||||||
return pd.dc.TxExec(tx, sqlStr, args...)
|
return pd.dc.TxExec(tx, sqlStr, args...)
|
||||||
|
|||||||
Reference in New Issue
Block a user