CB-4348 fix: parse sql script (#2212)

Co-authored-by: Evgenia Bezborodova <139753579+EvgeniaBzzz@users.noreply.github.com>
This commit is contained in:
Alexey
2023-12-14 12:57:54 +03:00
committed by GitHub
co-authored by Evgenia Bezborodova
parent 6c456c8a63
commit 5779100b9a
18 changed files with 214 additions and 208 deletions
@@ -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;
@@ -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<IconButtonProps> = observer(function IconButton({ name, img, viewBox, style, className, ...rest }) {
export const IconButton: React.FC<IconButtonProps> = observer(function IconButton({ tag, name, img, viewBox, style, className, ...rest }) {
const styles = useS(IconButtonStyles);
const Button = tag ?? ReakitButton;
return styled(useStyles(style))(
<Button {...rest} className={s(styles, { iconButton: true }, className)}>
{img && <StaticImage className={s(styles, { staticImage: true })} icon={name} />}
@@ -33,7 +33,7 @@ export function useSQLCodeEditorPanel(data: ISQLEditorData, editor: IEditor) {
}
},
onQueryChange(query: string) {
this.data.setQuery(query);
this.data.setScript(query);
},
onUpdate(update: ViewUpdate) {
const transactions = update.transactions.filter(t => t.selection !== undefined);
@@ -29,7 +29,6 @@ export interface ISQLScriptLine {
export class SQLParser {
get scripts(): ISQLScriptSegment[] {
this.update();
return this._scripts;
}
@@ -39,30 +38,25 @@ export class SQLParser {
private _scripts: ISQLScriptSegment[];
private script: string;
private parsedScript: string | null;
constructor() {
this._scripts = [];
this.script = '';
this.parsedScript = null;
makeObservable<this, '_scripts' | 'script' | 'parsedScript' | 'update'>(this, {
makeObservable<this, '_scripts' | 'script'>(this, {
actualScript: computed,
_scripts: observable.ref,
script: observable.ref,
parsedScript: observable.ref,
getScriptSegment: action,
getSegment: action,
getQueryAtPos: action,
setScript: action,
parse: action,
setQueries: action,
update: action,
});
}
getScriptSegment(): ISQLScriptSegment {
const script = this.parsedScript || '';
const script = this.script || '';
return {
query: script,
@@ -80,10 +74,8 @@ export class SQLParser {
end = begin;
}
this.update();
return {
query: (this.parsedScript || '').substring(begin, end),
query: (this.script || '').substring(begin, end),
begin,
end,
};
@@ -107,15 +99,9 @@ export class SQLParser {
setScript(script: string): void {
this.script = script;
this.update();
}
parse(script: string): void {
this.parsedScript = script;
}
setQueries(queries: IQueryInfo[]): this {
this.update();
this._scripts = queries.map<ISQLScriptSegment>(query => ({
query: this.script.substring(query.start, query.end),
begin: query.start,
@@ -124,10 +110,4 @@ export class SQLParser {
return this;
}
private update() {
if (this.parsedScript !== this.script) {
this.parse(this.script);
}
}
}
@@ -9,7 +9,7 @@ import { action, computed, makeObservable, observable, toJS } from 'mobx';
import type { IConnectionExecutionContextInfo } from '@cloudbeaver/core-connections';
import { ISyncExecutor, SyncExecutor } from '@cloudbeaver/core-executor';
import { isContainsException, isObjectsEqual, isValuesEqual, staticImplements } from '@cloudbeaver/core-utils';
import { isContainsException, isValuesEqual, staticImplements } from '@cloudbeaver/core-utils';
import type { IDatabaseDataModel, IDatabaseResultSet } from '@cloudbeaver/plugin-data-viewer';
import type { IDataQueryOptions } from '../QueryDataSource';
@@ -19,13 +19,13 @@ export interface ISegmentExecutionData {
}
export interface ICursor {
begin: number;
end: number;
readonly begin: number;
readonly end: number;
}
export interface ISQLEditorData {
readonly cursor: ICursor;
readonly activeSegmentMode: ISQLEditorMode;
activeSegmentMode: ISQLEditorMode;
readonly parser: SQLParser;
readonly dialect: SqlDialectInfo | undefined;
readonly activeSegment: ISQLScriptSegment | undefined;
@@ -48,7 +48,7 @@ export interface ISQLEditorData {
readonly hintsLimitIsMet: boolean;
updateParserScriptsThrottle(): Promise<void>;
setQuery(query: string): void;
setScript(query: string): void;
init(): void;
destruct(): void;
setCursor(begin: number, end?: number): void;
@@ -0,0 +1,27 @@
/*
* 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.
*/
.container {
composes: theme-border-color-background from global;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: auto;
border-right: solid 1px;
}
.actions {
display: flex;
flex-direction: column;
align-items: center;
user-select: none;
&:empty {
width: initial;
}
}
@@ -0,0 +1,81 @@
/*
* 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.
*/
import { observer } from 'mobx-react-lite';
import { ActionIconButton, getComputed, preventFocusHandler, s, useS, useTranslate } from '@cloudbeaver/core-blocks';
import type { ISqlEditorTabState } from '../ISqlEditorTabState';
import { ESqlDataSourceFeatures } from '../SqlDataSource/ESqlDataSourceFeatures';
import type { ISQLEditorData } from './ISQLEditorData';
import style from './SQLEditorActions.m.css';
import { SqlEditorActionsMenu } from './SqlEditorActionsMenu';
import { SqlEditorTools } from './SqlEditorTools';
interface Props {
data: ISQLEditorData;
state: ISqlEditorTabState;
className?: string;
}
export const SQLEditorActions = observer<Props>(function SQLEditorActions({ data, state }) {
const styles = useS(style);
const translate = useTranslate();
const isActiveSegmentMode = getComputed(() => data.activeSegmentMode.activeSegmentMode);
const disabled = getComputed(() => data.isLineScriptEmpty || data.isDisabled);
const isQuery = data.dataSource?.hasFeature(ESqlDataSourceFeatures.query);
const isExecutable = data.dataSource?.hasFeature(ESqlDataSourceFeatures.executable);
return (
<div className={s(styles, { container: true })}>
<div className={s(styles, { actions: true })} onMouseDown={preventFocusHandler}>
{isExecutable && (
<>
{isQuery && (
<>
<ActionIconButton
name="/icons/sql_exec.svg"
disabled={disabled}
title={translate('sql_editor_sql_execution_button_tooltip')}
img
onClick={data.executeQuery}
/>
<ActionIconButton
name="/icons/sql_exec_new.svg"
disabled={disabled}
title={translate('sql_editor_sql_execution_new_tab_button_tooltip')}
img
onClick={data.executeQueryNewTab}
/>
</>
)}
<ActionIconButton
name="/icons/sql_script_exec.svg"
disabled={disabled}
hidden={isActiveSegmentMode}
title={translate('sql_editor_sql_execution_script_button_tooltip')}
img
onClick={data.executeScript}
/>
{isQuery && data.dialect?.supportsExplainExecutionPlan && (
<ActionIconButton
name="/icons/sql_execution_plan.svg"
disabled={disabled}
hidden={isActiveSegmentMode}
title={translate('sql_editor_execution_plan_button_tooltip')}
img
onClick={data.showExecutionPlan}
/>
)}
</>
)}
<SqlEditorActionsMenu state={state} />
</div>
<SqlEditorTools data={data} state={state} />
</div>
);
});
@@ -17,7 +17,9 @@ export interface ISQLEditorMode {
export const SQLEditorModeContext: ISyncContextLoader<ISQLEditorMode, ISQLEditorData> = function SQLEditorModeContext(context, data) {
return {
activeSegment: data.parser.getSegment(data.cursor.begin, data.cursor.end),
get activeSegment() {
return data.parser.getSegment(data.cursor.begin, data.cursor.end);
},
activeSegmentMode: false,
};
};
@@ -9,25 +9,19 @@ import { observer } from 'mobx-react-lite';
import { useEffect, useMemo, useState } from 'react';
import styled, { css } from 'reshadow';
import { getComputed, preventFocusHandler, StaticImage, useSplit, useTranslate } from '@cloudbeaver/core-blocks';
import { getComputed, useSplit } from '@cloudbeaver/core-blocks';
import { useService } from '@cloudbeaver/core-di';
import { BASE_TAB_STYLES, ITabData, TabList, TabPanelList, TabsState, VERTICAL_ROTATED_TAB_STYLES } from '@cloudbeaver/core-ui';
import { MetadataMap } from '@cloudbeaver/core-utils';
import { useCaptureViewContext } from '@cloudbeaver/core-view';
import { ESqlDataSourceFeatures } from '../SqlDataSource/ESqlDataSourceFeatures';
import { ISqlEditorModeProps, SqlEditorModeService } from '../SqlEditorModeService';
import { DATA_CONTEXT_SQL_EDITOR_DATA } from './DATA_CONTEXT_SQL_EDITOR_DATA';
import type { ISqlEditorProps } from './ISqlEditorProps';
import { SqlEditorActionsMenu } from './SqlEditorActionsMenu';
import { SqlEditorTools } from './SqlEditorTools';
import { SQLEditorActions } from './SQLEditorActions';
import { useSqlEditor } from './useSqlEditor';
const styles = css`
button,
upload {
composes: theme-ripple from global;
}
sql-editor {
position: relative;
z-index: 0;
@@ -36,47 +30,6 @@ const styles = css`
display: flex;
overflow: auto;
}
container {
composes: theme-border-color-background from global;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: auto;
border-right: solid 1px;
}
actions {
width: 32px;
display: flex;
flex-direction: column;
align-items: center;
user-select: none;
&:empty {
width: initial;
}
}
button,
upload {
background: none;
padding: 0;
margin: 0;
height: 32px;
width: 32px;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
flex-shrink: 0;
}
StaticImage {
height: 16px;
width: 16px;
cursor: pointer;
}
`;
const tabStyles = css`
@@ -104,7 +57,6 @@ const tabStyles = css`
const tabListStyles = [BASE_TAB_STYLES, VERTICAL_ROTATED_TAB_STYLES, tabStyles];
export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ state, className }) {
const translate = useTranslate();
const split = useSplit();
const sqlEditorModeService = useService(SqlEditorModeService);
const data = useSqlEditor(state);
@@ -122,8 +74,6 @@ export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ state, c
state.currentModeId = tab.tabId;
}
const disabled = getComputed(() => data.isLineScriptEmpty || data.isDisabled);
const isActiveSegmentMode = getComputed(() => data.activeSegmentMode.activeSegmentMode);
const displayedEditors = getComputed(() => sqlEditorModeService.tabsContainer.getDisplayed({ state, data }).length);
useEffect(() => {
@@ -135,9 +85,6 @@ export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ state, c
}
});
const isQuery = data.dataSource?.hasFeature(ESqlDataSourceFeatures.query);
const isExecutable = data.dataSource?.hasFeature(ESqlDataSourceFeatures.executable);
return styled(
styles,
BASE_TAB_STYLES,
@@ -154,43 +101,7 @@ export const SqlEditor = observer<ISqlEditorProps>(function SqlEditor({ state, c
onChange={handleModeSelect}
>
<sql-editor className={className}>
<container>
<actions onMouseDown={preventFocusHandler}>
{isExecutable && (
<>
{isQuery && (
<>
<button disabled={disabled} title={translate('sql_editor_sql_execution_button_tooltip')} onClick={data.executeQuery}>
<StaticImage icon="/icons/sql_exec.svg" />
</button>
<button
disabled={disabled}
title={translate('sql_editor_sql_execution_new_tab_button_tooltip')}
onClick={data.executeQueryNewTab}
>
<StaticImage icon="/icons/sql_exec_new.svg" />
</button>
</>
)}
<button
disabled={disabled}
title={translate('sql_editor_sql_execution_script_button_tooltip')}
hidden={isActiveSegmentMode}
onClick={data.executeScript}
>
<StaticImage icon="/icons/sql_script_exec.svg" />
</button>
{isQuery && data.dialect?.supportsExplainExecutionPlan && (
<button disabled={disabled} title={translate('sql_editor_execution_plan_button_tooltip')} onClick={data.showExecutionPlan}>
<StaticImage icon="/icons/sql_execution_plan.svg" />
</button>
)}
</>
)}
<SqlEditorActionsMenu state={state} />
</actions>
<SqlEditorTools data={data} state={state} style={styles} />
</container>
<SQLEditorActions data={data} state={state} />
<TabPanelList />
{displayedEditors > 1 ? (
<tabs>
@@ -41,12 +41,12 @@ const registry: StyleRegistry = [
];
export const SqlEditorActionsMenu = observer<Props>(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 (
<SContext registry={registry}>
<MenuBar menu={menu} className={s(styles, { sqlActions: true }, className)} />
<MenuBar menu={menu} className={s(menuBarStyles, { toolsMenu: true, floating: true, sqlActions: true }, className)} />
</SContext>
);
});
@@ -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;
}
@@ -1,7 +1,5 @@
.sqlActions .menuBarItem {
padding: 4px;
}
.sqlActions .menuBarItemLabel {
display: none;
.toolsMenu .menuBarItem {
& .menuBarItemLabel {
display: none;
}
}
@@ -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;
}
}
@@ -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<Props>(function SqlEditorTools({ data, state, style, className }) {
export const SqlEditorTools = observer<Props>(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<Props>(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<Props>(function SqlEditorTools({ data, st
const isScript = data.dataSource?.hasFeature(ESqlDataSourceFeatures.script);
return styled(useStyles(style, styles))(
<tools className={className} onMouseDown={preventFocusHandler}>
<SqlEditorToolsMenu state={state} />
return (
<div className={s(styles, { tools: true }, className)} onMouseDown={preventFocusHandler}>
<SqlEditorToolsMenu state={state} data={data} />
{isScript && (
<>
<button
<ActionIconButton
name="/icons/sql_format_sm.svg"
disabled={disabled || data.readonly}
title={translate('sql_editor_sql_format_button_tooltip')}
hidden={isActiveSegmentMode}
img
onClick={data.formatScript}
>
<StaticImage icon="/icons/sql_format_sm.svg" />
</button>
<button
/>
<ActionIconButton
name="/icons/export.svg"
disabled={scriptEmpty}
title={translate('sql_editor_download_script_tooltip')}
hidden={isActiveSegmentMode}
img
onClick={downloadScriptHandler}
>
<StaticImage icon="/icons/export.svg" />
</button>
/>
{!isActiveSegmentMode && (
<UploadArea
accept=".sql"
@@ -94,20 +80,11 @@ export const SqlEditorTools = observer<Props>(function SqlEditorTools({ data, st
reset
onChange={handleScriptUpload}
>
<upload disabled={data.readonly}>
<StaticImage icon="/icons/import.svg" />
</upload>
<ActionIconButton tag="div" name="/icons/import.svg" disabled={data.readonly} img />
</UploadArea>
)}
{/*<button
title={translate('sql_editor_sql_execution_script_lock_tooltip')}
hidden={data.dataSource?.isReadonly() ?? true}
onClick={data.switchEditing}
>
<StaticImage icon={data.editing ? '/icons/sql_unlock_sm.svg' : '/icons/sql_lock_sm.svg'} />
</button>*/}
</>
)}
</tools>,
</div>
);
});
@@ -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<Props>(function SqlEditorToolsMenu({ state, context, className }) {
const styles = useS(SqlEditorActionsMenuBarStyles, SqlEditorActionsMenuBarItemStyles);
export const SqlEditorToolsMenu = observer<Props>(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 (
<SContext registry={registry}>
<MenuBar menu={menu} className={s(styles, { sqlActions: true }, className)} />
<MenuBar menu={menu} className={s(menuBarStyles, { sqlActions: true, floating: true }, className)} />
</SContext>
);
});
@@ -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;
@@ -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<IQueryChangeData>;
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) {