Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Query): support PostgreSQL syntax #515

Merged
merged 2 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,14 @@
@include flex-container();
@include table-styles;

&__table-row {
cursor: pointer;
}

&__query {
overflow: hidden;
flex-grow: 1;

cursor: pointer;
white-space: pre;
text-overflow: ellipsis;
}
Expand Down
53 changes: 42 additions & 11 deletions src/containers/Tenant/Query/QueriesHistory/QueriesHistory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import block from 'bem-cn-lite';

import DataTable, {Column} from '@gravity-ui/react-data-table';

import type {QueryInHistory} from '../../../../types/store/executeQuery';
import {TruncatedQuery} from '../../../../components/TruncatedQuery/TruncatedQuery';
import {setQueryTab} from '../../../../store/reducers/tenant/tenant';
import {selectQueriesHistory} from '../../../../store/reducers/executeQuery';
import {TENANT_QUERY_TABS_ID} from '../../../../store/reducers/tenant/constants';
import {useTypedSelector} from '../../../../utils/hooks';
import {useQueryModes, useTypedSelector} from '../../../../utils/hooks';
import {QUERY_MODES, QUERY_MODES_TITLES, QUERY_SYNTAX} from '../../../../utils/query';
import {MAX_QUERY_HEIGHT, QUERY_TABLE_SETTINGS} from '../../utils/constants';

