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) {