Line data Source code
1 : import { QueryExecutionState, QueryExecutionStatus, queryIsDone } from '../query/query_execution_state.js';
2 : import { IndicatorStatus } from '../ui/foundations/status_indicator.js';
3 : import { summarizeQueryProgress } from '../views/query_progress/query_progress_model.js';
4 :
5 : /// Which source produced the status shown in the bar.
6 1 : export const enum EntryStatusKind {
7 1 : Query = 0,
8 1 : Idle = 1,
9 1 : }
10 :
11 : /// The presentation-ready status for one session entry, derived from its latest query execution.
12 : /// Every entry has a status, including entries that
13 : /// have not run yet, so the server card always has a stable status header.
14 : export interface EntryStatus {
15 : kind: EntryStatusKind;
16 : /// The spinner/check/cross state.
17 : indicator: IndicatorStatus;
18 : /// The single-line query status message.
19 : message: string;
20 : /// The trace to reveal in the footer log when the bar is clicked.
21 : traceId: number | null;
22 : /// Everything known about a failed query, shown in the status bar's error-details overlay.
23 : /// Null for non-error statuses.
24 : errorDetail: Record<string, unknown> | null;
25 : }
26 :
27 1 : function compactObject(value: Record<string, unknown>): Record<string, unknown> {
28 1 : return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry != null));
29 1 : }
30 :
31 1 : function getQueryErrorDetail(query: QueryExecutionState, message: string): Record<string, unknown> {
32 1 : const error = query.error;
33 1 : return compactObject({
34 1 : status: query.status === QueryExecutionStatus.FAILED ? 'failed' : 'cancelled',
35 1 : message,
36 1 : target: error?.target,
37 1 : queryId: query.queryId,
38 1 : traceId: query.traceId,
39 1 : statementIndex: query.statementIndex,
40 1 : statementCount: query.statementCount,
41 1 : query: query.queryText,
42 1 : metadata: query.queryMetadata,
43 1 : details: error != null && Object.keys(error.keyValues).length > 0 ? error.keyValues : undefined,
44 1 : });
45 1 : }
46 :
47 1 : function getStatementStatusText(message: string, statementIndex: number | null, statementCount: number | null): string {
48 1 : return statementIndex != null && statementCount != null && statementCount > 1
49 1 : ? `Statement ${statementIndex} of ${statementCount}: ${message}`
50 1 : : message;
51 1 : }
52 :
53 : /// Human-readable label for a query execution status. Shared by the feed status bar and the Details
54 : /// query status panel so both stay in sync.
55 1 : export function getQueryStatusText(
56 1 : status: QueryExecutionStatus,
57 1 : statementIndex: number | null = null,
58 1 : statementCount: number | null = null,
59 1 : statementSucceeded: boolean = false,
60 1 : ): string {
61 1 : const hasMultipleStatements = statementIndex != null && statementCount != null && statementCount > 1;
62 1 : if (hasMultipleStatements && statementSucceeded && !queryIsDone(status)) {
63 1 : return `Statement ${statementIndex} of ${statementCount} executed successfully`;
64 : }
65 1 : switch (status) {
66 1 : case QueryExecutionStatus.REQUESTED:
67 1 : return hasMultipleStatements
68 1 : ? `Requested statement ${statementIndex} of ${statementCount}`
69 1 : : 'Requested query';
70 1 : case QueryExecutionStatus.PREPARING:
71 1 : return hasMultipleStatements
72 1 : ? `Preparing statement ${statementIndex} of ${statementCount}`
73 0 : : 'Preparing query';
74 1 : case QueryExecutionStatus.SENDING:
75 1 : return hasMultipleStatements
76 1 : ? `Sending statement ${statementIndex} of ${statementCount}`
77 1 : : 'Sending query';
78 1 : case QueryExecutionStatus.QUEUED:
79 1 : return hasMultipleStatements
80 1 : ? `Statement ${statementIndex} of ${statementCount} queued`
81 0 : : 'Queued query';
82 1 : case QueryExecutionStatus.RUNNING:
83 1 : return hasMultipleStatements
84 1 : ? `Executing statement ${statementIndex} of ${statementCount}`
85 1 : : 'Executing query';
86 1 : case QueryExecutionStatus.RECEIVED_FIRST_BATCH:
87 1 : return hasMultipleStatements
88 1 : ? `Executing statement ${statementIndex} of ${statementCount}, fetching results`
89 1 : : 'Executing query, fetching results';
90 1 : case QueryExecutionStatus.RECEIVED_ALL_BATCHES:
91 1 : return hasMultipleStatements
92 1 : ? `Executing statement ${statementIndex} of ${statementCount}, received all results`
93 0 : : 'Executing query, received all results';
94 1 : case QueryExecutionStatus.PROCESSING_RESULTS:
95 1 : return hasMultipleStatements
96 1 : ? `Processing results for statement ${statementIndex} of ${statementCount}`
97 1 : : 'Processing results';
98 1 : case QueryExecutionStatus.PROCESSED_RESULTS:
99 1 : return hasMultipleStatements
100 1 : ? `Processed results for statement ${statementIndex} of ${statementCount}`
101 1 : : 'Processed results';
102 1 : case QueryExecutionStatus.FAILED:
103 1 : return hasMultipleStatements
104 1 : ? `Statement ${statementIndex} of ${statementCount} execution failed`
105 0 : : 'Statement execution failed';
106 1 : case QueryExecutionStatus.CANCELLED:
107 1 : return hasMultipleStatements
108 1 : ? `Statement ${statementIndex} of ${statementCount} execution was cancelled`
109 0 : : 'Statement execution was cancelled';
110 1 : case QueryExecutionStatus.SUCCEEDED:
111 1 : return hasMultipleStatements
112 1 : ? `Statement ${statementIndex} of ${statementCount} executed successfully`
113 1 : : 'Statement executed successfully';
114 1 : }
115 1 : }
116 :
117 : /// Derive the status bar contents for a session entry from its query execution.
118 1 : export function deriveEntryStatus(query: QueryExecutionState | null): EntryStatus {
119 1 : if (query != null && !queryIsDone(query.status)) {
120 1 : const statusText = getQueryStatusText(
121 1 : query.status,
122 1 : query.statementIndex,
123 1 : query.statementCount,
124 1 : query.statementSucceeded,
125 1 : );
126 : // Engines with structured progress add the phase, the progress and the throughput
127 1 : const progress = summarizeQueryProgress(query.latestProgressUpdate);
128 1 : return {
129 1 : kind: EntryStatusKind.Query,
130 1 : indicator: IndicatorStatus.Running,
131 1 : message: progress == null ? statusText : `${statusText} ยท ${progress}`,
132 1 : traceId: query.traceId,
133 1 : errorDetail: null,
134 1 : };
135 1 : }
136 1 : if (query != null && (query.status === QueryExecutionStatus.FAILED || query.status === QueryExecutionStatus.CANCELLED)) {
137 1 : const message = query.error?.message != null
138 1 : ? getStatementStatusText(query.error.message, query.statementIndex, query.statementCount)
139 1 : : getQueryStatusText(query.status, query.statementIndex, query.statementCount);
140 1 : return {
141 1 : kind: EntryStatusKind.Query,
142 1 : indicator: IndicatorStatus.Failed,
143 1 : message,
144 1 : traceId: query.traceId,
145 1 : errorDetail: getQueryErrorDetail(query, message),
146 1 : };
147 1 : }
148 1 : if (query != null) {
149 1 : return {
150 1 : kind: EntryStatusKind.Query,
151 1 : indicator: IndicatorStatus.Succeeded,
152 1 : message: query.status === QueryExecutionStatus.SUCCEEDED && query.servedFromCache
153 1 : ? getStatementStatusText('Result loaded from cache', query.statementIndex, query.statementCount)
154 1 : : getQueryStatusText(query.status, query.statementIndex, query.statementCount),
155 1 : traceId: query.traceId,
156 1 : errorDetail: null,
157 1 : };
158 : }
159 1 : return {
160 1 : kind: EntryStatusKind.Idle,
161 1 : indicator: IndicatorStatus.None,
162 1 : message: 'Not run yet',
163 1 : traceId: null,
164 1 : errorDetail: null,
165 1 : };
166 1 : }
|