import i18n from '../i18n';
Expand All @@ -22,24 +25,51 @@ interface QueriesHistoryProps {
function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
const dispatch = useDispatch();

const queriesHistory = useTypedSelector((state) => state.executeQuery.history.queries) ?? [];
const [queryMode, setQueryMode] = useQueryModes();

const queriesHistory = useTypedSelector(selectQueriesHistory);
const reversedHistory = [...queriesHistory].reverse();

const onQueryClick = (queryText: string) => {
changeUserInput({input: queryText});
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
const onQueryClick = (query: QueryInHistory) => {
let isQueryModeSet = true;

if (query.syntax === QUERY_SYNTAX.pg && queryMode !== QUERY_MODES.pg) {
isQueryModeSet = setQueryMode(
QUERY_MODES.pg,
i18n('history.cannot-set-mode', {mode: QUERY_MODES_TITLES[QUERY_MODES.pg]}),
);
} else if (query.syntax !== QUERY_SYNTAX.pg && queryMode === QUERY_MODES.pg) {
// Set query mode for queries with yql syntax
isQueryModeSet = setQueryMode(QUERY_MODES.script);
}

if (isQueryModeSet) {
changeUserInput({input: query.queryText});
dispatch(setQueryTab(TENANT_QUERY_TABS_ID.newQuery));
}
};

const columns: Column<string>[] = [
const columns: Column<QueryInHistory>[] = [
{
name: 'queryText',
header: 'Query Text',
render: ({row: query}) => (
<div className={b('query')}>
<TruncatedQuery value={query} maxQueryHeight={MAX_QUERY_HEIGHT} />
</div>
),
render: ({row}) => {
return (
<div className={b('query')}>
<TruncatedQuery value={row.queryText} maxQueryHeight={MAX_QUERY_HEIGHT} />
</div>
);
},
sortable: false,
},
{
name: 'syntax',
header: 'Syntax',
render: ({row}) => {
return row.syntax === QUERY_SYNTAX.pg ? 'PostgreSQL' : 'YQL';
},
sortable: false,
width: 200,
},
];

Expand All @@ -52,6 +82,7 @@ function QueriesHistory({changeUserInput}: QueriesHistoryProps) {
settings={QUERY_TABLE_SETTINGS}
emptyDataMessage={i18n('history.empty')}
onRowClick={(row) => onQueryClick(row)}
rowClassName={() => b('table-row')}
/>
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Tenant/Query/QueryEditor/QueryEditor.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ function QueryEditor(props) {

const {queries, currentIndex} = history;
if (input !== queries[currentIndex]) {
saveQueryToHistory(input);
saveQueryToHistory(input, mode);
}
dispatchResultVisibilityState(PaneVisibilityActionTypes.triggerExpand);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {Button, ButtonView, DropdownMenu} from '@gravity-ui/uikit';
import {useMemo} from 'react';

import type {QueryAction, QueryMode} from '../../../../types/store/query';
import {QUERY_MODES} from '../../../../utils/query';
import {QUERY_MODES, QUERY_MODES_TITLES} from '../../../../utils/query';
import {Icon} from '../../../../components/Icon';
import {LabelWithPopover} from '../../../../components/LabelWithPopover';

Expand All @@ -21,32 +21,36 @@ const b = block('ydb-query-editor-controls');

const OldQueryModeSelectorOptions = {
[QUERY_MODES.script]: {
title: 'YQL Script',
title: QUERY_MODES_TITLES[QUERY_MODES.script],
description: i18n('method-description.script'),
},
[QUERY_MODES.scan]: {
title: 'Scan',
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
description: i18n('method-description.scan'),
},
} as const;

const QueryModeSelectorOptions = {
[QUERY_MODES.script]: {
title: 'YQL Script',
title: QUERY_MODES_TITLES[QUERY_MODES.script],
description: i18n('method-description.script'),
},
[QUERY_MODES.scan]: {
title: 'Scan',
title: QUERY_MODES_TITLES[QUERY_MODES.scan],
description: i18n('method-description.scan'),
},
[QUERY_MODES.data]: {
title: 'Data',
title: QUERY_MODES_TITLES[QUERY_MODES.data],
description: i18n('method-description.data'),
},
[QUERY_MODES.query]: {
title: 'YQL - QueryService',
title: QUERY_MODES_TITLES[QUERY_MODES.query],
description: i18n('method-description.query'),
},
[QUERY_MODES.pg]: {
title: QUERY_MODES_TITLES[QUERY_MODES.pg],
description: i18n('method-description.pg'),
},
} as const;

interface QueryEditorControlsProps {
Expand Down
3 changes: 3 additions & 0 deletions src/containers/Tenant/Query/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"history.empty": "History is empty",
"saved.empty": "There are no saved queries",

"history.cannot-set-mode": "This query is available only with '{{mode}}' query mode. You need to turn in additional query modes in settings to enable it",

"delete-dialog.header": "Delete query",
"delete-dialog.question": "Are you sure you want to delete query",
"delete-dialog.delete": "Delete",
Expand All @@ -21,6 +23,7 @@
"method-description.scan": "Read-only queries, potentially reading a lot of data.\nAPI call: table.ExecuteScan",
"method-description.data": "DML queries for changing and fetching data in serialization mode.\nAPI call: table.executeDataQuery",
"method-description.query": "Any query. An experimental API call supposed to replace all existing methods.\nAPI Call: query.ExecuteScript",
"method-description.pg": "Queries in postgresql syntax.\nAPI call: query.ExecuteScript",

"query-duration.description": "Duration of server-side query execution"
}
3 changes: 3 additions & 0 deletions src/containers/Tenant/Query/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"history.empty": "История пуста",
"saved.empty": "Нет сохраненных запросов",

"history.cannot-set-mode": "Этот запрос доступен только в режиме '{{mode}}'. Вам необходимо включить дополнительные режимы выполнения запросов в настройках",

"delete-dialog.header": "Удалить запрос",
"delete-dialog.question": "Вы уверены что хотите удалить запрос",
"delete-dialog.delete": "Удалить",
Expand All @@ -21,6 +23,7 @@
"method-description.scan": "Только читающие запросы, потенциально читающие много данных.\nAPI call: table.ExecuteScan",
"method-description.data": "DML-запросы для изменения и выборки данных в режиме изоляции Serializable.\nAPI call: table.executeDataQuery",
"method-description.query": "Любые запросы. Экспериментальный перспективный метод, который в будущем заменит все остальные.\nAPI call: query.ExecuteScript",
"method-description.pg": "Запросы в синтаксисе postgresql.\nAPI call: query.ExecuteScript",

"query-duration.description": "Время выполнения запроса на стороне сервера"
}
16 changes: 6 additions & 10 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import type {DescribeTopicResult} from '../types/api/topic';
import type {TEvPDiskStateResponse} from '../types/api/pdisk';
import type {TEvVDiskStateResponse} from '../types/api/vdisk';
import type {TUserToken} from '../types/api/whoami';
import type {QuerySyntax} from '../types/store/query';
import type {ComputeApiRequestParams, NodesApiRequestParams} from '../store/reducers/nodes/types';
import type {StorageApiRequestParams} from '../store/reducers/storage/types';

Expand Down Expand Up @@ -281,17 +282,15 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
}
sendQuery<Action extends Actions, Schema extends Schemas = undefined>(
{
query,
database,
action,
stats,
schema,
...params
}: {
query?: string;
database?: string;
action?: Action;
stats?: string;
schema?: Schema;
syntax?: QuerySyntax;
},
{concurrentId}: AxiosOptions = {},
) {
Expand All @@ -303,12 +302,7 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
this.getPath(
`/viewer/json/query?timeout=${backendTimeout}${schema ? `&schema=${schema}` : ''}`,
),
{
query,
database,
action,
stats,
},
params,
{},
{
concurrentId,
Expand All @@ -320,13 +314,15 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
query: string,
database: string,
action: Action,
syntax?: QuerySyntax,
) {
return this.post<ExplainResponse<Action>>(
this.getPath('/viewer/json/query'),
{
query,
database,
action: action || 'explain',
syntax,
timeout: 600000,
},
{},
Expand Down
40 changes: 33 additions & 7 deletions src/store/reducers/executeQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import type {ExecuteActions} from '../../types/api/query';
import type {
ExecuteQueryAction,
ExecuteQueryState,
ExecuteQueryStateSlice,
MonacoHotKeyAction,
QueryInHistory,
} from '../../types/store/executeQuery';
import type {QueryRequestParams, QueryMode} from '../../types/store/query';
import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query';
import {getValueFromLS, parseJson} from '../../utils/utils';
import {QUERIES_HISTORY_KEY} from '../../utils/constants';
import {parseQueryAPIExecuteResponse} from '../../utils/query';
import {QUERY_MODES, QUERY_SYNTAX, parseQueryAPIExecuteResponse} from '../../utils/query';
import {parseQueryError} from '../../utils/error';
import '../../services/api';

Expand Down Expand Up @@ -87,8 +89,12 @@ const executeQuery: Reducer<ExecuteQueryState, ExecuteQueryAction> = (
}

case SAVE_QUERY_TO_HISTORY: {
const query = action.data;
const newQueries = [...state.history.queries, query].slice(
const queryText = action.data.queryText;

// Do not save explicit yql syntax value for easier further support (use yql by default)
const syntax = action.data.mode === QUERY_MODES.pg ? QUERY_SYNTAX.pg : undefined;

const newQueries = [...state.history.queries, {queryText, syntax}].slice(
state.history.queries.length >= MAXIMUM_QUERIES_IN_HISTORY ? 1 : 0,
);
window.localStorage.setItem(QUERIES_HISTORY_KEY, JSON.stringify(newQueries));
Expand Down Expand Up @@ -151,25 +157,34 @@ interface SendQueryParams extends QueryRequestParams {
}

export const sendExecuteQuery = ({query, database, mode}: SendQueryParams) => {
const action: ExecuteActions = mode ? `execute-${mode}` : 'execute';
let action: ExecuteActions = 'execute';
let syntax: QuerySyntax = QUERY_SYNTAX.yql;

if (mode === 'pg') {
action = 'execute-query';
syntax = QUERY_SYNTAX.pg;
} else if (mode) {
action = `execute-${mode}`;
}

return createApiRequest({
request: window.api.sendQuery({
schema: 'modern',
query,
database,
action,
syntax,
stats: 'profile',
}),
actions: SEND_QUERY,
dataHandler: parseQueryAPIExecuteResponse,
});
};

export const saveQueryToHistory = (query: string) => {
export const saveQueryToHistory = (queryText: string, mode: QueryMode) => {
return {
type: SAVE_QUERY_TO_HISTORY,
data: query,
data: {queryText, mode},
} as const;
};

Expand Down Expand Up @@ -206,4 +221,15 @@ export const setTenantPath = (value: string) => {
} as const;
};

export const selectQueriesHistory = (state: ExecuteQueryStateSlice): QueryInHistory[] => {
return state.executeQuery.history.queries.map((rawQuery) => {
if (typeof rawQuery === 'string') {
return {
queryText: rawQuery,
};
}
return rawQuery;
});
};

export default executeQuery;
16 changes: 12 additions & 4 deletions src/store/reducers/explainQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import type {
ExplainQueryState,
PreparedExplainResponse,
} from '../../types/store/explainQuery';
import type {QueryRequestParams, QueryMode} from '../../types/store/query';
import type {QueryRequestParams, QueryMode, QuerySyntax} from '../../types/store/query';

import {preparePlan} from '../../utils/prepareQueryExplain';
import {parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../utils/query';
import {QUERY_SYNTAX, parseQueryAPIExplainResponse, parseQueryExplainPlan} from '../../utils/query';
import {parseQueryError} from '../../utils/error';

import {createRequestActionTypes, createApiRequest} from '../utils';
Expand Down Expand Up @@ -103,10 +103,18 @@ interface ExplainQueryParams extends QueryRequestParams {
}

export const getExplainQuery = ({query, database, mode}: ExplainQueryParams) => {
const action: ExplainActions = mode ? `explain-${mode}` : 'explain';
let action: ExplainActions = 'explain';
let syntax: QuerySyntax = QUERY_SYNTAX.yql;

if (mode === 'pg') {
action = 'explain-query';
syntax = QUERY_SYNTAX.pg;
} else if (mode) {
action = `explain-${mode}`;
}

return createApiRequest({
request: window.api.getExplainQuery(query, database, action),
request: window.api.getExplainQuery(query, database, action, syntax),
actions: GET_EXPLAIN_QUERY,
dataHandler: (response): PreparedExplainResponse => {
const {plan: rawPlan, ast} = parseQueryAPIExplainResponse(response);
Expand Down
Loading
Loading