@finos/legend-application-data-cube 0.3.2 → 0.3.4
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/__lib__/LegendDataCubeNavigation.d.ts +2 -0
- package/lib/__lib__/LegendDataCubeNavigation.d.ts.map +1 -1
- package/lib/__lib__/LegendDataCubeNavigation.js +2 -0
- package/lib/__lib__/LegendDataCubeNavigation.js.map +1 -1
- package/lib/application/LegendDataCubeApplicationConfig.d.ts +4 -0
- package/lib/application/LegendDataCubeApplicationConfig.d.ts.map +1 -1
- package/lib/application/LegendDataCubeApplicationConfig.js +5 -0
- package/lib/application/LegendDataCubeApplicationConfig.js.map +1 -1
- package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.d.ts +18 -0
- package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.d.ts.map +1 -0
- package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.js +48 -0
- package/lib/application/__test-utils__/LegendDataCubeApplicationTestUtils.js.map +1 -0
- package/lib/components/LegendDataCubeBlockingWindow.d.ts +2 -1
- package/lib/components/LegendDataCubeBlockingWindow.d.ts.map +1 -1
- package/lib/components/LegendDataCubeBlockingWindow.js +9 -3
- package/lib/components/LegendDataCubeBlockingWindow.js.map +1 -1
- package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.d.ts +42 -0
- package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.d.ts.map +1 -0
- package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js +104 -0
- package/lib/components/__test-utils__/LegendDataCubeStoreTestUtils.js.map +1 -0
- package/lib/components/builder/LegendDataCubeBuilder.d.ts +5 -0
- package/lib/components/builder/LegendDataCubeBuilder.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeBuilder.js +26 -5
- package/lib/components/builder/LegendDataCubeBuilder.js.map +1 -1
- package/lib/components/builder/LegendDataCubeBuilderStoreProvider.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeBuilderStoreProvider.js +2 -1
- package/lib/components/builder/LegendDataCubeBuilderStoreProvider.js.map +1 -1
- package/lib/components/builder/LegendDataCubeSourceViewer.d.ts.map +1 -1
- package/lib/components/builder/LegendDataCubeSourceViewer.js +61 -1
- package/lib/components/builder/LegendDataCubeSourceViewer.js.map +1 -1
- package/lib/components/builder/source/LegendDataCubeSourceLoader.d.ts +25 -0
- package/lib/components/builder/source/LegendDataCubeSourceLoader.d.ts.map +1 -0
- package/lib/components/builder/source/LegendDataCubeSourceLoader.js +47 -0
- package/lib/components/builder/source/LegendDataCubeSourceLoader.js.map +1 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.d.ts.map +1 -1
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js +1 -2
- package/lib/components/builder/source/LocalFileDataCubeSourceBuilder.js.map +1 -1
- package/lib/components/builder/source/LocalFileDataCubeSourceLoader.d.ts +22 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceLoader.d.ts.map +1 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceLoader.js +28 -0
- package/lib/components/builder/source/LocalFileDataCubeSourceLoader.js.map +1 -0
- package/lib/components/builder/source/UserDefinedFunctionDataCubeSourceBuilder.d.ts.map +1 -1
- package/lib/index.css +2 -2
- package/lib/index.css.map +1 -1
- package/lib/package.json +7 -5
- package/lib/stores/LegendDataCubeBaseStore.d.ts +2 -1
- package/lib/stores/LegendDataCubeBaseStore.d.ts.map +1 -1
- package/lib/stores/LegendDataCubeBaseStore.js +6 -3
- package/lib/stores/LegendDataCubeBaseStore.js.map +1 -1
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts +8 -5
- package/lib/stores/LegendDataCubeDataCubeEngine.d.ts.map +1 -1
- package/lib/stores/LegendDataCubeDataCubeEngine.js +132 -114
- package/lib/stores/LegendDataCubeDataCubeEngine.js.map +1 -1
- package/lib/stores/LegendDataCubeDuckDBEngine.d.ts +11 -4
- package/lib/stores/LegendDataCubeDuckDBEngine.d.ts.map +1 -1
- package/lib/stores/LegendDataCubeDuckDBEngine.js +92 -20
- package/lib/stores/LegendDataCubeDuckDBEngine.js.map +1 -1
- package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts +7 -0
- package/lib/stores/builder/LegendDataCubeBuilderStore.d.ts.map +1 -1
- package/lib/stores/builder/LegendDataCubeBuilderStore.js +68 -17
- package/lib/stores/builder/LegendDataCubeBuilderStore.js.map +1 -1
- package/lib/stores/builder/source/LegendDataCubeSourceLoaderState.d.ts +39 -0
- package/lib/stores/builder/source/LegendDataCubeSourceLoaderState.d.ts.map +1 -0
- package/lib/stores/builder/source/LegendDataCubeSourceLoaderState.js +66 -0
- package/lib/stores/builder/source/LegendDataCubeSourceLoaderState.js.map +1 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts +3 -2
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.d.ts.map +1 -1
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js +7 -10
- package/lib/stores/builder/source/LocalFileDataCubeSourceBuilderState.js.map +1 -1
- package/lib/stores/builder/source/LocalFileDataCubeSourceLoaderState.d.ts +45 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceLoaderState.d.ts.map +1 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceLoaderState.js +142 -0
- package/lib/stores/builder/source/LocalFileDataCubeSourceLoaderState.js.map +1 -0
- package/lib/stores/model/LegendQueryDataCubeSource.d.ts +2 -1
- package/lib/stores/model/LegendQueryDataCubeSource.d.ts.map +1 -1
- package/lib/stores/model/LegendQueryDataCubeSource.js +1 -0
- package/lib/stores/model/LegendQueryDataCubeSource.js.map +1 -1
- package/lib/stores/model/LocalFileDataCubeSource.d.ts +3 -8
- package/lib/stores/model/LocalFileDataCubeSource.d.ts.map +1 -1
- package/lib/stores/model/LocalFileDataCubeSource.js +5 -15
- package/lib/stores/model/LocalFileDataCubeSource.js.map +1 -1
- package/package.json +17 -15
- package/src/__lib__/LegendDataCubeNavigation.ts +21 -0
- package/src/application/LegendDataCubeApplicationConfig.ts +10 -0
- package/src/application/__test-utils__/LegendDataCubeApplicationTestUtils.ts +52 -0
- package/src/components/LegendDataCubeBlockingWindow.tsx +9 -2
- package/src/components/__test-utils__/LegendDataCubeStoreTestUtils.tsx +231 -0
- package/src/components/builder/LegendDataCubeBuilder.tsx +51 -6
- package/src/components/builder/LegendDataCubeBuilderStoreProvider.tsx +2 -0
- package/src/components/builder/LegendDataCubeSourceViewer.tsx +171 -1
- package/src/components/builder/source/LegendDataCubeSourceLoader.tsx +111 -0
- package/src/components/builder/source/LocalFileDataCubeSourceBuilder.tsx +1 -2
- package/src/components/builder/source/LocalFileDataCubeSourceLoader.tsx +58 -0
- package/src/stores/LegendDataCubeBaseStore.ts +13 -6
- package/src/stores/LegendDataCubeDataCubeEngine.ts +167 -131
- package/src/stores/LegendDataCubeDuckDBEngine.ts +110 -20
- package/src/stores/builder/LegendDataCubeBuilderStore.tsx +114 -23
- package/src/stores/builder/source/LegendDataCubeSourceLoaderState.tsx +104 -0
- package/src/stores/builder/source/LocalFileDataCubeSourceBuilderState.ts +9 -14
- package/src/stores/builder/source/LocalFileDataCubeSourceLoaderState.ts +232 -0
- package/src/stores/model/LegendQueryDataCubeSource.ts +2 -0
- package/src/stores/model/LocalFileDataCubeSource.ts +6 -15
- package/tsconfig.json +7 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { observer } from 'mobx-react-lite';
|
|
18
|
+
import { FormButton } from '@finos/legend-data-cube';
|
|
19
|
+
import { useLegendDataCubeBuilderStore } from '../LegendDataCubeBuilderStoreProvider.js';
|
|
20
|
+
import { LocalFileDataCubeSourceLoaderState } from '../../../stores/builder/source/LocalFileDataCubeSourceLoaderState.js';
|
|
21
|
+
import { LocalFileDataCubePartialSourceLoader } from './LocalFileDataCubeSourceLoader.js';
|
|
22
|
+
import { DataCubeIcon } from '@finos/legend-art';
|
|
23
|
+
import { formatDistanceToNow } from '@finos/legend-shared';
|
|
24
|
+
import type { LegendDataCubeSourceLoaderState } from '../../../stores/builder/source/LegendDataCubeSourceLoaderState.js';
|
|
25
|
+
import { LegendDataCubeBlockingWindow } from '../../LegendDataCubeBlockingWindow.js';
|
|
26
|
+
|
|
27
|
+
export const LegendDataCubeSourceLoader = observer(
|
|
28
|
+
(props: { state: LegendDataCubeSourceLoaderState }) => {
|
|
29
|
+
const { state } = props;
|
|
30
|
+
const store = useLegendDataCubeBuilderStore();
|
|
31
|
+
const persistentDataCube = state.persistentDataCube;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<>
|
|
35
|
+
<div className="h-[calc(100%_-_40px)] w-full px-2 pt-2">
|
|
36
|
+
<div className="h-full w-full border border-neutral-300 bg-white">
|
|
37
|
+
<div className="h-full w-full select-none p-2">
|
|
38
|
+
<div className="relative mb-0.5 flex h-[42px] w-full border border-neutral-200 bg-neutral-100">
|
|
39
|
+
<div className="w-full">
|
|
40
|
+
<div className="h-6 w-4/5 overflow-hidden text-ellipsis whitespace-nowrap px-1.5 leading-6">
|
|
41
|
+
{persistentDataCube.name}
|
|
42
|
+
</div>
|
|
43
|
+
<div className="flex h-[18px] items-start justify-between px-1.5">
|
|
44
|
+
<div className="flex">
|
|
45
|
+
<DataCubeIcon.ClockEdit className="text-sm text-neutral-500" />
|
|
46
|
+
<div className="ml-1 text-sm text-neutral-500">
|
|
47
|
+
{persistentDataCube.lastUpdatedAt
|
|
48
|
+
? formatDistanceToNow(
|
|
49
|
+
new Date(persistentDataCube.lastUpdatedAt),
|
|
50
|
+
{
|
|
51
|
+
includeSeconds: true,
|
|
52
|
+
addSuffix: true,
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
: '(unknown)'}
|
|
56
|
+
</div>
|
|
57
|
+
</div>
|
|
58
|
+
<div className="flex">
|
|
59
|
+
<DataCubeIcon.User className="text-sm text-neutral-500" />
|
|
60
|
+
<div className="ml-1 text-sm text-neutral-500">
|
|
61
|
+
{persistentDataCube.owner}
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
66
|
+
</div>
|
|
67
|
+
<div className="flex h-10 w-full items-center">
|
|
68
|
+
<div className="flex h-full w-32 flex-shrink-0 items-center text-sm">
|
|
69
|
+
Source Type:
|
|
70
|
+
<div className="pl-3">{state.label}</div>
|
|
71
|
+
</div>
|
|
72
|
+
</div>
|
|
73
|
+
<div className="h-[calc(100%_-_41px)] w-full overflow-auto">
|
|
74
|
+
{state instanceof LocalFileDataCubeSourceLoaderState && (
|
|
75
|
+
<LocalFileDataCubePartialSourceLoader
|
|
76
|
+
partialSourceLoader={state}
|
|
77
|
+
/>
|
|
78
|
+
)}
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
<div className="flex h-10 items-center justify-end px-2">
|
|
84
|
+
<FormButton onClick={state.display.onClose}>Cancel</FormButton>
|
|
85
|
+
<FormButton
|
|
86
|
+
className="ml-2"
|
|
87
|
+
disabled={!state.isValid || state.finalizeState.isInProgress}
|
|
88
|
+
onClick={() => {
|
|
89
|
+
state.finalize().catch((error) => {
|
|
90
|
+
store.alertService.alertUnhandledError(error);
|
|
91
|
+
});
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
OK
|
|
95
|
+
</FormButton>
|
|
96
|
+
</div>
|
|
97
|
+
</>
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
export const LegendDataCubeSourceLoaderBlockingWindow = observer(() => {
|
|
103
|
+
const store = useLegendDataCubeBuilderStore();
|
|
104
|
+
|
|
105
|
+
if (!store.sourceLoader) {
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
return (
|
|
109
|
+
<LegendDataCubeBlockingWindow windowState={store.sourceLoader.display} />
|
|
110
|
+
);
|
|
111
|
+
});
|
|
@@ -30,8 +30,7 @@ export const LocalFileDataCubeSourceBuilder = observer(
|
|
|
30
30
|
type={AlertType.WARNING}
|
|
31
31
|
text={`Currently, support for local file comes with the following limitations:
|
|
32
32
|
- Only CSV files are supported, but not all variants of CSV files are supported (required header row, comma delimiter, single escape quote).
|
|
33
|
-
- Data from uploaded file will not be stored nor shared
|
|
34
|
-
- DataCube created with local file source cannot be saved.`}
|
|
33
|
+
- Data from uploaded file will not be stored nor shared.`}
|
|
35
34
|
/>
|
|
36
35
|
<div className="mt-2 flex h-6 w-full items-center text-neutral-500">
|
|
37
36
|
<input
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) 2020-present, Goldman Sachs
|
|
3
|
+
*
|
|
4
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
* you may not use this file except in compliance with the License.
|
|
6
|
+
* You may obtain a copy of the License at
|
|
7
|
+
*
|
|
8
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
*
|
|
10
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
* See the License for the specific language governing permissions and
|
|
14
|
+
* limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { observer } from 'mobx-react-lite';
|
|
18
|
+
import { AlertType, FormAlert, FormCodeEditor } from '@finos/legend-data-cube';
|
|
19
|
+
import { CODE_EDITOR_LANGUAGE } from '@finos/legend-code-editor';
|
|
20
|
+
import type { LocalFileDataCubeSourceLoaderState } from '../../../stores/builder/source/LocalFileDataCubeSourceLoaderState.js';
|
|
21
|
+
|
|
22
|
+
export const LocalFileDataCubePartialSourceLoader = observer(
|
|
23
|
+
(props: { partialSourceLoader: LocalFileDataCubeSourceLoaderState }) => {
|
|
24
|
+
const { partialSourceLoader } = props;
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<div className="h-full w-full">
|
|
28
|
+
<FormAlert
|
|
29
|
+
message="Local file support is experimental"
|
|
30
|
+
type={AlertType.WARNING}
|
|
31
|
+
text={`Currently, support for local file comes with the following limitations:
|
|
32
|
+
- Only CSV files are supported, but not all variants of CSV files are supported (required header row, comma delimiter, single escape quote).
|
|
33
|
+
- Data from uploaded file will not be stored nor shared.`}
|
|
34
|
+
/>
|
|
35
|
+
<div className="mt-2 flex h-6 w-full items-center text-neutral-500">
|
|
36
|
+
<input
|
|
37
|
+
type="file"
|
|
38
|
+
onChange={(event) => {
|
|
39
|
+
partialSourceLoader.processFile(event.target.files?.[0]);
|
|
40
|
+
}}
|
|
41
|
+
className="w-full"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
{partialSourceLoader.previewText !== undefined && (
|
|
45
|
+
<div className="mt-2 h-40">
|
|
46
|
+
<FormCodeEditor
|
|
47
|
+
value={partialSourceLoader.previewText}
|
|
48
|
+
language={CODE_EDITOR_LANGUAGE.TEXT}
|
|
49
|
+
isReadOnly={true}
|
|
50
|
+
hidePadding={true}
|
|
51
|
+
title="Data Preview"
|
|
52
|
+
/>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
);
|
|
57
|
+
},
|
|
58
|
+
);
|
|
@@ -23,8 +23,9 @@ import type { LegendDataCubePluginManager } from '../application/LegendDataCubeP
|
|
|
23
23
|
import { DepotServerClient } from '@finos/legend-server-depot';
|
|
24
24
|
import type { LegendDataCubeApplicationConfig } from '../application/LegendDataCubeApplicationConfig.js';
|
|
25
25
|
import {
|
|
26
|
-
V1_EngineServerClient,
|
|
26
|
+
type V1_EngineServerClient,
|
|
27
27
|
V1_PureGraphManager,
|
|
28
|
+
V1_RemoteEngine,
|
|
28
29
|
} from '@finos/legend-graph';
|
|
29
30
|
import {
|
|
30
31
|
ActionState,
|
|
@@ -61,6 +62,7 @@ export class LegendDataCubeBaseStore {
|
|
|
61
62
|
readonly pluginManager: LegendDataCubePluginManager;
|
|
62
63
|
readonly depotServerClient: DepotServerClient;
|
|
63
64
|
readonly graphManager: V1_PureGraphManager;
|
|
65
|
+
readonly remoteEngine: V1_RemoteEngine;
|
|
64
66
|
readonly engineServerClient: V1_EngineServerClient;
|
|
65
67
|
|
|
66
68
|
readonly engine: LegendDataCubeDataCubeEngine;
|
|
@@ -116,11 +118,15 @@ export class LegendDataCubeBaseStore {
|
|
|
116
118
|
} satisfies DataCubeSetting<string>,
|
|
117
119
|
];
|
|
118
120
|
|
|
119
|
-
this.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
121
|
+
this.remoteEngine = new V1_RemoteEngine(
|
|
122
|
+
{
|
|
123
|
+
baseUrl: this.getEngineServerBaseUrlSettingValue(),
|
|
124
|
+
queryBaseUrl: this.application.config.engineQueryServerUrl,
|
|
125
|
+
enableCompression: this.getEngineEnableCompressionSettingValue(),
|
|
126
|
+
},
|
|
127
|
+
application.logService,
|
|
128
|
+
);
|
|
129
|
+
this.engineServerClient = this.remoteEngine.getEngineServerClient();
|
|
124
130
|
this.engineServerClient.setTracerService(application.tracerService);
|
|
125
131
|
|
|
126
132
|
this.engine = new LegendDataCubeDataCubeEngine(
|
|
@@ -200,6 +206,7 @@ export class LegendDataCubeBaseStore {
|
|
|
200
206
|
},
|
|
201
207
|
},
|
|
202
208
|
{
|
|
209
|
+
engine: this.remoteEngine,
|
|
203
210
|
tracerService: this.application.tracerService,
|
|
204
211
|
},
|
|
205
212
|
);
|
|
@@ -82,6 +82,8 @@ import {
|
|
|
82
82
|
V1_deserializePureModelContext,
|
|
83
83
|
type V1_ConcreteFunctionDefinition,
|
|
84
84
|
V1_deserializeValueSpecification,
|
|
85
|
+
LET_TOKEN,
|
|
86
|
+
V1_AppliedFunction,
|
|
85
87
|
} from '@finos/legend-graph';
|
|
86
88
|
import {
|
|
87
89
|
_elementPtr,
|
|
@@ -101,6 +103,8 @@ import {
|
|
|
101
103
|
DataCubeExecutionError,
|
|
102
104
|
RawUserDefinedFunctionDataCubeSource,
|
|
103
105
|
ADHOC_FUNCTION_DATA_CUBE_SOURCE_TYPE,
|
|
106
|
+
UserDefinedFunctionDataCubeSource,
|
|
107
|
+
DataCubeQueryFilterOperator,
|
|
104
108
|
} from '@finos/legend-data-cube';
|
|
105
109
|
import {
|
|
106
110
|
isNonNullable,
|
|
@@ -199,12 +203,87 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
199
203
|
RawLocalFileQueryDataCubeSource.serialization.fromJson(value);
|
|
200
204
|
const source = new LocalFileDataCubeSource();
|
|
201
205
|
source.fileName = rawSource.fileName;
|
|
202
|
-
source.
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
206
|
+
source.fileFormat = rawSource.fileFormat;
|
|
207
|
+
|
|
208
|
+
const tableCatalog = this._duckDBEngine.retrieveCatalogTable(
|
|
209
|
+
rawSource._ref,
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const { model, database, schema, table, runtime } =
|
|
213
|
+
this._synthesizeMinimalModelContext({
|
|
214
|
+
schemaName: tableCatalog.schemaName,
|
|
215
|
+
tableName: tableCatalog.tableName,
|
|
216
|
+
tableColumns: tableCatalog.columns.map((col) => {
|
|
217
|
+
const column = new V1_Column();
|
|
218
|
+
column.name = col[0] as string;
|
|
219
|
+
// TODO: confirm this is in accordance to engine
|
|
220
|
+
// check if we have a duckdb enum mapping
|
|
221
|
+
// See https://duckdb.org/docs/sql/data_types/overview.html
|
|
222
|
+
switch (col[1] as string) {
|
|
223
|
+
case 'BIT': {
|
|
224
|
+
column.type = new V1_Bit();
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
case 'BOOLEAN': {
|
|
228
|
+
// TODO: understand why boolean is not present in relationalDataType
|
|
229
|
+
column.type = new V1_VarChar();
|
|
230
|
+
break;
|
|
231
|
+
}
|
|
232
|
+
case 'DATE': {
|
|
233
|
+
column.type = new V1_Date();
|
|
234
|
+
break;
|
|
235
|
+
}
|
|
236
|
+
case 'DECIMAL': {
|
|
237
|
+
column.type = new V1_Decimal();
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
case 'DOUBLE': {
|
|
241
|
+
column.type = new V1_Double();
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
case 'FLOAT': {
|
|
245
|
+
column.type = new V1_Float();
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
case 'INTEGER': {
|
|
249
|
+
column.type = new V1_Integer();
|
|
250
|
+
break;
|
|
251
|
+
}
|
|
252
|
+
case 'TININT': {
|
|
253
|
+
column.type = new V1_TinyInt();
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
case 'SMALLINT': {
|
|
257
|
+
column.type = new V1_SmallInt();
|
|
258
|
+
break;
|
|
259
|
+
}
|
|
260
|
+
case 'BIGINT': {
|
|
261
|
+
column.type = new V1_BigInt();
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
case 'TIMESTAMP': {
|
|
265
|
+
column.type = new V1_Timestamp();
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
case 'VARCHAR': {
|
|
269
|
+
column.type = new V1_VarChar();
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
default: {
|
|
273
|
+
throw new UnsupportedOperationError(
|
|
274
|
+
`Can't ingest local file data: failed to find matching relational data type for DuckDB type '${col[1]}' when synthesizing table definition`,
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return column;
|
|
279
|
+
}),
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
source.db = database.path;
|
|
283
|
+
source.model = model;
|
|
284
|
+
source.table = table.name;
|
|
285
|
+
source.schema = schema.name;
|
|
286
|
+
source.runtime = runtime.path;
|
|
208
287
|
|
|
209
288
|
const query = new V1_ClassInstance();
|
|
210
289
|
query.type = V1_ClassInstanceType.RELATION_STORE_ACCESSOR;
|
|
@@ -244,7 +323,8 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
244
323
|
);
|
|
245
324
|
}
|
|
246
325
|
|
|
247
|
-
const source = new
|
|
326
|
+
const source = new UserDefinedFunctionDataCubeSource();
|
|
327
|
+
source.functionPath = rawSource.functionPath;
|
|
248
328
|
source.runtime = rawSource.runtime;
|
|
249
329
|
source.model = rawSource.model;
|
|
250
330
|
if (deserializedModel.sdlcInfo instanceof V1_LegendSDLC) {
|
|
@@ -327,32 +407,6 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
327
407
|
),
|
|
328
408
|
);
|
|
329
409
|
source.query = at(source.lambda.body, 0);
|
|
330
|
-
// use the default parameter values from the query
|
|
331
|
-
//
|
|
332
|
-
// TODO?: we should probably allow configuring the parameters?
|
|
333
|
-
// this would mean we need to create first-class support for parameters in DataCube component
|
|
334
|
-
const parameterValues = await Promise.all(
|
|
335
|
-
source.lambda.parameters.map(async (parameter) => {
|
|
336
|
-
if (parameter.genericType?.rawType instanceof V1_PackageableType) {
|
|
337
|
-
const paramValue = new V1_ParameterValue();
|
|
338
|
-
paramValue.name = parameter.name;
|
|
339
|
-
const type = parameter.genericType.rawType.fullPath;
|
|
340
|
-
const defaultValue = queryInfo.defaultParameterValues?.find(
|
|
341
|
-
(val) => val.name === parameter.name,
|
|
342
|
-
)?.content;
|
|
343
|
-
paramValue.value =
|
|
344
|
-
defaultValue !== undefined
|
|
345
|
-
? await this.parseValueSpecification(defaultValue)
|
|
346
|
-
: {
|
|
347
|
-
_type: V1_deserializeRawValueSpecificationType(type),
|
|
348
|
-
value: _defaultPrimitiveTypeValue(type),
|
|
349
|
-
};
|
|
350
|
-
return paramValue;
|
|
351
|
-
}
|
|
352
|
-
return undefined;
|
|
353
|
-
}),
|
|
354
|
-
);
|
|
355
|
-
source.parameterValues = parameterValues.filter(isNonNullable);
|
|
356
410
|
try {
|
|
357
411
|
source.columns = (
|
|
358
412
|
await this._getLambdaRelationType(
|
|
@@ -366,6 +420,59 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
366
420
|
`Can't get query result columns. Make sure the saved query return a relation (i.e. typed TDS). Error: ${error.message}`,
|
|
367
421
|
);
|
|
368
422
|
}
|
|
423
|
+
// To handle parameter value with function calls we
|
|
424
|
+
// 1. Separate the parameters with function calls from regular parameters
|
|
425
|
+
// 2. Add let statements for function parameter values and store them in the source's letParameterValueSpec
|
|
426
|
+
// 3. Prepend the let statements to the lambda body when we execute the query
|
|
427
|
+
const letFuncs: V1_ValueSpecification[] = [];
|
|
428
|
+
const parameterValues = (
|
|
429
|
+
await Promise.all(
|
|
430
|
+
source.lambda.parameters.map(async (parameter) => {
|
|
431
|
+
if (
|
|
432
|
+
parameter.genericType?.rawType instanceof V1_PackageableType
|
|
433
|
+
) {
|
|
434
|
+
const type = parameter.genericType.rawType.fullPath;
|
|
435
|
+
const defaultValueString =
|
|
436
|
+
queryInfo.defaultParameterValues?.find(
|
|
437
|
+
(val) => val.name === parameter.name,
|
|
438
|
+
)?.content;
|
|
439
|
+
const defaultValueSpec =
|
|
440
|
+
defaultValueString !== undefined
|
|
441
|
+
? await this.parseValueSpecification(defaultValueString)
|
|
442
|
+
: {
|
|
443
|
+
_type: V1_deserializeRawValueSpecificationType(type),
|
|
444
|
+
value: _defaultPrimitiveTypeValue(type),
|
|
445
|
+
};
|
|
446
|
+
if (defaultValueSpec instanceof V1_AppliedFunction) {
|
|
447
|
+
const letFunc = guaranteeType(
|
|
448
|
+
this.deserializeValueSpecification(
|
|
449
|
+
await this._engineServerClient.grammarToJSON_lambda(
|
|
450
|
+
`${LET_TOKEN} ${parameter.name} ${DataCubeQueryFilterOperator.EQUAL} ${defaultValueString}`,
|
|
451
|
+
'',
|
|
452
|
+
undefined,
|
|
453
|
+
undefined,
|
|
454
|
+
false,
|
|
455
|
+
),
|
|
456
|
+
),
|
|
457
|
+
V1_Lambda,
|
|
458
|
+
);
|
|
459
|
+
letFuncs.push(...letFunc.body);
|
|
460
|
+
} else {
|
|
461
|
+
const paramValue = new V1_ParameterValue();
|
|
462
|
+
paramValue.name = parameter.name;
|
|
463
|
+
paramValue.value = defaultValueSpec;
|
|
464
|
+
return paramValue;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return undefined;
|
|
468
|
+
}),
|
|
469
|
+
)
|
|
470
|
+
).filter(isNonNullable);
|
|
471
|
+
source.letParameterValueSpec = letFuncs;
|
|
472
|
+
source.parameterValues = parameterValues;
|
|
473
|
+
source.lambda.parameters = source.lambda.parameters.filter((param) =>
|
|
474
|
+
parameterValues.find((p) => p.name === param.name),
|
|
475
|
+
);
|
|
369
476
|
return source;
|
|
370
477
|
}
|
|
371
478
|
default:
|
|
@@ -434,6 +541,13 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
434
541
|
model: source.model,
|
|
435
542
|
})
|
|
436
543
|
).completions as CompletionItem[];
|
|
544
|
+
} else if (source instanceof UserDefinedFunctionDataCubeSource) {
|
|
545
|
+
return (
|
|
546
|
+
await this._engineServerClient.completeCode({
|
|
547
|
+
codeBlock,
|
|
548
|
+
model: source.model,
|
|
549
|
+
})
|
|
550
|
+
).completions as CompletionItem[];
|
|
437
551
|
} else if (source instanceof LegendQueryDataCubeSource) {
|
|
438
552
|
return (
|
|
439
553
|
await this._engineServerClient.completeCode({
|
|
@@ -524,8 +638,11 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
524
638
|
try {
|
|
525
639
|
if (source instanceof AdhocQueryDataCubeSource) {
|
|
526
640
|
result = await this._runQuery(query, source.model, undefined, options);
|
|
641
|
+
} else if (source instanceof UserDefinedFunctionDataCubeSource) {
|
|
642
|
+
result = await this._runQuery(query, source.model, undefined, options);
|
|
527
643
|
} else if (source instanceof LegendQueryDataCubeSource) {
|
|
528
644
|
query.parameters = source.lambda.parameters;
|
|
645
|
+
query.body = [...source.letParameterValueSpec, ...query.body];
|
|
529
646
|
result = await this._runQuery(
|
|
530
647
|
query,
|
|
531
648
|
source.model,
|
|
@@ -598,19 +715,14 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
598
715
|
TDSExecutionResult,
|
|
599
716
|
`Can't process execution result: expected tabular data set format`,
|
|
600
717
|
);
|
|
601
|
-
const endTime = performance.now();
|
|
602
|
-
const queryCode = await queryCodePromise;
|
|
603
|
-
const sql = guaranteeNonNullable(
|
|
604
|
-
result.activities?.[0] instanceof RelationalExecutionActivities
|
|
605
|
-
? result.activities[0].sql
|
|
606
|
-
: undefined,
|
|
607
|
-
`Can't process execution result: failed to extract generated SQL`,
|
|
608
|
-
);
|
|
609
718
|
return {
|
|
610
719
|
result: result,
|
|
611
|
-
executedQuery:
|
|
612
|
-
executedSQL:
|
|
613
|
-
|
|
720
|
+
executedQuery: await queryCodePromise,
|
|
721
|
+
executedSQL:
|
|
722
|
+
result.activities?.at(-1) instanceof RelationalExecutionActivities
|
|
723
|
+
? (result.activities.at(-1) as RelationalExecutionActivities).sql
|
|
724
|
+
: undefined,
|
|
725
|
+
executionTime: performance.now() - startTime,
|
|
614
726
|
};
|
|
615
727
|
} catch (error) {
|
|
616
728
|
assertErrorThrown(error);
|
|
@@ -631,6 +743,11 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
631
743
|
DataCubeFunction.FROM,
|
|
632
744
|
[_elementPtr(source.runtime)].filter(isNonNullable),
|
|
633
745
|
);
|
|
746
|
+
} else if (source instanceof UserDefinedFunctionDataCubeSource) {
|
|
747
|
+
return _function(
|
|
748
|
+
DataCubeFunction.FROM,
|
|
749
|
+
[_elementPtr(source.runtime)].filter(isNonNullable),
|
|
750
|
+
);
|
|
634
751
|
} else if (source instanceof LegendQueryDataCubeSource) {
|
|
635
752
|
return _function(
|
|
636
753
|
DataCubeFunction.FROM,
|
|
@@ -748,6 +865,8 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
748
865
|
) {
|
|
749
866
|
if (source instanceof AdhocQueryDataCubeSource) {
|
|
750
867
|
return this._getLambdaRelationType(query, source.model);
|
|
868
|
+
} else if (source instanceof UserDefinedFunctionDataCubeSource) {
|
|
869
|
+
return this._getLambdaRelationType(query, source.model);
|
|
751
870
|
} else if (source instanceof LegendQueryDataCubeSource) {
|
|
752
871
|
return this._getLambdaRelationType(query, source.model);
|
|
753
872
|
} else if (source instanceof CachedDataCubeSource) {
|
|
@@ -853,93 +972,10 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
|
|
|
853
972
|
}
|
|
854
973
|
}
|
|
855
974
|
|
|
856
|
-
async ingestLocalFileData(
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
const {
|
|
861
|
-
schema: schemaName,
|
|
862
|
-
table: tableName,
|
|
863
|
-
tableSpec,
|
|
864
|
-
} = await this._duckDBEngine.ingestLocalFileData(data, format);
|
|
865
|
-
|
|
866
|
-
const { model, database, schema, table, runtime } =
|
|
867
|
-
this._synthesizeMinimalModelContext({
|
|
868
|
-
schemaName,
|
|
869
|
-
tableName,
|
|
870
|
-
tableColumns: tableSpec.map((col) => {
|
|
871
|
-
const column = new V1_Column();
|
|
872
|
-
column.name = col[0] as string;
|
|
873
|
-
// TODO: confirm this is in accordance to engine
|
|
874
|
-
// check if we have a duckdb enum mapping
|
|
875
|
-
// See https://duckdb.org/docs/sql/data_types/overview.html
|
|
876
|
-
switch (col[1] as string) {
|
|
877
|
-
case 'BIT': {
|
|
878
|
-
column.type = new V1_Bit();
|
|
879
|
-
break;
|
|
880
|
-
}
|
|
881
|
-
case 'BOOLEAN': {
|
|
882
|
-
// TODO: understand why boolean is not present in relationalDataType
|
|
883
|
-
column.type = new V1_VarChar();
|
|
884
|
-
break;
|
|
885
|
-
}
|
|
886
|
-
case 'DATE': {
|
|
887
|
-
column.type = new V1_Date();
|
|
888
|
-
break;
|
|
889
|
-
}
|
|
890
|
-
case 'DECIMAL': {
|
|
891
|
-
column.type = new V1_Decimal();
|
|
892
|
-
break;
|
|
893
|
-
}
|
|
894
|
-
case 'DOUBLE': {
|
|
895
|
-
column.type = new V1_Double();
|
|
896
|
-
break;
|
|
897
|
-
}
|
|
898
|
-
case 'FLOAT': {
|
|
899
|
-
column.type = new V1_Float();
|
|
900
|
-
break;
|
|
901
|
-
}
|
|
902
|
-
case 'INTEGER': {
|
|
903
|
-
column.type = new V1_Integer();
|
|
904
|
-
break;
|
|
905
|
-
}
|
|
906
|
-
case 'TININT': {
|
|
907
|
-
column.type = new V1_TinyInt();
|
|
908
|
-
break;
|
|
909
|
-
}
|
|
910
|
-
case 'SMALLINT': {
|
|
911
|
-
column.type = new V1_SmallInt();
|
|
912
|
-
break;
|
|
913
|
-
}
|
|
914
|
-
case 'BIGINT': {
|
|
915
|
-
column.type = new V1_BigInt();
|
|
916
|
-
break;
|
|
917
|
-
}
|
|
918
|
-
case 'TIMESTAMP': {
|
|
919
|
-
column.type = new V1_Timestamp();
|
|
920
|
-
break;
|
|
921
|
-
}
|
|
922
|
-
case 'VARCHAR': {
|
|
923
|
-
column.type = new V1_VarChar();
|
|
924
|
-
break;
|
|
925
|
-
}
|
|
926
|
-
default: {
|
|
927
|
-
throw new UnsupportedOperationError(
|
|
928
|
-
`Can't ingest local file data: failed to find matching relational data type for DuckDB type '${col[1]}' when synthesizing table definition`,
|
|
929
|
-
);
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
return column;
|
|
933
|
-
}),
|
|
934
|
-
});
|
|
935
|
-
|
|
936
|
-
const source = new LocalFileDataCubeSource();
|
|
937
|
-
source.model = model;
|
|
938
|
-
source.runtime = runtime.path;
|
|
939
|
-
source.db = database.path;
|
|
940
|
-
source.schema = schema.name;
|
|
941
|
-
source.table = table.name;
|
|
942
|
-
return source;
|
|
975
|
+
async ingestLocalFileData(data: string, format: string, refId?: string) {
|
|
976
|
+
const { dbReference, columnNames } =
|
|
977
|
+
await this._duckDBEngine.ingestLocalFileData(data, format, refId);
|
|
978
|
+
return { dbReference, columnNames };
|
|
943
979
|
}
|
|
944
980
|
|
|
945
981
|
private _synthesizeMinimalModelContext(data: {
|