@finos/legend-query-builder 3.2.7 → 3.2.9
Sign up to get free protection for your applications and to get access to all the features.
- package/lib/components/QueryBuilderResultPanel.d.ts.map +1 -1
- package/lib/components/QueryBuilderResultPanel.js +30 -11
- package/lib/components/QueryBuilderResultPanel.js.map +1 -1
- package/lib/components/execution-plan/ExecutionPlanViewer.d.ts.map +1 -1
- package/lib/components/execution-plan/ExecutionPlanViewer.js +11 -3
- package/lib/components/execution-plan/ExecutionPlanViewer.js.map +1 -1
- package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts +2 -2
- package/lib/components/execution-plan/SQLExecutionNodeViewer.d.ts.map +1 -1
- package/lib/components/execution-plan/SQLExecutionNodeViewer.js +15 -4
- package/lib/components/execution-plan/SQLExecutionNodeViewer.js.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.d.ts.map +1 -1
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js +29 -9
- package/lib/components/fetch-structure/QueryBuilderTDSPanel.js.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +1 -1
- package/lib/stores/QueryBuilderResultState.d.ts +1 -0
- package/lib/stores/QueryBuilderResultState.d.ts.map +1 -1
- package/lib/stores/QueryBuilderResultState.js +26 -5
- package/lib/stores/QueryBuilderResultState.js.map +1 -1
- package/lib/stores/QueryBuilderValueSpecificationBuilderHelper.d.ts +5 -0
- package/lib/stores/QueryBuilderValueSpecificationBuilderHelper.d.ts.map +1 -1
- package/lib/stores/QueryLoaderState.d.ts.map +1 -1
- package/lib/stores/QueryLoaderState.js +2 -1
- package/lib/stores/QueryLoaderState.js.map +1 -1
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.js.map +1 -1
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.d.ts +1 -0
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.d.ts.map +1 -1
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js +1 -1
- package/lib/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.js.map +1 -1
- package/package.json +9 -9
- package/src/components/QueryBuilderResultPanel.tsx +93 -7
- package/src/components/execution-plan/ExecutionPlanViewer.tsx +29 -11
- package/src/components/execution-plan/SQLExecutionNodeViewer.tsx +99 -12
- package/src/components/fetch-structure/QueryBuilderTDSPanel.tsx +32 -16
- package/src/stores/QueryBuilderResultState.ts +43 -18
- package/src/stores/QueryBuilderValueSpecificationBuilderHelper.ts +5 -0
- package/src/stores/QueryLoaderState.ts +2 -1
- package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionStateBuilder.ts +1 -4
- package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.ts +5 -3
@@ -15,13 +15,19 @@
|
|
15
15
|
*/
|
16
16
|
|
17
17
|
import { observer } from 'mobx-react-lite';
|
18
|
-
import
|
18
|
+
import {
|
19
|
+
type ExecutionPlanState,
|
20
|
+
EXECUTION_PLAN_VIEW_MODE,
|
21
|
+
} from '../../stores/execution-plan/ExecutionPlanState.js';
|
19
22
|
import { format as formatSQL } from 'sql-formatter';
|
20
|
-
import type
|
23
|
+
import { type SQLResultColumn, stringifyDataType } from '@finos/legend-graph';
|
21
24
|
import {
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
PanelListItem,
|
26
|
+
CopyIcon,
|
27
|
+
PanelDivider,
|
28
|
+
Button,
|
29
|
+
PanelContent,
|
30
|
+
} from '@finos/legend-art';
|
25
31
|
|
26
32
|
/**
|
27
33
|
* TODO: Create a new `AbstractPlugin` for this, called `ExecutionPlanViewerPlugin`
|
@@ -35,14 +41,95 @@ export const SQLExecutionNodeViewer: React.FC<{
|
|
35
41
|
resultColumns: SQLResultColumn[];
|
36
42
|
executionPlanState: ExecutionPlanState;
|
37
43
|
}> = observer((props) => {
|
38
|
-
const { query } = props;
|
44
|
+
const { query, resultColumns, executionPlanState } = props;
|
39
45
|
|
46
|
+
const applicationStore = executionPlanState.applicationStore;
|
47
|
+
const copyExpression = (value: string): void => {
|
48
|
+
applicationStore.clipboardService
|
49
|
+
.copyTextToClipboard(value)
|
50
|
+
.then(() =>
|
51
|
+
applicationStore.notificationService.notifySuccess(
|
52
|
+
'SQL Query copied',
|
53
|
+
undefined,
|
54
|
+
2500,
|
55
|
+
),
|
56
|
+
)
|
57
|
+
.catch(applicationStore.alertUnhandledError);
|
58
|
+
};
|
40
59
|
return (
|
41
|
-
<
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
60
|
+
<PanelContent
|
61
|
+
darkMode={
|
62
|
+
!applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
|
63
|
+
}
|
64
|
+
>
|
65
|
+
<div className="query-builder__sql__container">
|
66
|
+
<PanelDivider />
|
67
|
+
<div key={query}>
|
68
|
+
<div className="query-builder__sql__container__item__label">
|
69
|
+
<PanelListItem>
|
70
|
+
SQL
|
71
|
+
<div>
|
72
|
+
<button
|
73
|
+
onClick={() => {
|
74
|
+
copyExpression(query);
|
75
|
+
}}
|
76
|
+
title="Copy SQL Expression"
|
77
|
+
className="query-builder__sql__container__icon"
|
78
|
+
>
|
79
|
+
<CopyIcon />
|
80
|
+
</button>
|
81
|
+
</div>
|
82
|
+
</PanelListItem>
|
83
|
+
</div>
|
84
|
+
<PanelListItem className="query-builder__sql__container__item">
|
85
|
+
<pre>{formatSQL(query)} </pre>
|
86
|
+
</PanelListItem>
|
87
|
+
<PanelDivider />
|
88
|
+
</div>
|
89
|
+
</div>
|
90
|
+
<div className="query-builder__sql__container">
|
91
|
+
<div>
|
92
|
+
<PanelListItem className="query-builder__sql__container__item__label">
|
93
|
+
Result Columns
|
94
|
+
</PanelListItem>
|
95
|
+
<PanelDivider />
|
96
|
+
<table className="table query-builder__sql__container__table">
|
97
|
+
<thead>
|
98
|
+
<tr>
|
99
|
+
<th className="table__cell--left">Label</th>
|
100
|
+
<th className="table__cell--left">Data Type</th>
|
101
|
+
</tr>
|
102
|
+
</thead>
|
103
|
+
<tbody>
|
104
|
+
{resultColumns.map((column) => (
|
105
|
+
<tr key={column.label}>
|
106
|
+
<td className="table__cell--left">
|
107
|
+
{column.label.replaceAll(`"`, '')}
|
108
|
+
</td>
|
109
|
+
|
110
|
+
{column.dataType && (
|
111
|
+
<td className="table__cell--left">
|
112
|
+
{stringifyDataType(column.dataType)}
|
113
|
+
</td>
|
114
|
+
)}
|
115
|
+
</tr>
|
116
|
+
))}
|
117
|
+
</tbody>
|
118
|
+
</table>
|
119
|
+
</div>
|
120
|
+
</div>
|
121
|
+
<PanelDivider />
|
122
|
+
|
123
|
+
<div className="query-builder__sql__container">
|
124
|
+
<Button
|
125
|
+
className="btn--dark execution-node-viewer__unsupported-view__to-text-mode__btn"
|
126
|
+
onClick={(): void =>
|
127
|
+
executionPlanState.setViewMode(EXECUTION_PLAN_VIEW_MODE.JSON)
|
128
|
+
}
|
129
|
+
text="View JSON"
|
130
|
+
/>
|
131
|
+
</div>
|
132
|
+
<PanelDivider />
|
133
|
+
</PanelContent>
|
47
134
|
);
|
48
135
|
});
|
@@ -433,26 +433,42 @@ const QueryBuilderProjectionColumnEditor = observer(
|
|
433
433
|
if (option?.value !== aggregateColumnState?.calendarFunction) {
|
434
434
|
const lambdaParameterName =
|
435
435
|
aggregateColumnState?.calendarFunction?.lambdaParameterName;
|
436
|
+
const dateColumn = aggregateColumnState?.calendarFunction?.dateColumn;
|
437
|
+
const endDate = aggregateColumnState?.calendarFunction?.endDate;
|
438
|
+
const calendarType =
|
439
|
+
aggregateColumnState?.calendarFunction?.calendarType;
|
436
440
|
aggregateColumnState?.setCalendarFunction(option?.value ?? undefined);
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
aggregateColumnState.calendarFunction.setDateColumn(
|
445
|
-
guaranteeNonNullable(calendarFunctionDateColumnOptions[0]).value,
|
446
|
-
);
|
441
|
+
const calendarFunction = aggregateColumnState?.calendarFunction;
|
442
|
+
if (calendarFunction) {
|
443
|
+
if (lambdaParameterName) {
|
444
|
+
calendarFunction.setLambdaParameterName(lambdaParameterName);
|
445
|
+
}
|
446
|
+
if (dateColumn) {
|
447
|
+
calendarFunction.setDateColumn(dateColumn);
|
447
448
|
} else {
|
448
|
-
|
449
|
-
|
449
|
+
if (calendarFunctionDateColumnOptions[0]) {
|
450
|
+
calendarFunction.setDateColumn(
|
451
|
+
guaranteeNonNullable(calendarFunctionDateColumnOptions[0])
|
452
|
+
.value,
|
453
|
+
);
|
454
|
+
} else {
|
455
|
+
applicationStore.notificationService.notifyWarning(
|
456
|
+
'Please provide a date attribute for the calendar function',
|
457
|
+
);
|
458
|
+
}
|
459
|
+
}
|
460
|
+
if (endDate) {
|
461
|
+
calendarFunction.setEndDate(endDate);
|
462
|
+
} else {
|
463
|
+
calendarFunction.setEndDate(defaultEndDate);
|
464
|
+
}
|
465
|
+
if (calendarType) {
|
466
|
+
calendarFunction.setCalendarType(calendarType);
|
467
|
+
} else {
|
468
|
+
calendarFunction.setCalendarType(
|
469
|
+
guaranteeNonNullable(calendarTypeOptions[0]).value,
|
450
470
|
);
|
451
471
|
}
|
452
|
-
aggregateColumnState.calendarFunction.setCalendarType(
|
453
|
-
guaranteeNonNullable(calendarTypeOptions[0]).value,
|
454
|
-
);
|
455
|
-
aggregateColumnState.calendarFunction.setEndDate(defaultEndDate);
|
456
472
|
}
|
457
473
|
}
|
458
474
|
};
|
@@ -86,6 +86,7 @@ export class QueryBuilderResultState {
|
|
86
86
|
setQueryRunPromise: action,
|
87
87
|
exportData: flow,
|
88
88
|
runQuery: flow,
|
89
|
+
cancelQuery: flow,
|
89
90
|
generatePlan: flow,
|
90
91
|
});
|
91
92
|
|
@@ -163,7 +164,9 @@ export class QueryBuilderResultState {
|
|
163
164
|
const contentType = exportData.contentType;
|
164
165
|
const serializationFormat = exportData.serializationFormat;
|
165
166
|
this.exportDataState.inProgress();
|
166
|
-
const query = this.buildExecutionRawLambda(
|
167
|
+
const query = this.buildExecutionRawLambda({
|
168
|
+
isExportingResult: true,
|
169
|
+
});
|
167
170
|
const result =
|
168
171
|
(yield this.queryBuilderState.graphManagerState.graphManager.runQuery(
|
169
172
|
query,
|
@@ -205,6 +208,7 @@ export class QueryBuilderResultState {
|
|
205
208
|
}
|
206
209
|
|
207
210
|
*runQuery(): GeneratorFn<void> {
|
211
|
+
let promise;
|
208
212
|
try {
|
209
213
|
this.setIsRunningQuery(true);
|
210
214
|
const currentHashCode = this.queryBuilderState.hashCode;
|
@@ -231,16 +235,15 @@ export class QueryBuilderResultState {
|
|
231
235
|
this.queryBuilderState.graphManagerState.graph,
|
232
236
|
);
|
233
237
|
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
);
|
238
|
+
promise = this.queryBuilderState.graphManagerState.graphManager.runQuery(
|
239
|
+
query,
|
240
|
+
mapping,
|
241
|
+
runtime,
|
242
|
+
this.queryBuilderState.graphManagerState.graph,
|
243
|
+
{
|
244
|
+
parameterValues,
|
245
|
+
},
|
246
|
+
);
|
244
247
|
|
245
248
|
this.setQueryRunPromise(promise);
|
246
249
|
const result = (yield promise) as ExecutionResult;
|
@@ -260,17 +263,39 @@ export class QueryBuilderResultState {
|
|
260
263
|
);
|
261
264
|
}
|
262
265
|
} catch (error) {
|
263
|
-
|
266
|
+
// When user cancels the query by calling the cancelQuery api, it will throw an exeuction failure error.
|
267
|
+
// For now, we don't want to notify users about this failure. Therefore we check to ensure the promise is still the same one.
|
268
|
+
// When cancelled the query, we set the queryRunPromise as undefined.
|
269
|
+
if (this.queryRunPromise === promise) {
|
270
|
+
assertErrorThrown(error);
|
271
|
+
this.queryBuilderState.applicationStore.logService.error(
|
272
|
+
LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
|
273
|
+
error,
|
274
|
+
);
|
275
|
+
this.queryBuilderState.applicationStore.notificationService.notifyError(
|
276
|
+
error,
|
277
|
+
);
|
278
|
+
}
|
279
|
+
} finally {
|
280
|
+
this.setIsRunningQuery(false);
|
281
|
+
this.pressedRunQuery.complete();
|
282
|
+
}
|
283
|
+
}
|
284
|
+
|
285
|
+
*cancelQuery(): GeneratorFn<void> {
|
286
|
+
this.pressedRunQuery.complete();
|
287
|
+
this.setIsRunningQuery(false);
|
288
|
+
this.setQueryRunPromise(undefined);
|
289
|
+
try {
|
290
|
+
yield this.queryBuilderState.graphManagerState.graphManager.cancelUserExecutions(
|
291
|
+
true,
|
292
|
+
);
|
293
|
+
} catch (error) {
|
294
|
+
// Don't notify users about success or failure
|
264
295
|
this.queryBuilderState.applicationStore.logService.error(
|
265
296
|
LogEvent.create(GRAPH_MANAGER_EVENT.EXECUTION_FAILURE),
|
266
297
|
error,
|
267
298
|
);
|
268
|
-
this.queryBuilderState.applicationStore.notificationService.notifyError(
|
269
|
-
error,
|
270
|
-
);
|
271
|
-
} finally {
|
272
|
-
this.setIsRunningQuery(false);
|
273
|
-
this.pressedRunQuery.complete();
|
274
299
|
}
|
275
300
|
}
|
276
301
|
|
@@ -194,4 +194,9 @@ export type LambdaFunctionBuilderOption = {
|
|
194
194
|
*/
|
195
195
|
useAllVersionsForMilestoning?: boolean | undefined;
|
196
196
|
keepSourceInformation?: boolean | undefined;
|
197
|
+
/**
|
198
|
+
* Set this to `true` when we export query results since we do want to ignore an overriding
|
199
|
+
* limit for the query results if it exists so the exported results contain all the data
|
200
|
+
*/
|
201
|
+
isExportingResult?: boolean | undefined;
|
197
202
|
};
|
@@ -205,6 +205,7 @@ export class QueryLoaderState {
|
|
205
205
|
let searchSpecification = new QuerySearchSpecification();
|
206
206
|
searchSpecification.searchTerm = searchText;
|
207
207
|
searchSpecification.limit = QUERY_LOADER_TYPEAHEAD_SEARCH_LIMIT + 1;
|
208
|
+
searchSpecification.exactMatchName = true;
|
208
209
|
searchSpecification.showCurrentUserQueriesOnly =
|
209
210
|
this.showCurrentUserQueriesOnly;
|
210
211
|
if (this.queryBuilderState) {
|
@@ -246,7 +247,7 @@ export class QueryLoaderState {
|
|
246
247
|
name,
|
247
248
|
)) as Query;
|
248
249
|
this.onQueryRenamed?.(query);
|
249
|
-
this.applicationStore.notificationService.
|
250
|
+
this.applicationStore.notificationService.notifySuccess(
|
250
251
|
'Renamed query successfully',
|
251
252
|
);
|
252
253
|
this.renameQueryState.pass();
|
@@ -36,7 +36,6 @@ import {
|
|
36
36
|
guaranteeType,
|
37
37
|
isNonNullable,
|
38
38
|
returnUndefOnError,
|
39
|
-
type PlainObject,
|
40
39
|
} from '@finos/legend-shared';
|
41
40
|
import { QUERY_BUILDER_SUPPORTED_FUNCTIONS } from '../../../../graph/QueryBuilderMetaModelConst.js';
|
42
41
|
import type { QueryBuilderState } from '../../../QueryBuilderState.js';
|
@@ -218,9 +217,7 @@ export const processTDSProjectionDerivationExpression = (
|
|
218
217
|
queryBuilderState.fetchStructureState.implementation;
|
219
218
|
const rawLambdaProtocol = returnUndefOnError(() =>
|
220
219
|
guaranteeType(
|
221
|
-
V1_deserializeRawValueSpecification(
|
222
|
-
value.content as PlainObject<V1_RawLambda>,
|
223
|
-
),
|
220
|
+
V1_deserializeRawValueSpecification(value.content),
|
224
221
|
V1_RawLambda,
|
225
222
|
),
|
226
223
|
);
|
package/src/stores/fetch-structure/tds/projection/QueryBuilderProjectionValueSpecificationBuilder.ts
CHANGED
@@ -164,6 +164,7 @@ export const appendProjection = (
|
|
164
164
|
*/
|
165
165
|
isBuildingExecutionQuery?: boolean | undefined;
|
166
166
|
keepSourceInformation?: boolean | undefined;
|
167
|
+
isExportingResult?: boolean | undefined;
|
167
168
|
},
|
168
169
|
): void => {
|
169
170
|
const queryBuilderState = tdsState.queryBuilderState;
|
@@ -394,8 +395,9 @@ export const appendProjection = (
|
|
394
395
|
|
395
396
|
// build result set modifiers
|
396
397
|
appendResultSetModifier(tdsState.resultSetModifierState, lambdaFunction, {
|
397
|
-
overridingLimit:
|
398
|
-
|
399
|
-
|
398
|
+
overridingLimit:
|
399
|
+
options?.isBuildingExecutionQuery && !options.isExportingResult
|
400
|
+
? queryBuilderState.resultState.previewLimit
|
401
|
+
: undefined,
|
400
402
|
});
|
401
403
|
};
|