Skip to content

Commit 2010164

Browse files
CopilotRaubzeug
andauthored
feat(query): add multi-line Pragmas field to Query Settings Dialog (#2563)
Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: Elena Makarova <[email protected]>
1 parent 848f754 commit 2010164

File tree

13 files changed

+164
-5
lines changed

13 files changed

+164
-5
lines changed

src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettings.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ describe('getChangedQueryExecutionSettings', () => {
3535
limitRows: DEFAULT_QUERY_SETTINGS.limitRows,
3636
statisticsMode: STATISTICS_MODES.basic,
3737
tracingLevel: TRACING_LEVELS.basic,
38+
pragmas: 'PRAGMA TestPragma;',
3839
};
3940
const result = getChangedQueryExecutionSettings(currentSettings, DEFAULT_QUERY_SETTINGS);
4041
expect(result).toEqual([
@@ -43,6 +44,7 @@ describe('getChangedQueryExecutionSettings', () => {
4344
'timeout',
4445
'statisticsMode',
4546
'tracingLevel',
47+
'pragmas',
4648
]);
4749
});
4850
});

src/containers/Tenant/Query/QueryEditorControls/utils/getChangedQueryExecutionSettingsDescription.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ describe('getChangedQueryExecutionSettingsDescription', () => {
5757
limitRows: DEFAULT_QUERY_SETTINGS.limitRows,
5858
statisticsMode: STATISTICS_MODES.profile,
5959
tracingLevel: TRACING_LEVELS.diagnostic,
60+
pragmas: 'PRAGMA TestPragma;',
6061
};
6162

6263
const result = getChangedQueryExecutionSettingsDescription({
@@ -71,6 +72,7 @@ describe('getChangedQueryExecutionSettingsDescription', () => {
7172
[QUERY_SETTINGS_FIELD_SETTINGS.timeout.title]: '120',
7273
[QUERY_SETTINGS_FIELD_SETTINGS.statisticsMode.title]: STATISTICS_MODES_TITLES.profile,
7374
[QUERY_SETTINGS_FIELD_SETTINGS.tracingLevel.title]: TRACING_LEVELS_TITLES.diagnostic,
75+
[QUERY_SETTINGS_FIELD_SETTINGS.pragmas.title]: 'PRAGMA TestPragma;',
7476
});
7577
});
7678
});

src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
margin-right: var(--g-spacing-2);
3434
}
3535

