From dc012643adf607848e2c1283f0886eb9c2609b52 Mon Sep 17 00:00:00 2001 From: Aleksei Potsetsuev Date: Fri, 8 Dec 2023 18:30:21 +0800 Subject: [PATCH] CB-4348 fix: parse sql script --- .../core-blocks/src/ActionIconButton.m.css | 1 + .../packages/core-blocks/src/IconButton.tsx | 7 +- .../useSQLCodeEditorPanel.ts | 2 +- .../plugin-sql-editor/src/SQLParser.ts | 26 +---- .../src/SqlDataSource/BaseSqlDataSource.ts | 2 +- .../src/SqlEditor/ISQLEditorData.ts | 8 +- .../src/SqlEditor/SQLEditorActions.m.css | 27 ++++++ .../src/SqlEditor/SQLEditorActions.tsx | 81 ++++++++++++++++ .../src/SqlEditor/SQLEditorModeContext.ts | 4 +- .../src/SqlEditor/SqlEditor.tsx | 95 +------------------ .../src/SqlEditor/SqlEditorActionsMenu.tsx | 4 +- .../SqlEditor/SqlEditorActionsMenuBar.m.css | 8 +- .../SqlEditorActionsMenuBarItem.m.css | 10 +- .../src/SqlEditor/SqlEditorTools.m.css | 16 ++++ .../src/SqlEditor/SqlEditorTools.tsx | 59 ++++-------- .../src/SqlEditor/SqlEditorToolsMenu.tsx | 10 +- .../src/SqlEditor/useSqlEditor.ts | 40 +++++--- .../plugin-sql-editor/src/SqlEditorService.ts | 22 +---- 18 files changed, 214 insertions(+), 208 deletions(-) create mode 100644 webapp/packages/plugin-sql-editor/src/SqlEditor/SQLEditorActions.m.css create mode 100644 webapp/packages/plugin-sql-editor/src/SqlEditor/SQLEditorActions.tsx create mode 100644 webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.m.css diff --git a/webapp/packages/core-blocks/src/ActionIconButton.m.css b/webapp/packages/core-blocks/src/ActionIconButton.m.css index 5dce1ceef1..3ec6372511 100644 --- a/webapp/packages/core-blocks/src/ActionIconButton.m.css +++ b/webapp/packages/core-blocks/src/ActionIconButton.m.css @@ -5,6 +5,7 @@ margin: 2px !important; width: 24px !important; height: 24px !important; + box-sizing: border-box; overflow: hidden; flex-shrink: 0; flex-grow: 0; diff --git a/webapp/packages/core-blocks/src/IconButton.tsx b/webapp/packages/core-blocks/src/IconButton.tsx index 1158f6d740..1a2ae79179 100644 --- a/webapp/packages/core-blocks/src/IconButton.tsx +++ b/webapp/packages/core-blocks/src/IconButton.tsx @@ -7,7 +7,7 @@ */ import { observer } from 'mobx-react-lite'; import type React from 'react'; -import { Button, ButtonProps } from 'reakit/Button'; +import { ButtonProps, Button as ReakitButton } from 'reakit/Button'; import styled from 'reshadow'; import type { ComponentStyle } from '@cloudbeaver/core-theming'; @@ -20,6 +20,7 @@ import { useS } from './useS'; import { useStyles } from './useStyles'; interface Props { + tag?: 'button' | 'a' | 'div'; name: string; img?: boolean; viewBox?: string; @@ -28,9 +29,11 @@ interface Props { export type IconButtonProps = Props & ButtonProps; -export const IconButton: React.FC = observer(function IconButton({ name, img, viewBox, style, className, ...rest }) { +export const IconButton: React.FC = observer(function IconButton({ tag, name, img, viewBox, style, className, ...rest }) { const styles = useS(IconButtonStyles); + const Button = tag ?? ReakitButton; + return styled(useStyles(style))( - - - )} - - {isQuery && data.dialect?.supportsExplainExecutionPlan && ( - - )} - - )} - - - - + {displayedEditors > 1 ? ( diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenu.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenu.tsx index f33f54e54b..eb6889e172 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenu.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenu.tsx @@ -41,12 +41,12 @@ const registry: StyleRegistry = [ ]; export const SqlEditorActionsMenu = observer(function SqlEditorActionsMenu({ state, context, className }) { - const styles = useS(SqlEditorActionsMenuBarStyles, SqlEditorActionsMenuBarItemStyles); + const menuBarStyles = useS(SqlEditorActionsMenuBarStyles, SqlEditorActionsMenuBarItemStyles, MenuBarStyles, MenuBarItemStyles); const menu = useMenu({ menu: SQL_EDITOR_ACTIONS_MENU, context }); return ( - + ); }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBar.m.css b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBar.m.css index a8066255f9..cf4d55dc17 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBar.m.css +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBar.m.css @@ -1,3 +1,10 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2023 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ .sqlActions { display: flex; flex-direction: column; @@ -5,5 +12,4 @@ .sqlActions.menuBar { height: unset; - width: 32px; } diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBarItem.m.css b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBarItem.m.css index e2bce2142b..22e78f4ff8 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBarItem.m.css +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorActionsMenuBarItem.m.css @@ -1,7 +1,5 @@ -.sqlActions .menuBarItem { - padding: 4px; -} - -.sqlActions .menuBarItemLabel { - display: none; +.toolsMenu .menuBarItem { + & .menuBarItemLabel { + display: none; + } } diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.m.css b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.m.css new file mode 100644 index 0000000000..c333fcaf40 --- /dev/null +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.m.css @@ -0,0 +1,16 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2023 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ +.tools { + display: flex; + flex-direction: column; + align-items: center; + + &:empty { + display: none; + } +} diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.tsx index f71d390db6..70522dc61e 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorTools.tsx @@ -6,39 +6,25 @@ * you may not use this file except in compliance with the License. */ import { observer } from 'mobx-react-lite'; -import styled, { css } from 'reshadow'; -import { getComputed, preventFocusHandler, StaticImage, UploadArea, useStyles, useTranslate } from '@cloudbeaver/core-blocks'; -import type { ComponentStyle } from '@cloudbeaver/core-theming'; +import { ActionIconButton, getComputed, preventFocusHandler, s, UploadArea, useS, useTranslate } from '@cloudbeaver/core-blocks'; import type { ISqlEditorTabState } from '../ISqlEditorTabState'; import { ESqlDataSourceFeatures } from '../SqlDataSource/ESqlDataSourceFeatures'; import type { ISQLEditorData } from './ISQLEditorData'; +import style from './SqlEditorTools.m.css'; import { SqlEditorToolsMenu } from './SqlEditorToolsMenu'; import { useTools } from './useTools'; -const styles = css` - tools { - width: 32px; - display: flex; - flex-direction: column; - align-items: center; - - &:empty { - display: none; - } - } -`; - interface Props { data: ISQLEditorData; state: ISqlEditorTabState; - style?: ComponentStyle; className?: string; } -export const SqlEditorTools = observer(function SqlEditorTools({ data, state, style, className }) { +export const SqlEditorTools = observer(function SqlEditorTools({ data, state, className }) { const translate = useTranslate(); + const styles = useS(style); const tools = useTools(state); const scriptEmpty = getComputed(() => data.value.length === 0); const disabled = getComputed(() => data.isDisabled || data.isScriptEmpty); @@ -55,7 +41,7 @@ export const SqlEditorTools = observer(function SqlEditorTools({ data, st const script = await tools.tryReadScript(file, prevScript); if (script) { - data.setQuery(script); + data.setScript(script); } } @@ -65,27 +51,27 @@ export const SqlEditorTools = observer(function SqlEditorTools({ data, st const isScript = data.dataSource?.hasFeature(ESqlDataSourceFeatures.script); - return styled(useStyles(style, styles))( - - + return ( +
+ {isScript && ( <> - - + /> {!isActiveSegmentMode && ( (function SqlEditorTools({ data, st reset onChange={handleScriptUpload} > - - - + )} - {/**/} )} - , +
); }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorToolsMenu.tsx b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorToolsMenu.tsx index f9961ea9d7..c4ef20e7be 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorToolsMenu.tsx +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/SqlEditorToolsMenu.tsx @@ -14,11 +14,14 @@ import { useMenu } from '@cloudbeaver/core-view'; import { DATA_CONTEXT_SQL_EDITOR_STATE } from '../DATA_CONTEXT_SQL_EDITOR_STATE'; import type { ISqlEditorTabState } from '../ISqlEditorTabState'; +import { DATA_CONTEXT_SQL_EDITOR_DATA } from './DATA_CONTEXT_SQL_EDITOR_DATA'; +import type { ISQLEditorData } from './ISQLEditorData'; import { SQL_EDITOR_TOOLS_MENU } from './SQL_EDITOR_TOOLS_MENU'; import SqlEditorActionsMenuBarStyles from './SqlEditorActionsMenuBar.m.css'; import SqlEditorActionsMenuBarItemStyles from './SqlEditorActionsMenuBarItem.m.css'; interface Props { + data: ISQLEditorData; state: ISqlEditorTabState; context?: IDataContext; className?: string; @@ -40,14 +43,15 @@ const registry: StyleRegistry = [ }, ], ]; -export const SqlEditorToolsMenu = observer(function SqlEditorToolsMenu({ state, context, className }) { - const styles = useS(SqlEditorActionsMenuBarStyles, SqlEditorActionsMenuBarItemStyles); +export const SqlEditorToolsMenu = observer(function SqlEditorToolsMenu({ data, state, context, className }) { + const menuBarStyles = useS(SqlEditorActionsMenuBarStyles, SqlEditorActionsMenuBarItemStyles, MenuBarStyles, MenuBarItemStyles); const menu = useMenu({ menu: SQL_EDITOR_TOOLS_MENU, context }); menu.context.set(DATA_CONTEXT_SQL_EDITOR_STATE, state); + context?.set(DATA_CONTEXT_SQL_EDITOR_DATA, data); return ( - + ); }); diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts index 4e01986fb5..2b9645db74 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditor/useSqlEditor.ts @@ -29,7 +29,7 @@ import { OUTPUT_LOGS_TAB_ID } from '../SqlResultTabs/OutputLogs/OUTPUT_LOGS_TAB_ import { SqlQueryService } from '../SqlResultTabs/SqlQueryService'; import { SqlResultTabsService } from '../SqlResultTabs/SqlResultTabsService'; import type { ICursor, ISQLEditorData } from './ISQLEditorData'; -import { ISQLEditorMode, SQLEditorModeContext } from './SQLEditorModeContext'; +import { SQLEditorModeContext } from './SQLEditorModeContext'; interface ISQLEditorDataPrivate extends ISQLEditorData { readonly sqlDialectInfoService: SqlDialectInfoService; @@ -83,11 +83,9 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { return this.sqlDialectInfoService.getDialectInfo(createConnectionParam(executionContext.projectId, executionContext.connectionId)); }, - get activeSegmentMode(): ISQLEditorMode { - const contexts = this.onMode.execute(this); - const mode = contexts.getContext(SQLEditorModeContext); - - return mode; + activeSegmentMode: { + activeSegment: undefined, + activeSegmentMode: false, }, get activeSegment(): ISQLScriptSegment | undefined { @@ -228,7 +226,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { const formatted = await this.sqlDialectInfoService.formatScript(this.dataSource.executionContext, script.query); this.onFormat.execute([script, formatted]); - this.setQuery(query.substring(0, script.begin) + formatted + query.substring(script.end)); + this.setScript(query.substring(0, script.begin) + formatted + query.substring(script.end)); } finally { this.readonlyState = false; } @@ -361,9 +359,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { } }, - setQuery(query: string): void { - this.sqlEditorService.setQuery(query, this.state); - this.updateParserScriptsThrottle().catch(() => {}); + setScript(query: string): void { + this.dataSource?.setScript(query); }, updateParserScriptsThrottle: throttleAsync(async function updateParserScriptsThrottle() { @@ -378,6 +375,8 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { const script = this.parser.actualScript; if (!connectionId || !script) { + this.parser.setQueries([]); + this.onUpdate.execute(); return; } @@ -390,8 +389,10 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { } }); + // check if script was changed while we were waiting for response if (this.parser.actualScript === script) { this.parser.setQueries(queries); + this.onUpdate.execute(); } }, @@ -470,12 +471,12 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { executeScript: action.bound, switchEditing: action.bound, dialect: computed, - isLineScriptEmpty: computed, isDisabled: computed, value: computed, readonly: computed, + activeSegmentMode: observable.ref, hintsLimitIsMet: observable.ref, - cursor: observable, + cursor: observable.ref, readonlyState: observable, executingScript: observable, }, @@ -499,6 +500,7 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { handlers: [ function setScript({ script }) { data.parser.setScript(script); + data.updateParserScriptsThrottle().catch(() => {}); data.onUpdate.execute(); }, ], @@ -513,6 +515,20 @@ export function useSqlEditor(state: ISqlEditorTabState): ISQLEditorData { ], }); + useExecutor({ + executor: data.onUpdate, + handlers: [ + function updateActiveSegmentMode() { + // Probably we need to rework this logic + // we want to track active segment mode with mobx + // right now it's leads to bag when script changed from empty to not empty + // data.isLineScriptEmpty skips this change + const contexts = data.onMode.execute(data); + data.activeSegmentMode = contexts.getContext(SQLEditorModeContext); + }, + ], + }); + useEffect(() => () => data.destruct(), []); return data; diff --git a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts index 46ae935668..6a2f3a6f17 100644 --- a/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts +++ b/webapp/packages/plugin-sql-editor/src/SqlEditorService.ts @@ -21,7 +21,6 @@ import { } from '@cloudbeaver/core-connections'; import { injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; -import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor'; import { CachedMapAllKey } from '@cloudbeaver/core-resource'; import { FEATURE_GIT_ID, ServerConfigResource } from '@cloudbeaver/core-root'; import { GraphQLService, SqlCompletionProposal, SqlScriptInfoFragment } from '@cloudbeaver/core-sdk'; @@ -34,20 +33,12 @@ import { SqlEditorSettingsService } from './SqlEditorSettingsService'; export type SQLProposal = SqlCompletionProposal; -export interface IQueryChangeData { - prevQuery: string; - query: string; - state: ISqlEditorTabState; -} - @injectable() export class SqlEditorService { get autoSave() { return this.sqlEditorSettingsService.settings.getValue('autoSave') && !this.serverConfigResource.isFeatureEnabled(FEATURE_GIT_ID, true); } - readonly onQueryChange: ISyncExecutor; - constructor( private readonly graphQLService: GraphQLService, private readonly connectionsManagerService: ConnectionsManagerService, @@ -58,9 +49,7 @@ export class SqlEditorService { private readonly sqlDataSourceService: SqlDataSourceService, private readonly sqlEditorSettingsService: SqlEditorSettingsService, private readonly serverConfigResource: ServerConfigResource, - ) { - this.onQueryChange = new SyncExecutor(); - } + ) {} getState(editorId: string, datasourceKey: string, order: number, source?: string): ISqlEditorTabState { return observable({ @@ -138,15 +127,10 @@ export class SqlEditorService { } } - setQuery(query: string, state: ISqlEditorTabState) { + setScript(script: string, state: ISqlEditorTabState) { const dataSource = this.sqlDataSourceService.get(state.editorId); - if (dataSource) { - const prevQuery = dataSource.script; - - dataSource.setScript(query); - this.onQueryChange.execute({ prevQuery, query, state }); - } + dataSource!.setScript(script); } async resetExecutionContext(state: ISqlEditorTabState) {