diff --git a/src/lib/olap-engine/EventEmitter.ts b/src/lib/olap-engine/EventEmitter.ts index 47036e1..23eef1d 100644 --- a/src/lib/olap-engine/EventEmitter.ts +++ b/src/lib/olap-engine/EventEmitter.ts @@ -3,7 +3,7 @@ type Callback = (...param: any[]) => void; type OptionalRecord = { [P in K]?: T }; export interface IEventEmitter { - on(event: Events, fn: Callback): () => void; + on(event: Events, fn: Callback): () => any; emit(event: Events, param: any): void; } diff --git a/src/lib/olap-engine/engine-chdb.ts b/src/lib/olap-engine/engine-chdb.ts index 975a0f0..2c5c248 100644 --- a/src/lib/olap-engine/engine-chdb.ts +++ b/src/lib/olap-engine/engine-chdb.ts @@ -11,30 +11,31 @@ export class CHDBEngine extends EventEmitter implements OLAPEngine { await this.exec(CLICKHOUSE_INIT_DB); } - async exec(query: string) { + async exec(query: string, _emit = true) { try { const r: string = await invoke('query', { query }); - if (!r) return; - const data = JSON.parse(r) as OLAPResponse; - this.emit('success', query, data); + let data: OLAPResponse | undefined; + if (r) data = JSON.parse(r) as OLAPResponse; + + if (_emit) this.emit('success', query, data); return data; } catch (e) { if (typeof e === 'string') e = new Error(e); console.error(e); - this.emit('error', e); + if (_emit) this.emit('error', e); } } async getSchema() { - const response = await this.exec(CLICKHOUSE_GET_SCHEMA); + const response = await this.exec(CLICKHOUSE_GET_SCHEMA, false); if (!response) return []; return response.data as Table[]; } async getUDFs() { - const response = await this.exec(CLICKHOUSE_GET_UDFS); + const response = await this.exec(CLICKHOUSE_GET_UDFS, false); if (!response) return []; return response.data.map((row) => row.name as string); diff --git a/src/lib/olap-engine/engine-remote.ts b/src/lib/olap-engine/engine-remote.ts index e96b918..a6b5e66 100644 --- a/src/lib/olap-engine/engine-remote.ts +++ b/src/lib/olap-engine/engine-remote.ts @@ -7,7 +7,7 @@ import CLICKHOUSE_GET_UDFS from './queries/clickhouse_get_udfs.sql?raw'; export class RemoteEngine extends EventEmitter implements OLAPEngine { async init() {} - async exec(query: string) { + async exec(query: string, _emit = true) { try { const proxy = new URLSearchParams(window.location.search).get('proxy') ?? 'https://proxy.agx.app/query'; @@ -22,23 +22,23 @@ export class RemoteEngine extends EventEmitter implements OLAPEngine { if ('exception' in data) throw new Error(data.exception); - this.emit('success', query, data); + if (_emit) this.emit('success', query, data); return data; } catch (e) { console.error(e); - this.emit('error', e); + if (_emit) this.emit('error', e); } } async getSchema() { - const response = await this.exec(CLICKHOUSE_GET_SCHEMA); + const response = await this.exec(CLICKHOUSE_GET_SCHEMA, false); if (!response) return []; return response.data as Table[]; } async getUDFs() { - const response = await this.exec(CLICKHOUSE_GET_UDFS); + const response = await this.exec(CLICKHOUSE_GET_UDFS, false); if (!response) return []; return response.data.map((row) => row.name as string); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 67e32b2..53d4806 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -3,6 +3,9 @@ import { ContextMenuState } from '$lib/components/ContextMenu'; import ContextMenu from '$lib/components/ContextMenu/ContextMenu.svelte'; import Drawer from '$lib/components/Drawer.svelte'; + import { functions, keywords, operators, types } from '$lib/components/Editor/clickhouse'; + import Editor from '$lib/components/Editor/Editor.svelte'; + import { setupLanguage } from '$lib/components/Editor/language'; import { SaveQueryModal } from '$lib/components/Queries'; import Result from '$lib/components/Result.svelte'; import SideBar from '$lib/components/SideBar.svelte'; @@ -23,13 +26,10 @@ import { historyRepository, type HistoryEntry } from '$lib/repositories/history'; import { queryRepository, type Query } from '$lib/repositories/queries'; import { tabRepository, type Tab } from '$lib/repositories/tabs'; - import Editor from '$lib/components/Editor/Editor.svelte'; import { SplitPane } from '@rich_harris/svelte-split-pane'; import debounce from 'p-debounce'; import { format } from 'sql-formatter'; import { tick, type ComponentProps } from 'svelte'; - import { setupLanguage } from '$lib/components/Editor/language'; - import { keywords, functions, operators, types } from '$lib/components/Editor/clickhouse'; let response = $state.raw(); let loading = $state(false); @@ -37,24 +37,15 @@ async function handleExec() { const query = currentTab.content; - if (loading || !query) { - return; - } + if (loading || !query) return; loading = true; counter?.start(); - response = await engine.exec(query).finally(() => { + try { + response = await engine.exec(query); + } finally { loading = false; counter?.stop(); - }); - - const last = await historyRepository.getLast(); - - if (response && last?.content !== query) await addHistoryEntry(query); - - if (response) { - bottomPanel.open = true; - if (bottomPanelTab === 'logs') bottomPanelTab = 'data'; } } @@ -82,6 +73,23 @@ $effect(() => void historyRepository.getAll().then((entries) => (history = entries))); $effect(() => void queryRepository.getAll().then((q) => (queries = q))); + engine.on('success', async (query: string) => { + if (typeof query !== 'string') return; + if (/(CREATE|DROP)/gi.test(query)) tables = await engine.getSchema(); + }); + + engine.on('success', async (query: string, response?: OLAPResponse) => { + const last = await historyRepository.getLast(); + if (response && last?.content !== query) await addHistoryEntry(query); + }); + + engine.on('success', (query: string, response?: OLAPResponse) => { + if (response) { + bottomPanel.open = true; + if (bottomPanelTab === 'logs') bottomPanelTab = 'data'; + } + }); + async function addHistoryEntry(query: string) { try { const entry = await historyRepository.add(query);