36+
&__pragmas {
37+
width: 100%;
38+
}
39+
3640
&__postfix {
3741
margin-right: var(--g-spacing-2);
3842

src/containers/Tenant/Query/QuerySettingsDialog/QuerySettingsDialog.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22

3-
import {Button, Dialog, Flex, TextInput, Tooltip} from '@gravity-ui/uikit';
3+
import {Button, Dialog, Flex, TextArea, TextInput, Tooltip} from '@gravity-ui/uikit';
44
import {zodResolver} from '@hookform/resolvers/zod';
55
import {Controller, useForm} from 'react-hook-form';
66

@@ -220,6 +220,29 @@ function QuerySettingsForm({initialValues, onSubmit, onClose}: QuerySettingsForm
220220
/>
221221
</div>
222222
</Flex>
223+
<Flex direction="row" alignItems="flex-start" className={b('dialog-row')}>
224+
<label htmlFor="pragmas" className={b('field-title')}>
225+
{QUERY_SETTINGS_FIELD_SETTINGS.pragmas.title}
226+
</label>
227+
<div className={b('control-wrapper')}>
228+
<Controller
229+
name="pragmas"
230+
control={control}
231+
render={({field}) => (
232+
<TextArea
233+
id="pragmas"
234+
{...field}
235+
className={b('pragmas')}
236+
placeholder="PRAGMA OrderedColumns;"
237+
rows={3}
238+
validationState={errors.pragmas ? 'invalid' : undefined}
239+
errorMessage={errors.pragmas?.message}
240+
errorPlacement="inside"
241+
/>
242+
)}
243+
/>
244+
</div>
245+
</Flex>
223246
<Flex direction="row" alignItems="flex-start" className={b('dialog-row')}>
224247
<Controller
225248
name="timeout"

src/containers/Tenant/Query/QuerySettingsDialog/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,4 +151,7 @@ export const QUERY_SETTINGS_FIELD_SETTINGS = {
151151
limitRows: {
152152
title: formI18n('form.limit-rows'),
153153
},
154+
pragmas: {
155+
title: formI18n('form.pragmas'),
156+
},
154157
} as const;

src/containers/Tenant/Query/QuerySettingsDialog/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"form.statistics-mode": "Statistics collection mode",
77
"form.tracing-level": "Tracing level",
88
"form.limit-rows": "Limit rows",
9+
"form.pragmas": "Pragmas",
910
"button-done": "Save",
1011
"tooltip_plan-to-svg-statistics": "Statistics option is set to \"Full\" due to the enabled \"Execution plan\" experiment.\n To disable it, go to the \"Experiments\" section in the user settings.",
1112
"button-cancel": "Cancel",

src/containers/Tenant/Query/QuerySettingsDialog/i18n/ru.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"form.statistics-mode": "Режим сбора статистики",
77
"form.tracing-level": "Tracing level",
88
"form.limit-rows": "Лимит строк",
9+
"form.pragmas": "Прагмы",
910
"tooltip_plan-to-svg-statistics": "Опция статистики установлена в значение \"Full\" из-за включенного эксперимента \"Execution plan\".\n Чтобы отключить его, перейдите в раздел \"Experiments\" в настройках пользователя.",
1011
"button-done": "Готово",
1112
"button-cancel": "Отменить",
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// Integration test for pragmas functionality
2+
describe('Pragmas Integration Tests', () => {
3+
test('should correctly prepend pragmas to query for execution', () => {
4+
// Test the actual function logic that would be used in query execution
5+
const testQuery = 'SELECT * FROM users WHERE id = 1;';
6+
const testPragmas = 'PRAGMA OrderedColumns;';
7+
8+
// This is the logic from our prepareQueryWithPragmas function
9+
const prepareQuery = (query: string, pragmas?: string): string => {
10+
if (!pragmas || !pragmas.trim()) {
11+
return query;
12+
}
13+
14+
const trimmedPragmas = pragmas.trim();
15+
const separator = trimmedPragmas.endsWith(';') ? '\n\n' : ';\n\n';
16+
17+
return `${trimmedPragmas}${separator}${query}`;
18+
};
19+
20+
const result = prepareQuery(testQuery, testPragmas);
21+
22+
expect(result).toBe('PRAGMA OrderedColumns;\n\nSELECT * FROM users WHERE id = 1;');
23+
});
24+
25+
test('should handle multiple pragmas correctly', () => {
26+
const query = 'SELECT COUNT(*) FROM orders;';
27+
const pragmas = `PRAGMA OrderedColumns;
28+
PRAGMA AnsiOptionalAS;
29+
PRAGMA DisableAnsiRankForNullableKeys;`;
30+
31+
const prepareQuery = (query: string, pragmas?: string): string => {
32+
if (!pragmas || !pragmas.trim()) {
33+
return query;
34+
}
35+
36+
const trimmedPragmas = pragmas.trim();
37+
const separator = trimmedPragmas.endsWith(';') ? '\n\n' : ';\n\n';
38+
39+
return `${trimmedPragmas}${separator}${query}`;
40+
};
41+
42+
const result = prepareQuery(query, pragmas);
43+
44+
expect(result).toContain('PRAGMA OrderedColumns;');
45+
expect(result).toContain('PRAGMA AnsiOptionalAS;');
46+
expect(result).toContain('PRAGMA DisableAnsiRankForNullableKeys;');
47+
expect(result).toContain('SELECT COUNT(*) FROM orders;');
48+
});
49+
50+
test('should preserve query when pragmas are empty', () => {
51+
const query = 'EXPLAIN SELECT * FROM table;';
52+
const pragmas = '';
53+
54+
const prepareQuery = (query: string, pragmas?: string): string => {
55+
if (!pragmas || !pragmas.trim()) {
56+
return query;
57+
}
58+
59+
const trimmedPragmas = pragmas.trim();
60+
const separator = trimmedPragmas.endsWith(';') ? '\n\n' : ';\n\n';
61+
62+
return `${trimmedPragmas}${separator}${query}`;
63+
};
64+
65+
const result = prepareQuery(query, pragmas);
66+
67+
expect(result).toBe(query);
68+
});
69+
});
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import {prepareQueryWithPragmas} from '../utils';
2+
3+
describe('prepareQueryWithPragmas', () => {
4+
test('Should prepend pragmas correctly', () => {
5+
// This tests the behavior through the actual query API
6+
const pragma = 'PRAGMA OrderedColumns;';
7+
const query = 'SELECT * FROM table;';
8+
const expectedResult = prepareQueryWithPragmas(query, pragma);
9+
10+
// The actual test would be integration test with the query API
11+
expect(expectedResult).toBe('PRAGMA OrderedColumns;\n\nSELECT * FROM table;');
12+
});
13+
14+
test('Should handle empty pragmas', () => {
15+
const query = 'SELECT * FROM table;';
16+
const pragma = '';
17+
const expectedResult = prepareQueryWithPragmas(query, pragma);
18+
19+
// When pragma is empty, query should remain unchanged
20+
expect(expectedResult).toBe('SELECT * FROM table;');
21+
});
22+
23+
test('Should handle pragmas without semicolon', () => {
24+
const pragma = 'PRAGMA OrderedColumns';
25+
const query = 'SELECT * FROM table;';
26+
const expectedResult = prepareQueryWithPragmas(query, pragma);
27+
28+
expect(expectedResult).toBe('PRAGMA OrderedColumns;\n\nSELECT * FROM table;');
29+
});
30+
});

src/store/reducers/query/query.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
setStreamSession as setStreamSessionReducer,
1919
} from './streamingReducers';
2020
import type {QueryResult, QueryState} from './types';
21-
import {getActionAndSyntaxFromQueryMode, getQueryInHistory} from './utils';
21+
import {getActionAndSyntaxFromQueryMode, getQueryInHistory, prepareQueryWithPragmas} from './utils';
2222

2323
const MAXIMUM_QUERIES_IN_HISTORY = 20;
2424

@@ -231,6 +231,8 @@ export const queryApi = api.injectEndpoints({
231231
querySettings?.queryMode,
232232
);
233233

234+
const finalQuery = prepareQueryWithPragmas(query, querySettings.pragmas);
235+
234236
try {
235237
let streamDataChunkBatch: StreamDataChunk[] = [];
236238
let batchTimeout: number | null = null;
@@ -245,7 +247,7 @@ export const queryApi = api.injectEndpoints({
245247

246248
await window.api.streaming.streamQuery(
247249
{
248-
query,
250+
query: finalQuery,
249251
database,
250252
action,
251253
syntax,
@@ -342,11 +344,13 @@ export const queryApi = api.injectEndpoints({
342344
querySettings?.queryMode,
343345
);
344346

347+
const finalQuery = prepareQueryWithPragmas(query, querySettings.pragmas);
348+
345349
try {
346350
const timeStart = Date.now();
347351
const response = await window.api.viewer.sendQuery(
348352
{
349-
query,
353+
query: finalQuery,
350354
database,
351355
action,
352356
syntax,

0 commit comments

Comments
 (0)