From 1d5b475482e5d6e95b0f2b62be27e37b2a78ddca Mon Sep 17 00:00:00 2001 From: Sychev Andrey <44414066+SychevAndrey@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:07:31 +0100 Subject: [PATCH] dbeaver/pro#8553 sql execution plan diagram (#4218) * dbeaver/pro#8553 feat: add execution plan diagram component * dbeaver/pro#8553 feat: enhance ExecutionPlanDiagram component with selectedNodeId prop * dbeaver/pro#8553 feat: add flex-shrink to metric value for better layout * dbeaver/pro#8553 refactor: remove queryString from IPlanData interface * dbeaver/pro#8553 feat: integrate execution plan diagram component * dbeaver/pro#8553 refactor: execution plan diagram separate state, add l10n, make toolbar composable * dbeaver/pro#8553 feat: implement keyboard navigation for execution plan nodes * dbeaver/pro#8553 feat: add translation support for execution plan diagram labels * dbeaver/pro#8553 fix: execution plan node dimensions * dbeaver/pro#8553 refactor: move formatNumber to helpers to use it * dbeaver/pro#8553 feat: add translation support for execution plan diagram API * dbeaver/pro#8553 docs: add README and API documentation for execution plan diagram component * dbeaver/pro#8553 feat: adjust cost bar height and collapse indicator position * dbeaver/pro#8553 feat: update PRIMARY_AXIS_WEIGHT for improved directional navigation * dbeaver/pro#8553 feat: refactor diagram actions and add collapse state management * dbeaver/pro#8553 refactor: move execution plan diagram to separate EE plugin * dbeaver/pro#8553 docs: remove redundant comment * dbeaver/pro#8553 docs: update comments for source and target anchors in ILayoutEdge interface * dbeaver/pro#8553 docs: update comment in ILayoutNode interface * dbeaver/pro#8553 refactor: simplify heavy route computation * dbeaver/pro#8553 refactor: optimize node handling in computePlanLayout function * dbeaver/pro#8553 refactor: improve tree structure handling in usePlanTree and buildTree functions * dbeaver/pro#8553 refactor: simplify usePlanTree * dbeaver/pro#8553 refactor: extract actions from useExecutionPlanDiagramState * dbeaver/pro#8553 refactor: improve configuration in execution plan diagram * dbeaver/pro#8553 refactor: simplify metric rendering in PlanNode component * dbeaver/pro#8553 feat: collapse by hotkey, not an extra element * dbeaver/pro#8553 feat: add tooltip hints for plan node interactions * dbeaver/pro#8553 refactor: remove high cost calculation and add node kind attribute * dbeaver/pro#8553 style: fix formatting in index.ts for ISqlExecutionPlanViewProps export * dbeaver/pro#8553 ci: remove unused dependency on @dbeaver/ui-kit * dbeaver/pro#8553 refactor: remove SQL execution plan diagram translations from locale files * dbeaver/pro#8553 refactor: update translation keys for SQL execution plan components * dbeaver/pro#8553 refactor: rename components * dbeaver/pro#8553 style: const * dbeaver/pro#8553 refactor: reset execution plan diagram state properties * dbeaver/pro#8553 refactor: update component imports and extract a component * dbeaver/pro#8553 refactor: rename PlanNodeKind to EPlanNodeKind and update references * dbeaver/pro#8553 feat: add zoom icons and update action references * dbeaver/pro#8553 refactor: enhance translation functionality by adding args interpolation * dbeaver/pro#8553 fix: Polynomial regular expression used on uncontrolled data * dbeaver/pro#8553 refactor: update ActionsCapture to use ref for toolbar actions * dbeaver/pro#8553 refactor: use jsx * dbeaver/pro#8553 refactor: simplify * dbeaver/pro#8553 refactor: replace useContext with use in DiagramContext * dbeaver/pro#8553 refactor: convert ExecutionPlanEdge to memoized component * dbeaver/pro#8553 refactor: convert ExecutionPlanNode to memoized component * dbeaver/pro#8553 refactor: unify ref prop in ExecutionPlanNode and ExecutionPlanViewport * dbeaver/pro#8553 refactor: extract ExecutionPlanViewportContent component and clean up CSS * dbeaver/pro#8553 refactor: convert ExecutionPlanNodeMetric to memoized component * dbeaver/pro#8553 refactor: improve usePlanLayout hook by utilizing useEffectEvent for stable callback * dbeaver/pro#8553 refactor: simplify diagram actions handling in createExecutionPlanDiagram * dbeaver/pro#8553 refactor: move out execution plan diagram component from --------- Co-authored-by: Evgenia <139753579+EvgeniaBzzz@users.noreply.github.com> Co-authored-by: mr-anton-t <42037741+mr-anton-t@users.noreply.github.com> Co-authored-by: alex <48489896+devnaumov@users.noreply.github.com> --- .../react-translate/src/TranslateContext.ts | 30 +++++++++++--- .../@dbeaver/js-helpers/src/formatNumber.ts | 24 +++++++++++ .../@dbeaver/js-helpers/src/index.ts | 1 + webapp/packages/core-utils/package.json | 1 + .../packages/core-utils/src/formatNumber.ts | 19 +-------- webapp/packages/core-utils/tsconfig.json | 3 ++ .../core-view/public/icons/zoom-in.svg | 3 -- .../core-view/public/icons/zoom-out.svg | 3 -- .../packages/core-view/public/icons/zoom.svg | 11 +++++ .../core-view/public/icons/zoom_in.svg | 13 ++++++ .../core-view/public/icons/zoom_in_m.svg | 13 ++++++ .../core-view/public/icons/zoom_in_sm.svg | 18 ++++++++ .../core-view/public/icons/zoom_m.svg | 11 +++++ .../core-view/public/icons/zoom_out.svg | 12 ++++++ .../core-view/public/icons/zoom_out_m.svg | 12 ++++++ .../core-view/public/icons/zoom_out_sm.svg | 17 ++++++++ .../core-view/public/icons/zoom_sm.svg | 16 ++++++++ .../src/Action/Actions/ACTION_ZOOM_IN.ts | 4 +- .../src/Action/Actions/ACTION_ZOOM_OUT.ts | 4 +- .../ExecutionPlan/ExecutionPlanTreeBlock.tsx | 4 +- .../ExecutionPlan/ExecutionPlanTreeView.tsx | 20 +++++++++ .../ISqlExecutionPlanViewProps.ts | 16 ++++++++ .../PropertiesPanel/PropertiesPanel.tsx | 16 +++++--- .../SqlExecutionPlanPanel.module.css | 41 ++++++++++++++++++- .../ExecutionPlan/SqlExecutionPlanPanel.tsx | 20 +++++++-- .../SqlExecutionPlanViewBootstrap.ts | 30 ++++++++++++++ .../SqlExecutionPlanViewService.ts | 21 ++++++++++ .../packages/plugin-sql-editor/src/index.ts | 3 ++ .../plugin-sql-editor/src/locales/en.ts | 13 +++--- .../plugin-sql-editor/src/locales/fr.ts | 13 +++--- .../plugin-sql-editor/src/locales/it.ts | 13 +++--- .../plugin-sql-editor/src/locales/ru.ts | 13 +++--- .../plugin-sql-editor/src/locales/vi.ts | 13 +++--- .../plugin-sql-editor/src/locales/zh.ts | 13 +++--- .../packages/plugin-sql-editor/src/module.ts | 6 ++- webapp/yarn.lock | 1 + 36 files changed, 389 insertions(+), 82 deletions(-) create mode 100644 webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts delete mode 100644 webapp/packages/core-view/public/icons/zoom-in.svg delete mode 100644 webapp/packages/core-view/public/icons/zoom-out.svg create mode 100644 webapp/packages/core-view/public/icons/zoom.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_in_sm.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out_m.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_out_sm.svg create mode 100644 webapp/packages/core-view/public/icons/zoom_sm.svg create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts create mode 100644 webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts diff --git a/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts b/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts index f2605c4a63..eef5a781f9 100644 --- a/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts +++ b/webapp/common-react/@dbeaver/react-translate/src/TranslateContext.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -9,11 +9,31 @@ import { createContext } from 'react'; import type { LocalizationToken } from './LocalizationToken.js'; -export function defaultTranslateFn(token: T, fallback?: T): T { - return fallback || token; +export function interpolateTranslation(template: string, args?: Record): string { + if (!args) { + return template; + } + + let result = template; + + for (const [key, value] of Object.entries(args)) { + if (value != null) { + result = result.split(`{arg:${key}}`).join(String(value)); + } + } + + return result; } -export type TranslateFn = (token: T, fallback?: T, args?: Record) => T; +export function defaultTranslateFn(token: T, fallback?: T, args?: Record): T { + const result = fallback || token; + if (result && args) { + return interpolateTranslation(result as string, args) as T; + } + return result; +} + +export type TranslateFn = (token: T, fallback?: T, args?: Record) => T; export const TranslateContext = createContext<{ translate: TranslateFn }>({ - translate: (key, fallback) => fallback || key, + translate: defaultTranslateFn, }); diff --git a/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts b/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts new file mode 100644 index 0000000000..16699a7a95 --- /dev/null +++ b/webapp/common-typescript/@dbeaver/js-helpers/src/formatNumber.ts @@ -0,0 +1,24 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +export function formatNumber(n: number, d: number): string { + if (n < 1000) { + return n.toLocaleString(undefined, { maximumFractionDigits: 2 }); + } + + const numStr = n.toString(); + const exponent = numStr.length - (numStr.length % 3); + + const power = Math.pow(10, d); + const rounded = Math.round((n * power) / Math.pow(10, exponent)) / power; + + const units = ' kMBTPE'; + const unit = units[exponent / 3]!; + + return rounded + unit; +} \ No newline at end of file diff --git a/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts b/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts index e8565e0642..8a8f1be87e 100644 --- a/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts +++ b/webapp/common-typescript/@dbeaver/js-helpers/src/index.ts @@ -16,3 +16,4 @@ export * from './memoizeLast.js'; export * from './mutex.js'; export * from './reorderArray.js'; export * from './getLocalizedDisplayName.js'; +export * from './formatNumber.js'; diff --git a/webapp/packages/core-utils/package.json b/webapp/packages/core-utils/package.json index f62ccba5bd..c9972bacf6 100644 --- a/webapp/packages/core-utils/package.json +++ b/webapp/packages/core-utils/package.json @@ -23,6 +23,7 @@ "validate-dependencies": "core-cli-validate-dependencies" }, "dependencies": { + "@dbeaver/js-helpers": "workspace:*", "@timohausmann/quadtree-ts": "^2", "fast-deep-equal": "^3", "md5": "^2", diff --git a/webapp/packages/core-utils/src/formatNumber.ts b/webapp/packages/core-utils/src/formatNumber.ts index 1a3d8f9e12..30d442a075 100644 --- a/webapp/packages/core-utils/src/formatNumber.ts +++ b/webapp/packages/core-utils/src/formatNumber.ts @@ -1,24 +1,9 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. */ -export function formatNumber(n: number, d: number) { - if (n < 1000) { - return n.toLocaleString(undefined, { maximumFractionDigits: 2 }); - } - - const numStr = n.toString(); - const exponent = numStr.length - (numStr.length % 3); - - const power = Math.pow(10, d); - const rounded = Math.round((n * power) / Math.pow(10, exponent)) / power; - - const units = ' kMBTPE'; - const unit = units[exponent / 3]!; - - return rounded + unit; -} +export { formatNumber } from '@dbeaver/js-helpers'; diff --git a/webapp/packages/core-utils/tsconfig.json b/webapp/packages/core-utils/tsconfig.json index 964a4a5f8b..30236ffc9b 100644 --- a/webapp/packages/core-utils/tsconfig.json +++ b/webapp/packages/core-utils/tsconfig.json @@ -13,6 +13,9 @@ { "path": "../../common-typescript/@dbeaver/cli" }, + { + "path": "../../common-typescript/@dbeaver/js-helpers" + }, { "path": "../core-cli" }, diff --git a/webapp/packages/core-view/public/icons/zoom-in.svg b/webapp/packages/core-view/public/icons/zoom-in.svg deleted file mode 100644 index 7ca279793b..0000000000 --- a/webapp/packages/core-view/public/icons/zoom-in.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/webapp/packages/core-view/public/icons/zoom-out.svg b/webapp/packages/core-view/public/icons/zoom-out.svg deleted file mode 100644 index efa287627c..0000000000 --- a/webapp/packages/core-view/public/icons/zoom-out.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/webapp/packages/core-view/public/icons/zoom.svg b/webapp/packages/core-view/public/icons/zoom.svg new file mode 100644 index 0000000000..8f0f823ec6 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in.svg b/webapp/packages/core-view/public/icons/zoom_in.svg new file mode 100644 index 0000000000..0a07b3fa4b --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in_m.svg b/webapp/packages/core-view/public/icons/zoom_in_m.svg new file mode 100644 index 0000000000..5b863afa24 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in_m.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_in_sm.svg b/webapp/packages/core-view/public/icons/zoom_in_sm.svg new file mode 100644 index 0000000000..6ffe2e4d23 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_in_sm.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_m.svg b/webapp/packages/core-view/public/icons/zoom_m.svg new file mode 100644 index 0000000000..5f66f43286 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_m.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out.svg b/webapp/packages/core-view/public/icons/zoom_out.svg new file mode 100644 index 0000000000..01af24e5f0 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out_m.svg b/webapp/packages/core-view/public/icons/zoom_out_m.svg new file mode 100644 index 0000000000..72c80d928b --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out_m.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_out_sm.svg b/webapp/packages/core-view/public/icons/zoom_out_sm.svg new file mode 100644 index 0000000000..fe110d6b54 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_out_sm.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/public/icons/zoom_sm.svg b/webapp/packages/core-view/public/icons/zoom_sm.svg new file mode 100644 index 0000000000..7228cdb884 --- /dev/null +++ b/webapp/packages/core-view/public/icons/zoom_sm.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts index 536eac3898..fe01f537f4 100644 --- a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts +++ b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_IN.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,5 +10,5 @@ import { createAction } from '../createAction.js'; export const ACTION_ZOOM_IN = createAction('zoom-in', { label: 'core_view_zoom_in', tooltip: 'core_view_zoom_in', - icon: '/icons/zoom-in.svg', + icon: '/icons/zoom_in.svg', }); diff --git a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts index 5f63cb7822..8ed984b25e 100644 --- a/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts +++ b/webapp/packages/core-view/src/Action/Actions/ACTION_ZOOM_OUT.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,5 +10,5 @@ import { createAction } from '../createAction.js'; export const ACTION_ZOOM_OUT = createAction('zoom-out', { label: 'core_view_zoom_out', tooltip: 'core_view_zoom_out', - icon: '/icons/zoom-out.svg', + icon: '/icons/zoom_out.svg', }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx index abd6111b21..395355f184 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeBlock.tsx @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -64,7 +64,7 @@ export const ExecutionPlanTreeBlock = observer(function ExecutionPlanTree ) : ( - {translate('sql_execution_plan_placeholder')} + {translate('plugin_sql_execution_plan_placeholder')} )} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx new file mode 100644 index 0000000000..f2ae91aa78 --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ExecutionPlanTreeView.tsx @@ -0,0 +1,20 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { observer } from 'mobx-react-lite'; + +import type { ISqlExecutionPlanViewProps } from './ISqlExecutionPlanViewProps.js'; +import { ExecutionPlanTreeBlock } from './ExecutionPlanTreeBlock.js'; + +export const ExecutionPlanTreeView = observer(function ExecutionPlanTreeView({ + nodes, + query, + onNodeSelect, +}) { + return ; +}); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts new file mode 100644 index 0000000000..4d295ea10d --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.ts @@ -0,0 +1,16 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import type { SqlExecutionPlanNode } from '@cloudbeaver/core-sdk'; + +export interface ISqlExecutionPlanViewProps { + nodes: SqlExecutionPlanNode[]; + query: string; + selectedNode: string | null; + onNodeSelect: (nodeId: string | null) => void; +} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx index 9e64f308c6..8abb0f1cd6 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/PropertiesPanel/PropertiesPanel.tsx @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -50,11 +50,11 @@ export const PropertiesPanel = observer(function PropertiesPanel({ select ).get(); if (!general.length && !details.length) { - return {translate('sql_execution_plan_properties_panel_placeholder')}; + return {translate('plugin_sql_execution_plan_properties_panel_placeholder')}; } - const nameColumnTitle = translate('sql_execution_plan_properties_panel_name'); - const valueColumnTitle = translate('sql_execution_plan_properties_panel_value'); + const nameColumnTitle = translate('plugin_sql_execution_plan_properties_panel_name'); + const valueColumnTitle = translate('plugin_sql_execution_plan_properties_panel_value'); return ( @@ -63,8 +63,12 @@ export const PropertiesPanel = observer(function PropertiesPanel({ select {valueColumnTitle} - {!!general.length && } - {!!details.length && } + {!!general.length && ( + + )} + {!!details.length && ( + + )}
); diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css index c03d414621..7b3d848fba 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.module.css @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -8,6 +8,45 @@ .pane { &:first-child { + display: flex; + flex-direction: column; overflow: hidden; + + /* Map diagram component variables to CloudBeaver theme */ + + /* Viewport */ + --dbv-plan-viewport-bg: var(--theme-surface); + + /* Node */ + --dbv-plan-node-bg: var(--theme-sub-secondary); + --dbv-plan-node-border-color: var(--theme-input-border); + --dbv-plan-node-selected-border: var(--theme-primary); + --dbv-plan-node-selected-glow: color-mix(in srgb, var(--theme-primary) 30%, transparent); + --dbv-plan-node-heavy-color: var(--theme-negative); + + /* Node caption */ + --dbv-plan-node-caption-bg: var(--theme-background); + --dbv-plan-node-caption-color: var(--theme-on-surface); + + /* Node name */ + --dbv-plan-node-name-color: var(--theme-on-surface); + + /* Node metrics */ + --dbv-plan-node-metric-color: var(--theme-text-secondary-on-light); + --dbv-plan-node-metric-value-color: var(--theme-on-surface); + + /* Node cost bar */ + --dbv-plan-node-cost-bar-bg: var(--theme-input-border); + --dbv-plan-node-cost-bar-color: var(--theme-status); + + /* Edges */ + --dbv-plan-edge-color: var(--theme-text-hint-on-light); + + /* Toolbar */ + --dbv-plan-toolbar-border: var(--theme-input-border); + --dbv-plan-toolbar-bg: var(--theme-surface); + --dbv-plan-toolbar-color: var(--theme-on-surface); + --dbv-plan-toolbar-hover-bg: var(--theme-sub-secondary); + --dbv-plan-toolbar-active-bg: var(--theme-secondary); } } diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx index 64d23e5c43..b177772a0d 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanPanel.tsx @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2024 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -10,12 +10,13 @@ import { useState } from 'react'; import { Loader, Pane, ResizerControls, s, Split, useS, useSplitUserState } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; +import { TabList, TabPanelList, TabsState } from '@cloudbeaver/core-ui'; import type { IExecutionPlanTab } from '../../ISqlEditorTabState.js'; -import { ExecutionPlanTreeBlock } from './ExecutionPlanTreeBlock.js'; import { PropertiesPanel } from './PropertiesPanel/PropertiesPanel.js'; -import style from './SqlExecutionPlanPanel.module.css'; import { SqlExecutionPlanService } from './SqlExecutionPlanService.js'; +import { SqlExecutionPlanViewService } from './SqlExecutionPlanViewService.js'; +import style from './SqlExecutionPlanPanel.module.css'; interface Props { executionPlanTab: IExecutionPlanTab; @@ -24,6 +25,7 @@ interface Props { export const SqlExecutionPlanPanel = observer(function SqlExecutionPlanPanel({ executionPlanTab }) { const styles = useS(style); const sqlExecutionPlanService = useService(SqlExecutionPlanService); + const sqlExecutionPlanViewService = useService(SqlExecutionPlanViewService); const data = sqlExecutionPlanService.data.get(executionPlanTab.tabId); const [selectedNode, setSelectedNode] = useState(null); const splitState = useSplitUserState('execution-plan'); @@ -35,7 +37,17 @@ export const SqlExecutionPlanPanel = observer(function SqlExecutionPlanPa return ( - + + + + diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts new file mode 100644 index 0000000000..2e4165bd9d --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.ts @@ -0,0 +1,30 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { importLazyComponent } from '@cloudbeaver/core-blocks'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; + +import { SqlExecutionPlanViewService } from './SqlExecutionPlanViewService.js'; + +const ExecutionPlanTreeView = importLazyComponent(() => import('./ExecutionPlanTreeView.js').then(m => m.ExecutionPlanTreeView)); + +@injectable(() => [SqlExecutionPlanViewService]) +export class SqlExecutionPlanViewBootstrap extends Bootstrap { + constructor(private readonly sqlExecutionPlanViewService: SqlExecutionPlanViewService) { + super(); + } + + override register(): void { + this.sqlExecutionPlanViewService.tabs.add({ + key: 'table', + name: 'plugin_sql_execution_plan_view_table', + order: 0, + panel: () => ExecutionPlanTreeView, + }); + } +} diff --git a/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts new file mode 100644 index 0000000000..7ec9d84f81 --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.ts @@ -0,0 +1,21 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2026 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { injectable } from '@cloudbeaver/core-di'; +import { TabsContainer } from '@cloudbeaver/core-ui'; + +import type { ISqlExecutionPlanViewProps } from './ISqlExecutionPlanViewProps.js'; + +@injectable() +export class SqlExecutionPlanViewService { + readonly tabs: TabsContainer; + + constructor() { + this.tabs = new TabsContainer('Execution Plan Views'); + } +} diff --git a/webapp/packages/plugin-sql-editor/src/index.ts b/webapp/packages/plugin-sql-editor/src/index.ts index f6a6a39475..fc2ea4dd5e 100644 --- a/webapp/packages/plugin-sql-editor/src/index.ts +++ b/webapp/packages/plugin-sql-editor/src/index.ts @@ -54,3 +54,6 @@ export * from './SqlEditorService.js'; export * from './SqlEditorSettingsService.js'; export * from './SqlEditorView.js'; export * from './SqlResultTabs/SqlQueryService.js'; +export * from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.js'; +export type { ISqlExecutionPlanViewProps } from './SqlResultTabs/ExecutionPlan/ISqlExecutionPlanViewProps.js'; + diff --git a/webapp/packages/plugin-sql-editor/src/locales/en.ts b/webapp/packages/plugin-sql-editor/src/locales/en.ts index 311762bf22..faed9e7eeb 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/en.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/en.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Statistics is not available'], ['sql_editor_sql_execution_executed_queries', 'Executed queries:'], ['sql_editor_sql_execution_executing', 'Executing...'], - ['sql_execution_plan_placeholder', 'Execution plan is empty'], - ['sql_execution_plan_properties_panel_placeholder', 'There are no items to show'], - ['sql_execution_plan_properties_panel_name', 'Name'], - ['sql_execution_plan_properties_panel_value', 'Value'], - ['sql_execution_plan_properties_panel_general', 'General'], - ['sql_execution_plan_properties_panel_details', 'Details'], + ['plugin_sql_execution_plan_placeholder', 'Execution plan is empty'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'There are no items to show'], + ['plugin_sql_execution_plan_properties_panel_name', 'Name'], + ['plugin_sql_execution_plan_properties_panel_value', 'Value'], + ['plugin_sql_execution_plan_properties_panel_general', 'General'], + ['plugin_sql_execution_plan_properties_panel_details', 'Details'], + ['plugin_sql_execution_plan_view_table', 'Table'], ['sql_editor_upload_script_tooltip', 'Upload sql script'], ['sql_editor_download_script_tooltip', 'Download sql script'], [ diff --git a/webapp/packages/plugin-sql-editor/src/locales/fr.ts b/webapp/packages/plugin-sql-editor/src/locales/fr.ts index 9f566bf708..2d633dc962 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/fr.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/fr.ts @@ -31,12 +31,13 @@ export default [ ['sql_editor_lock_button', "Verrouiller l'éditeur"], ['sql_editor_sql_statistics_unavailable', 'Les statistiques SQL ne sont pas disponibles'], ['sql_editor_queries_tab_title', 'Requêtes exécutées'], - ['sql_execution_plan_placeholder', "Le plan d'exécution est vide"], - ['sql_execution_plan_properties_panel_placeholder', 'Aucun élément à afficher'], - ['sql_execution_plan_properties_panel_name', 'Nom'], - ['sql_execution_plan_properties_panel_value', 'Valeur'], - ['sql_execution_plan_properties_panel_general', 'Général'], - ['sql_execution_plan_properties_panel_details', 'Détails'], + ['plugin_sql_execution_plan_placeholder', "Le plan d'exécution est vide"], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Aucun élément à afficher'], + ['plugin_sql_execution_plan_properties_panel_name', 'Nom'], + ['plugin_sql_execution_plan_properties_panel_value', 'Valeur'], + ['plugin_sql_execution_plan_properties_panel_general', 'Général'], + ['plugin_sql_execution_plan_properties_panel_details', 'Détails'], + ['plugin_sql_execution_plan_view_table', 'Tableau'], ['sql_editor_upload_script_tooltip', 'Télécharger le script SQL'], ['sql_editor_upload_script', 'Télécharger le script'], ['sql_editor_upload_script_unsaved_changes_dialog_message', "Certains fichiers n'ont pas été enregistrés. Voulez-vous d'abord les enregistrer ?"], diff --git a/webapp/packages/plugin-sql-editor/src/locales/it.ts b/webapp/packages/plugin-sql-editor/src/locales/it.ts index cf2c12f840..0833b07197 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/it.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/it.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Statistiche non disponibili'], ['sql_editor_sql_execution_executed_queries', 'Executed queries:'], ['sql_editor_sql_execution_executing', 'Esecuzione in corso...'], - ['sql_execution_plan_placeholder', 'Piano di esecuzione vuoto'], - ['sql_execution_plan_properties_panel_placeholder', 'Non ci sono elementi da mostrare'], - ['sql_execution_plan_properties_panel_name', 'Nome'], - ['sql_execution_plan_properties_panel_value', 'Valore'], - ['sql_execution_plan_properties_panel_general', 'Generale'], - ['sql_execution_plan_properties_panel_details', 'Dettagli'], + ['plugin_sql_execution_plan_placeholder', 'Piano di esecuzione vuoto'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Non ci sono elementi da mostrare'], + ['plugin_sql_execution_plan_properties_panel_name', 'Nome'], + ['plugin_sql_execution_plan_properties_panel_value', 'Valore'], + ['plugin_sql_execution_plan_properties_panel_general', 'Generale'], + ['plugin_sql_execution_plan_properties_panel_details', 'Dettagli'], + ['plugin_sql_execution_plan_view_table', 'Tabella'], ['sql_editor_close_result_tabs_dialog_title', 'Confirm closing tabs'], ['plugin_sql_editor_action_overlay_title', 'Action required'], ['plugin_sql_editor_action_overlay_description', 'Please reopen editor to continue working.'], diff --git a/webapp/packages/plugin-sql-editor/src/locales/ru.ts b/webapp/packages/plugin-sql-editor/src/locales/ru.ts index 0e16614799..3fc919c7ed 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/ru.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/ru.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Статистика не доступна'], ['sql_editor_sql_execution_executed_queries', 'Выполненные запросы:'], ['sql_editor_sql_execution_executing', 'Выполняется...'], - ['sql_execution_plan_placeholder', 'План выполнения пуст'], - ['sql_execution_plan_properties_panel_placeholder', 'Информация не доступна'], - ['sql_execution_plan_properties_panel_name', 'Название'], - ['sql_execution_plan_properties_panel_value', 'Значение'], - ['sql_execution_plan_properties_panel_general', 'Общие'], - ['sql_execution_plan_properties_panel_details', 'Дополнительные'], + ['plugin_sql_execution_plan_placeholder', 'План выполнения пуст'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Информация не доступна'], + ['plugin_sql_execution_plan_properties_panel_name', 'Название'], + ['plugin_sql_execution_plan_properties_panel_value', 'Значение'], + ['plugin_sql_execution_plan_properties_panel_general', 'Общие'], + ['plugin_sql_execution_plan_properties_panel_details', 'Дополнительные'], + ['plugin_sql_execution_plan_view_table', 'Таблица'], ['sql_editor_upload_script_tooltip', 'Загрузить sql скрипт'], ['sql_editor_download_script_tooltip', 'Скачать sql скрипт'], ['sql_editor_upload_script_unsaved_changes_dialog_message', 'Текущий скрипт будет перезаписан загруженным. Сохранить текущий?'], diff --git a/webapp/packages/plugin-sql-editor/src/locales/vi.ts b/webapp/packages/plugin-sql-editor/src/locales/vi.ts index 513f0746d9..111b4b62b0 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/vi.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/vi.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', 'Thống kê không khả dụng'], ['sql_editor_sql_execution_executed_queries', 'Các truy vấn đã thực thi:'], ['sql_editor_sql_execution_executing', 'Đang thực thi...'], - ['sql_execution_plan_placeholder', 'Kế hoạch thực thi trống'], - ['sql_execution_plan_properties_panel_placeholder', 'Không có mục nào để hiển thị'], - ['sql_execution_plan_properties_panel_name', 'Tên'], - ['sql_execution_plan_properties_panel_value', 'Giá trị'], - ['sql_execution_plan_properties_panel_general', 'Chung'], - ['sql_execution_plan_properties_panel_details', 'Chi tiết'], + ['plugin_sql_execution_plan_placeholder', 'Kế hoạch thực thi trống'], + ['plugin_sql_execution_plan_properties_panel_placeholder', 'Không có mục nào để hiển thị'], + ['plugin_sql_execution_plan_properties_panel_name', 'Tên'], + ['plugin_sql_execution_plan_properties_panel_value', 'Giá trị'], + ['plugin_sql_execution_plan_properties_panel_general', 'Chung'], + ['plugin_sql_execution_plan_properties_panel_details', 'Chi tiết'], + ['plugin_sql_execution_plan_view_table', 'Bảng'], ['sql_editor_upload_script_tooltip', 'Tải lên kịch bản SQL'], ['sql_editor_download_script_tooltip', 'Tải xuống kịch bản SQL'], [ diff --git a/webapp/packages/plugin-sql-editor/src/locales/zh.ts b/webapp/packages/plugin-sql-editor/src/locales/zh.ts index 7cec11ca77..4a04931be1 100644 --- a/webapp/packages/plugin-sql-editor/src/locales/zh.ts +++ b/webapp/packages/plugin-sql-editor/src/locales/zh.ts @@ -27,12 +27,13 @@ export default [ ['sql_editor_sql_statistics_unavailable', '统计不可用'], ['sql_editor_sql_execution_queries', '查询:'], ['sql_editor_sql_execution_executing', '执行...'], - ['sql_execution_plan_placeholder', '执行计划为空'], - ['sql_execution_plan_properties_panel_placeholder', '没有要显示的项目'], - ['sql_execution_plan_properties_panel_name', '名称'], - ['sql_execution_plan_properties_panel_value', '值'], - ['sql_execution_plan_properties_panel_general', '一般'], - ['sql_execution_plan_properties_panel_details', '详情'], + ['plugin_sql_execution_plan_placeholder', '执行计划为空'], + ['plugin_sql_execution_plan_properties_panel_placeholder', '没有要显示的项目'], + ['plugin_sql_execution_plan_properties_panel_name', '名称'], + ['plugin_sql_execution_plan_properties_panel_value', '值'], + ['plugin_sql_execution_plan_properties_panel_general', '一般'], + ['plugin_sql_execution_plan_properties_panel_details', '详情'], + ['plugin_sql_execution_plan_view_table', '表格'], ['sql_editor_upload_script_tooltip', '上传SQL脚本'], ['sql_editor_download_script_tooltip', '下载SQL脚本'], ['sql_editor_upload_script_unsaved_changes_dialog_message', '您当前的脚本将被上传的脚本覆盖。要先保存吗?'], diff --git a/webapp/packages/plugin-sql-editor/src/module.ts b/webapp/packages/plugin-sql-editor/src/module.ts index 7b4d16819d..3cf6b6541e 100644 --- a/webapp/packages/plugin-sql-editor/src/module.ts +++ b/webapp/packages/plugin-sql-editor/src/module.ts @@ -1,6 +1,6 @@ /* * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2025 DBeaver Corp and others + * Copyright (C) 2020-2026 DBeaver Corp and others * * Licensed under the Apache License, Version 2.0. * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ import { OutputLogsService } from './SqlResultTabs/OutputLogs/OutputLogsService. import { OutputLogsResource } from './SqlResultTabs/OutputLogs/OutputLogsResource.js'; import { OutputLogsEventHandler } from './SqlResultTabs/OutputLogs/OutputLogsEventHandler.js'; import { SqlExecutionPlanService } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanService.js'; +import { SqlExecutionPlanViewService } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewService.js'; +import { SqlExecutionPlanViewBootstrap } from './SqlResultTabs/ExecutionPlan/SqlExecutionPlanViewBootstrap.js'; import { SqlEditorView } from './SqlEditorView.js'; import { SqlEditorSettingsService } from './SqlEditorSettingsService.js'; import { SqlEditorService } from './SqlEditorService.js'; @@ -49,6 +51,8 @@ export default ModuleRegistry.add({ .addSingleton(OutputLogsResource) .addSingleton(OutputLogsEventHandler) .addSingleton(SqlExecutionPlanService) + .addSingleton(SqlExecutionPlanViewService) + .addSingleton(Bootstrap, SqlExecutionPlanViewBootstrap) .addSingleton(SqlEditorView) .addSingleton(SqlEditorSettingsService) .addSingleton(SqlEditorService) diff --git a/webapp/yarn.lock b/webapp/yarn.lock index eba0f28e25..035ded3ebc 100644 --- a/webapp/yarn.lock +++ b/webapp/yarn.lock @@ -2260,6 +2260,7 @@ __metadata: "@cloudbeaver/tests-runner": "workspace:*" "@cloudbeaver/tsconfig": "workspace:*" "@dbeaver/cli": "workspace:*" + "@dbeaver/js-helpers": "workspace:*" "@dbeaver/react-tests": "workspace:*" "@timohausmann/quadtree-ts": "npm:^2" "@types/md5": "npm:^2"