feat: 数据库表操作显示表size&其他小优化

This commit is contained in:
meilin.huang
2023-11-02 12:46:21 +08:00
parent 3155380f16
commit 37026f3269
26 changed files with 163 additions and 213 deletions

View File

@@ -3,21 +3,16 @@
* @param size byte size
* @returns
*/
export function formatByteSize(size: any) {
const value = Number(size);
if (size && !isNaN(value)) {
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
let index = 0;
let k = value;
if (value >= 1024) {
while (k > 1024) {
k = k / 1024;
index++;
}
}
return `${k.toFixed(2)}${units[index]}`;
export function formatByteSize(size: number, fixed = 2) {
if (size === 0) {
return '0B';
}
return '-';
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const base = 1024;
const exponent = Math.floor(Math.log(size) / Math.log(base));
return parseFloat((size / Math.pow(base, exponent)).toFixed(fixed)) + units[exponent];
}
/**

View File

@@ -169,7 +169,6 @@ self.MonacoEnvironment = {
};
const state = reactive({
editorHeight: '500px',
languageMode: 'shell',
});

View File

@@ -30,6 +30,8 @@
<span class="ml3">
<slot name="label" :data="data"> {{ data.label }}</slot>
</span>
<slot :node="node" :data="data" name="suffix"></slot>
</span>
</template>
</el-tree>

View File

@@ -201,7 +201,7 @@ const columns = ref([
TableColumn.new('tagPath', '标签路径').isSlot().setAddWidth(20),
TableColumn.new('instanceName', '实例名'),
TableColumn.new('type', '类型'),
TableColumn.new('host', 'ip:port').isSlot().setAddWidth(20),
TableColumn.new('host', 'ip:port').isSlot().setAddWidth(40),
TableColumn.new('username', 'username'),
TableColumn.new('name', '名称'),
TableColumn.new('database', '数据库').isSlot().setMinWidth(70),

View File

@@ -1,5 +1,5 @@
<template>
<div>
<div class="db-sql-exec">
<el-row class="mb5">
<el-col :span="4">
<el-button
@@ -11,6 +11,7 @@
>新建查询</el-button
>
</el-col>
<el-col :span="20" v-if="state.db">
<el-descriptions :column="4" size="small" border style="height: 10px" class="ml5">
<el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item>
@@ -27,6 +28,7 @@
</el-descriptions>
</el-col>
</el-row>
<el-row type="flex">
<el-col :span="4">
<tag-tree
@@ -74,8 +76,13 @@
<SvgIcon name="Files" v-if="data.type == NodeType.SqlMenu || data.type == NodeType.Sql" color="#f56c6c" />
</template>
<template #suffix="{ data }">
<span class="db-table-size" v-if="data.type == NodeType.Table">{{ ` ${data.params.size}` }}</span>
</template>
</tag-tree>
</el-col>
<el-col :span="20">
<el-container id="data-exec" class="mt5 ml5">
<el-tabs @tab-remove="onRemoveTab" @tab-change="onTabChange" style="width: 100%" v-model="state.activeName">
@@ -110,7 +117,7 @@
<script lang="ts" setup>
import { defineAsyncComponent, onMounted, reactive, ref, toRefs, onBeforeUnmount } from 'vue';
import { ElMessage } from 'element-plus';
import { formatByteSize } from '@/common/utils/format';
import { DbInst, TabInfo, TabType, registerDbCompletionItemProvider } from './db';
import { TagTreeNode } from '../component/tag';
import TagTree from '../component/TagTree.vue';
@@ -304,6 +311,7 @@ const getTables = async (params: any) => {
db,
tableName: x.tableName,
tableComment: x.tableComment,
size: formatByteSize(x.dataLength + x.indexLength, 1),
});
});
};
@@ -461,47 +469,54 @@ const reloadTables = (nodeKey: string) => {
</script>
<style lang="scss">
.sql-file-exec {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
vertical-align: middle;
position: relative;
text-decoration: none;
}
.db-sql-exec {
.sql-file-exec {
display: inline-flex;
flex-direction: row;
align-items: center;
justify-content: center;
vertical-align: middle;
position: relative;
text-decoration: none;
}
.sqlEditor {
font-size: 8pt;
font-weight: 600;
border: 1px solid #ccc;
}
.db-table-size {
color: #c4c9c4;
font-size: 10px;
}
.editor-move-resize {
cursor: n-resize;
height: 3px;
text-align: center;
}
.sqlEditor {
font-size: 8pt;
font-weight: 600;
border: 1px solid #ccc;
}
#data-exec {
min-height: calc(100vh - 155px);
.editor-move-resize {
cursor: n-resize;
height: 3px;
text-align: center;
}
.el-tabs__header {
margin: 0 0 5px;
#data-exec {
min-height: calc(100vh - 155px);
.el-tabs__item {
padding: 0 5px;
.el-tabs__header {
margin: 0 0 5px;
.el-tabs__item {
padding: 0 5px;
}
}
}
.update_field_active {
background-color: var(--el-color-success);
}
.instances-pop-form {
.el-form-item {
margin-bottom: unset;
}
}
}
.update_field_active {
background-color: var(--el-color-success);
}
.instances-pop-form {
.el-form-item {
margin-bottom: unset;
}
}
</style>

View File

@@ -10,7 +10,6 @@ export const dbApi = {
tableInfos: Api.newGet('/dbs/{id}/t-infos'),
tableIndex: Api.newGet('/dbs/{id}/t-index'),
tableDdl: Api.newGet('/dbs/{id}/t-create-ddl'),
tableMetadata: Api.newGet('/dbs/{id}/t-metadata'),
columnMetadata: Api.newGet('/dbs/{id}/c-metadata'),
// 获取表即列提示
hintTables: Api.newGet('/dbs/{id}/hint-tables'),
@@ -27,7 +26,7 @@ export const dbApi = {
// 获取权限列表
instances: Api.newGet('/instances'),
getInstance: Api.newGet("/instances/{instanceId}"),
getInstance: Api.newGet('/instances/{instanceId}'),
getAllDatabase: Api.newGet('/instances/{instanceId}/databases'),
saveInstance: Api.newPost('/instances'),
getInstancePwd: Api.newGet('/instances/{id}/pwd'),

View File

@@ -44,7 +44,7 @@
</div>
</div>
<MonacoEditor ref="monacoEditorRef" class="mt5" v-model="state.sql" language="sql" :height="editorHeight" :id="'MonacoTextarea-' + ti.key" />
<MonacoEditor ref="monacoEditorRef" class="mt5" v-model="state.sql" language="sql" :height="state.editorHeight" :id="'MonacoTextarea-' + ti.key" />
<div class="editor-move-resize" @mousedown="onDragSetHeight">
<el-icon>

View File

@@ -33,26 +33,26 @@
<el-link @click="onDeleteData()" type="danger" icon="delete" :underline="false"></el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="commit" placement="top">
<el-tooltip :show-after="500" class="box-item" effect="dark" content="commit" placement="top">
<el-link @click="onCommit()" type="success" icon="CircleCheck" :underline="false"> </el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="生成insert sql" placement="top">
<el-tooltip :show-after="500" class="box-item" effect="dark" content="生成insert sql" placement="top">
<el-link @click="onGenerateInsertSql()" type="success" :underline="false">gi</el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="导出当前页的csv文件" placement="top">
<el-tooltip :show-after="500" class="box-item" effect="dark" content="导出当前页的csv文件" placement="top">
<el-link type="success" :underline="false" @click="exportData"><span class="f12">导出</span></el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip v-if="hasUpdatedFileds" class="box-item" effect="dark" content="提交修改" placement="top">
<el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="提交修改" placement="top">
<el-link @click="submitUpdateFields()" type="success" :underline="false" class="f12">提交</el-link>
</el-tooltip>
<el-divider v-if="hasUpdatedFileds" direction="vertical" border-style="dashed" />
<el-tooltip v-if="hasUpdatedFileds" class="box-item" effect="dark" content="取消修改" placement="top">
<el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="取消修改" placement="top">
<el-link @click="cancelUpdateFields" type="warning" :underline="false" class="f12">取消</el-link>
</el-tooltip>
</el-col>

View File

@@ -88,7 +88,7 @@ export class DbInst {
db.columnsMap?.clear();
db.tableHints = null;
console.log(`load tables -> dbName: ${dbName}`);
tables = await dbApi.tableMetadata.request({ id: this.id, db: dbName });
tables = await dbApi.tableInfos.request({ id: this.id, db: dbName });
db.tables = tables;
return tables;
}

View File

@@ -195,7 +195,7 @@ const queryConfig = [TableQuery.slot('tagPath', '标签', 'tagPathSelect'), Tabl
const columns = ref([
TableColumn.new('tagPath', '标签路径').isSlot().setAddWidth(20),
TableColumn.new('name', '名称'),
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(45),
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
TableColumn.new('username', '用户名'),
TableColumn.new('status', '状态').isSlot().setMinWidth(85),
TableColumn.new('remark', '备注'),

View File

@@ -20,7 +20,7 @@
</div>
</template>
<script lang="ts" setup>
import { defineAsyncComponent, watch, ref, shallowReactive, reactive, computed, toRefs, onMounted } from 'vue';
import { defineAsyncComponent, watch, ref, shallowReactive, reactive, computed, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import KeyHeader from './KeyHeader.vue';
@@ -107,8 +107,6 @@ watch(
onMounted(() => {
setKeyInfo(props.keyInfo);
});
const {} = toRefs(state);
</script>
<style lang="scss">
.key-tab-container {

View File

@@ -17,7 +17,7 @@
<div class="key-header-item key-ttl-input">
<el-input type="number" v-model.number="ki.timed" placeholder="单位(秒),负数永久" title="点击修改过期时间">
<template #prepend>
<span slot="prepend">TTL</span>
<span>TTL</span>
</template>
<template #suffix>
@@ -37,8 +37,8 @@
<!-- del & refresh btn -->
<div class="key-header-item key-header-btn-con">
<el-button slot="reference" type="success" @click="refreshKey" icon="refresh" title="刷新"></el-button>
<el-button v-auth="'redis:data:del'" slot="reference" type="danger" @click="delKey" icon="delete" title="删除"></el-button>
<el-button type="success" @click="refreshKey" icon="refresh" title="刷新"></el-button>
<el-button v-auth="'redis:data:del'" type="danger" @click="delKey" icon="delete" title="删除"></el-button>
</div>
</div>
</template>
@@ -74,6 +74,7 @@ const state = reactive({
timed: -1,
} as any,
oldKey: '',
memuse: 0,
});
onMounted(() => {

View File

@@ -114,14 +114,14 @@ const getListValue = async (resetTableData = false) => {
state.loadMoreDisable = state.values.length === state.total;
};
const lset = async (row: any, rowIndex: number) => {
await redisApi.setListValue.request({
...getBaseReqParam(),
index: (state.pageNum - 1) * state.pageSize + rowIndex,
value: row.value,
});
ElMessage.success('数据保存成功');
};
// const lset = async (row: any, rowIndex: number) => {
// await redisApi.setListValue.request({
// ...getBaseReqParam(),
// index: (state.pageNum - 1) * state.pageSize + rowIndex,
// value: row.value,
// });
// ElMessage.success('数据保存成功');
// };
const showEditDialog = (row: any) => {
state.editDialog.dataRow = row;

View File

@@ -11,6 +11,7 @@ export const redisApi = {
keyInfo: Api.newGet('/redis/{id}/{db}/key-info'),
keyTtl: Api.newGet('/redis/{id}/{db}/key-ttl'),
keyMemuse: Api.newGet('/redis/{id}/{db}/key-memuse'),
renameKey: Api.newPost('/redis/{id}/{db}/rename-key'),
expireKey: Api.newPost('/redis/{id}/{db}/expire-key'),
persistKey: Api.newDelete('/redis/{id}/{db}/persist-key'),

View File

@@ -21,7 +21,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.6
github.com/pquerna/otp v1.4.0
github.com/redis/go-redis/v9 v9.2.1
github.com/redis/go-redis/v9 v9.3.0
github.com/robfig/cron/v3 v3.0.1 //
github.com/stretchr/testify v1.8.4
go.mongodb.org/mongo-driver v1.12.1 // mongo

View File

@@ -83,34 +83,6 @@ func (d *Db) DeleteDb(rc *req.Ctx) {
}
}
func (d *Db) getDbConn(g *gin.Context) *dbm.DbConn {
dc, err := d.DbApp.GetDbConn(getDbId(g), getDbName(g))
biz.ErrIsNil(err)
return dc
}
func (d *Db) TableInfos(rc *req.Ctx) {
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTableInfos()
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
rc.ResData = res
}
func (d *Db) TableIndex(rc *req.Ctx) {
tn := rc.GinCtx.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTableIndex(tn)
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
rc.ResData = res
}
func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
tn := rc.GinCtx.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetCreateTableDdl(tn)
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
rc.ResData = res
}
func (d *Db) ExecSql(rc *req.Ctx) {
g := rc.GinCtx
form := &form.DbSqlExecForm{}
@@ -348,7 +320,7 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
dbMeta := dbConn.GetMeta()
if len(tables) == 0 {
ti, err := dbMeta.GetTableInfos()
ti, err := dbMeta.GetTables()
biz.ErrIsNil(err)
tables = make([]string, len(ti))
for i, table := range ti {
@@ -396,11 +368,17 @@ func (d *Db) dumpDb(writer *gzipWriter, dbId uint64, dbName string, tables []str
writer.WriteString(dbConn.Info.Type.StmtSetForeignKeyChecks(true))
}
// @router /api/db/:dbId/t-metadata [get]
func (d *Db) TableMA(rc *req.Ctx) {
dbi := d.getDbConn(rc.GinCtx)
res, err := dbi.GetMeta().GetTables()
biz.ErrIsNilAppendErr(err, "获取表基础信息失败: %s")
func (d *Db) TableInfos(rc *req.Ctx) {
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTables()
biz.ErrIsNilAppendErr(err, "获取表信息失败: %s")
rc.ResData = res
}
func (d *Db) TableIndex(rc *req.Ctx) {
tn := rc.GinCtx.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetTableIndex(tn)
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
rc.ResData = res
}
@@ -458,6 +436,14 @@ func (d *Db) HintTables(rc *req.Ctx) {
rc.ResData = res
}
func (d *Db) GetCreateTableDdl(rc *req.Ctx) {
tn := rc.GinCtx.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
res, err := d.getDbConn(rc.GinCtx).GetMeta().GetCreateTableDdl(tn)
biz.ErrIsNilAppendErr(err, "获取表ddl失败: %s")
rc.ResData = res
}
// @router /api/db/:dbId/sql [post]
func (d *Db) SaveSql(rc *req.Ctx) {
g := rc.GinCtx
@@ -536,3 +522,9 @@ func getDbName(g *gin.Context) string {
biz.NotEmpty(db, "db不能为空")
return db
}
func (d *Db) getDbConn(g *gin.Context) *dbm.DbConn {
dc, err := d.DbApp.GetDbConn(getDbId(g), getDbName(g))
biz.ErrIsNil(err)
return dc
}

View File

@@ -43,7 +43,7 @@ type Index struct {
// 数据库元信息接口(表、列、获取表数据等元信息)
type DbMetadata interface {
// 获取表基础元信息
// 获取表信息
GetTables() ([]Table, error)
// 获取指定表名的所有列元信息
@@ -52,8 +52,8 @@ type DbMetadata interface {
// 获取表主键字段名,没有主键标识则默认第一个字段
GetPrimaryKey(tablename string) (string, error)
// 获取表信息比GetTables获取更详细的表信息
GetTableInfos() ([]Table, error)
// // 获取表信息比GetTables获取更详细的表信息
// GetTableInfos() ([]Table, error)
// 获取表索引信息
GetTableIndex(tableName string) ([]Index, error)

View File

@@ -1,15 +1,3 @@
--MYSQL_TABLE_MA
SELECT
table_name tableName,
table_comment tableComment
from
information_schema.tables
WHERE
table_schema = (
SELECT
database ()
)
---------------------------------------
--MYSQL_TABLE_INFO
SELECT
table_name tableName,
@@ -21,7 +9,8 @@ SELECT
FROM
information_schema.tables
WHERE
table_schema = (
table_type = 'BASE TABLE'
AND table_schema = (
SELECT
database ()
)

View File

@@ -1,33 +1,22 @@
--PGSQL_TABLE_MA
SELECT
obj_description (c.oid) AS "tableComment",
c.relname AS "tableName"
FROM
pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE
n.nspname = (
select
current_schema ()
)
AND c.reltype > 0
---------------------------------------
--PGSQL_TABLE_INFO
SELECT
obj_description (c.oid) AS "tableComment",
c.relname AS "tableName",
pg_table_size ('"' || n.nspname || '"."' || c.relname || '"') as "dataLength",
pg_indexes_size ('"' || n.nspname || '"."' || c.relname || '"') as "indexLength",
c.reltuples as "tableRows"
FROM
pg_class c
JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE
n.nspname = (
select
current_schema ()
select
c.relname as "tableName",
obj_description (c.oid) as "tableComment",
pg_table_size ('"' || n.nspname || '"."' || c.relname || '"') as "dataLength",
pg_indexes_size ('"' || n.nspname || '"."' || c.relname || '"') as "indexLength",
psut.n_live_tup as "tableRows"
from
pg_class c
join pg_namespace n on
c.relnamespace = n.oid
join pg_stat_user_tables psut on
psut.relid = c."oid"
where
n.nspname = (
select
current_schema ()
)
AND c.reltype > 0
and c.reltype > 0
---------------------------------------
--PGSQL_INDEX_INFO 表索引信息
SELECT

View File

@@ -34,7 +34,6 @@ func getMysqlDB(d *DbInfo) (*sql.DB, error) {
// ---------------------------------- mysql元数据 -----------------------------------
const (
MYSQL_META_FILE = "metasql/mysql_meta.sql"
MYSQL_TABLE_MA_KEY = "MYSQL_TABLE_MA"
MYSQL_TABLE_INFO_KEY = "MYSQL_TABLE_INFO"
MYSQL_INDEX_INFO_KEY = "MYSQL_INDEX_INFO"
MYSQL_COLUMN_MA_KEY = "MYSQL_COLUMN_MA"
@@ -46,7 +45,7 @@ type MysqlMetadata struct {
// 获取表基础元信息, 如表名等
func (mm *MysqlMetadata) GetTables() ([]Table, error) {
_, res, err := mm.dc.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_MA_KEY))
_, res, err := mm.dc.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
if err != nil {
return nil, err
}
@@ -56,6 +55,10 @@ func (mm *MysqlMetadata) GetTables() ([]Table, error) {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: anyx.ConvString(re["tableComment"]),
CreateTime: anyx.ConvString(re["createTime"]),
TableRows: anyx.ConvInt(re["tableRows"]),
DataLength: anyx.ConvInt64(re["dataLength"]),
IndexLength: anyx.ConvInt64(re["indexLength"]),
})
}
return tables, nil
@@ -110,27 +113,6 @@ func (mm *MysqlMetadata) GetPrimaryKey(tablename string) (string, error) {
return columns[0].ColumnName, nil
}
// 获取表信息比GetTableMetedatas获取更详细的表信息
func (mm *MysqlMetadata) GetTableInfos() ([]Table, error) {
_, res, err := mm.dc.SelectData(GetLocalSql(MYSQL_META_FILE, MYSQL_TABLE_INFO_KEY))
if err != nil {
return nil, err
}
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: anyx.ConvString(re["tableComment"]),
CreateTime: anyx.ConvString(re["createTime"]),
TableRows: anyx.ConvInt(re["tableRows"]),
DataLength: anyx.ConvInt64(re["dataLength"]),
IndexLength: anyx.ConvInt64(re["indexLength"]),
})
}
return tables, nil
}
// 获取表索引信息
func (mm *MysqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
_, res, err := mm.dc.SelectData(fmt.Sprintf(GetLocalSql(MYSQL_META_FILE, MYSQL_INDEX_INFO_KEY), tableName))

View File

@@ -67,7 +67,6 @@ func (pd *PqSqlDialer) DialTimeout(network, address string, timeout time.Duratio
// ---------------------------------- pgsql元数据 -----------------------------------
const (
PGSQL_META_FILE = "metasql/pgsql_meta.sql"
PGSQL_TABLE_MA_KEY = "PGSQL_TABLE_MA"
PGSQL_TABLE_INFO_KEY = "PGSQL_TABLE_INFO"
PGSQL_INDEX_INFO_KEY = "PGSQL_INDEX_INFO"
PGSQL_COLUMN_MA_KEY = "PGSQL_COLUMN_MA"
@@ -80,7 +79,7 @@ type PgsqlMetadata struct {
// 获取表基础元信息, 如表名等
func (pm *PgsqlMetadata) GetTables() ([]Table, error) {
_, res, err := pm.dc.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_MA_KEY))
_, res, err := pm.dc.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
if err != nil {
return nil, err
}
@@ -90,6 +89,10 @@ func (pm *PgsqlMetadata) GetTables() ([]Table, error) {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: anyx.ConvString(re["tableComment"]),
CreateTime: anyx.ConvString(re["createTime"]),
TableRows: anyx.ConvInt(re["tableRows"]),
DataLength: anyx.ConvInt64(re["dataLength"]),
IndexLength: anyx.ConvInt64(re["indexLength"]),
})
}
return tables, nil
@@ -142,27 +145,6 @@ func (pm *PgsqlMetadata) GetPrimaryKey(tablename string) (string, error) {
return columns[0].ColumnName, nil
}
// 获取表信息比GetTables获取更详细的表信息
func (pm *PgsqlMetadata) GetTableInfos() ([]Table, error) {
_, res, err := pm.dc.SelectData(GetLocalSql(PGSQL_META_FILE, PGSQL_TABLE_INFO_KEY))
if err != nil {
return nil, err
}
tables := make([]Table, 0)
for _, re := range res {
tables = append(tables, Table{
TableName: re["tableName"].(string),
TableComment: anyx.ConvString(re["tableComment"]),
CreateTime: anyx.ConvString(re["createTime"]),
TableRows: anyx.ConvInt(re["tableRows"]),
DataLength: anyx.ConvInt64(re["dataLength"]),
IndexLength: anyx.ConvInt64(re["indexLength"]),
})
}
return tables, nil
}
// 获取表索引信息
func (pm *PgsqlMetadata) GetTableIndex(tableName string) ([]Index, error) {
_, res, err := pm.dc.SelectData(fmt.Sprintf(GetLocalSql(PGSQL_META_FILE, PGSQL_INDEX_INFO_KEY), tableName))

View File

@@ -31,10 +31,6 @@ func InitDbRouter(router *gin.RouterGroup) {
req.NewDelete(":dbId", d.DeleteDb).Log(req.NewLogSave("db-删除数据库信息")),
req.NewGet(":dbId/t-infos", d.TableInfos),
req.NewGet(":dbId/t-index", d.TableIndex),
req.NewGet(":dbId/t-create-ddl", d.GetCreateTableDdl),
req.NewPost(":dbId/exec-sql", d.ExecSql).Log(req.NewLog("db-执行Sql")),
@@ -43,7 +39,9 @@ func InitDbRouter(router *gin.RouterGroup) {
req.NewGet(":dbId/dump", d.DumpSql).Log(req.NewLogSave("db-导出sql文件")).NoRes(),
req.NewGet(":dbId/t-metadata", d.TableMA),
req.NewGet(":dbId/t-infos", d.TableInfos),
req.NewGet(":dbId/t-index", d.TableIndex),
req.NewGet(":dbId/c-metadata", d.ColumnMA),

View File

@@ -66,7 +66,8 @@ func (m *MachineScript) RunMachineScript(rc *req.Ctx) {
script := ms.Script
// 如果有脚本参数,则用脚本参数替换脚本中的模板占位符参数
if params := g.Query("params"); params != "" {
script = stringx.TemplateParse(ms.Script, jsonx.ToMap(params))
script, err = stringx.TemplateParse(ms.Script, jsonx.ToMap(params))
biz.ErrIsNilAppendErr(err, "脚本模板参数解析失败: %s")
}
cli, err := m.MachineApp.GetCli(machineId)
biz.ErrIsNilAppendErr(err, "获取客户端连接失败: %s")

View File

@@ -131,6 +131,11 @@ func (r *Redis) TtlKey(rc *req.Ctx) {
}
}
func (r *Redis) MemoryUsage(rc *req.Ctx) {
ri, key := r.checkKeyAndGetRedisConn(rc)
rc.ResData = ri.GetCmdable().MemoryUsage(context.Background(), key).Val()
}
func (r *Redis) DeleteKey(rc *req.Ctx) {
ri, key := r.checkKeyAndGetRedisConn(rc)
rc.ReqParam = collx.Kvs("redis", ri.Info, "key", key)

View File

@@ -45,6 +45,8 @@ func InitRedisRouter(router *gin.RouterGroup) {
req.NewGet(":id/:db/key-ttl", rs.TtlKey),
req.NewGet(":id/:db/key-memuse", rs.MemoryUsage),
req.NewDelete(":id/:db/key", rs.DeleteKey).Log(req.NewLogSave("redis-删除key")).RequiredPermission(deleteDataP),
req.NewPost(":id/:db/rename-key", rs.RenameKey).Log(req.NewLogSave("redis-重命名key")).RequiredPermission(saveDataP),

View File

@@ -5,24 +5,24 @@ import (
"text/template"
)
func parse(t *template.Template, vars any) string {
func parse(t *template.Template, vars any) (string, error) {
var tmplBytes bytes.Buffer
err := t.Execute(&tmplBytes, vars)
if err != nil {
panic(err)
return "", err
}
return tmplBytes.String()
return tmplBytes.String(), nil
}
// 模板字符串解析
// @param str 模板字符串
// @param vars 参数变量
func TemplateParse(str string, vars any) string {
func TemplateParse(str string, vars any) (string, error) {
tmpl, err := template.New("tmpl").Parse(str)
if err != nil {
panic(err)
return "", err
}
return parse(tmpl, vars)
}