Skip to content

Commit fb3a2fb

Browse files
committed
fix: save progress
1 parent 471b72d commit fb3a2fb

File tree

10 files changed

+355
-90
lines changed

10 files changed

+355
-90
lines changed

src/containers/Tenant/Query/QueryResult/QueryResultViewer.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,6 @@ export function QueryResultViewer({
109109

110110
const {error, isLoading, queryId, data = {}, streaming} = result;
111111
const {preparedPlan, simplifiedPlan, stats, resultSets, ast} = data;
112-
const isStreaming = streaming?.active;
113112

114113
React.useEffect(() => {
115114
if (resultType === 'execute' && !EXECUTE_SECTIONS.includes(activeSection)) {
@@ -218,17 +217,20 @@ export function QueryResultViewer({
218217
return <QueryResultError error={error} />;
219218
}
220219
if (activeSection === RESULT_OPTIONS_IDS.result) {
221-
return isStreaming ? (
220+
if (!streaming) {
221+
return (
222+
<ResultSetsViewer
223+
type="regular"
224+
resultSets={resultSets || []}
225+
selectedResultSet={selectedResultSet}
226+
setSelectedResultSet={setSelectedResultSet}
227+
/>
228+
);
229+
}
230+
return (
222231
<ResultSetsViewer
223232
type="streaming"
224-
chunks={streaming?.chunks || []}
225-
selectedResultSet={selectedResultSet}
226-
setSelectedResultSet={setSelectedResultSet}
227-
/>
228-
) : (
229-
<ResultSetsViewer
230-
type="regular"
231-
resultSets={resultSets || []}
233+
streaming={streaming}
232234
selectedResultSet={selectedResultSet}
233235
setSelectedResultSet={setSelectedResultSet}
234236
/>
@@ -272,8 +274,8 @@ export function QueryResultViewer({
272274
const renderLeftControls = () => {
273275
return (
274276
<div className={b('controls-left')}>
275-
<QueryExecutionStatus error={error} loading={isLoading || isStreaming} />
276-
{!error && !isLoading && !isStreaming && (
277+
<QueryExecutionStatus error={error} loading={isLoading} />
278+
{!error && !isLoading && (
277279
<React.Fragment>
278280
{valueIsDefined(stats?.DurationUs) ? (
279281
<QueryDuration duration={Number(stats.DurationUs)} />

src/containers/Tenant/Query/QueryResult/components/ResultSetsViewer/ResultSetsViewer.tsx

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import {Tabs, Text} from '@gravity-ui/uikit';
22

33
import {QueryResultTable} from '../../../../../../components/QueryResultTable';
4-
import type {ExecuteQueryResponse} from '../../../../../../types/api/query';
5-
import type {ParsedResultSet} from '../../../../../../types/store/query';
4+
import type {ParsedResultSet, StreamingState} from '../../../../../../types/store/query';
65
import {getArray} from '../../../../../../utils';
76
import {cn} from '../../../../../../utils/cn';
87
import i18n from '../../i18n';
@@ -23,37 +22,57 @@ interface RegularResultSetsProps extends BaseResultSetsViewerProps {
2322

2423
interface StreamingResultSetsProps extends BaseResultSetsViewerProps {
2524
type: 'streaming';
26-
chunks: ExecuteQueryResponse[];
25+
streaming: StreamingState;
2726
}
2827

2928
type ResultSetsViewerProps = RegularResultSetsProps | StreamingResultSetsProps;
3029

3130
export function ResultSetsViewer(props: ResultSetsViewerProps) {
31+
console.log('[ResultSetsViewer] Rendering with props:', {
32+
type: props.type,
33+
selectedResultSet: props.selectedResultSet,
34+
isStreaming: props.type === 'streaming',
35+
streamingState:
36+
props.type === 'streaming'
37+
? {
38+
hasColumns: Boolean(props.streaming.columns),
39+
rowCount: props.streaming.rows?.length,
40+
active: props.streaming.active,
41+
progress: props.streaming.progress,
42+
}
43+
: undefined,
44+
regularState:
45+
props.type === 'regular'
46+
? {
47+
resultSetsCount: props.resultSets.length,
48+
currentSetColumns: Boolean(
49+
props.resultSets[props.selectedResultSet]?.columns,
50+
),
51+
currentSetRows: props.resultSets[props.selectedResultSet]?.result?.length,
52+
}
53+
: undefined,
54+
});
55+
3256
const {selectedResultSet, setSelectedResultSet} = props;
3357

34-
const resultsSetsCount =
35-
props.type === 'streaming' ? props.chunks.length : props.resultSets.length;
58+
const resultsSetsCount = props.type === 'streaming' ? 1 : props.resultSets.length;
3659

3760
const currentResult =
3861
props.type === 'streaming'
3962
? {
40-
result:
41-
props.chunks[selectedResultSet]?.result?.[0]?.rows?.map((row) => {
42-
if (!Array.isArray(row)) {
43-
return row || {};
44-
}
45-
return row.reduce<Record<string, any>>((obj, val, idx) => {
46-
const columnName =
47-
props.chunks[0]?.result?.[0]?.columns?.[idx]?.name ||
48-
`column${idx}`;
49-
return {...obj, [columnName]: val};
50-
}, {});
51-
}) || [],
52-
columns: props.chunks[0]?.result?.[0]?.columns,
63+
result: props.streaming.rows,
64+
columns: props.streaming.columns,
5365
truncated: false,
5466
}
5567
: props.resultSets[selectedResultSet];
5668

69+
console.log('[ResultSetsViewer] Current result:', {
70+
hasResult: Boolean(currentResult),
71+
hasColumns: Boolean(currentResult?.columns),
72+
rowCount: currentResult?.result?.length,
73+
truncated: currentResult?.truncated,
74+
});
75+
5776
const renderTabs = () => {
5877
if (resultsSetsCount > 1) {
5978
const tabsItems = getArray(resultsSetsCount).map((item) => ({
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type {ParsedQueryResponseChunk, QueryResponseChunk} from '../../../../types/store/streaming';
2+
3+
export function parseQueryResponseChunk(chunk: QueryResponseChunk): ParsedQueryResponseChunk {
4+
console.log('[QueryResponseParser] Parsing response chunk:', {
5+
hasPlan: Boolean(chunk.content.plan),
6+
hasStats: Boolean(chunk.content.stats),
7+
});
8+
9+
const {plan, stats} = chunk.content;
10+
11+
return {
12+
type: 'response',
13+
plan,
14+
stats,
15+
};
16+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/* eslint-disable camelcase */
2+
import type {ParsedSessionChunk, SessionChunk} from '../../../../types/store/streaming';
3+
4+
export function parseSessionChunk(chunk: SessionChunk): ParsedSessionChunk {
5+
console.log('[SessionParser] Parsing session chunk:', {
6+
nodeId: chunk.content.meta.node_id,
7+
queryId: chunk.content.meta.query_id,
8+
});
9+
10+
const {node_id, query_id, session_id} = chunk.content.meta;
11+
return {
12+
type: 'session',
13+
nodeId: node_id,
14+
queryId: query_id,
15+
sessionId: session_id,
16+
};
17+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import type {ParsedStreamDataChunk, StreamDataChunk} from '../../../../types/store/streaming';
2+
3+
function convertRowToKeyValue(
4+
row: any[],
5+
columns: Array<{name: string; type: string}>,
6+
): Record<string, any> {
7+
return columns.reduce((obj: Record<string, any>, col: {name: string}, idx: number) => {
8+
obj[col.name] = row[idx];
9+
return obj;
10+
}, {});
11+
}
12+
13+
let storedColumns: Array<{name: string; type: string}> | null = null;
14+
15+
export function parseStreamDataChunk(chunk: StreamDataChunk): ParsedStreamDataChunk {
16+
console.log('[StreamDataParser] Parsing data chunk:', {
17+
seqNo: chunk.content.meta.seq_no,
18+
hasColumns: Boolean(chunk.content.result.columns),
19+
rowCount: chunk.content.result.rows?.length,
20+
});
21+
22+
// Store columns from first chunk or use stored columns
23+
if (chunk.content.result.columns) {
24+
storedColumns = chunk.content.result.columns;
25+
}
26+
27+
if (!storedColumns) {
28+
throw new Error('No columns available for parsing rows');
29+
}
30+
31+
const rows = chunk.content.result.rows.map((row) => convertRowToKeyValue(row, storedColumns!));
32+
33+
console.log('[StreamDataParser] Processed rows:', {
34+
rowCount: rows.length,
35+
firstRow: rows[0],
36+
});
37+
38+
return {
39+
type: 'data',
40+
seqNo: chunk.content.meta.seq_no,
41+
resultSet: {
42+
columns: storedColumns,
43+
result: rows,
44+
},
45+
};
46+
}
47+
48+
// Reset stored columns when starting a new query
49+
export function resetStoredColumns() {
50+
storedColumns = null;
51+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import type {ParsedChunk, StreamingChunk} from '../../../types/store/streaming';
2+
3+
import {parseQueryResponseChunk} from './parsers/queryResponseParser';
4+
import {parseSessionChunk} from './parsers/sessionParser';
5+
import {parseStreamDataChunk, resetStoredColumns} from './parsers/streamDataParser';
6+
7+
export function processStreamingChunk(chunk: StreamingChunk): ParsedChunk | null {
8+
console.log('[StreamChunk] Processing chunk:', {
9+
event: chunk.content.meta.event,
10+
partNumber: chunk.part_number,
11+
totalParts: chunk.total_parts,
12+
});
13+
14+
try {
15+
const event = chunk.content.meta.event;
16+
17+
switch (event) {
18+
case 'SessionCreated': {
19+
// Reset stored columns when starting a new query session
20+
resetStoredColumns();
21+
return parseSessionChunk(chunk as any);
22+
}
23+
24+
case 'StreamData':
25+
return parseStreamDataChunk(chunk as any);
26+
27+
case 'QueryResponse':
28+
return parseQueryResponseChunk(chunk as any);
29+
30+
default:
31+
console.warn('[StreamChunk] Unknown chunk type:', event, chunk);
32+
return null;
33+
}
34+
} catch (error) {
35+
console.error('[StreamChunk] Error processing chunk:', error);
36+
return null;
37+
}
38+
}

0 commit comments

Comments
 (0)