@finos/legend-query-builder 3.2.7 → 3.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
};
|