diff --git a/package-lock.json b/package-lock.json
index 1911b045c..5d2ebe8b1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -36,7 +36,6 @@
"path-to-regexp": "^3.0.0",
"qs": "^6.12.0",
"react-error-boundary": "^4.0.13",
- "react-freeze": "^1.0.4",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.52.1",
"react-json-inspector": "^7.1.1",
@@ -20923,18 +20922,6 @@
"resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.2.tgz",
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
- "node_modules/react-freeze": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
- "integrity": "sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==",
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "peerDependencies": {
- "react": ">=17.0.0"
- }
- },
"node_modules/react-helmet-async": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/react-helmet-async/-/react-helmet-async-2.0.5.tgz",
diff --git a/package.json b/package.json
index 68876631b..830c4599c 100644
--- a/package.json
+++ b/package.json
@@ -38,7 +38,6 @@
"path-to-regexp": "^3.0.0",
"qs": "^6.12.0",
"react-error-boundary": "^4.0.13",
- "react-freeze": "^1.0.4",
"react-helmet-async": "^2.0.5",
"react-hook-form": "^7.52.1",
"react-json-inspector": "^7.1.1",
diff --git a/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.scss b/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.scss
deleted file mode 100644
index 29b1a843d..000000000
--- a/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-.ydb-not-render-until-first-visible {
- display: contents;
-}
diff --git a/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.tsx b/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.tsx
deleted file mode 100644
index 5c6356739..000000000
--- a/src/components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-
-import {Freeze} from 'react-freeze';
-
-import {cn} from '../../utils/cn';
-
-import './NotRenderUntilFirstVisible.scss';
-
-const block = cn('ydb-not-render-until-first-visible');
-
-interface Props {
- show?: boolean;
- className?: string;
- children: React.ReactNode;
-}
-
-export default function NotRenderUntilFirstVisible({show, className, children}: Props) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx
index 4a347b440..ed8a6e15b 100644
--- a/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx
+++ b/src/containers/Tenant/ObjectGeneral/ObjectGeneral.tsx
@@ -1,8 +1,5 @@
-import React from 'react';
-
import {useThemeValue} from '@gravity-ui/uikit';
-import NotRenderUntilFirstVisible from '../../../components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible';
import {TENANT_PAGES_IDS} from '../../../store/reducers/tenant/constants';
import type {AdditionalNodesProps, AdditionalTenantsProps} from '../../../types/additionalProps';
import type {EPathType} from '../../../types/api/schema';
@@ -31,13 +28,12 @@ function ObjectGeneral(props: ObjectGeneralProps) {
const renderPageContent = () => {
const {type, additionalTenantProps, additionalNodesProps, tenantName, path} = props;
-
- return (
-
-
-
-
-
+ switch (tenantPage) {
+ case TENANT_PAGES_IDS.query: {
+ return ;
+ }
+ default: {
+ return (
-
-
- );
+ );
+ }
+ }
};
return (
diff --git a/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.scss b/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.scss
new file mode 100644
index 000000000..6c8350720
--- /dev/null
+++ b/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.scss
@@ -0,0 +1,9 @@
+@import '../../../../styles/mixins.scss';
+
+.cancel-query-button {
+ &__stop-button {
+ &_error {
+ @include query-buttons-animations();
+ }
+ }
+}
diff --git a/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.tsx b/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.tsx
new file mode 100644
index 000000000..937b292e7
--- /dev/null
+++ b/src/containers/Tenant/Query/CancelQueryButton/CancelQueryButton.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+
+import {StopFill} from '@gravity-ui/icons';
+import {Button, Icon} from '@gravity-ui/uikit';
+
+import {cancelQueryApi} from '../../../../store/reducers/cancelQuery';
+import {cn} from '../../../../utils/cn';
+import i18n from '../i18n';
+
+import './CancelQueryButton.scss';
+
+const b = cn('cancel-query-button');
+
+interface CancelQueryButtonProps {
+ queryId: string;
+ tenantName: string;
+}
+
+export function CancelQueryButton({queryId, tenantName}: CancelQueryButtonProps) {
+ const [sendCancelQuery, cancelQueryResponse] = cancelQueryApi.useCancelQueryMutation();
+
+ const onStopButtonClick = React.useCallback(() => {
+ sendCancelQuery({queryId, database: tenantName});
+ }, [queryId, sendCancelQuery, tenantName]);
+
+ return (
+
+ );
+}
diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
index 52ec4bdb1..76eebd224 100644
--- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
+++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.scss
@@ -78,10 +78,4 @@
&__elapsed-label {
margin-left: var(--g-spacing-3);
}
-
- &__stop-button {
- &_error {
- @include query-buttons-animations();
- }
- }
}
diff --git a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx
index e1f3e76b5..35208f56e 100644
--- a/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx
+++ b/src/containers/Tenant/Query/ExecuteResult/ExecuteResult.tsx
@@ -1,8 +1,7 @@
import React from 'react';
-import {StopFill} from '@gravity-ui/icons';
import type {ControlGroupOption} from '@gravity-ui/uikit';
-import {Button, Icon, RadioButton, Tabs} from '@gravity-ui/uikit';
+import {RadioButton, Tabs} from '@gravity-ui/uikit';
import JSONTree from 'react-json-inspector';
import {ClipboardButton} from '../../../../components/ClipboardButton';
@@ -17,13 +16,14 @@ import {QueryResultTable} from '../../../../components/QueryResultTable/QueryRes
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
import type {ColumnType, KeyValueRow, TKqpStatsQuery} from '../../../../types/api/query';
import type {ValueOf} from '../../../../types/common';
-import type {IQueryResult} from '../../../../types/store/query';
+import type {ExecuteQueryResult} from '../../../../types/store/executeQuery';
import {getArray} from '../../../../utils';
import {cn} from '../../../../utils/cn';
import {getStringifiedData} from '../../../../utils/dataFormatters/dataFormatters';
import {useTypedDispatch} from '../../../../utils/hooks';
import {parseQueryError} from '../../../../utils/query';
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
+import {CancelQueryButton} from '../CancelQueryButton/CancelQueryButton';
import {SimplifiedPlan} from '../ExplainResult/components/SimplifiedPlan/SimplifiedPlan';
import {ResultIssues} from '../Issues/Issues';
import {QueryDuration} from '../QueryDuration/QueryDuration';
@@ -49,34 +49,28 @@ const resultOptionsIds = {
type SectionID = ValueOf;
interface ExecuteResultProps {
- data: IQueryResult | undefined;
- error: unknown;
- cancelError: unknown;
+ result: ExecuteQueryResult;
isResultsCollapsed?: boolean;
+ theme?: string;
+ tenantName: string;
onCollapseResults: VoidFunction;
onExpandResults: VoidFunction;
- onStopButtonClick: VoidFunction;
- theme?: string;
- loading?: boolean;
- cancelQueryLoading?: boolean;
}
export function ExecuteResult({
- data,
- error,
- cancelError,
+ result,
isResultsCollapsed,
+ theme,
+ tenantName,
onCollapseResults,
onExpandResults,
- onStopButtonClick,
- theme,
- loading,
- cancelQueryLoading,
}: ExecuteResultProps) {
const [selectedResultSet, setSelectedResultSet] = React.useState(0);
const [activeSection, setActiveSection] = React.useState(resultOptionsIds.result);
const dispatch = useTypedDispatch();
+ const {error, isLoading, queryId, data} = result;
+
const stats: TKqpStatsQuery | undefined = data?.stats;
const resultsSetsCount = data?.resultSets?.length;
const isMulti = resultsSetsCount && resultsSetsCount > 0;
@@ -111,10 +105,10 @@ export function ExecuteResult({
};
const renderResultTable = (
- result: KeyValueRow[] | undefined,
+ resultSet: KeyValueRow[] | undefined,
columns: ColumnType[] | undefined,
) => {
- return ;
+ return ;
};
const renderResult = () => {
@@ -243,8 +237,8 @@ export function ExecuteResult({
-
- {!error && !loading && (
+
+ {!error && !isLoading && (
{stats?.DurationUs !== undefined && (
@@ -261,17 +255,10 @@ export function ExecuteResult({
)}
)}
- {loading ? (
+ {isLoading ? (
-
+
) : null}
{data?.traceId ? : null}
@@ -287,8 +274,8 @@ export function ExecuteResult({
/>
- {loading || isQueryCancelledError(error) ? null : }
-
+ {isLoading || isQueryCancelledError(error) ? null : }
+
{renderResultSection()}
diff --git a/src/containers/Tenant/Query/ExecuteResult/i18n/en.json b/src/containers/Tenant/Query/ExecuteResult/i18n/en.json
index fcc63d028..b2294fa64 100644
--- a/src/containers/Tenant/Query/ExecuteResult/i18n/en.json
+++ b/src/containers/Tenant/Query/ExecuteResult/i18n/en.json
@@ -3,7 +3,6 @@
"action.result": "Result",
"action.stats": "Stats",
"action.schema": "Schema",
- "action.stop": "Stop",
"action.explain-plan": "Explain Plan",
"action.copy": "Copy {{activeSection}}",
"trace": "Trace"
diff --git a/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss b/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss
index 375ced444..5b247d9f4 100644
--- a/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss
+++ b/src/containers/Tenant/Query/ExplainResult/ExplainResult.scss
@@ -43,10 +43,4 @@
&__elapsed-label {
margin-left: var(--g-spacing-3);
}
-
- &__stop-button {
- &_error {
- @include query-buttons-animations();
- }
- }
}
diff --git a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx
index fd02e3a50..8d0cc756a 100644
--- a/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx
+++ b/src/containers/Tenant/Query/ExplainResult/ExplainResult.tsx
@@ -1,7 +1,6 @@
import React from 'react';
-import {StopFill} from '@gravity-ui/icons';
-import {Button, Icon, RadioButton} from '@gravity-ui/uikit';
+import {RadioButton} from '@gravity-ui/uikit';
import {ClipboardButton} from '../../../../components/ClipboardButton';
import Divider from '../../../../components/Divider/Divider';
@@ -10,14 +9,15 @@ import EnableFullscreenButton from '../../../../components/EnableFullscreenButto
import Fullscreen from '../../../../components/Fullscreen/Fullscreen';
import {LoaderWrapper} from '../../../../components/LoaderWrapper/LoaderWrapper';
import {QueryExecutionStatus} from '../../../../components/QueryExecutionStatus';
-import type {PreparedExplainResponse} from '../../../../store/reducers/explainQuery/types';
import {disableFullscreen} from '../../../../store/reducers/fullscreen';
import type {ValueOf} from '../../../../types/common';
+import type {ExplainQueryResult} from '../../../../types/store/executeQuery';
import {cn} from '../../../../utils/cn';
import {getStringifiedData} from '../../../../utils/dataFormatters/dataFormatters';
import {useTypedDispatch} from '../../../../utils/hooks';
import {parseQueryErrorToString} from '../../../../utils/query';
import {PaneVisibilityToggleButtons} from '../../utils/paneVisibilityToggleHelpers';
+import {CancelQueryButton} from '../CancelQueryButton/CancelQueryButton';
import {QueryDuration} from '../QueryDuration/QueryDuration';
import {QuerySettingsBanner} from '../QuerySettingsBanner/QuerySettingsBanner';
import {isQueryCancelledError} from '../utils/isQueryCancelledError';
@@ -60,38 +60,29 @@ const explainOptions = [
interface ExplainResultProps {
theme: string;
- explain?: PreparedExplainResponse['plan'] & {DurationUs?: number};
- simplifiedPlan?: PreparedExplainResponse['simplifiedPlan'];
- ast?: string;
- loading?: boolean;
- cancelQueryLoading?: boolean;
+ result: ExplainQueryResult;
+ tenantName: string;
isResultsCollapsed?: boolean;
- error: unknown;
- cancelError: unknown;
onCollapseResults: VoidFunction;
onExpandResults: VoidFunction;
- onStopButtonClick: VoidFunction;
}
export function ExplainResult({
- explain,
- ast,
theme,
- error,
- cancelError,
- loading,
- cancelQueryLoading,
+ result,
+ tenantName,
onCollapseResults,
onExpandResults,
- onStopButtonClick,
isResultsCollapsed,
- simplifiedPlan,
}: ExplainResultProps) {
const dispatch = useTypedDispatch();
const [activeOption, setActiveOption] = React.useState(
EXPLAIN_OPTIONS_IDS.schema,
);
const [isPending, startTransition] = React.useTransition();
+ const {error, isLoading, queryId} = result;
+
+ const {plan: explain, ast, simplifiedPlan} = result.data || {};
React.useEffect(() => {
return () => {
@@ -169,9 +160,9 @@ export function ExplainResult({
-
+
- {!error && !loading && (
+ {!error && !isLoading && (
{explain?.DurationUs !== undefined && (
@@ -188,17 +179,10 @@ export function ExplainResult({
)}
- {loading ? (
+ {isLoading ? (
-
+
) : null}
@@ -219,8 +203,8 @@ export function ExplainResult({
/>
- {loading || isQueryCancelledError(error) ? null : }
-
+ {isLoading || isQueryCancelledError(error) ? null : }
+
{renderContent()}
diff --git a/src/containers/Tenant/Query/ExplainResult/i18n/en.json b/src/containers/Tenant/Query/ExplainResult/i18n/en.json
index 1befd1a5d..15bd21a2e 100644
--- a/src/containers/Tenant/Query/ExplainResult/i18n/en.json
+++ b/src/containers/Tenant/Query/ExplainResult/i18n/en.json
@@ -5,6 +5,5 @@
"action.explain-plan": "Explain Plan",
"action.json": "JSON",
"action.ast": "AST",
- "action.copy": "Copy {{activeOption}}",
- "action.stop": "Stop"
+ "action.copy": "Copy {{activeOption}}"
}
diff --git a/src/containers/Tenant/Query/Query.tsx b/src/containers/Tenant/Query/Query.tsx
index 63f61fe83..1e78eae8e 100644
--- a/src/containers/Tenant/Query/Query.tsx
+++ b/src/containers/Tenant/Query/Query.tsx
@@ -2,7 +2,6 @@ import React from 'react';
import {Helmet} from 'react-helmet-async';
-import NotRenderUntilFirstVisible from '../../../components/NotRenderUntilFirstVisible/NotRenderUntilFirstVisible';
import {changeUserInput} from '../../../store/reducers/executeQuery';
import {TENANT_QUERY_TABS_ID} from '../../../store/reducers/tenant/constants';
import type {EPathType} from '../../../types/api/schema';
@@ -40,19 +39,20 @@ export const Query = (props: QueryProps) => {
);
const renderContent = () => {
- return (
-
-
-
-
-
-
-
-
-
-
-
- );
+ switch (queryTab) {
+ case TENANT_QUERY_TABS_ID.newQuery: {
+ return ;
+ }
+ case TENANT_QUERY_TABS_ID.history: {
+ return ;
+ }
+ case TENANT_QUERY_TABS_ID.saved: {
+ return ;
+ }
+ default: {
+ return null;
+ }
+ }
};
return (
diff --git a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx
index 576249e9c..383217728 100644
--- a/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx
+++ b/src/containers/Tenant/Query/QueryEditor/QueryEditor.tsx
@@ -9,23 +9,22 @@ import {v4 as uuidv4} from 'uuid';
import {MonacoEditor} from '../../../../components/MonacoEditor/MonacoEditor';
import SplitPane from '../../../../components/SplitPane';
import type {RootState} from '../../../../store';
-import {cancelQueryApi} from '../../../../store/reducers/cancelQuery';
import {useTracingLevelOptionAvailable} from '../../../../store/reducers/capabilities/hooks';
import {
executeQueryApi,
goToNextQuery,
goToPreviousQuery,
saveQueryToHistory,
+ setQueryResult,
setTenantPath,
} from '../../../../store/reducers/executeQuery';
import {explainQueryApi} from '../../../../store/reducers/explainQuery/explainQuery';
-import type {PreparedExplainResponse} from '../../../../store/reducers/explainQuery/types';
import {setQueryAction} from '../../../../store/reducers/queryActions/queryActions';
import {setShowPreview} from '../../../../store/reducers/schema/schema';
import type {EPathType} from '../../../../types/api/schema';
-import type {ValueOf} from '../../../../types/common';
-import type {ExecuteQueryState} from '../../../../types/store/executeQuery';
-import type {IQueryResult, QueryAction} from '../../../../types/store/query';
+import {ResultType} from '../../../../types/store/executeQuery';
+import type {ExecuteQueryState, QueryResult} from '../../../../types/store/executeQuery';
+import type {QueryAction} from '../../../../types/store/query';
import {cn} from '../../../../utils/cn';
import {
DEFAULT_IS_QUERY_RESULT_COLLAPSED,
@@ -57,10 +56,6 @@ import {getKeyBindings} from './keybindings';
import './QueryEditor.scss';
const CONTEXT_MENU_GROUP_ID = 'navigation';
-const RESULT_TYPES = {
- EXECUTE: 'execute',
- EXPLAIN: 'explain',
-} as const;
const b = cn('query-editor');
@@ -78,6 +73,7 @@ interface QueryEditorProps {
goToPreviousQuery: (...args: Parameters) => void;
setTenantPath: (...args: Parameters) => void;
setQueryAction: (...args: Parameters) => void;
+ setQueryResult: (...args: Parameters) => void;
executeQuery: ExecuteQueryState;
theme: string;
type?: EPathType;
@@ -96,12 +92,13 @@ function QueryEditor(props: QueryEditorProps) {
type,
theme,
changeUserInput,
+ setQueryResult,
showPreview,
} = props;
const {tenantPath: savedPath} = executeQuery;
- const [resultType, setResultType] = React.useState>();
- const [isResultLoaded, setIsResultLoaded] = React.useState(false);
+ const isResultLoaded = Boolean(executeQuery.result);
+
const [querySettings] = useQueryExecutionSettings();
const enableTracingLevel = useTracingLevelOptionAvailable();
const [lastQueryExecutionSettings, setLastQueryExecutionSettings] =
@@ -113,18 +110,18 @@ function QueryEditor(props: QueryEditorProps) {
LAST_USED_QUERY_ACTION_KEY,
);
- const [sendExecuteQuery, executeQueryResult] = executeQueryApi.useExecuteQueryMutation();
- const [sendExplainQuery, explainQueryResult] = explainQueryApi.useExplainQueryMutation();
- const [sendCancelQuery, cancelQueryResult] = cancelQueryApi.useCancelQueryMutation();
+ const [sendExecuteQuery] = executeQueryApi.useExecuteQueryMutation();
+ const [sendExplainQuery] = explainQueryApi.useExplainQueryMutation();
React.useEffect(() => {
if (savedPath !== tenantName) {
if (savedPath) {
changeUserInput({input: ''});
+ setQueryResult();
}
setPath(tenantName);
}
- }, [changeUserInput, setPath, tenantName, savedPath]);
+ }, [changeUserInput, setPath, setQueryResult, tenantName, savedPath]);
const [resultVisibilityState, dispatchResultVisibilityState] = React.useReducer(
paneVisibilityToggleReducerCreator(DEFAULT_IS_QUERY_RESULT_COLLAPSED),
@@ -161,7 +158,6 @@ function QueryEditor(props: QueryEditorProps) {
setLastQueryExecutionSettings(querySettings);
}
const queryId = uuidv4();
- setResultType(RESULT_TYPES.EXECUTE);
sendExecuteQuery({
query,
@@ -171,9 +167,8 @@ function QueryEditor(props: QueryEditorProps) {
enableTracingLevel,
queryId,
});
- setIsResultLoaded(true);
+
props.setShowPreview(false);
- cancelQueryResult.reset();
// Don't save partial queries in history
if (!text) {
@@ -200,7 +195,6 @@ function QueryEditor(props: QueryEditorProps) {
}
const queryId = uuidv4();
- setResultType(RESULT_TYPES.EXPLAIN);
sendExplainQuery({
query: input,
@@ -210,23 +204,11 @@ function QueryEditor(props: QueryEditorProps) {
queryId,
});
- setIsResultLoaded(true);
props.setShowPreview(false);
- cancelQueryResult.reset();
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
});
- const currentQueryId = executeQueryResult.isLoading
- ? executeQueryResult.originalArgs?.queryId
- : explainQueryResult.originalArgs?.queryId;
-
- const handleStopButtonClick = React.useCallback(() => {
- if (currentQueryId) {
- sendCancelQuery({queryId: currentQueryId, database: tenantName});
- }
- }, [currentQueryId, sendCancelQuery, tenantName]);
-
const handleSendQuery = useEventHandler(() => {
if (lastUsedQueryAction === QUERY_ACTIONS.explain) {
handleGetExplainQueryClick();
@@ -335,7 +317,7 @@ function QueryEditor(props: QueryEditorProps) {
| undefined;
+ result?: QueryResult;
tenantName: string;
path: string;
showPreview?: boolean;
}
function Result({
- executeQueryData,
- executeQueryError,
- cancelQueryError,
- explainQueryData,
- explainQueryError,
- explainQueryLoading,
- executeResultLoading,
- cancelQueryLoading,
resultVisibilityState,
onExpandResultHandler,
onCollapseResultHandler,
- onStopButtonClick,
type,
theme,
- resultType,
+ result,
tenantName,
path,
showPreview,
@@ -466,40 +421,28 @@ function Result({
return ;
}
- if (resultType === RESULT_TYPES.EXECUTE) {
+ if (result?.type === ResultType.EXECUTE) {
return (
);
}
- if (resultType === RESULT_TYPES.EXPLAIN) {
- const {plan, ast, simplifiedPlan} = explainQueryData || {};
-
+ if (result?.type === ResultType.EXPLAIN) {
return (
);
}
diff --git a/src/containers/Tenant/Query/i18n/en.json b/src/containers/Tenant/Query/i18n/en.json
index 36c36e235..1af598ba4 100644
--- a/src/containers/Tenant/Query/i18n/en.json
+++ b/src/containers/Tenant/Query/i18n/en.json
@@ -49,6 +49,7 @@
"action.previous-query": "Previous query in history",
"action.next-query": "Next query in history",
"action.save-query": "Save query",
+ "action.stop": "Stop",
"filter.text.placeholder": "Search by query text...",
diff --git a/src/store/reducers/cancelQuery.ts b/src/store/reducers/cancelQuery.ts
index cd65af085..3999f013d 100644
--- a/src/store/reducers/cancelQuery.ts
+++ b/src/store/reducers/cancelQuery.ts
@@ -29,6 +29,7 @@ export const cancelQueryApi = api.injectEndpoints({
}
const data = parseQueryAPIExecuteResponse(response);
+
return {data};
} catch (error) {
return {error};
diff --git a/src/store/reducers/executeQuery.ts b/src/store/reducers/executeQuery.ts
index 449b6cb49..b73d611f5 100644
--- a/src/store/reducers/executeQuery.ts
+++ b/src/store/reducers/executeQuery.ts
@@ -3,18 +3,15 @@ import type {Reducer} from '@reduxjs/toolkit';
import {settingsManager} from '../../services/settings';
import {TracingLevelNumber} from '../../types/api/query';
import type {ExecuteActions, Schemas} from '../../types/api/query';
+import {ResultType} from '../../types/store/executeQuery';
import type {
ExecuteQueryAction,
ExecuteQueryState,
ExecuteQueryStateSlice,
QueryInHistory,
+ QueryResult,
} from '../../types/store/executeQuery';
-import type {
- IQueryResult,
- QueryRequestParams,
- QuerySettings,
- QuerySyntax,
-} from '../../types/store/query';
+import type {QueryRequestParams, QuerySettings, QuerySyntax} from '../../types/store/query';
import {QUERIES_HISTORY_KEY} from '../../utils/constants';
import {QUERY_SYNTAX, isQueryErrorResponse, parseQueryAPIExecuteResponse} from '../../utils/query';
import {isNumeric} from '../../utils/utils';
@@ -24,6 +21,7 @@ import {api} from './api';
const MAXIMUM_QUERIES_IN_HISTORY = 20;
const CHANGE_USER_INPUT = 'query/CHANGE_USER_INPUT';
+const SET_QUERY_RESULT = 'query/SET_QUERY_RESULT';
const SAVE_QUERY_TO_HISTORY = 'query/SAVE_QUERY_TO_HISTORY';
const UPDATE_QUERY_IN_HISTORY = 'query/UPDATE_QUERY_IN_HISTORY';
const SET_QUERY_HISTORY_FILTER = 'query/SET_QUERY_HISTORY_FILTER';
@@ -65,6 +63,13 @@ const executeQuery: Reducer = (
};
}
+ case SET_QUERY_RESULT: {
+ return {
+ ...state,
+ result: action.data,
+ };
+ }
+
case SAVE_QUERY_TO_HISTORY: {
const {queryText, queryId} = action.data;
@@ -190,7 +195,7 @@ interface QueryStats {
export const executeQueryApi = api.injectEndpoints({
endpoints: (build) => ({
- executeQuery: build.mutation({
+ executeQuery: build.mutation({
queryFn: async (
{
query,
@@ -205,6 +210,8 @@ export const executeQueryApi = api.injectEndpoints({
let action: ExecuteActions = 'execute';
let syntax: QuerySyntax = QUERY_SYNTAX.yql;
+ dispatch(setQueryResult({type: ResultType.EXECUTE, queryId, isLoading: true}));
+
if (querySettings.queryMode === 'pg') {
action = 'execute-query';
syntax = QUERY_SYNTAX.pg;
@@ -239,6 +246,14 @@ export const executeQueryApi = api.injectEndpoints({
);
if (isQueryErrorResponse(response)) {
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXECUTE,
+ error: response,
+ isLoading: false,
+ queryId,
+ }),
+ );
return {error: response};
}
@@ -257,8 +272,24 @@ export const executeQueryApi = api.injectEndpoints({
}
dispatch(updateQueryInHistory(queryStats, queryId));
- return {data};
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXECUTE,
+ data,
+ isLoading: false,
+ queryId,
+ }),
+ );
+ return {data: null};
} catch (error) {
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXECUTE,
+ error,
+ isLoading: false,
+ queryId,
+ }),
+ );
return {error};
}
},
@@ -281,6 +312,13 @@ export function updateQueryInHistory(stats: QueryStats, queryId: string) {
} as const;
}
+export function setQueryResult(data?: QueryResult) {
+ return {
+ type: SET_QUERY_RESULT,
+ data,
+ } as const;
+}
+
export const goToPreviousQuery = () => {
return {
type: GO_TO_PREVIOUS_QUERY,
diff --git a/src/store/reducers/explainQuery/explainQuery.ts b/src/store/reducers/explainQuery/explainQuery.ts
index 67cefb794..c0f35ab54 100644
--- a/src/store/reducers/explainQuery/explainQuery.ts
+++ b/src/store/reducers/explainQuery/explainQuery.ts
@@ -1,11 +1,12 @@
import {TracingLevelNumber} from '../../../types/api/query';
import type {ExplainActions} from '../../../types/api/query';
+import {ResultType} from '../../../types/store/executeQuery';
import type {QueryRequestParams, QuerySettings, QuerySyntax} from '../../../types/store/query';
import {QUERY_SYNTAX, isQueryErrorResponse} from '../../../utils/query';
import {isNumeric} from '../../../utils/utils';
import {api} from '../api';
+import {setQueryResult} from '../executeQuery';
-import type {PreparedExplainResponse} from './types';
import {prepareExplainResponse} from './utils';
interface ExplainQueryParams extends QueryRequestParams {
@@ -18,14 +19,16 @@ interface ExplainQueryParams extends QueryRequestParams {
export const explainQueryApi = api.injectEndpoints({
endpoints: (build) => ({
- explainQuery: build.mutation({
+ explainQuery: build.mutation({
queryFn: async (
{query, database, querySettings, enableTracingLevel, queryId},
- {signal},
+ {signal, dispatch},
) => {
let action: ExplainActions = 'explain';
let syntax: QuerySyntax = QUERY_SYNTAX.yql;
+ dispatch(setQueryResult({type: ResultType.EXPLAIN, queryId, isLoading: true}));
+
if (querySettings?.queryMode === 'pg') {
action = 'explain-query';
syntax = QUERY_SYNTAX.pg;
@@ -58,12 +61,36 @@ export const explainQueryApi = api.injectEndpoints({
);
if (isQueryErrorResponse(response)) {
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXPLAIN,
+ error: response,
+ queryId,
+ isLoading: false,
+ }),
+ );
return {error: response};
}
const data = prepareExplainResponse(response);
- return {data};
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXPLAIN,
+ data,
+ queryId,
+ isLoading: false,
+ }),
+ );
+ return {data: null};
} catch (error) {
+ dispatch(
+ setQueryResult({
+ type: ResultType.EXPLAIN,
+ error,
+ queryId,
+ isLoading: false,
+ }),
+ );
return {error};
}
},
diff --git a/src/store/reducers/explainQuery/types.ts b/src/store/reducers/explainQuery/types.ts
index 7b6cfbdea..29a3a3da2 100644
--- a/src/store/reducers/explainQuery/types.ts
+++ b/src/store/reducers/explainQuery/types.ts
@@ -15,6 +15,7 @@ export interface PreparedExplainResponse {
tables?: PlanTable[];
version?: string;
pristine?: QueryPlan | ScriptPlan;
+ DurationUs?: string | number;
};
simplifiedPlan?: {
plan?: SimplifiedPlanItem[];
diff --git a/src/types/store/executeQuery.ts b/src/types/store/executeQuery.ts
index bbfdae689..62b88db1e 100644
--- a/src/types/store/executeQuery.ts
+++ b/src/types/store/executeQuery.ts
@@ -4,9 +4,13 @@ import type {
goToPreviousQuery,
saveQueryToHistory,
setQueryHistoryFilter,
+ setQueryResult,
setTenantPath,
updateQueryInHistory,
} from '../../store/reducers/executeQuery';
+import type {PreparedExplainResponse} from '../../store/reducers/explainQuery/types';
+
+import type {IQueryResult} from './query';
export interface QueryInHistory {
queryId?: string;
@@ -16,9 +20,33 @@ export interface QueryInHistory {
durationUs?: string | number;
}
+export enum ResultType {
+ EXECUTE = 'execute',
+ EXPLAIN = 'explain',
+}
+
+interface CommonResultParams {
+ queryId: string;
+ isLoading: boolean;
+}
+
+export type ExecuteQueryResult = {
+ type: ResultType.EXECUTE;
+ data?: IQueryResult;
+ error?: unknown;
+} & CommonResultParams;
+
+export type ExplainQueryResult = {
+ type: ResultType.EXPLAIN;
+ data?: PreparedExplainResponse;
+ error?: unknown;
+} & CommonResultParams;
+
+export type QueryResult = ExecuteQueryResult | ExplainQueryResult;
+
export interface ExecuteQueryState {
- loading: boolean;
input: string;
+ result?: QueryResult;
history: {
// String type for backward compatibility
queries: QueryInHistory[];
@@ -32,6 +60,7 @@ export type ExecuteQueryAction =
| ReturnType
| ReturnType
| ReturnType
+ | ReturnType
| ReturnType
| ReturnType
| ReturnType