diff --git a/packages/api/src/controllers/databaseConnections.js b/packages/api/src/controllers/databaseConnections.js index e58685736..eea07817a 100644 --- a/packages/api/src/controllers/databaseConnections.js +++ b/packages/api/src/controllers/databaseConnections.js @@ -181,6 +181,16 @@ module.exports = { return res.result || null; }, + callMethod_meta: true, + async callMethod({ conid, database, method, args }) { + const opened = await this.ensureOpened(conid, database); + const res = await this.sendRequest(opened, { msgtype: 'callMethod', method, args }); + if (res.errorMessage) { + console.error(res.errorMessage); + } + return res.result || null; + }, + updateCollection_meta: true, async updateCollection({ conid, database, changeSet }) { const opened = await this.ensureOpened(conid, database); diff --git a/packages/api/src/proc/databaseConnectionProcess.js b/packages/api/src/proc/databaseConnectionProcess.js index 6e124019c..da730ba68 100644 --- a/packages/api/src/proc/databaseConnectionProcess.js +++ b/packages/api/src/proc/databaseConnectionProcess.js @@ -205,6 +205,17 @@ async function handleLoadKeyInfo({ msgid, key }) { } } +async function handleCallMethod({ msgid, method, args }) { + await waitConnected(); + const driver = requireEngineDriver(storedConnection); + try { + const result = await driver.callMethod(systemConnection, method, args); + process.send({ msgtype: 'response', msgid, result }); + } catch (err) { + process.send({ msgtype: 'response', msgid, errorMessage: err.message }); + } +} + async function handleLoadKeyTableRange({ msgid, key, cursor, count }) { await waitConnected(); const driver = requireEngineDriver(storedConnection); @@ -283,6 +294,7 @@ const messageHandlers = { collectionData: handleCollectionData, loadKeys: handleLoadKeys, loadKeyInfo: handleLoadKeyInfo, + callMethod: handleCallMethod, loadKeyTableRange: handleLoadKeyTableRange, sqlPreview: handleSqlPreview, ping: handlePing, diff --git a/packages/types/engines.d.ts b/packages/types/engines.d.ts index cb161f726..c7a5545fe 100644 --- a/packages/types/engines.d.ts +++ b/packages/types/engines.d.ts @@ -91,6 +91,8 @@ export interface EngineDriver { getQuerySplitterOptions(usage: 'stream' | 'script'): any; script(pool: any, sql: string): Promise; getNewObjectTemplates(): NewObjectTemplate[]; + // direct call of pool method, only some methods could be supported, on only some drivers + callMethod(pool, method, args); analyserClass?: any; dumperClass?: any; diff --git a/packages/web/src/appobj/AppObjectCore.svelte b/packages/web/src/appobj/AppObjectCore.svelte index 629f71a03..ed0e33bd8 100644 --- a/packages/web/src/appobj/AppObjectCore.svelte +++ b/packages/web/src/appobj/AppObjectCore.svelte @@ -61,7 +61,7 @@ } } - $: console.log(title, indentLevel); + // $: console.log(title, indentLevel);
- import { onMount } from 'svelte'; + import { onMount, tick } from 'svelte'; import ScrollableTableControl from '../elements/ScrollableTableControl.svelte'; import { apiCall } from '../utility/api'; + import createRef from '../utility/createRef'; export let conid; export let database; export let keyInfo; + export let onChangeSelected; let rows = []; let cursor = 0; + let isLoading = false; + let loadNextNeeded = false; + let isLoadedAll = false; + let selectedIndex; + const oldIndexRef = createRef(null); - async function loadRows() { - const res = await apiCall('database-connections/load-key-table-range', { - conid, - database, - key: keyInfo.key, - cursor, - count: 10, - }); - rows = res.items; + async function loadNextRows() { + if (isLoading) { + // console.log('ALREADY LOADING'); + loadNextNeeded = true; + return; + } + isLoading = true; + try { + const res = await apiCall('database-connections/load-key-table-range', { + conid, + database, + key: keyInfo.key, + cursor, + count: 10, + }); + const newRows = [...rows]; + for (const row of res.items) { + if (keyInfo.keyColumn && newRows.find(x => x[keyInfo.keyColumn] == row[keyInfo.keyColumn])) { + continue; + } + newRows.push({ rowNumber: newRows.length + 1, ...row }); + } + + rows = newRows; + cursor = res.cursor; + isLoadedAll = cursor == 0; + } finally { + isLoading = false; + } + + if (loadNextNeeded) { + loadNextNeeded = false; + await tick(); + loadNextRows(); + } + } + + $: { + if (onChangeSelected && rows[selectedIndex]) { + if (oldIndexRef.get() != selectedIndex) { + oldIndexRef.set(selectedIndex); + onChangeSelected(rows[selectedIndex]); + } + } } $: { keyInfo; } onMount(() => { - loadRows(); + loadNextRows(); }); ({ - fieldName, - header: fieldName, + { + fieldName: 'rowNumber', + header: 'num', + width: '60px', + }, + ...keyInfo.tableColumns.map(column => ({ + fieldName: column.name, + header: column.name, })), ]} {rows} + onLoadNext={isLoadedAll ? null : loadNextRows} + selectable + singleLineRow + bind:selectedIndex /> diff --git a/packages/web/src/elements/ScrollableTableControl.svelte b/packages/web/src/elements/ScrollableTableControl.svelte index fefdc3a7c..10af4f3e4 100644 --- a/packages/web/src/elements/ScrollableTableControl.svelte +++ b/packages/web/src/elements/ScrollableTableControl.svelte @@ -15,7 +15,7 @@ {#await apiCall('database-connections/load-key-info', { conid, database, key, refreshToken })} @@ -36,28 +74,38 @@ {:then keyInfo}
-
+ {keyInfo.type}
- - {key} - TTL:{keyInfo.ttl} - { - refreshToken += 1; - }} - /> +
+ +
+ handleChangeTtl(keyInfo)} /> +
{#if keyInfo.tableColumns} - + { + currentRow = row; + }} + /> - PROPS +
+ {#each keyInfo.tableColumns as column} +
{column.name}
+
+ +
+ {/each} +
{:else} @@ -80,9 +128,36 @@ .top-panel { display: flex; + background: var(--theme-bg-2); } .type { font-weight: bold; + margin-right: 10px; + align-self: center; + } + + .props { + flex: 1; + display: flex; + flex-direction: column; + } + + .colname { + margin: 10px; + } + + .colvalue { + position: relative; + flex: 1; + } + + .key-name { + flex-grow: 1; + display: flex; + } + + .key-name :global(input) { + flex-grow: 1; } diff --git a/plugins/dbgate-plugin-redis/src/backend/driver.js b/plugins/dbgate-plugin-redis/src/backend/driver.js index f03d0f84d..399f5101b 100644 --- a/plugins/dbgate-plugin-redis/src/backend/driver.js +++ b/plugins/dbgate-plugin-redis/src/backend/driver.js @@ -59,7 +59,10 @@ const driver = { async getVersion(pool) { const info = await this.info(pool); - return { version: info.redis_version }; + return { + version: info.redis_version, + versionText: `Redis ${info.redis_version}`, + }; }, async listDatabases(pool) { const info = await this.info(pool); @@ -159,18 +162,18 @@ const driver = { res.value = await pool.get(key); break; case 'list': - res.tableColumns = ['value']; + res.tableColumns = [{ name: 'value' }]; break; case 'set': - res.tableColumns = ['value']; + res.tableColumns = [{ name: 'value' }]; res.keyColumn = 'value'; break; case 'zset': - res.tableColumns = ['value', 'score']; + res.tableColumns = [{ name: 'value' }, { name: 'score' }]; res.keyColumn = 'value'; break; case 'hash': - res.tableColumns = ['key', 'value']; + res.tableColumns = [{ name: 'key' }, { name: 'value' }]; res.keyColumn = 'key'; break; } @@ -178,6 +181,10 @@ const driver = { return res; }, + async callMethod(pool, method, args) { + return await pool[method](...args); + }, + async loadKeyTableRange(pool, key, cursor, count) { const type = await pool.type(key); switch (type) {