mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 07:50:25 +08:00
refactor: code review
This commit is contained in:
@@ -1,6 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="db-list">
|
<div class="db-list">
|
||||||
<page-table ref="pageTableRef" :query="queryConfig" v-model:query-form="query" :show-selection="true" v-model:selection-data="state.selectionData" :data="datas" :columns="columns" :total="total" v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
|
<page-table
|
||||||
|
ref="pageTableRef"
|
||||||
|
:query="queryConfig"
|
||||||
|
v-model:query-form="query"
|
||||||
|
:show-selection="true"
|
||||||
|
v-model:selection-data="state.selectionData"
|
||||||
|
:data="datas"
|
||||||
|
:columns="columns"
|
||||||
|
:total="total"
|
||||||
|
v-model:page-size="query.pageSize"
|
||||||
|
v-model:page-num="query.pageNum"
|
||||||
|
@pageChange="search()"
|
||||||
|
>
|
||||||
<template #tagPathSelect>
|
<template #tagPathSelect>
|
||||||
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable style="width: 200px">
|
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable style="width: 200px">
|
||||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||||
@@ -8,7 +20,15 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #instanceSelect>
|
<template #instanceSelect>
|
||||||
<el-select remote :remote-method="getInstances" v-model="query.instanceId" placeholder="输入并选择实例" filterable clearable style="width: 200px">
|
<el-select
|
||||||
|
remote
|
||||||
|
:remote-method="getInstances"
|
||||||
|
v-model="query.instanceId"
|
||||||
|
placeholder="输入并选择实例"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 200px"
|
||||||
|
>
|
||||||
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
|
<el-option v-for="item in state.instances" :key="item.id" :label="`${item.name}`" :value="item.id">
|
||||||
{{ item.name }}
|
{{ item.name }}
|
||||||
<el-divider direction="vertical" border-style="dashed" />
|
<el-divider direction="vertical" border-style="dashed" />
|
||||||
@@ -86,7 +106,15 @@
|
|||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-transfer v-model="exportDialog.value" filterable filter-placeholder="按数据库名称筛选" :titles="['全部数据库', '导出数据库']" :data="exportDialog.data" max-height="300" size="small" />
|
<el-transfer
|
||||||
|
v-model="exportDialog.value"
|
||||||
|
filterable
|
||||||
|
filter-placeholder="按数据库名称筛选"
|
||||||
|
:titles="['全部数据库', '导出数据库']"
|
||||||
|
:data="exportDialog.data"
|
||||||
|
max-height="300"
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -97,7 +125,13 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<el-dialog width="90%" :title="`${sqlExecLogDialog.title} - SQL执行记录`" :before-close="onBeforeCloseSqlExecDialog" :close-on-click-modal="false" v-model="sqlExecLogDialog.visible">
|
<el-dialog
|
||||||
|
width="90%"
|
||||||
|
:title="`${sqlExecLogDialog.title} - SQL执行记录`"
|
||||||
|
:before-close="onBeforeCloseSqlExecDialog"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
v-model="sqlExecLogDialog.visible"
|
||||||
|
>
|
||||||
<db-sql-exec-log :db-id="sqlExecLogDialog.dbId" :dbs="sqlExecLogDialog.dbs" />
|
<db-sql-exec-log :db-id="sqlExecLogDialog.dbId" :dbs="sqlExecLogDialog.dbs" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
@@ -138,7 +172,7 @@ import PageTable from '@/components/pagetable/PageTable.vue';
|
|||||||
import { TableColumn, TableQuery } from '@/components/pagetable';
|
import { TableColumn, TableQuery } from '@/components/pagetable';
|
||||||
import { hasPerms } from '@/components/auth/auth';
|
import { hasPerms } from '@/components/auth/auth';
|
||||||
import DbSqlExecLog from './DbSqlExecLog.vue';
|
import DbSqlExecLog from './DbSqlExecLog.vue';
|
||||||
import { DbType } from '@/views/ops/db/component/table/dbs/db-option';
|
import { DbType } from './dialect';
|
||||||
|
|
||||||
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
|
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
|
||||||
|
|
||||||
@@ -369,7 +403,12 @@ const dumpDbs = () => {
|
|||||||
type += 2;
|
type += 2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
a.setAttribute('href', `${config.baseApiUrl}/dbs/${state.exportDialog.dbId}/dump?db=${state.exportDialog.value.join(',')}&type=${type}&extName=${state.exportDialog.extName}&${joinClientParams()}`);
|
a.setAttribute(
|
||||||
|
'href',
|
||||||
|
`${config.baseApiUrl}/dbs/${state.exportDialog.dbId}/dump?db=${state.exportDialog.value.join(',')}&type=${type}&extName=${
|
||||||
|
state.exportDialog.extName
|
||||||
|
}&${joinClientParams()}`
|
||||||
|
);
|
||||||
a.click();
|
a.click();
|
||||||
state.exportDialog.visible = false;
|
state.exportDialog.visible = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<span v-if="data.type.value == SqlExecNodeType.DbInst">
|
<span v-if="data.type.value == SqlExecNodeType.DbInst">
|
||||||
<el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="250">
|
<el-popover :show-after="500" placement="right-start" title="数据库实例信息" trigger="hover" :width="250">
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<SvgIcon :name="DbInst.getIconName(data.params.type)" :size="18" />
|
<SvgIcon :name="getDbDialect(data.params.type).getIcon()" :size="18" />
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<el-descriptions :column="1" size="small">
|
<el-descriptions :column="1" size="small">
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<el-descriptions-item label-align="right">
|
<el-descriptions-item label-align="right">
|
||||||
<template #label>
|
<template #label>
|
||||||
<div>
|
<div>
|
||||||
<SvgIcon :name="DbInst.getIconName(nowDbInst.type)" :size="18" />
|
<SvgIcon :name="getDbDialect(nowDbInst.type).getIcon()" :size="18" />
|
||||||
实例
|
实例
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
{{ dt.params.name }}
|
{{ dt.params.name }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="host">
|
<el-descriptions-item label="host">
|
||||||
<SvgIcon :name="DbInst.getIconName(dt.params.type)" :size="18" />
|
<SvgIcon :name="getDbDialect(dt.params.type).getIcon()" :size="18" />
|
||||||
{{ dt.params.host }}
|
{{ dt.params.host }}
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
<el-descriptions-item label="库名">
|
<el-descriptions-item label="库名">
|
||||||
@@ -159,6 +159,7 @@ import { dbApi } from './api';
|
|||||||
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
||||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
import { ContextmenuItem } from '@/components/contextmenu';
|
import { ContextmenuItem } from '@/components/contextmenu';
|
||||||
|
import { getDbDialect } from './dialect/index';
|
||||||
|
|
||||||
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
||||||
const DbTableDataOp = defineAsyncComponent(() => import('./component/table/DbTableDataOp.vue'));
|
const DbTableDataOp = defineAsyncComponent(() => import('./component/table/DbTableDataOp.vue'));
|
||||||
|
|||||||
@@ -23,8 +23,15 @@
|
|||||||
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name"> </el-input>
|
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name"> </el-input>
|
||||||
|
|
||||||
<el-select v-else-if="item.prop === 'type'" filterable size="small" v-model="scope.row.type">
|
<el-select v-else-if="item.prop === 'type'" filterable size="small" v-model="scope.row.type">
|
||||||
<el-option v-for="pgsqlType in state.columnTypeList" :key="pgsqlType.dataType" :value="pgsqlType.udtName" :label="pgsqlType.dataType">
|
<el-option
|
||||||
<span v-if="pgsqlType.dataType === pgsqlType.udtName">{{ pgsqlType.dataType }}{{ pgsqlType.desc && ':' + pgsqlType.desc }}</span>
|
v-for="pgsqlType in state.columnTypeList"
|
||||||
|
:key="pgsqlType.dataType"
|
||||||
|
:value="pgsqlType.udtName"
|
||||||
|
:label="pgsqlType.dataType"
|
||||||
|
>
|
||||||
|
<span v-if="pgsqlType.dataType === pgsqlType.udtName"
|
||||||
|
>{{ pgsqlType.dataType }}{{ pgsqlType.desc && ':' + pgsqlType.desc }}</span
|
||||||
|
>
|
||||||
<span v-else>{{ pgsqlType.dataType }},别名:{{ pgsqlType.udtName }} {{ pgsqlType.desc }}</span>
|
<span v-else>{{ pgsqlType.dataType }},别名:{{ pgsqlType.udtName }} {{ pgsqlType.desc }}</span>
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
@@ -39,11 +46,25 @@
|
|||||||
|
|
||||||
<el-checkbox v-else-if="item.prop === 'pri'" size="small" v-model="scope.row.pri"> </el-checkbox>
|
<el-checkbox v-else-if="item.prop === 'pri'" size="small" v-model="scope.row.pri"> </el-checkbox>
|
||||||
|
|
||||||
<el-checkbox v-else-if="item.prop === 'auto_increment'" size="small" v-model="scope.row.auto_increment" :disabled="dbType === DbType.postgresql"> </el-checkbox>
|
<el-checkbox
|
||||||
|
v-else-if="item.prop === 'auto_increment'"
|
||||||
|
size="small"
|
||||||
|
v-model="scope.row.auto_increment"
|
||||||
|
:disabled="dbType === DbType.postgresql"
|
||||||
|
>
|
||||||
|
</el-checkbox>
|
||||||
|
|
||||||
<el-input v-else-if="item.prop === 'remark'" size="small" v-model="scope.row.remark"> </el-input>
|
<el-input v-else-if="item.prop === 'remark'" size="small" v-model="scope.row.remark"> </el-input>
|
||||||
|
|
||||||
<el-link v-else-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteRow(scope.$index)">删除</el-link>
|
<el-link
|
||||||
|
v-else-if="item.prop === 'action'"
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
:underline="false"
|
||||||
|
@click.prevent="deleteRow(scope.$index)"
|
||||||
|
>删除</el-link
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -58,13 +79,24 @@
|
|||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-input v-if="item.prop === 'indexName'" size="small" disabled v-model="scope.row.indexName"></el-input>
|
<el-input v-if="item.prop === 'indexName'" size="small" disabled v-model="scope.row.indexName"></el-input>
|
||||||
|
|
||||||
<el-select v-if="item.prop === 'columnNames'" v-model="scope.row.columnNames" multiple collapse-tags collapse-tags-tooltip filterable placeholder="请选择字段" @change="indexChanges(scope.row)" style="width: 100%">
|
<el-select
|
||||||
|
v-if="item.prop === 'columnNames'"
|
||||||
|
v-model="scope.row.columnNames"
|
||||||
|
multiple
|
||||||
|
collapse-tags
|
||||||
|
collapse-tags-tooltip
|
||||||
|
filterable
|
||||||
|
placeholder="请选择字段"
|
||||||
|
@change="indexChanges(scope.row)"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name" :label="cl.name" :value="cl.name">
|
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name" :label="cl.name" :value="cl.name">
|
||||||
{{ cl.name + ' - ' + (cl.remark || '') }}
|
{{ cl.name + ' - ' + (cl.remark || '') }}
|
||||||
</el-option>
|
</el-option>
|
||||||
</el-select>
|
</el-select>
|
||||||
|
|
||||||
<el-checkbox v-if="item.prop === 'unique'" size="small" v-model="scope.row.unique" @change="indexChanges(scope.row)"> </el-checkbox>
|
<el-checkbox v-if="item.prop === 'unique'" size="small" v-model="scope.row.unique" @change="indexChanges(scope.row)">
|
||||||
|
</el-checkbox>
|
||||||
|
|
||||||
<el-select v-if="item.prop === 'indexType'" filterable size="small" v-model="scope.row.indexType">
|
<el-select v-if="item.prop === 'indexType'" filterable size="small" v-model="scope.row.indexType">
|
||||||
<el-option v-for="typeValue in indexTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
|
<el-option v-for="typeValue in indexTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
|
||||||
@@ -72,7 +104,15 @@
|
|||||||
|
|
||||||
<el-input v-if="item.prop === 'indexComment'" size="small" v-model="scope.row.indexComment"> </el-input>
|
<el-input v-if="item.prop === 'indexComment'" size="small" v-model="scope.row.indexComment"> </el-input>
|
||||||
|
|
||||||
<el-link v-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteIndex(scope.$index)">删除</el-link>
|
<el-link
|
||||||
|
v-if="item.prop === 'action'"
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
size="small"
|
||||||
|
:underline="false"
|
||||||
|
@click.prevent="deleteIndex(scope.$index)"
|
||||||
|
>删除</el-link
|
||||||
|
>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
@@ -92,10 +132,9 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, toRefs, watch } from 'vue';
|
import { reactive, ref, toRefs, watch } from 'vue';
|
||||||
import { getDbOption } from './service';
|
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import SqlExecBox from '../sqleditor/SqlExecBox';
|
import SqlExecBox from '../sqleditor/SqlExecBox';
|
||||||
import { DbType } from '@/views/ops/db/component/table/dbs/db-option';
|
import { getDbDialect, DbType } from '../../dialect/index';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
visible: {
|
visible: {
|
||||||
@@ -121,14 +160,14 @@ const props = defineProps({
|
|||||||
//定义事件
|
//定义事件
|
||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change', 'submit-sql']);
|
const emit = defineEmits(['update:visible', 'cancel', 'val-change', 'submit-sql']);
|
||||||
|
|
||||||
const dbOption = getDbOption(props.dbType);
|
const dbDialect = getDbDialect(props.dbType);
|
||||||
|
|
||||||
const formRef: any = ref();
|
const formRef: any = ref();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
btnloading: false,
|
btnloading: false,
|
||||||
activeName: '1',
|
activeName: '1',
|
||||||
columnTypeList: dbOption.getTypeList(),
|
columnTypeList: dbDialect.getColumnTypes(),
|
||||||
indexTypeList: ['BTREE'], // mysql索引类型详解 http://c.biancheng.net/view/7897.html
|
indexTypeList: ['BTREE'], // mysql索引类型详解 http://c.biancheng.net/view/7897.html
|
||||||
tableData: {
|
tableData: {
|
||||||
fields: {
|
fields: {
|
||||||
@@ -225,7 +264,7 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
tableName: '',
|
tableName: '',
|
||||||
tableComment: '',
|
tableComment: '',
|
||||||
height: 550,
|
height: 450,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -269,21 +308,101 @@ const addDefaultRows = () => {
|
|||||||
state.tableData.fields.res.push(
|
state.tableData.fields.res.push(
|
||||||
{ name: 'id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID' },
|
{ name: 'id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID' },
|
||||||
{ name: 'creator_id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人id' },
|
{ name: 'creator_id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人id' },
|
||||||
{ name: 'creator', type: 'varchar', length: '100', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人姓名' },
|
{
|
||||||
{ name: 'create_time', type: 'datetime', length: '', numScale: '', value: 'CURRENT_TIMESTAMP', notNull: true, pri: false, auto_increment: false, remark: '创建时间' },
|
name: 'creator',
|
||||||
|
type: 'varchar',
|
||||||
|
length: '100',
|
||||||
|
numScale: '',
|
||||||
|
value: '',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '创建人姓名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'create_time',
|
||||||
|
type: 'datetime',
|
||||||
|
length: '',
|
||||||
|
numScale: '',
|
||||||
|
value: 'CURRENT_TIMESTAMP',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '创建时间',
|
||||||
|
},
|
||||||
{ name: 'updator_id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人id' },
|
{ name: 'updator_id', type: 'bigint', length: '20', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人id' },
|
||||||
{ name: 'updator', type: 'varchar', length: '100', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人姓名' },
|
{
|
||||||
{ name: 'update_time', type: 'datetime', length: '', numScale: '', value: 'CURRENT_TIMESTAMP', notNull: true, pri: false, auto_increment: false, remark: '修改时间' }
|
name: 'updator',
|
||||||
|
type: 'varchar',
|
||||||
|
length: '100',
|
||||||
|
numScale: '',
|
||||||
|
value: '',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '修改人姓名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'update_time',
|
||||||
|
type: 'datetime',
|
||||||
|
length: '',
|
||||||
|
numScale: '',
|
||||||
|
value: 'CURRENT_TIMESTAMP',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '修改时间',
|
||||||
|
}
|
||||||
);
|
);
|
||||||
} else if (props.dbType === DbType.postgresql) {
|
} else if (props.dbType === DbType.postgresql) {
|
||||||
state.tableData.fields.res.push(
|
state.tableData.fields.res.push(
|
||||||
{ name: 'id', type: 'bigserial', length: '', numScale: '', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID' },
|
{ name: 'id', type: 'bigserial', length: '', numScale: '', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID' },
|
||||||
{ name: 'creator_id', type: 'int8', length: '', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人id' },
|
{ name: 'creator_id', type: 'int8', length: '', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人id' },
|
||||||
{ name: 'creator', type: 'varchar', length: '100', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '创建人姓名' },
|
{
|
||||||
{ name: 'create_time', type: 'timestamp', length: '', numScale: '', value: 'CURRENT_TIMESTAMP', notNull: true, pri: false, auto_increment: false, remark: '创建时间' },
|
name: 'creator',
|
||||||
|
type: 'varchar',
|
||||||
|
length: '100',
|
||||||
|
numScale: '',
|
||||||
|
value: '',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '创建人姓名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'create_time',
|
||||||
|
type: 'timestamp',
|
||||||
|
length: '',
|
||||||
|
numScale: '',
|
||||||
|
value: 'CURRENT_TIMESTAMP',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '创建时间',
|
||||||
|
},
|
||||||
{ name: 'updator_id', type: 'int8', length: '', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人id' },
|
{ name: 'updator_id', type: 'int8', length: '', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人id' },
|
||||||
{ name: 'updator', type: 'varchar', length: '100', numScale: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改人姓名' },
|
{
|
||||||
{ name: 'update_time', type: 'timestamp', length: '', numScale: '', value: 'CURRENT_TIMESTAMP', notNull: true, pri: false, auto_increment: false, remark: '修改时间' }
|
name: 'updator',
|
||||||
|
type: 'varchar',
|
||||||
|
length: '100',
|
||||||
|
numScale: '',
|
||||||
|
value: '',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '修改人姓名',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'update_time',
|
||||||
|
type: 'timestamp',
|
||||||
|
length: '',
|
||||||
|
numScale: '',
|
||||||
|
value: 'CURRENT_TIMESTAMP',
|
||||||
|
notNull: true,
|
||||||
|
pri: false,
|
||||||
|
auto_increment: false,
|
||||||
|
remark: '修改时间',
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -377,20 +496,20 @@ const genSql = () => {
|
|||||||
// 创建表
|
// 创建表
|
||||||
if (!props.data?.edit) {
|
if (!props.data?.edit) {
|
||||||
if (state.activeName === '1') {
|
if (state.activeName === '1') {
|
||||||
return dbOption.getCreateTableSql(data);
|
return dbDialect.getCreateTableSql(data);
|
||||||
} else if (state.activeName === '2' && data.indexs.res.length > 0) {
|
} else if (state.activeName === '2' && data.indexs.res.length > 0) {
|
||||||
return dbOption.getCreateIndexSql(data);
|
return dbDialect.getCreateIndexSql(data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 修改
|
// 修改
|
||||||
if (state.activeName === '1') {
|
if (state.activeName === '1') {
|
||||||
// 修改列
|
// 修改列
|
||||||
let changeData = filterChangedData(oldData.fields, state.tableData.fields.res, 'name');
|
let changeData = filterChangedData(oldData.fields, state.tableData.fields.res, 'name');
|
||||||
return dbOption.getModifyColumnSql(data.tableName, changeData);
|
return dbDialect.getModifyColumnSql(data.tableName, changeData);
|
||||||
} else if (state.activeName === '2') {
|
} else if (state.activeName === '2') {
|
||||||
// 修改索引
|
// 修改索引
|
||||||
let changeData = filterChangedData(oldData.indexs, state.tableData.indexs.res, 'indexName');
|
let changeData = filterChangedData(oldData.indexs, state.tableData.indexs.res, 'indexName');
|
||||||
return dbOption.getModifyIndexSql(data.tableName, changeData);
|
return dbDialect.getModifyIndexSql(data.tableName, changeData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -41,23 +41,36 @@
|
|||||||
<el-input v-model="tableCommentSearch" size="small" placeholder="备注: 输入可过滤" clearable />
|
<el-input v-model="tableCommentSearch" size="small" placeholder="备注: 输入可过滤" clearable />
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="tableRows" label="Rows" min-width="70" sortable :sort-method="(a: any, b: any) => parseInt(a.tableRows) - parseInt(b.tableRows)"></el-table-column>
|
<el-table-column
|
||||||
|
prop="tableRows"
|
||||||
|
label="Rows"
|
||||||
|
min-width="70"
|
||||||
|
sortable
|
||||||
|
:sort-method="(a: any, b: any) => parseInt(a.tableRows) - parseInt(b.tableRows)"
|
||||||
|
></el-table-column>
|
||||||
<el-table-column property="dataLength" label="数据大小" sortable :sort-method="(a: any, b: any) => parseInt(a.dataLength) - parseInt(b.dataLength)">
|
<el-table-column property="dataLength" label="数据大小" sortable :sort-method="(a: any, b: any) => parseInt(a.dataLength) - parseInt(b.dataLength)">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ formatByteSize(scope.row.dataLength) }}
|
{{ formatByteSize(scope.row.dataLength) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column property="indexLength" label="索引大小" sortable :sort-method="(a: any, b: any) => parseInt(a.indexLength) - parseInt(b.indexLength)">
|
<el-table-column
|
||||||
|
property="indexLength"
|
||||||
|
label="索引大小"
|
||||||
|
sortable
|
||||||
|
:sort-method="(a: any, b: any) => parseInt(a.indexLength) - parseInt(b.indexLength)"
|
||||||
|
>
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
{{ formatByteSize(scope.row.indexLength) }}
|
{{ formatByteSize(scope.row.indexLength) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column v-if="dbType===DbType.mysql" property="createTime" label="创建时间" min-width="150"> </el-table-column>
|
<el-table-column v-if="dbType === DbType.mysql" property="createTime" label="创建时间" min-width="150"> </el-table-column>
|
||||||
<el-table-column label="更多信息" min-width="160">
|
<el-table-column label="更多信息" min-width="160">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
|
<el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
|
||||||
<el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
|
<el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
|
||||||
<el-link class="ml5" v-if="tableCreateDialog.enableEditTypes.indexOf(dbType) > -1" @click.prevent="openEditTable(scope.row)" type="warning">编辑表</el-link>
|
<el-link class="ml5" v-if="tableCreateDialog.enableEditTypes.indexOf(dbType) > -1" @click.prevent="openEditTable(scope.row)" type="warning"
|
||||||
|
>编辑表</el-link
|
||||||
|
>
|
||||||
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">DDL</el-link>
|
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">DDL</el-link>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -91,7 +104,17 @@
|
|||||||
<el-input disabled type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="ddlDialog.ddl" size="small"> </el-input>
|
<el-input disabled type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="ddlDialog.ddl" size="small"> </el-input>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<db-table-op :title="tableCreateDialog.title" :active-name="tableCreateDialog.activeName" :dbId="dbId" :db="db" :dbType="dbType" :data="tableCreateDialog.data" v-model:visible="tableCreateDialog.visible" @submit-sql="onSubmitSql"> </db-table-op>
|
<db-table-op
|
||||||
|
:title="tableCreateDialog.title"
|
||||||
|
:active-name="tableCreateDialog.activeName"
|
||||||
|
:dbId="dbId"
|
||||||
|
:db="db"
|
||||||
|
:dbType="dbType"
|
||||||
|
:data="tableCreateDialog.data"
|
||||||
|
v-model:visible="tableCreateDialog.visible"
|
||||||
|
@submit-sql="onSubmitSql"
|
||||||
|
>
|
||||||
|
</db-table-op>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -104,7 +127,7 @@ import SqlExecBox from '../sqleditor/SqlExecBox';
|
|||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { joinClientParams } from '@/common/request';
|
import { joinClientParams } from '@/common/request';
|
||||||
import { isTrue } from '@/common/assert';
|
import { isTrue } from '@/common/assert';
|
||||||
import { DbType } from '@/views/ops/db/component/table/dbs/db-option';
|
import { DbType } from '../../dialect/index';
|
||||||
|
|
||||||
const DbTableOp = defineAsyncComponent(() => import('./DbTableOp.vue'));
|
const DbTableOp = defineAsyncComponent(() => import('./DbTableOp.vue'));
|
||||||
|
|
||||||
@@ -174,7 +197,19 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { loading, tables, tableNameSearch, tableCommentSearch, showDumpInfo, dumpInfo, chooseTableName, columnDialog, indexDialog, ddlDialog, tableCreateDialog } = toRefs(state);
|
const {
|
||||||
|
loading,
|
||||||
|
tables,
|
||||||
|
tableNameSearch,
|
||||||
|
tableCommentSearch,
|
||||||
|
showDumpInfo,
|
||||||
|
dumpInfo,
|
||||||
|
chooseTableName,
|
||||||
|
columnDialog,
|
||||||
|
indexDialog,
|
||||||
|
ddlDialog,
|
||||||
|
tableCreateDialog,
|
||||||
|
} = toRefs(state);
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
getTables();
|
getTables();
|
||||||
@@ -229,7 +264,10 @@ const handleDumpTableSelectionChange = (vals: any) => {
|
|||||||
const dump = (db: string) => {
|
const dump = (db: string) => {
|
||||||
isTrue(state.dumpInfo.tables.length > 0, '请选择要导出的表');
|
isTrue(state.dumpInfo.tables.length > 0, '请选择要导出的表');
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.setAttribute('href', `${config.baseApiUrl}/dbs/${props.dbId}/dump?db=${db}&type=${state.dumpInfo.type}&tables=${state.dumpInfo.tables.join(',')}&${joinClientParams()}`);
|
a.setAttribute(
|
||||||
|
'href',
|
||||||
|
`${config.baseApiUrl}/dbs/${props.dbId}/dump?db=${db}&type=${state.dumpInfo.type}&tables=${state.dumpInfo.tables.join(',')}&${joinClientParams()}`
|
||||||
|
);
|
||||||
a.click();
|
a.click();
|
||||||
state.showDumpInfo = false;
|
state.showDumpInfo = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,25 +0,0 @@
|
|||||||
export interface sqlType {
|
|
||||||
udtName: string;
|
|
||||||
dataType: string;
|
|
||||||
desc: string;
|
|
||||||
space: string;
|
|
||||||
range?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface DbOption {
|
|
||||||
/** 生成字段类型列表 */
|
|
||||||
getTypeList(): sqlType[];
|
|
||||||
/** 生成创建表sql */
|
|
||||||
getCreateTableSql(tableData: any): string;
|
|
||||||
/** 生成创建索引sql */
|
|
||||||
getCreateIndexSql(tableData: any): string;
|
|
||||||
/** 生成编辑列sql */
|
|
||||||
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string;
|
|
||||||
/** 生成编辑索引sql */
|
|
||||||
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DbType = {
|
|
||||||
mysql: 'mysql',
|
|
||||||
postgresql: 'postgres',
|
|
||||||
};
|
|
||||||
@@ -1,126 +0,0 @@
|
|||||||
import { DbOption, sqlType } from '@/views/ops/db/component/table/dbs/db-option';
|
|
||||||
import { MYSQL_TYPE_LIST } from '@/views/ops/db/component/table/service';
|
|
||||||
|
|
||||||
export class MysqlOption implements DbOption {
|
|
||||||
getTypeList(): sqlType[] {
|
|
||||||
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}` : '';
|
|
||||||
let length = cl.length ? `(${cl.length})` : '';
|
|
||||||
let onUpdate = 'update_time' === cl.name ? ' ON UPDATE CURRENT_TIMESTAMP ' : '';
|
|
||||||
return ` ${cl.name} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : 'NULL'} ${cl.auto_increment ? 'AUTO_INCREMENT' : ''} ${defVal} ${onUpdate} comment '${cl.remark || ''}' `;
|
|
||||||
}
|
|
||||||
getCreateTableSql(data: any): string {
|
|
||||||
// 创建表结构
|
|
||||||
let pks = [] as string[];
|
|
||||||
let fields: string[] = [];
|
|
||||||
data.fields.res.forEach((item: any) => {
|
|
||||||
item.name && fields.push(this.genColumnBasicSql(item));
|
|
||||||
if (item.pri) {
|
|
||||||
pks.push(item.name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return `CREATE TABLE ${data.tableName}
|
|
||||||
( ${fields.join(',')}
|
|
||||||
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
|
||||||
) COMMENT='${data.tableComment}';`;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCreateIndexSql(data: any): string {
|
|
||||||
// 创建索引
|
|
||||||
let sql = `ALTER TABLE ${data.tableName}`;
|
|
||||||
data.indexs.res.forEach((a: any) => {
|
|
||||||
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
|
||||||
});
|
|
||||||
return sql.substring(0, sql.length - 1) + ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
|
||||||
let addSql = '',
|
|
||||||
updSql = '',
|
|
||||||
delSql = '';
|
|
||||||
if (changeData.add.length > 0) {
|
|
||||||
addSql = `ALTER TABLE ${tableName}`;
|
|
||||||
changeData.add.forEach((a) => {
|
|
||||||
addSql += ` ADD ${this.genColumnBasicSql(a)},`;
|
|
||||||
});
|
|
||||||
addSql = addSql.substring(0, addSql.length - 1);
|
|
||||||
addSql += ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
|
||||||
updSql = `ALTER TABLE ${tableName}`;
|
|
||||||
let arr = [] as string[];
|
|
||||||
changeData.upd.forEach((a) => {
|
|
||||||
arr.push(` MODIFY ${this.genColumnBasicSql(a)}`);
|
|
||||||
});
|
|
||||||
updSql += arr.join(',');
|
|
||||||
updSql += ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.del.length > 0) {
|
|
||||||
changeData.del.forEach((a) => {
|
|
||||||
delSql += ` ALTER TABLE ${tableName} DROP COLUMN ${a.name}; `;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return addSql + updSql + delSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
|
||||||
// 搜集修改和删除的索引,添加到drop index xx
|
|
||||||
// 收集新增和修改的索引,添加到ADD xx
|
|
||||||
// ALTER TABLE `test1`
|
|
||||||
// DROP INDEX `test1_name_uindex`,
|
|
||||||
// DROP INDEX `test1_column_name4_index`,
|
|
||||||
// ADD UNIQUE INDEX `test1_name_uindex`(`id`) USING BTREE COMMENT 'ASDASD',
|
|
||||||
// ADD INDEX `111`(`column_name4`) USING BTREE COMMENT 'zasf';
|
|
||||||
|
|
||||||
let dropIndexNames: string[] = [];
|
|
||||||
let addIndexs: any[] = [];
|
|
||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
|
||||||
changeData.upd.forEach((a) => {
|
|
||||||
dropIndexNames.push(a.indexName);
|
|
||||||
addIndexs.push(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.del.length > 0) {
|
|
||||||
changeData.del.forEach((a) => {
|
|
||||||
dropIndexNames.push(a.indexName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.add.length > 0) {
|
|
||||||
changeData.add.forEach((a) => {
|
|
||||||
addIndexs.push(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dropIndexNames.length > 0 || addIndexs.length > 0) {
|
|
||||||
let sql = `ALTER TABLE ${tableName} `;
|
|
||||||
if (dropIndexNames.length > 0) {
|
|
||||||
dropIndexNames.forEach((a) => {
|
|
||||||
sql += `DROP INDEX ${a},`;
|
|
||||||
});
|
|
||||||
sql = sql.substring(0, sql.length - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addIndexs.length > 0) {
|
|
||||||
if (dropIndexNames.length > 0) {
|
|
||||||
sql += ',';
|
|
||||||
}
|
|
||||||
addIndexs.forEach((a) => {
|
|
||||||
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
|
||||||
});
|
|
||||||
sql = sql.substring(0, sql.length - 1);
|
|
||||||
}
|
|
||||||
return sql;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
import { DbOption, sqlType } from '@/views/ops/db/component/table/dbs/db-option';
|
|
||||||
import { GAUSS_TYPE_LIST } from '@/views/ops/db/component/table/service';
|
|
||||||
|
|
||||||
export class PostgresqlOption implements DbOption {
|
|
||||||
getTypeList(): sqlType[] {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
if (text.indexOf(arr[i]) > -1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
getDefaultValueSql(cl: any): string {
|
|
||||||
if (cl.value && cl.value.length > 0) {
|
|
||||||
// 哪些字段默认值需要加引号
|
|
||||||
let marks = false;
|
|
||||||
if (this.matchType(cl.type, ['char', 'time', 'date', 'text'])) {
|
|
||||||
// 默认值是now()的time或date不需要加引号
|
|
||||||
if (cl.value.toLowerCase().replace(' ', '') === 'CURRENT_TIMESTAMP' && this.matchType(cl.type, ['time', 'date'])) {
|
|
||||||
marks = false;
|
|
||||||
} else {
|
|
||||||
marks = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ` DEFAULT ${marks ? "'" : ''}${cl.value}${marks ? "'" : ''}`;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getTypeLengthSql(cl: any) {
|
|
||||||
// 哪些字段可以指定长度
|
|
||||||
if (cl.length && this.matchType(cl.type, ['char', 'time', 'bit', 'num', 'decimal'])) {
|
|
||||||
// 哪些字段类型可以指定小数点
|
|
||||||
if (cl.numScale && this.matchType(cl.type, ['num', 'decimal'])) {
|
|
||||||
return `(${cl.length}, ${cl.numScale})`;
|
|
||||||
} else {
|
|
||||||
return `(${cl.length})`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
genColumnBasicSql(cl: any): string {
|
|
||||||
let length = this.getTypeLengthSql(cl);
|
|
||||||
// 默认值
|
|
||||||
let defVal = this.getDefaultValueSql(cl);
|
|
||||||
return ` ${cl.name} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : ''} ${defVal} `;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCreateTableSql(data: any): string {
|
|
||||||
let createSql = '';
|
|
||||||
let tableCommentSql = '';
|
|
||||||
let columCommentSql = '';
|
|
||||||
|
|
||||||
// 创建表结构
|
|
||||||
let pks = [] as string[];
|
|
||||||
let fields: string[] = [];
|
|
||||||
data.fields.res.forEach((item: any) => {
|
|
||||||
item.name && fields.push(this.genColumnBasicSql(item));
|
|
||||||
if (item.pri) {
|
|
||||||
pks.push(item.name);
|
|
||||||
}
|
|
||||||
// 列注释
|
|
||||||
if (item.remark) {
|
|
||||||
columCommentSql += ` comment on column ${data.tableName}.${item.name} is '${item.remark}'; `;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 建表
|
|
||||||
createSql = `CREATE TABLE ${data.tableName}
|
|
||||||
(
|
|
||||||
${fields.join(',')}
|
|
||||||
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
|
||||||
);`;
|
|
||||||
// 表注释
|
|
||||||
if (data.tableComment) {
|
|
||||||
tableCommentSql = ` comment on table ${data.tableName} is '${data.tableComment}'; `;
|
|
||||||
}
|
|
||||||
|
|
||||||
return createSql + tableCommentSql + columCommentSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
getCreateIndexSql(tableData: any): string {
|
|
||||||
// CREATE UNIQUE INDEX idx_column_name ON your_table (column1, column2);
|
|
||||||
// COMMENT ON INDEX idx_column_name IS 'Your index comment here';
|
|
||||||
// 创建索引
|
|
||||||
let sql: string[] = [];
|
|
||||||
tableData.indexs.res.forEach((a: any) => {
|
|
||||||
sql.push(` CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName} USING btree ("${a.columnNames.join('","')})"`);
|
|
||||||
if (a.indexComment) {
|
|
||||||
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return sql.join(';');
|
|
||||||
}
|
|
||||||
|
|
||||||
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
|
||||||
let sql: string[] = [];
|
|
||||||
if (changeData.add.length > 0) {
|
|
||||||
changeData.add.forEach((a) => {
|
|
||||||
let typeLength = this.getTypeLengthSql(a);
|
|
||||||
let defaultSql = this.getDefaultValueSql(a);
|
|
||||||
sql.push(`ALTER TABLE ${tableName} add ${a.name} ${a.type}${typeLength} ${defaultSql}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
|
||||||
changeData.upd.forEach((a) => {
|
|
||||||
let typeLength = this.getTypeLengthSql(a);
|
|
||||||
sql.push(`ALTER TABLE ${tableName} alter column ${a.name} type ${a.type}${typeLength}`);
|
|
||||||
let defaultSql = this.getDefaultValueSql(a);
|
|
||||||
if (defaultSql) {
|
|
||||||
sql.push(`alter table ${tableName} alter column ${a.name} set ${defaultSql}`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.del.length > 0) {
|
|
||||||
changeData.del.forEach((a) => {
|
|
||||||
sql.push(`ALTER TABLE ${tableName} DROP COLUMN ${a.name}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return sql.join(';');
|
|
||||||
}
|
|
||||||
|
|
||||||
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
|
||||||
// 不能直接修改索引名或字段、需要先删后加
|
|
||||||
let dropIndexNames: string[] = [];
|
|
||||||
let addIndexs: any[] = [];
|
|
||||||
|
|
||||||
if (changeData.upd.length > 0) {
|
|
||||||
changeData.upd.forEach((a) => {
|
|
||||||
dropIndexNames.push(a.indexName);
|
|
||||||
addIndexs.push(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.del.length > 0) {
|
|
||||||
changeData.del.forEach((a) => {
|
|
||||||
dropIndexNames.push(a.indexName);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (changeData.add.length > 0) {
|
|
||||||
changeData.add.forEach((a) => {
|
|
||||||
addIndexs.push(a);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dropIndexNames.length > 0 || addIndexs.length > 0) {
|
|
||||||
let sql: string[] = [];
|
|
||||||
if (dropIndexNames.length > 0) {
|
|
||||||
dropIndexNames.forEach((a) => {
|
|
||||||
sql.push(`DROP INDEX ${a}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (addIndexs.length > 0) {
|
|
||||||
addIndexs.forEach((a) => {
|
|
||||||
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')})`);
|
|
||||||
if (a.indexComment) {
|
|
||||||
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return sql.join(';');
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import { DbOption, DbType, sqlType } from '@/views/ops/db/component/table/dbs/db-option';
|
|
||||||
import { MysqlOption } from '@/views/ops/db/component/table/dbs/mysql-option';
|
|
||||||
import { PostgresqlOption } from '@/views/ops/db/component/table/dbs/postgresql-option';
|
|
||||||
|
|
||||||
export const MYSQL_TYPE_LIST = ['bigint', 'binary', 'blob', 'char', 'datetime', 'date', 'decimal', 'double', 'enum', 'float', 'int', 'json', 'longblob', 'longtext', 'mediumblob', 'mediumtext', 'set', 'smallint', 'text', 'time', 'timestamp', 'tinyint', 'varbinary', 'varchar'];
|
|
||||||
|
|
||||||
export const GAUSS_TYPE_LIST: sqlType[] = [
|
|
||||||
// 数值 - 整数型
|
|
||||||
{ udtName: 'int1', dataType: 'tinyint', desc: '微整数,别名为INT1', space: '1字节', range: '0 ~ +255' },
|
|
||||||
{ udtName: 'int2', dataType: 'smallint', desc: '小范围整数,别名为INT2。', space: '2字节', range: '-32,768 ~ +32,767' },
|
|
||||||
{ udtName: 'int4', dataType: 'integer', desc: '常用的整数,别名为INT4。', space: '4字节', range: '-2,147,483,648 ~ +2,147,483,647' },
|
|
||||||
{ udtName: 'int8', dataType: 'bigint', desc: '大范围的整数,别名为INT8。', space: '8字节', range: '很大' },
|
|
||||||
|
|
||||||
// 数值 - 任意精度型
|
|
||||||
{
|
|
||||||
udtName: 'numeric',
|
|
||||||
dataType: 'numeric',
|
|
||||||
desc: '精度(总位数)取值范围为[1,1000],标度(小数位数)取值范围为[0,精度]。',
|
|
||||||
space: '每四位(十进制位)占用两个字节,然后在整个数据上加上八个字节的额外开销',
|
|
||||||
range: '未指定精度的情况下,小数点前最大131,072位,小数点后最大16,383位',
|
|
||||||
},
|
|
||||||
// 数值 - 任意精度型
|
|
||||||
{ udtName: 'decimal', dataType: 'decimal', desc: '等同于number类型', space: '等同于number类型' },
|
|
||||||
|
|
||||||
// 数值 - 序列整型
|
|
||||||
{ udtName: 'smallserial', dataType: 'smallserial', desc: '二字节序列整型。', space: '2字节', range: '-32,768 ~ +32,767' },
|
|
||||||
{ udtName: 'serial', dataType: 'serial', desc: '四字节序列整型。', space: '4字节', range: '-2,147,483,648 ~ +2,147,483,647' },
|
|
||||||
{ udtName: 'bigserial', dataType: 'bigserial', desc: '八字节序列整型', space: '8字节', range: '-9,223,372,036,854,775,808 ~ +9,223,372,036,854,775,807' },
|
|
||||||
{
|
|
||||||
udtName: 'largeserial',
|
|
||||||
dataType: 'largeserial',
|
|
||||||
desc: '默认插入十六字节序列整型,实际数值类型和numeric相同',
|
|
||||||
space: '变长类型,每四位(十进制位)占用两个字节,然后在整个数据上加上八个字节的额外开销。',
|
|
||||||
range: '小数点前最大131,072位,小数点后最大16,383位。',
|
|
||||||
},
|
|
||||||
|
|
||||||
// 数值 - 浮点类型(不常用 就不列出来了)
|
|
||||||
|
|
||||||
// 货币类型
|
|
||||||
{ udtName: 'money', dataType: 'money', desc: '货币金额', space: '8字节', range: '-92233720368547758.08 ~ +92233720368547758.07' },
|
|
||||||
|
|
||||||
// 布尔类型
|
|
||||||
{ udtName: 'bool', dataType: 'bool', desc: '布尔类型', space: '1字节', range: 'true:真 , false:假 , null:未知(unknown)' },
|
|
||||||
|
|
||||||
// 字符类型
|
|
||||||
{ udtName: 'char', dataType: 'char', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
|
||||||
{ udtName: 'character', dataType: 'character', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
|
||||||
{ udtName: 'nchar', dataType: 'nchar', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
|
||||||
{ udtName: 'varchar', dataType: 'varchar', desc: '变长字符串。PG兼容模式下,n是字符长度。其他兼容模式下,n是指字节长度。', space: '最大为10MB。' },
|
|
||||||
{ udtName: 'text', dataType: 'text', desc: '变长字符串。', space: '最大稍微小于1GB-1。' },
|
|
||||||
{ udtName: 'clob', dataType: 'clob', desc: '文本大对象。是TEXT类型的别名。', space: '最大稍微小于32TB-1。' },
|
|
||||||
|
|
||||||
//特殊字符类型 用的很少,先屏蔽了
|
|
||||||
// { udtName: 'name', dataType: 'name', desc: '用于对象名的内部类型。', space: '64字节。' },
|
|
||||||
// { udtName: '"char"', dataType: '"char"', desc: '单字节内部类型。', space: '1字节。' },
|
|
||||||
|
|
||||||
// 二进制类型
|
|
||||||
{ udtName: 'bytea', dataType: 'bytea', desc: '变长的二进制字符串', space: '4字节加上实际的二进制字符串。最大为1GB减去8203字节(即1073733621字节)。' },
|
|
||||||
|
|
||||||
// 日期/时间类型
|
|
||||||
{ udtName: 'date', dataType: 'date', desc: '日期', space: '4字节' },
|
|
||||||
{ udtName: 'time', dataType: 'time', desc: 'TIME [(p)] 只用于一日内时间,p表示小数点后的精度,取值范围为0~6。', space: '8-12字节' },
|
|
||||||
{ udtName: 'timestamp', dataType: 'timestamp', desc: 'TIMESTAMP[(p)]日期和时间,p表示小数点后的精度,取值范围为0~6', space: '8字节' },
|
|
||||||
// 带时区的时间戳用的少,先屏蔽了
|
|
||||||
//{ udtName: 'TIMESTAMPTZ', dataType: 'TIMESTAMP WITH TIME ZONE', desc: '带时区的时间戳', space: '8字节' },
|
|
||||||
{
|
|
||||||
udtName: 'interval',
|
|
||||||
dataType: 'interval',
|
|
||||||
desc: '时间间隔', // 可以跟参数:YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,DAY TO HOUR,DAY TO MINUTE,DAY TO SECOND,HOUR TO MINUTE,HOUR TO SECOND,MINUTE TO SECOND
|
|
||||||
space: '精度取值范围为0~6,且参数为SECOND,DAY TO SECOND,HOUR TO SECOND或MINUTE TO SECOND时,参数p才有效',
|
|
||||||
},
|
|
||||||
// 几何类型
|
|
||||||
{ udtName: 'point', dataType: 'point', desc: '平面中的点, 如:(x,y)', space: '16字节' },
|
|
||||||
{ udtName: 'lseg', dataType: 'lseg', desc: '(有限)线段, 如:((x1,y1),(x2,y2))', space: '32字节' },
|
|
||||||
{ udtName: 'box', dataType: 'box', desc: '矩形, 如:((x1,y1),(x2,y2))', space: '32字节' },
|
|
||||||
{ udtName: 'path', dataType: 'path', desc: '闭合路径(与多边形类似), 如:((x1,y1),...)', space: '16+16n字节' },
|
|
||||||
{ udtName: 'path', dataType: 'path', desc: '开放路径(与多边形类似), 如:[(x1,y1),...]', space: '16+16n字节' },
|
|
||||||
{ udtName: 'polygon', dataType: 'polygon', desc: '多边形(与闭合路径相似), 如:((x1,y1),...)', space: '40+16n字节' },
|
|
||||||
{ udtName: 'circle', dataType: 'polygon', desc: '圆,如:<(x,y),r> (圆心和半径)', space: '24 字节' },
|
|
||||||
|
|
||||||
// 网络地址类型
|
|
||||||
{ udtName: 'cidr', dataType: 'cidr', desc: 'IPv4网络', space: '7字节' },
|
|
||||||
{ udtName: 'inet', dataType: 'inet', desc: 'IPv4主机和网络', space: '7字节' },
|
|
||||||
{ udtName: 'macaddr', dataType: 'macaddr', desc: 'MAC地址', space: '6字节' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export const getDbOption = (dbType: string | undefined): DbOption => {
|
|
||||||
if (dbType === DbType.mysql) {
|
|
||||||
return new MysqlOption();
|
|
||||||
} else if (dbType === DbType.postgresql) {
|
|
||||||
return new PostgresqlOption();
|
|
||||||
}
|
|
||||||
throw new Error('不支持的数据库');
|
|
||||||
};
|
|
||||||
@@ -4,12 +4,12 @@ import { getTextWidth } from '@/common/utils/string';
|
|||||||
import SqlExecBox from './component/sqleditor/SqlExecBox';
|
import SqlExecBox from './component/sqleditor/SqlExecBox';
|
||||||
|
|
||||||
import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js';
|
import { language as sqlLanguage } from 'monaco-editor/esm/vs/basic-languages/mysql/mysql.js';
|
||||||
import { language as addSqlLanguage } from './lang/mysql.js';
|
import { language as addSqlLanguage } from './dialect/mysql_dialect';
|
||||||
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api';
|
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 { DbType } from '@/views/ops/db/component/table/dbs/db-option';
|
import { getDbDialect } from './dialect';
|
||||||
|
|
||||||
const sqlCompletionKeywords = [...sqlLanguage.keywords, ...addSqlLanguage.keywords];
|
const sqlCompletionKeywords = [...sqlLanguage.keywords, ...addSqlLanguage.keywords];
|
||||||
const sqlCompletionOperators = [...sqlLanguage.operators, ...addSqlLanguage.operators];
|
const sqlCompletionOperators = [...sqlLanguage.operators, ...addSqlLanguage.operators];
|
||||||
@@ -217,14 +217,7 @@ export class DbInst {
|
|||||||
|
|
||||||
// 获取指定表的默认查询sql
|
// 获取指定表的默认查询sql
|
||||||
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number = DbInst.DefaultLimit) {
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number = DbInst.DefaultLimit) {
|
||||||
const baseSql = `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''}`;
|
return getDbDialect(this.type).getDefaultSelectSql(table, condition, orderBy, pageNum, limit);
|
||||||
if (this.type == DbType.mysql) {
|
|
||||||
return `${baseSql} LIMIT ${(pageNum - 1) * limit}, ${limit};`;
|
|
||||||
}
|
|
||||||
if (this.type == DbType.postgresql) {
|
|
||||||
return `${baseSql} OFFSET ${(pageNum - 1) * limit} LIMIT ${limit};`;
|
|
||||||
}
|
|
||||||
return baseSql;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -285,13 +278,7 @@ export class DbInst {
|
|||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
wrapName = (name: string) => {
|
wrapName = (name: string) => {
|
||||||
if (this.type === DbType.mysql) {
|
return getDbDialect(this.type).wrapName(name);
|
||||||
return `\`${name}\``;
|
|
||||||
}
|
|
||||||
// if (this.type == 'postgres') {
|
|
||||||
// return `"${name}"`;
|
|
||||||
// }
|
|
||||||
return name;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -415,31 +402,6 @@ export class DbInst {
|
|||||||
const flexWidth: number = contentWidth > columnWidth ? contentWidth : columnWidth;
|
const flexWidth: number = contentWidth > columnWidth ? contentWidth : columnWidth;
|
||||||
return flexWidth > 500 ? 500 : flexWidth;
|
return flexWidth > 500 ? 500 : flexWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* 根据数据库类型获取对应的图标名
|
|
||||||
* @param dbType 数据库类型
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
static getIconName = (dbType: string) => {
|
|
||||||
if (dbType == DbType.mysql) {
|
|
||||||
return 'iconfont icon-op-mysql';
|
|
||||||
}
|
|
||||||
if (dbType == DbType.postgresql) {
|
|
||||||
return 'iconfont icon-op-postgres';
|
|
||||||
}
|
|
||||||
return 'InfoFilled';
|
|
||||||
};
|
|
||||||
|
|
||||||
static getIcon = (dbType: string) => {
|
|
||||||
if (dbType == DbType.mysql) {
|
|
||||||
return 'iconfont icon-op-mysql';
|
|
||||||
}
|
|
||||||
if (dbType == DbType.postgresql) {
|
|
||||||
return 'iconfont icon-op-postgres';
|
|
||||||
}
|
|
||||||
return 'InfoFilled';
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
82
mayfly_go_web/src/views/ops/db/dialect/index.ts
Normal file
82
mayfly_go_web/src/views/ops/db/dialect/index.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { MysqlDialect } from './mysql_dialect';
|
||||||
|
import { PostgresqlDialect } from './postgres_dialect';
|
||||||
|
|
||||||
|
export interface sqlColumnType {
|
||||||
|
udtName: string;
|
||||||
|
dataType: string;
|
||||||
|
desc: string;
|
||||||
|
space: string;
|
||||||
|
range?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DbType = {
|
||||||
|
mysql: 'mysql',
|
||||||
|
postgresql: 'postgres',
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface DbDialect {
|
||||||
|
/**
|
||||||
|
* 获取图标信息
|
||||||
|
*/
|
||||||
|
getIcon(): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取默认查询sql
|
||||||
|
* @param table 表名
|
||||||
|
* @param condition 条件
|
||||||
|
* @param orderBy 排序
|
||||||
|
* @param pageNum 页数
|
||||||
|
* @param limit 条数
|
||||||
|
*/
|
||||||
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 包裹数据库表名、字段名等,避免使用关键字为字段名或表名时报错
|
||||||
|
* @param name 名称
|
||||||
|
*/
|
||||||
|
wrapName(name: string): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成字段类型列表
|
||||||
|
* */
|
||||||
|
getColumnTypes(): sqlColumnType[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成创建表sql
|
||||||
|
* @param tableData 建表数据
|
||||||
|
*/
|
||||||
|
getCreateTableSql(tableData: any): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成创建索引sql
|
||||||
|
* @param tableData
|
||||||
|
*/
|
||||||
|
getCreateIndexSql(tableData: any): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成编辑列sql
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param changeData 改变信息
|
||||||
|
*/
|
||||||
|
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成编辑索引sql
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param changeData 改变数据
|
||||||
|
*/
|
||||||
|
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mysqlDialect = new MysqlDialect();
|
||||||
|
let postgresDialect = new PostgresqlDialect();
|
||||||
|
|
||||||
|
export const getDbDialect = (dbType: string | undefined): DbDialect => {
|
||||||
|
if (dbType === DbType.mysql) {
|
||||||
|
return mysqlDialect;
|
||||||
|
}
|
||||||
|
if (dbType === DbType.postgresql) {
|
||||||
|
return postgresDialect;
|
||||||
|
}
|
||||||
|
throw new Error('不支持的数据库');
|
||||||
|
};
|
||||||
225
mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts
Normal file
225
mayfly_go_web/src/views/ops/db/dialect/mysql_dialect.ts
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
import { DbDialect, sqlColumnType } from './index';
|
||||||
|
|
||||||
|
export { MYSQL_TYPE_LIST, MysqlDialect, language };
|
||||||
|
|
||||||
|
const MYSQL_TYPE_LIST = [
|
||||||
|
'bigint',
|
||||||
|
'binary',
|
||||||
|
'blob',
|
||||||
|
'char',
|
||||||
|
'datetime',
|
||||||
|
'date',
|
||||||
|
'decimal',
|
||||||
|
'double',
|
||||||
|
'enum',
|
||||||
|
'float',
|
||||||
|
'int',
|
||||||
|
'json',
|
||||||
|
'longblob',
|
||||||
|
'longtext',
|
||||||
|
'mediumblob',
|
||||||
|
'mediumtext',
|
||||||
|
'set',
|
||||||
|
'smallint',
|
||||||
|
'text',
|
||||||
|
'time',
|
||||||
|
'timestamp',
|
||||||
|
'tinyint',
|
||||||
|
'varbinary',
|
||||||
|
'varchar',
|
||||||
|
];
|
||||||
|
|
||||||
|
class MysqlDialect implements DbDialect {
|
||||||
|
getIcon() {
|
||||||
|
return 'iconfont icon-op-mysql';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
||||||
|
return `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} LIMIT ${
|
||||||
|
(pageNum - 1) * limit
|
||||||
|
}, ${limit};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapName = (name: string) => {
|
||||||
|
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}` : '';
|
||||||
|
let length = cl.length ? `(${cl.length})` : '';
|
||||||
|
let onUpdate = 'update_time' === cl.name ? ' ON UPDATE CURRENT_TIMESTAMP ' : '';
|
||||||
|
return ` ${cl.name} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : 'NULL'} ${
|
||||||
|
cl.auto_increment ? 'AUTO_INCREMENT' : ''
|
||||||
|
} ${defVal} ${onUpdate} comment '${cl.remark || ''}' `;
|
||||||
|
}
|
||||||
|
getCreateTableSql(data: any): string {
|
||||||
|
// 创建表结构
|
||||||
|
let pks = [] as string[];
|
||||||
|
let fields: string[] = [];
|
||||||
|
data.fields.res.forEach((item: any) => {
|
||||||
|
item.name && fields.push(this.genColumnBasicSql(item));
|
||||||
|
if (item.pri) {
|
||||||
|
pks.push(item.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return `CREATE TABLE ${data.tableName}
|
||||||
|
( ${fields.join(',')}
|
||||||
|
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
||||||
|
) COMMENT='${data.tableComment}';`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreateIndexSql(data: any): string {
|
||||||
|
// 创建索引
|
||||||
|
let sql = `ALTER TABLE ${data.tableName}`;
|
||||||
|
data.indexs.res.forEach((a: any) => {
|
||||||
|
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${a.indexComment}',`;
|
||||||
|
});
|
||||||
|
return sql.substring(0, sql.length - 1) + ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
||||||
|
let addSql = '',
|
||||||
|
updSql = '',
|
||||||
|
delSql = '';
|
||||||
|
if (changeData.add.length > 0) {
|
||||||
|
addSql = `ALTER TABLE ${tableName}`;
|
||||||
|
changeData.add.forEach((a) => {
|
||||||
|
addSql += ` ADD ${this.genColumnBasicSql(a)},`;
|
||||||
|
});
|
||||||
|
addSql = addSql.substring(0, addSql.length - 1);
|
||||||
|
addSql += ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.upd.length > 0) {
|
||||||
|
updSql = `ALTER TABLE ${tableName}`;
|
||||||
|
let arr = [] as string[];
|
||||||
|
changeData.upd.forEach((a) => {
|
||||||
|
arr.push(` MODIFY ${this.genColumnBasicSql(a)}`);
|
||||||
|
});
|
||||||
|
updSql += arr.join(',');
|
||||||
|
updSql += ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.del.length > 0) {
|
||||||
|
changeData.del.forEach((a) => {
|
||||||
|
delSql += ` ALTER TABLE ${tableName} DROP COLUMN ${a.name}; `;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return addSql + updSql + delSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
||||||
|
// 搜集修改和删除的索引,添加到drop index xx
|
||||||
|
// 收集新增和修改的索引,添加到ADD xx
|
||||||
|
// ALTER TABLE `test1`
|
||||||
|
// DROP INDEX `test1_name_uindex`,
|
||||||
|
// DROP INDEX `test1_column_name4_index`,
|
||||||
|
// ADD UNIQUE INDEX `test1_name_uindex`(`id`) USING BTREE COMMENT 'ASDASD',
|
||||||
|
// ADD INDEX `111`(`column_name4`) USING BTREE COMMENT 'zasf';
|
||||||
|
|
||||||
|
let dropIndexNames: string[] = [];
|
||||||
|
let addIndexs: any[] = [];
|
||||||
|
|
||||||
|
if (changeData.upd.length > 0) {
|
||||||
|
changeData.upd.forEach((a) => {
|
||||||
|
dropIndexNames.push(a.indexName);
|
||||||
|
addIndexs.push(a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.del.length > 0) {
|
||||||
|
changeData.del.forEach((a) => {
|
||||||
|
dropIndexNames.push(a.indexName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.add.length > 0) {
|
||||||
|
changeData.add.forEach((a) => {
|
||||||
|
addIndexs.push(a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropIndexNames.length > 0 || addIndexs.length > 0) {
|
||||||
|
let sql = `ALTER TABLE ${tableName} `;
|
||||||
|
if (dropIndexNames.length > 0) {
|
||||||
|
dropIndexNames.forEach((a) => {
|
||||||
|
sql += `DROP INDEX ${a},`;
|
||||||
|
});
|
||||||
|
sql = sql.substring(0, sql.length - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addIndexs.length > 0) {
|
||||||
|
if (dropIndexNames.length > 0) {
|
||||||
|
sql += ',';
|
||||||
|
}
|
||||||
|
addIndexs.forEach((a) => {
|
||||||
|
sql += ` ADD ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')}) USING ${a.indexType} COMMENT '${
|
||||||
|
a.indexComment
|
||||||
|
}',`;
|
||||||
|
});
|
||||||
|
sql = sql.substring(0, sql.length - 1);
|
||||||
|
}
|
||||||
|
return sql;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/basic-languages/mysql/mysql.ts
|
||||||
|
var language = {
|
||||||
|
keywords: ['GROUP BY', 'ORDER BY', 'LEFT JOIN', 'RIGHT JOIN', 'INNER JOIN', 'SELECT * FROM'],
|
||||||
|
operators: [],
|
||||||
|
builtinFunctions: [],
|
||||||
|
builtinVariables: [],
|
||||||
|
replaceFunctions: [
|
||||||
|
// 自定义修改函数提示
|
||||||
|
|
||||||
|
/** 字符串相关函数 */
|
||||||
|
{ label: 'CONCAT', insertText: 'CONCAT(str1,str2,...)', description: '多字符串合并' },
|
||||||
|
{ label: 'ASCII', insertText: 'ASCII(char)', description: '返回字符的ASCII值' },
|
||||||
|
{ label: 'BIT_LENGTH', insertText: 'BIT_LENGTH(str1)', description: '多字符串合并' },
|
||||||
|
{ label: 'INSTR', insertText: 'INSTR(str,substr)', description: '返回字符串substr所在str位置' },
|
||||||
|
{ label: 'LEFT', insertText: 'LEFT(str,len)', description: '返回字符串str的左端len个字符' },
|
||||||
|
{ label: 'RIGHT', insertText: 'RIGHT(str,len)', description: '返回字符串str的右端len个字符' },
|
||||||
|
{ label: 'MID', insertText: 'MID(str,pos,len)', description: '返回字符串str的位置pos起len个字符' },
|
||||||
|
{ label: 'SUBSTRING', insertText: 'SUBSTRING(exp, start, length)', description: '截取字符串' },
|
||||||
|
{ label: 'REPLACE', insertText: 'REPLACE(str,from_str,to_str)', description: '替换字符串' },
|
||||||
|
{ label: 'REPEAT', insertText: 'REPEAT(str,count)', description: '重复字符串count遍' },
|
||||||
|
{ label: 'UPPER', insertText: 'UPPER(str)', description: '返回大写的字符串' },
|
||||||
|
{ label: 'LOWER', insertText: 'LOWER(str)', description: '返回小写的字符串' },
|
||||||
|
{ label: 'TRIM', insertText: 'TRIM(str)', description: '去除字符串首尾空格' },
|
||||||
|
/** 数学相关函数 */
|
||||||
|
{ label: 'ABS', insertText: 'ABS(n)', description: '返回n的绝对值' },
|
||||||
|
{ label: 'FLOOR', insertText: 'FLOOR(n)', description: '返回不大于n的最大整数' },
|
||||||
|
{ label: 'CEILING', insertText: 'CEILING(n)', description: '返回不小于n的最小整数值' },
|
||||||
|
{ label: 'ROUND', insertText: 'ROUND(n,d)', description: '返回n的四舍五入值,保留d(默认0)位小数' },
|
||||||
|
{ label: 'RAND', insertText: 'RAND()', description: '返回在范围0到1.0内的随机浮点值' },
|
||||||
|
|
||||||
|
/** 日期函数 */
|
||||||
|
{ label: 'DATE', insertText: "DATE('date')", description: '返回指定表达式的日期部分' },
|
||||||
|
{ label: 'WEEK', insertText: "WEEK('date')", description: '返回指定日期是一年中的第几周' },
|
||||||
|
{ label: 'MONTH', insertText: "MONTH('date')", description: '返回指定日期的月份' },
|
||||||
|
{ label: 'QUARTER', insertText: "QUARTER('date')", description: '返回指定日期是一年的第几个季度' },
|
||||||
|
{ label: 'YEAR', insertText: "YEAR('date')", description: '返回指定日期的年份' },
|
||||||
|
{ label: 'DATE_ADD', insertText: "DATE_ADD('date', interval 1 day)", description: '日期函数加减运算' },
|
||||||
|
{ label: 'DATE_SUB', insertText: "DATE_SUB('date', interval 1 day)", description: '日期函数加减运算' },
|
||||||
|
{ label: 'DATE_FORMAT', insertText: "DATE_FORMAT('date', '%Y-%m-%d %h:%i:%s')", description: '' },
|
||||||
|
{ label: 'CURDATE', insertText: 'CURDATE()', description: '返回当前日期' },
|
||||||
|
{ label: 'CURTIME', insertText: 'CURTIME()', description: '返回当前时间' },
|
||||||
|
{ label: 'NOW', insertText: 'NOW()', description: '返回当前日期时间' },
|
||||||
|
{ label: 'DATEDIFF', insertText: 'DATEDIFF(expr1,expr2)', description: '返回结束日expr1和起始日expr2之间的天数' },
|
||||||
|
{ label: 'UNIX_TIMESTAMP', insertText: 'UNIX_TIMESTAMP()', description: '返回指定时间(默认当前)unix时间戳' },
|
||||||
|
{ label: 'FROM_UNIXTIME', insertText: 'FROM_UNIXTIME(timestamp)', description: '把时间戳格式为年月日时分秒' },
|
||||||
|
|
||||||
|
/** 逻辑函数 */
|
||||||
|
{ label: 'IFNULL', insertText: 'IFNULL(expression, alt_value)', description: '表达式为空取第二个参数值,否则取表达式值' },
|
||||||
|
{ label: 'IF', insertText: 'IF(expr1, expr2, expr3)', description: 'expr1为true则取expr2,否则取expr3' },
|
||||||
|
{ label: 'CASE', insertText: '(CASE \n WHEN expr1 THEN expr2 \n ELSE expr3) col', description: 'CASE WHEN THEN ELSE' },
|
||||||
|
],
|
||||||
|
};
|
||||||
272
mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts
Normal file
272
mayfly_go_web/src/views/ops/db/dialect/postgres_dialect.ts
Normal file
@@ -0,0 +1,272 @@
|
|||||||
|
import { DbDialect, sqlColumnType } from './index';
|
||||||
|
|
||||||
|
export { PostgresqlDialect, GAUSS_TYPE_LIST };
|
||||||
|
|
||||||
|
const GAUSS_TYPE_LIST: sqlColumnType[] = [
|
||||||
|
// 数值 - 整数型
|
||||||
|
{ udtName: 'int1', dataType: 'tinyint', desc: '微整数,别名为INT1', space: '1字节', range: '0 ~ +255' },
|
||||||
|
{ udtName: 'int2', dataType: 'smallint', desc: '小范围整数,别名为INT2。', space: '2字节', range: '-32,768 ~ +32,767' },
|
||||||
|
{ udtName: 'int4', dataType: 'integer', desc: '常用的整数,别名为INT4。', space: '4字节', range: '-2,147,483,648 ~ +2,147,483,647' },
|
||||||
|
{ udtName: 'int8', dataType: 'bigint', desc: '大范围的整数,别名为INT8。', space: '8字节', range: '很大' },
|
||||||
|
|
||||||
|
// 数值 - 任意精度型
|
||||||
|
{
|
||||||
|
udtName: 'numeric',
|
||||||
|
dataType: 'numeric',
|
||||||
|
desc: '精度(总位数)取值范围为[1,1000],标度(小数位数)取值范围为[0,精度]。',
|
||||||
|
space: '每四位(十进制位)占用两个字节,然后在整个数据上加上八个字节的额外开销',
|
||||||
|
range: '未指定精度的情况下,小数点前最大131,072位,小数点后最大16,383位',
|
||||||
|
},
|
||||||
|
// 数值 - 任意精度型
|
||||||
|
{ udtName: 'decimal', dataType: 'decimal', desc: '等同于number类型', space: '等同于number类型' },
|
||||||
|
|
||||||
|
// 数值 - 序列整型
|
||||||
|
{ udtName: 'smallserial', dataType: 'smallserial', desc: '二字节序列整型。', space: '2字节', range: '-32,768 ~ +32,767' },
|
||||||
|
{ udtName: 'serial', dataType: 'serial', desc: '四字节序列整型。', space: '4字节', range: '-2,147,483,648 ~ +2,147,483,647' },
|
||||||
|
{ udtName: 'bigserial', dataType: 'bigserial', desc: '八字节序列整型', space: '8字节', range: '-9,223,372,036,854,775,808 ~ +9,223,372,036,854,775,807' },
|
||||||
|
{
|
||||||
|
udtName: 'largeserial',
|
||||||
|
dataType: 'largeserial',
|
||||||
|
desc: '默认插入十六字节序列整型,实际数值类型和numeric相同',
|
||||||
|
space: '变长类型,每四位(十进制位)占用两个字节,然后在整个数据上加上八个字节的额外开销。',
|
||||||
|
range: '小数点前最大131,072位,小数点后最大16,383位。',
|
||||||
|
},
|
||||||
|
|
||||||
|
// 数值 - 浮点类型(不常用 就不列出来了)
|
||||||
|
|
||||||
|
// 货币类型
|
||||||
|
{ udtName: 'money', dataType: 'money', desc: '货币金额', space: '8字节', range: '-92233720368547758.08 ~ +92233720368547758.07' },
|
||||||
|
|
||||||
|
// 布尔类型
|
||||||
|
{ udtName: 'bool', dataType: 'bool', desc: '布尔类型', space: '1字节', range: 'true:真 , false:假 , null:未知(unknown)' },
|
||||||
|
|
||||||
|
// 字符类型
|
||||||
|
{ udtName: 'char', dataType: 'char', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
||||||
|
{ udtName: 'character', dataType: 'character', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
||||||
|
{ udtName: 'nchar', dataType: 'nchar', desc: '定长字符串,不足补空格。n是指字节长度,如不带精度n,默认精度为1。', space: '最大为10MB' },
|
||||||
|
{ udtName: 'varchar', dataType: 'varchar', desc: '变长字符串。PG兼容模式下,n是字符长度。其他兼容模式下,n是指字节长度。', space: '最大为10MB。' },
|
||||||
|
{ udtName: 'text', dataType: 'text', desc: '变长字符串。', space: '最大稍微小于1GB-1。' },
|
||||||
|
{ udtName: 'clob', dataType: 'clob', desc: '文本大对象。是TEXT类型的别名。', space: '最大稍微小于32TB-1。' },
|
||||||
|
|
||||||
|
//特殊字符类型 用的很少,先屏蔽了
|
||||||
|
// { udtName: 'name', dataType: 'name', desc: '用于对象名的内部类型。', space: '64字节。' },
|
||||||
|
// { udtName: '"char"', dataType: '"char"', desc: '单字节内部类型。', space: '1字节。' },
|
||||||
|
|
||||||
|
// 二进制类型
|
||||||
|
{ udtName: 'bytea', dataType: 'bytea', desc: '变长的二进制字符串', space: '4字节加上实际的二进制字符串。最大为1GB减去8203字节(即1073733621字节)。' },
|
||||||
|
|
||||||
|
// 日期/时间类型
|
||||||
|
{ udtName: 'date', dataType: 'date', desc: '日期', space: '4字节' },
|
||||||
|
{ udtName: 'time', dataType: 'time', desc: 'TIME [(p)] 只用于一日内时间,p表示小数点后的精度,取值范围为0~6。', space: '8-12字节' },
|
||||||
|
{ udtName: 'timestamp', dataType: 'timestamp', desc: 'TIMESTAMP[(p)]日期和时间,p表示小数点后的精度,取值范围为0~6', space: '8字节' },
|
||||||
|
// 带时区的时间戳用的少,先屏蔽了
|
||||||
|
//{ udtName: 'TIMESTAMPTZ', dataType: 'TIMESTAMP WITH TIME ZONE', desc: '带时区的时间戳', space: '8字节' },
|
||||||
|
{
|
||||||
|
udtName: 'interval',
|
||||||
|
dataType: 'interval',
|
||||||
|
desc: '时间间隔', // 可以跟参数:YEAR,MONTH,DAY,HOUR,MINUTE,SECOND,DAY TO HOUR,DAY TO MINUTE,DAY TO SECOND,HOUR TO MINUTE,HOUR TO SECOND,MINUTE TO SECOND
|
||||||
|
space: '精度取值范围为0~6,且参数为SECOND,DAY TO SECOND,HOUR TO SECOND或MINUTE TO SECOND时,参数p才有效',
|
||||||
|
},
|
||||||
|
// 几何类型
|
||||||
|
{ udtName: 'point', dataType: 'point', desc: '平面中的点, 如:(x,y)', space: '16字节' },
|
||||||
|
{ udtName: 'lseg', dataType: 'lseg', desc: '(有限)线段, 如:((x1,y1),(x2,y2))', space: '32字节' },
|
||||||
|
{ udtName: 'box', dataType: 'box', desc: '矩形, 如:((x1,y1),(x2,y2))', space: '32字节' },
|
||||||
|
{ udtName: 'path', dataType: 'path', desc: '闭合路径(与多边形类似), 如:((x1,y1),...)', space: '16+16n字节' },
|
||||||
|
{ udtName: 'path', dataType: 'path', desc: '开放路径(与多边形类似), 如:[(x1,y1),...]', space: '16+16n字节' },
|
||||||
|
{ udtName: 'polygon', dataType: 'polygon', desc: '多边形(与闭合路径相似), 如:((x1,y1),...)', space: '40+16n字节' },
|
||||||
|
{ udtName: 'circle', dataType: 'polygon', desc: '圆,如:<(x,y),r> (圆心和半径)', space: '24 字节' },
|
||||||
|
|
||||||
|
// 网络地址类型
|
||||||
|
{ udtName: 'cidr', dataType: 'cidr', desc: 'IPv4网络', space: '7字节' },
|
||||||
|
{ udtName: 'inet', dataType: 'inet', desc: 'IPv4主机和网络', space: '7字节' },
|
||||||
|
{ udtName: 'macaddr', dataType: 'macaddr', desc: 'MAC地址', space: '6字节' },
|
||||||
|
];
|
||||||
|
|
||||||
|
class PostgresqlDialect implements DbDialect {
|
||||||
|
getIcon() {
|
||||||
|
return 'iconfont icon-op-postgres';
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultSelectSql(table: string, condition: string, orderBy: string, pageNum: number, limit: number) {
|
||||||
|
return `SELECT * FROM ${this.wrapName(table)} ${condition ? 'WHERE ' + condition : ''} ${orderBy ? orderBy : ''} OFFSET ${
|
||||||
|
(pageNum - 1) * limit
|
||||||
|
} LIMIT ${limit};`;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapName = (name: string) => {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
for (let i = 0; i < arr.length; i++) {
|
||||||
|
if (text.indexOf(arr[i]) > -1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getDefaultValueSql(cl: any): string {
|
||||||
|
if (cl.value && cl.value.length > 0) {
|
||||||
|
// 哪些字段默认值需要加引号
|
||||||
|
let marks = false;
|
||||||
|
if (this.matchType(cl.type, ['char', 'time', 'date', 'text'])) {
|
||||||
|
// 默认值是now()的time或date不需要加引号
|
||||||
|
if (cl.value.toLowerCase().replace(' ', '') === 'CURRENT_TIMESTAMP' && this.matchType(cl.type, ['time', 'date'])) {
|
||||||
|
marks = false;
|
||||||
|
} else {
|
||||||
|
marks = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ` DEFAULT ${marks ? "'" : ''}${cl.value}${marks ? "'" : ''}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
getTypeLengthSql(cl: any) {
|
||||||
|
// 哪些字段可以指定长度
|
||||||
|
if (cl.length && this.matchType(cl.type, ['char', 'time', 'bit', 'num', 'decimal'])) {
|
||||||
|
// 哪些字段类型可以指定小数点
|
||||||
|
if (cl.numScale && this.matchType(cl.type, ['num', 'decimal'])) {
|
||||||
|
return `(${cl.length}, ${cl.numScale})`;
|
||||||
|
} else {
|
||||||
|
return `(${cl.length})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
genColumnBasicSql(cl: any): string {
|
||||||
|
let length = this.getTypeLengthSql(cl);
|
||||||
|
// 默认值
|
||||||
|
let defVal = this.getDefaultValueSql(cl);
|
||||||
|
return ` ${cl.name} ${cl.type}${length} ${cl.notNull ? 'NOT NULL' : ''} ${defVal} `;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreateTableSql(data: any): string {
|
||||||
|
let createSql = '';
|
||||||
|
let tableCommentSql = '';
|
||||||
|
let columCommentSql = '';
|
||||||
|
|
||||||
|
// 创建表结构
|
||||||
|
let pks = [] as string[];
|
||||||
|
let fields: string[] = [];
|
||||||
|
data.fields.res.forEach((item: any) => {
|
||||||
|
item.name && fields.push(this.genColumnBasicSql(item));
|
||||||
|
if (item.pri) {
|
||||||
|
pks.push(item.name);
|
||||||
|
}
|
||||||
|
// 列注释
|
||||||
|
if (item.remark) {
|
||||||
|
columCommentSql += ` comment on column ${data.tableName}.${item.name} is '${item.remark}'; `;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 建表
|
||||||
|
createSql = `CREATE TABLE ${data.tableName}
|
||||||
|
(
|
||||||
|
${fields.join(',')}
|
||||||
|
${pks ? `, PRIMARY KEY (${pks.join(',')})` : ''}
|
||||||
|
);`;
|
||||||
|
// 表注释
|
||||||
|
if (data.tableComment) {
|
||||||
|
tableCommentSql = ` comment on table ${data.tableName} is '${data.tableComment}'; `;
|
||||||
|
}
|
||||||
|
|
||||||
|
return createSql + tableCommentSql + columCommentSql;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCreateIndexSql(tableData: any): string {
|
||||||
|
// CREATE UNIQUE INDEX idx_column_name ON your_table (column1, column2);
|
||||||
|
// COMMENT ON INDEX idx_column_name IS 'Your index comment here';
|
||||||
|
// 创建索引
|
||||||
|
let sql: string[] = [];
|
||||||
|
tableData.indexs.res.forEach((a: any) => {
|
||||||
|
sql.push(` CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName} USING btree ("${a.columnNames.join('","')})"`);
|
||||||
|
if (a.indexComment) {
|
||||||
|
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return sql.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifyColumnSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
||||||
|
let sql: string[] = [];
|
||||||
|
if (changeData.add.length > 0) {
|
||||||
|
changeData.add.forEach((a) => {
|
||||||
|
let typeLength = this.getTypeLengthSql(a);
|
||||||
|
let defaultSql = this.getDefaultValueSql(a);
|
||||||
|
sql.push(`ALTER TABLE ${tableName} add ${a.name} ${a.type}${typeLength} ${defaultSql}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.upd.length > 0) {
|
||||||
|
changeData.upd.forEach((a) => {
|
||||||
|
let typeLength = this.getTypeLengthSql(a);
|
||||||
|
sql.push(`ALTER TABLE ${tableName} alter column ${a.name} type ${a.type}${typeLength}`);
|
||||||
|
let defaultSql = this.getDefaultValueSql(a);
|
||||||
|
if (defaultSql) {
|
||||||
|
sql.push(`alter table ${tableName} alter column ${a.name} set ${defaultSql}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.del.length > 0) {
|
||||||
|
changeData.del.forEach((a) => {
|
||||||
|
sql.push(`ALTER TABLE ${tableName} DROP COLUMN ${a.name}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return sql.join(';');
|
||||||
|
}
|
||||||
|
|
||||||
|
getModifyIndexSql(tableName: string, changeData: { del: any[]; add: any[]; upd: any[] }): string {
|
||||||
|
// 不能直接修改索引名或字段、需要先删后加
|
||||||
|
let dropIndexNames: string[] = [];
|
||||||
|
let addIndexs: any[] = [];
|
||||||
|
|
||||||
|
if (changeData.upd.length > 0) {
|
||||||
|
changeData.upd.forEach((a) => {
|
||||||
|
dropIndexNames.push(a.indexName);
|
||||||
|
addIndexs.push(a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.del.length > 0) {
|
||||||
|
changeData.del.forEach((a) => {
|
||||||
|
dropIndexNames.push(a.indexName);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changeData.add.length > 0) {
|
||||||
|
changeData.add.forEach((a) => {
|
||||||
|
addIndexs.push(a);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dropIndexNames.length > 0 || addIndexs.length > 0) {
|
||||||
|
let sql: string[] = [];
|
||||||
|
if (dropIndexNames.length > 0) {
|
||||||
|
dropIndexNames.forEach((a) => {
|
||||||
|
sql.push(`DROP INDEX ${a}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addIndexs.length > 0) {
|
||||||
|
addIndexs.forEach((a) => {
|
||||||
|
sql.push(`CREATE ${a.unique ? 'UNIQUE' : ''} INDEX ${a.indexName}(${a.columnNames.join(',')})`);
|
||||||
|
if (a.indexComment) {
|
||||||
|
sql.push(`COMMENT ON INDEX ${a.indexName} IS '${a.indexComment}'`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return sql.join(';');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,63 +0,0 @@
|
|||||||
// src/basic-languages/mysql/mysql.ts
|
|
||||||
var language = {
|
|
||||||
keywords: [
|
|
||||||
"GROUP BY",
|
|
||||||
"ORDER BY",
|
|
||||||
"LEFT JOIN",
|
|
||||||
"RIGHT JOIN",
|
|
||||||
"INNER JOIN",
|
|
||||||
"SELECT * FROM",
|
|
||||||
],
|
|
||||||
operators: [
|
|
||||||
],
|
|
||||||
builtinFunctions: [
|
|
||||||
],
|
|
||||||
builtinVariables: [],
|
|
||||||
replaceFunctions:[ // 自定义修改函数提示
|
|
||||||
|
|
||||||
/** 字符串相关函数 */
|
|
||||||
{ label: 'CONCAT', insertText:'CONCAT(str1,str2,...)', description: '多字符串合并' },
|
|
||||||
{ label: 'ASCII', insertText:'ASCII(char)', description: '返回字符的ASCII值' },
|
|
||||||
{ label: 'BIT_LENGTH', insertText:'BIT_LENGTH(str1)', description: '多字符串合并' },
|
|
||||||
{ label: 'INSTR', insertText:'INSTR(str,substr)', description: '返回字符串substr所在str位置' },
|
|
||||||
{ label: 'LEFT', insertText:'LEFT(str,len)', description: '返回字符串str的左端len个字符' },
|
|
||||||
{ label: 'RIGHT', insertText:'RIGHT(str,len)', description: '返回字符串str的右端len个字符' },
|
|
||||||
{ label: 'MID', insertText:'MID(str,pos,len)', description: '返回字符串str的位置pos起len个字符' },
|
|
||||||
{ label: 'SUBSTRING', insertText:'SUBSTRING(exp, start, length)', description: '截取字符串' },
|
|
||||||
{ label: 'REPLACE', insertText:'REPLACE(str,from_str,to_str)', description: '替换字符串' },
|
|
||||||
{ label: 'REPEAT', insertText:'REPEAT(str,count)', description: '重复字符串count遍' },
|
|
||||||
{ label: 'UPPER', insertText:'UPPER(str)', description: '返回大写的字符串' },
|
|
||||||
{ label: 'LOWER', insertText:'LOWER(str)', description: '返回小写的字符串' },
|
|
||||||
{ label: 'TRIM', insertText:'TRIM(str)', description: '去除字符串首尾空格' },
|
|
||||||
/** 数学相关函数 */
|
|
||||||
{ label: 'ABS', insertText:'ABS(n)', description: '返回n的绝对值' },
|
|
||||||
{ label: 'FLOOR', insertText:'FLOOR(n)', description: '返回不大于n的最大整数' },
|
|
||||||
{ label: 'CEILING', insertText:'CEILING(n)', description: '返回不小于n的最小整数值' },
|
|
||||||
{ label: 'ROUND', insertText:'ROUND(n,d)', description: '返回n的四舍五入值,保留d(默认0)位小数' },
|
|
||||||
{ label: 'RAND', insertText:'RAND()', description: '返回在范围0到1.0内的随机浮点值' },
|
|
||||||
|
|
||||||
/** 日期函数 */
|
|
||||||
{ label: 'DATE', insertText:'DATE(\'date\')', description: '返回指定表达式的日期部分' },
|
|
||||||
{ label: 'WEEK', insertText:'WEEK(\'date\')', description: '返回指定日期是一年中的第几周' },
|
|
||||||
{ label: 'MONTH', insertText:'MONTH(\'date\')', description: '返回指定日期的月份' },
|
|
||||||
{ label: 'QUARTER', insertText:'QUARTER(\'date\')', description: '返回指定日期是一年的第几个季度' },
|
|
||||||
{ label: 'YEAR', insertText:'YEAR(\'date\')', description: '返回指定日期的年份' },
|
|
||||||
{ label: 'DATE_ADD', insertText:'DATE_ADD(\'date\', interval 1 day)', description: '日期函数加减运算' },
|
|
||||||
{ label: 'DATE_SUB', insertText:'DATE_SUB(\'date\', interval 1 day)', description: '日期函数加减运算' },
|
|
||||||
{ label: 'DATE_FORMAT', insertText:'DATE_FORMAT(\'date\', \'%Y-%m-%d %h:%i:%s\')', description: '' },
|
|
||||||
{ label: 'CURDATE', insertText:'CURDATE()', description: '返回当前日期' },
|
|
||||||
{ label: 'CURTIME', insertText:'CURTIME()', description: '返回当前时间' },
|
|
||||||
{ label: 'NOW', insertText:'NOW()', description: '返回当前日期时间' },
|
|
||||||
{ label: 'DATEDIFF', insertText:'DATEDIFF(expr1,expr2)', description: '返回结束日expr1和起始日expr2之间的天数' },
|
|
||||||
{ label: 'UNIX_TIMESTAMP', insertText:'UNIX_TIMESTAMP()', description: '返回指定时间(默认当前)unix时间戳' },
|
|
||||||
{ label: 'FROM_UNIXTIME', insertText:'FROM_UNIXTIME(timestamp)', description: '把时间戳格式为年月日时分秒' },
|
|
||||||
|
|
||||||
/** 逻辑函数 */
|
|
||||||
{ label: 'IFNULL', insertText:'IFNULL(expression, alt_value)', description: '表达式为空取第二个参数值,否则取表达式值' },
|
|
||||||
{ label: 'IF', insertText:'IF(expr1, expr2, expr3)', description: 'expr1为true则取expr2,否则取expr3' },
|
|
||||||
{ label: 'CASE', insertText:'(CASE \n WHEN expr1 THEN expr2 \n ELSE expr3) col', description: 'CASE WHEN THEN ELSE' },
|
|
||||||
]
|
|
||||||
};
|
|
||||||
export {
|
|
||||||
language
|
|
||||||
};
|
|
||||||
@@ -318,7 +318,7 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
|
|||||||
writer.WriteString(dbConn.Info.Type.StmtUseDatabase(dbName))
|
writer.WriteString(dbConn.Info.Type.StmtUseDatabase(dbName))
|
||||||
writer.WriteString(dbConn.Info.Type.StmtSetForeignKeyChecks(false))
|
writer.WriteString(dbConn.Info.Type.StmtSetForeignKeyChecks(false))
|
||||||
|
|
||||||
dbMeta := dbConn.GetMeta()
|
dbMeta := dbConn.GetDialect()
|
||||||
if len(tables) == 0 {
|
if len(tables) == 0 {
|
||||||
ti, err := dbMeta.GetTables()
|
ti, err := dbMeta.GetTables()
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -369,7 +369,7 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Db) TableInfos(rc *req.Ctx) {
|
func (d *Db) TableInfos(rc *req.Ctx) {
|
||||||
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTables()
|
res, err := d.getDbConn(rc.GinCtx).GetDialect().GetTables()
|
||||||
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -377,7 +377,7 @@ func (d *Db) TableInfos(rc *req.Ctx) {
|
|||||||
func (d *Db) TableIndex(rc *req.Ctx) {
|
func (d *Db) TableIndex(rc *req.Ctx) {
|
||||||
tn := rc.GinCtx.Query("tableName")
|
tn := rc.GinCtx.Query("tableName")
|
||||||
biz.NotEmpty(tn, "tableName不能为空")
|
biz.NotEmpty(tn, "tableName不能为空")
|
||||||
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTableIndex(tn)
|
res, err := d.getDbConn(rc.GinCtx).GetDialect().GetTableIndex(tn)
|
||||||
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -389,7 +389,7 @@ func (d *Db) ColumnMA(rc *req.Ctx) {
|
|||||||
biz.NotEmpty(tn, "tableName不能为空")
|
biz.NotEmpty(tn, "tableName不能为空")
|
||||||
|
|
||||||
dbi := d.getDbConn(rc.GinCtx)
|
dbi := d.getDbConn(rc.GinCtx)
|
||||||
res, err := dbi.GetMeta().GetColumns(tn)
|
res, err := dbi.GetDialect().GetColumns(tn)
|
||||||
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取数据库列信息失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -398,7 +398,7 @@ func (d *Db) ColumnMA(rc *req.Ctx) {
|
|||||||
func (d *Db) HintTables(rc *req.Ctx) {
|
func (d *Db) HintTables(rc *req.Ctx) {
|
||||||
dbi := d.getDbConn(rc.GinCtx)
|
dbi := d.getDbConn(rc.GinCtx)
|
||||||
|
|
||||||
dm := dbi.GetMeta()
|
dm := dbi.GetDialect()
|
||||||
// 获取所有表
|
// 获取所有表
|
||||||
tables, err := dm.GetTables()
|
tables, err := dm.GetTables()
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
@@ -439,7 +439,7 @@ func (d *Db) HintTables(rc *req.Ctx) {
|
|||||||
func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
|
func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
|
||||||
tn := rc.GinCtx.Query("tableName")
|
tn := rc.GinCtx.Query("tableName")
|
||||||
biz.NotEmpty(tn, "tableName不能为空")
|
biz.NotEmpty(tn, "tableName不能为空")
|
||||||
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetCreateTableDdl(tn)
|
res, err := d.getDbConn(rc.GinCtx).GetDialect().GetCreateTableDdl(tn)
|
||||||
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
@@ -447,7 +447,7 @@ func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
|
|||||||
func (d *Db) GetPgsqlSchemas(rc *req.Ctx) {
|
func (d *Db) GetPgsqlSchemas(rc *req.Ctx) {
|
||||||
conn := d.getDbConn(rc.GinCtx)
|
conn := d.getDbConn(rc.GinCtx)
|
||||||
biz.IsTrue(conn.Info.Type == dbm.DbTypePostgres, "非postgres无法获取该schemas")
|
biz.IsTrue(conn.Info.Type == dbm.DbTypePostgres, "非postgres无法获取该schemas")
|
||||||
res, err := d.getDbConn(rc.GinCtx).GetMeta().(*dbm.PgsqlMetadata).GetSchemas()
|
res, err := d.getDbConn(rc.GinCtx).GetDialect().(*dbm.PgsqlDialect).GetSchemas()
|
||||||
biz.ErrIsNilAppendErr(err, "获取schemas失败: %s")
|
biz.ErrIsNilAppendErr(err, "获取schemas失败: %s")
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ func doSelect(selectStmt *sqlparser.Select, execSqlReq *DbSqlExecReq) (*DbSqlExe
|
|||||||
func doRead(execSqlReq *DbSqlExecReq) (*DbSqlExecRes, error) {
|
func doRead(execSqlReq *DbSqlExecReq) (*DbSqlExecRes, error) {
|
||||||
dbConn := execSqlReq.DbConn
|
dbConn := execSqlReq.DbConn
|
||||||
sql := execSqlReq.Sql
|
sql := execSqlReq.Sql
|
||||||
colNames, res, err := dbConn.SelectData(sql)
|
colNames, res, err := dbConn.Query(sql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -216,7 +216,7 @@ func doUpdate(update *sqlparser.Update, execSqlReq *DbSqlExecReq, dbSqlExec *ent
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取表主键列名,排除使用别名
|
// 获取表主键列名,排除使用别名
|
||||||
primaryKey, err := dbConn.GetMeta().GetPrimaryKey(tableName)
|
primaryKey, err := dbConn.GetDialect().GetPrimaryKey(tableName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errorx.NewBiz("获取表主键信息失败")
|
return nil, errorx.NewBiz("获取表主键信息失败")
|
||||||
}
|
}
|
||||||
@@ -224,7 +224,7 @@ func doUpdate(update *sqlparser.Update, execSqlReq *DbSqlExecReq, dbSqlExec *ent
|
|||||||
updateColumnsAndPrimaryKey := strings.Join(updateColumns, ",") + "," + primaryKey
|
updateColumnsAndPrimaryKey := strings.Join(updateColumns, ",") + "," + primaryKey
|
||||||
// 查询要更新字段数据的旧值,以及主键值
|
// 查询要更新字段数据的旧值,以及主键值
|
||||||
selectSql := fmt.Sprintf("SELECT %s FROM %s %s LIMIT 200", updateColumnsAndPrimaryKey, tableStr, where)
|
selectSql := fmt.Sprintf("SELECT %s FROM %s %s LIMIT 200", updateColumnsAndPrimaryKey, tableStr, where)
|
||||||
_, res, err := dbConn.SelectData(selectSql)
|
_, res, err := dbConn.Query(selectSql)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
bytes, _ := json.Marshal(res)
|
bytes, _ := json.Marshal(res)
|
||||||
dbSqlExec.OldValue = string(bytes)
|
dbSqlExec.OldValue = string(bytes)
|
||||||
@@ -251,7 +251,7 @@ func doDelete(delete *sqlparser.Delete, execSqlReq *DbSqlExecReq, dbSqlExec *ent
|
|||||||
|
|
||||||
// 查询删除数据
|
// 查询删除数据
|
||||||
selectSql := fmt.Sprintf("SELECT * FROM %s %s LIMIT 200", tableStr, where)
|
selectSql := fmt.Sprintf("SELECT * FROM %s %s LIMIT 200", tableStr, where)
|
||||||
_, res, _ := dbConn.SelectData(selectSql)
|
_, res, _ := dbConn.Query(selectSql)
|
||||||
|
|
||||||
bytes, _ := json.Marshal(res)
|
bytes, _ := json.Marshal(res)
|
||||||
dbSqlExec.OldValue = string(bytes)
|
dbSqlExec.OldValue = string(bytes)
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ func (app *instanceAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
|
|
||||||
func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error) {
|
func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error) {
|
||||||
ed.Network = ed.GetNetwork()
|
ed.Network = ed.GetNetwork()
|
||||||
databases := make([]string, 0)
|
|
||||||
metaDb := ed.Type.MetaDbName()
|
metaDb := ed.Type.MetaDbName()
|
||||||
getDatabasesSql := ed.Type.StmtSelectDbName()
|
|
||||||
|
|
||||||
dbConn, err := toDbInfo(ed, 0, metaDb, "").Conn()
|
dbConn, err := toDbInfo(ed, 0, metaDb, "").Conn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -102,13 +100,5 @@ func (app *instanceAppImpl) GetDatabases(ed *entity.DbInstance) ([]string, error
|
|||||||
}
|
}
|
||||||
defer dbConn.Close()
|
defer dbConn.Close()
|
||||||
|
|
||||||
_, res, err := dbConn.SelectData(getDatabasesSql)
|
return dbConn.GetDialect().GetDbNames()
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
for _, re := range res {
|
|
||||||
databases = append(databases, re["dbname"].(string))
|
|
||||||
}
|
|
||||||
|
|
||||||
return databases, nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,32 @@ type DbConn struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 执行查询语句
|
// 执行查询语句
|
||||||
// 依次返回 列名数组,结果map,错误
|
// 依次返回 列名数组(顺序),结果map,错误
|
||||||
func (d *DbConn) SelectData(execSql string) ([]string, []map[string]any, error) {
|
func (d *DbConn) Query(querySql string) ([]string, []map[string]any, error) {
|
||||||
return selectDataByDb(d.db, execSql)
|
result := make([]map[string]any, 0, 16)
|
||||||
|
columns, err := walkTableRecord(d.db, querySql, func(record map[string]any, columns []string) {
|
||||||
|
result = append(result, record)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return columns, result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将查询结果映射至struct,可具体参考sqlx库
|
// 将查询结果映射至struct,可具体参考sqlx库
|
||||||
func (d *DbConn) SelectData2Struct(execSql string, dest any) error {
|
func (d *DbConn) Query2Struct(execSql string, dest any) error {
|
||||||
return select2StructByDb(d.db, execSql, dest)
|
rows, err := d.db.Query(execSql)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
||||||
|
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
||||||
|
defer func() {
|
||||||
|
if rows != nil {
|
||||||
|
rows.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return scanAll(rows, dest, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// WalkTableRecord 遍历表记录
|
// WalkTableRecord 遍历表记录
|
||||||
@@ -45,12 +63,12 @@ func (d *DbConn) Exec(sql string) (int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取数据库元信息实现接口
|
// 获取数据库元信息实现接口
|
||||||
func (d *DbConn) GetMeta() DbMetadata {
|
func (d *DbConn) GetDialect() DbDialect {
|
||||||
switch d.Info.Type {
|
switch d.Info.Type {
|
||||||
case DbTypeMysql:
|
case DbTypeMysql:
|
||||||
return &MysqlMetadata{dc: d}
|
return &MysqlDialect{dc: d}
|
||||||
case DbTypePostgres:
|
case DbTypePostgres:
|
||||||
return &PgsqlMetadata{dc: d}
|
return &PgsqlDialect{dc: d}
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("invalid database type: %s", d.Info.Type))
|
panic(fmt.Sprintf("invalid database type: %s", d.Info.Type))
|
||||||
}
|
}
|
||||||
@@ -66,17 +84,6 @@ func (d *DbConn) Close() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func selectDataByDb(db *sql.DB, selectSql string) ([]string, []map[string]any, error) {
|
|
||||||
result := make([]map[string]any, 0, 16)
|
|
||||||
columns, err := walkTableRecord(db, selectSql, func(record map[string]any, columns []string) {
|
|
||||||
result = append(result, record)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
return columns, result, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func walkTableRecord(db *sql.DB, selectSql string, walk func(record map[string]any, columns []string)) ([]string, error) {
|
func walkTableRecord(db *sql.DB, selectSql string, walk func(record map[string]any, columns []string)) ([]string, error) {
|
||||||
rows, err := db.Query(selectSql)
|
rows, err := db.Query(selectSql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -172,19 +179,3 @@ func valueConvert(data []byte, colType *sql.ColumnType) any {
|
|||||||
|
|
||||||
return stringV
|
return stringV
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询数据结果映射至struct。可参考sqlx库
|
|
||||||
func select2StructByDb(db *sql.DB, selectSql string, dest any) error {
|
|
||||||
rows, err := db.Query(selectSql)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
// rows对象一定要close掉,如果出错,不关掉则会很迅速的达到设置最大连接数,
|
|
||||||
// 后面的链接过来直接报错或拒绝,实际上也没有起效果
|
|
||||||
defer func() {
|
|
||||||
if rows != nil {
|
|
||||||
rows.Close()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
return scanAll(rows, dest, false)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -50,17 +50,6 @@ func (dbType DbType) QuoteLiteral(literal string) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbType DbType) StmtSelectDbName() string {
|
|
||||||
switch dbType {
|
|
||||||
case DbTypeMysql:
|
|
||||||
return "SELECT SCHEMA_NAME AS dbname FROM SCHEMATA"
|
|
||||||
case DbTypePostgres:
|
|
||||||
return "SELECT datname AS dbname FROM pg_database WHERE datistemplate = false AND has_database_privilege(datname, 'CONNECT')"
|
|
||||||
default:
|
|
||||||
panic(fmt.Sprintf("invalid database type: %s", dbType))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbType DbType) Dialect() sqlparser.Dialect {
|
func (dbType DbType) Dialect() sqlparser.Dialect {
|
||||||
switch dbType {
|
switch dbType {
|
||||||
case DbTypeMysql:
|
case DbTypeMysql:
|
||||||
|
|||||||
@@ -41,8 +41,10 @@ type Index struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------元数据接口定义------------------------------------------
|
// -----------------------------------元数据接口定义------------------------------------------
|
||||||
// 数据库元信息接口(表、列、获取表数据等元信息)
|
// 数据库方言、元信息接口(表、列、获取表数据等元信息)
|
||||||
type DbMetadata interface {
|
type DbDialect interface {
|
||||||
|
// 获取数据库名称列表
|
||||||
|
GetDbNames() ([]string, error)
|
||||||
|
|
||||||
// 获取表信息
|
// 获取表信息
|
||||||
GetTables() ([]Table, error)
|
GetTables() ([]Table, error)
|
||||||
@@ -53,9 +55,6 @@ type DbMetadata interface {
|
|||||||
// 获取表主键字段名,没有主键标识则默认第一个字段
|
// 获取表主键字段名,没有主键标识则默认第一个字段
|
||||||
GetPrimaryKey(tablename string) (string, error)
|
GetPrimaryKey(tablename string) (string, error)
|
||||||
|
|
||||||
// // 获取表信息,比GetTables获取更详细的表信息
|
|
||||||
// GetTableInfos() ([]Table, error)
|
|
||||||
|
|
||||||
// 获取表索引信息
|
// 获取表索引信息
|
||||||
GetTableIndex(tableName string) ([]Index, error)
|
GetTableIndex(tableName string) ([]Index, error)
|
||||||
|
|
||||||
@@ -39,13 +39,27 @@ const (
|
|||||||
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
|
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MysqlMetadata struct {
|
type MysqlDialect struct {
|
||||||
dc *DbConn
|
dc *DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (md *MysqlDialect) GetDbNames() ([]string, error) {
|
||||||
|
_, res, err := md.dc.Query("SELECT SCHEMA_NAME AS dbname FROM SCHEMATA")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
databases := make([]string, 0)
|
||||||
|
for _, re := range res {
|
||||||
|
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return databases, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 获取表基础元信息, 如表名等
|
// 获取表基础元信息, 如表名等
|
||||||
func (mm *MysqlMetadata) GetTables() ([]Table, error) {
|
func (md *MysqlDialect) GetTables() ([]Table, error) {
|
||||||
_, res, err := mm.dc.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
|
_, res, err := md.dc.Query(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -65,7 +79,7 @@ func (mm *MysqlMetadata) GetTables() ([]Table, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取列元信息, 如列名等
|
// 获取列元信息, 如列名等
|
||||||
func (mm *MysqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
func (md *MysqlDialect) GetColumns(tableNames ...string) ([]Column, error) {
|
||||||
tableName := ""
|
tableName := ""
|
||||||
for i := 0; i < len(tableNames); i++ {
|
for i := 0; i < len(tableNames); i++ {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
@@ -74,7 +88,7 @@ func (mm *MysqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
|||||||
tableName = tableName + "'" + tableNames[i] + "'"
|
tableName = tableName + "'" + tableNames[i] + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, res, err := mm.dc.SelectData(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_COLUMN_MA_KEY), tableName))
|
_, res, err := md.dc.Query(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_COLUMN_MA_KEY), tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -96,8 +110,8 @@ func (mm *MysqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
// 获取表主键字段名,不存在主键标识则默认第一个字段
|
||||||
func (mm *MysqlMetadata) GetPrimaryKey(tablename string) (string, error) {
|
func (md *MysqlDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := mm.GetColumns(tablename)
|
columns, err := md.GetColumns(tablename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -115,8 +129,8 @@ func (mm *MysqlMetadata) GetPrimaryKey(tablename string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取表索引信息
|
// 获取表索引信息
|
||||||
func (mm *MysqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
|
func (md *MysqlDialect) GetTableIndex(tableName string) ([]Index, error) {
|
||||||
_, res, err := mm.dc.SelectData(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName))
|
_, res, err := md.dc.Query(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -152,18 +166,18 @@ func (mm *MysqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (mm *MysqlMetadata) GetCreateTableDdl(tableName string) (string, error) {
|
func (md *MysqlDialect) GetCreateTableDdl(tableName string) (string, error) {
|
||||||
_, res, err := mm.dc.SelectData(fmt.Sprintf("show create table `%s` ", tableName))
|
_, res, err := md.dc.Query(fmt.Sprintf("show create table `%s` ", tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
return res[0]["Create Table"].(string) + ";", nil
|
return res[0]["Create Table"].(string) + ";", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mm *MysqlMetadata) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]any, error) {
|
func (md *MysqlDialect) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]any, error) {
|
||||||
return mm.dc.SelectData(fmt.Sprintf("SELECT * FROM %s LIMIT %d, %d", tableName, (pageNum-1)*pageSize, pageSize))
|
return md.dc.Query(fmt.Sprintf("SELECT * FROM %s LIMIT %d, %d", tableName, (pageNum-1)*pageSize, pageSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mm *MysqlMetadata) WalkTableRecord(tableName string, walk func(record map[string]any, columns []string)) error {
|
func (md *MysqlDialect) WalkTableRecord(tableName string, walk func(record map[string]any, columns []string)) error {
|
||||||
return mm.dc.WalkTableRecord(fmt.Sprintf("SELECT * FROM %s", tableName), walk)
|
return md.dc.WalkTableRecord(fmt.Sprintf("SELECT * FROM %s", tableName), walk)
|
||||||
}
|
}
|
||||||
@@ -99,13 +99,27 @@ const (
|
|||||||
PGSQL_TABLE_DDL_KEY = "PGSQL_TABLE_DDL_FUNC"
|
PGSQL_TABLE_DDL_KEY = "PGSQL_TABLE_DDL_FUNC"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PgsqlMetadata struct {
|
type PgsqlDialect struct {
|
||||||
dc *DbConn
|
dc *DbConn
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pd *PgsqlDialect) GetDbNames() ([]string, error) {
|
||||||
|
_, res, err := pd.dc.Query("SELECT datname AS dbname FROM pg_database WHERE datistemplate = false AND has_database_privilege(datname, 'CONNECT')")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
databases := make([]string, 0)
|
||||||
|
for _, re := range res {
|
||||||
|
databases = append(databases, anyx.ConvString(re["dbname"]))
|
||||||
|
}
|
||||||
|
|
||||||
|
return databases, nil
|
||||||
|
}
|
||||||
|
|
||||||
// 获取表基础元信息, 如表名等
|
// 获取表基础元信息, 如表名等
|
||||||
func (pm *PgsqlMetadata) GetTables() ([]Table, error) {
|
func (pd *PgsqlDialect) GetTables() ([]Table, error) {
|
||||||
_, res, err := pm.dc.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
|
_, res, err := pd.dc.Query(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -125,7 +139,7 @@ func (pm *PgsqlMetadata) GetTables() ([]Table, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取列元信息, 如列名等
|
// 获取列元信息, 如列名等
|
||||||
func (pm *PgsqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
func (pd *PgsqlDialect) GetColumns(tableNames ...string) ([]Column, error) {
|
||||||
tableName := ""
|
tableName := ""
|
||||||
for i := 0; i < len(tableNames); i++ {
|
for i := 0; i < len(tableNames); i++ {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
@@ -134,7 +148,7 @@ func (pm *PgsqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
|||||||
tableName = tableName + "'" + tableNames[i] + "'"
|
tableName = tableName + "'" + tableNames[i] + "'"
|
||||||
}
|
}
|
||||||
|
|
||||||
_, res, err := pm.dc.SelectData(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_COLUMN_MA_KEY), tableName))
|
_, res, err := pd.dc.Query(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_COLUMN_MA_KEY), tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -155,8 +169,8 @@ func (pm *PgsqlMetadata) GetColumns(tableNames ...string) ([]Column, error) {
|
|||||||
return columns, nil
|
return columns, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PgsqlMetadata) GetPrimaryKey(tablename string) (string, error) {
|
func (pd *PgsqlDialect) GetPrimaryKey(tablename string) (string, error) {
|
||||||
columns, err := pm.GetColumns(tablename)
|
columns, err := pd.GetColumns(tablename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -173,8 +187,8 @@ func (pm *PgsqlMetadata) GetPrimaryKey(tablename string) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取表索引信息
|
// 获取表索引信息
|
||||||
func (pm *PgsqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
|
func (pd *PgsqlDialect) GetTableIndex(tableName string) ([]Index, error) {
|
||||||
_, res, err := pm.dc.SelectData(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))
|
_, res, err := pd.dc.Query(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -210,17 +224,17 @@ func (pm *PgsqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获取建表ddl
|
// 获取建表ddl
|
||||||
func (pm *PgsqlMetadata) GetCreateTableDdl(tableName string) (string, error) {
|
func (pd *PgsqlDialect) GetCreateTableDdl(tableName string) (string, error) {
|
||||||
_, err := pm.dc.Exec(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_DDL_KEY))
|
_, err := pd.dc.Exec(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_DDL_KEY))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, schemaRes, _ := pm.dc.SelectData("select current_schema() as schema")
|
_, schemaRes, _ := pd.dc.Query("select current_schema() as schema")
|
||||||
schemaName := schemaRes[0]["schema"].(string)
|
schemaName := schemaRes[0]["schema"].(string)
|
||||||
|
|
||||||
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", schemaName, tableName)
|
ddlSql := fmt.Sprintf("select showcreatetable('%s','%s') as sql", schemaName, tableName)
|
||||||
_, res, err := pm.dc.SelectData(ddlSql)
|
_, res, err := pd.dc.Query(ddlSql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -228,18 +242,18 @@ func (pm *PgsqlMetadata) GetCreateTableDdl(tableName string) (string, error) {
|
|||||||
return res[0]["sql"].(string), nil
|
return res[0]["sql"].(string), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PgsqlMetadata) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]any, error) {
|
func (pd *PgsqlDialect) GetTableRecord(tableName string, pageNum, pageSize int) ([]string, []map[string]any, error) {
|
||||||
return pm.dc.SelectData(fmt.Sprintf("SELECT * FROM %s OFFSET %d LIMIT %d", tableName, (pageNum-1)*pageSize, pageSize))
|
return pd.dc.Query(fmt.Sprintf("SELECT * FROM %s OFFSET %d LIMIT %d", tableName, (pageNum-1)*pageSize, pageSize))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *PgsqlMetadata) WalkTableRecord(tableName string, walk func(record map[string]any, columns []string)) error {
|
func (pd *PgsqlDialect) WalkTableRecord(tableName string, walk func(record map[string]any, columns []string)) error {
|
||||||
return pm.dc.WalkTableRecord(fmt.Sprintf("SELECT * FROM %s", tableName), walk)
|
return pd.dc.WalkTableRecord(fmt.Sprintf("SELECT * FROM %s", tableName), walk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取pgsql当前连接的库可访问的schemaNames
|
// 获取pgsql当前连接的库可访问的schemaNames
|
||||||
func (pm *PgsqlMetadata) GetSchemas() ([]string, error) {
|
func (pd *PgsqlDialect) GetSchemas() ([]string, error) {
|
||||||
sql := GetLocalSql(PGSQL_META_FILE, PGSQL_DB_SCHEMAS)
|
sql := GetLocalSql(PGSQL_META_FILE, PGSQL_DB_SCHEMAS)
|
||||||
_, res, err := pm.dc.SelectData(sql)
|
_, res, err := pd.dc.Query(sql)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user