@finos/legend-application-data-cube 0.1.20 → 0.2.0

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.
@@ -23,7 +23,6 @@ import { LegendDataCubeNewQueryBuilder } from '../../components/query-builder/Le
23
23
  import { AdhocQueryDataCubeSourceBuilderState } from './source-builder/AdhocQueryDataCubeSourceBuilderState.js';
24
24
  import { LegendDataCubeQueryBuilderState, } from './LegendDataCubeQueryBuilderStore.js';
25
25
  import { generateQueryBuilderRoute } from '../../__lib__/LegendDataCubeNavigation.js';
26
- import { LEGEND_QUERY_DATA_CUBE_SOURCE_TYPE, RawLegendQueryDataCubeSource, } from '../model/LegendQueryDataCubeSource.js';
27
26
  export class LegendDataCubeNewQueryState {
28
27
  _application;
29
28
  _store;
@@ -48,24 +47,6 @@ export class LegendDataCubeNewQueryState {
48
47
  });
49
48
  this.sourceBuilder = this.createSourceBuilder(LegendDataCubeSourceBuilderType.LEGEND_QUERY);
50
49
  }
51
- initWithSourceData(source) {
52
- if (source._type === LEGEND_QUERY_DATA_CUBE_SOURCE_TYPE) {
53
- this.changeSourceBuilder(LegendDataCubeSourceBuilderType.LEGEND_QUERY);
54
- const serializedSourceData = RawLegendQueryDataCubeSource.serialization.fromJson(source);
55
- this._store.graphManager
56
- .getLightQuery(serializedSourceData.queryId)
57
- .then(async (currentQuery) => {
58
- if (this.sourceBuilder instanceof LegendQueryDataCubeSourceBuilderState) {
59
- this.sourceBuilder.query = currentQuery;
60
- await this.finalize();
61
- }
62
- })
63
- .catch(this._application.alertUnhandledError);
64
- }
65
- else {
66
- this._application.notificationService.notifyError(`Can't generate query: source of type ${source._type} is not supported`);
67
- }
68
- }
69
50
  changeSourceBuilder(type, skipCheck) {
70
51
  if (this.sourceBuilder.label !== type || skipCheck) {
71
52
  this.sourceBuilder = this.createSourceBuilder(type);
@@ -81,13 +62,13 @@ export class LegendDataCubeNewQueryState {
81
62
  throw new UnsupportedOperationError(`Can't create source builder for unsupported type '${type}'`);
82
63
  }
83
64
  }
84
- async finalize() {
85
- if (!this.sourceBuilder.isValid) {
65
+ async finalize(sourceData) {
66
+ if (!this.sourceBuilder.isValid && !sourceData) {
86
67
  throw new IllegalStateError(`Can't generate query: source is not valid`);
87
68
  }
88
69
  this.finalizeState.inProgress();
89
70
  try {
90
- const source = await this.sourceBuilder.build();
71
+ const source = sourceData ?? (await this.sourceBuilder.build());
91
72
  const query = new DataCubeQuery();
92
73
  const processedSource = await this._engine.processQuerySource(source);
93
74
  query.source = source;
@@ -1 +1 @@
1
- {"version":3,"file":"LegendDataCubeNewQueryState.js","sourceRoot":"","sources":["../../../src/stores/query-builder/LegendDataCubeNewQueryState.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LegendDataCubeNewQueryState.js","sourceRoot":"","sources":["../../../src/stores/query-builder/LegendDataCubeNewQueryState.tsx"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@finos/legend-application-data-cube",
3
- "version": "0.1.20",
3
+ "version": "0.2.0",
4
4
  "description": "Legend DataCube application core",
5
5
  "keywords": [
6
6
  "legend",
@@ -42,12 +42,13 @@
42
42
  "test:watch": "jest --watch"
43
43
  },
44
44
  "dependencies": {
45
+ "@duckdb/duckdb-wasm": "1.29.0",
45
46
  "@finos/legend-application": "16.0.22",
46
47
  "@finos/legend-art": "7.1.79",
47
- "@finos/legend-code-editor": "2.0.41",
48
- "@finos/legend-data-cube": "0.0.48",
49
- "@finos/legend-graph": "32.0.9",
50
- "@finos/legend-query-builder": "4.15.46",
48
+ "@finos/legend-code-editor": "2.0.42",
49
+ "@finos/legend-data-cube": "0.1.0",
50
+ "@finos/legend-graph": "32.1.0",
51
+ "@finos/legend-query-builder": "4.16.0",
51
52
  "@finos/legend-server-depot": "6.0.78",
52
53
  "@finos/legend-shared": "11.0.1",
53
54
  "@finos/legend-storage": "3.0.120",
@@ -60,7 +61,7 @@
60
61
  "serializr": "3.0.3"
61
62
  },
62
63
  "devDependencies": {
63
- "@finos/legend-dev-utils": "2.1.37",
64
+ "@finos/legend-dev-utils": "2.2.0",
64
65
  "@jest/globals": "29.7.0",
65
66
  "cross-env": "7.0.3",
66
67
  "eslint": "9.19.0",
@@ -33,7 +33,7 @@ import {
33
33
  } from '../../__lib__/LegendDataCubeNavigation.js';
34
34
  import { useEffect } from 'react';
35
35
  import { LegendDataCubeSettingStorageKey } from '../../__lib__/LegendDataCubeSetting.js';
36
- import type { PlainObject } from '@finos/legend-shared';
36
+ import { assertErrorThrown, type PlainObject } from '@finos/legend-shared';
37
37
 
38
38
  const LegendDataCubeQueryBuilderHeader = observer(() => {
39
39
  const store = useLegendDataCubeQueryBuilderStore();
@@ -76,8 +76,16 @@ export const LegendDataCubeQueryBuilder = withLegendDataCubeQueryBuilderStore(
76
76
 
77
77
  useEffect(() => {
78
78
  if (sourceData) {
79
- const sourceDataJson = JSON.parse(atob(sourceData)) as PlainObject;
80
- store.newQueryState.initWithSourceData(sourceDataJson);
79
+ try {
80
+ const sourceDataJson = JSON.parse(
81
+ decodeURIComponent(atob(sourceData)),
82
+ ) as PlainObject;
83
+ store.newQueryState
84
+ .finalize(sourceDataJson)
85
+ .catch((error) => store.alertService.alertUnhandledError(error));
86
+ } catch (error) {
87
+ assertErrorThrown(error);
88
+ }
81
89
  } else if (queryId !== store.builder?.persistentQuery?.id) {
82
90
  store
83
91
  .loadQuery(queryId)
@@ -149,6 +157,7 @@ export const LegendDataCubeQueryBuilder = withLegendDataCubeQueryBuilderStore(
149
157
  );
150
158
  },
151
159
  documentationUrl: application.documentationService.url,
160
+ enableCache: true,
152
161
  }}
153
162
  />
154
163
  );
@@ -0,0 +1,22 @@
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
+ declare module '*.wasm' {
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ const value: any;
20
+ // eslint-disable-next-line import/no-default-export
21
+ export default value;
22
+ }
@@ -0,0 +1,132 @@
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 * as duckdb from '@duckdb/duckdb-wasm';
18
+ import duckdb_wasm from '@duckdb/duckdb-wasm/dist/duckdb-mvp.wasm';
19
+ import duckdb_wasm_next from '@duckdb/duckdb-wasm/dist/duckdb-eh.wasm';
20
+ import {
21
+ TDSExecutionResult,
22
+ TDSRow,
23
+ TabularDataSet,
24
+ } from '@finos/legend-graph';
25
+ import type { AsyncDuckDBConnection } from '@duckdb/duckdb-wasm';
26
+ import { assertNonNullable } from '@finos/legend-shared';
27
+
28
+ export class LegendDataCubeDataCubeCacheEngine {
29
+ private _database?: duckdb.AsyncDuckDB | undefined;
30
+ private _connection?: AsyncDuckDBConnection | undefined;
31
+
32
+ // Documentation: https://duckdb.org/docs/api/wasm/instantiation.html
33
+ async initializeDuckDb(result: TDSExecutionResult) {
34
+ const MANUAL_BUNDLES: duckdb.DuckDBBundles = {
35
+ mvp: {
36
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
37
+ mainModule: duckdb_wasm,
38
+ mainWorker: new URL(
39
+ '@duckdb/duckdb-wasm/dist/duckdb-browser-mvp.worker.js',
40
+ import.meta.url,
41
+ ).toString(),
42
+ },
43
+ eh: {
44
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
45
+ mainModule: duckdb_wasm_next,
46
+ mainWorker: new URL(
47
+ '@duckdb/duckdb-wasm/dist/duckdb-browser-eh.worker.js',
48
+ import.meta.url,
49
+ ).toString(),
50
+ },
51
+ };
52
+ // Select a bundle based on browser checks
53
+ const bundle = await duckdb.selectBundle(MANUAL_BUNDLES);
54
+ // Instantiate the asynchronus version of DuckDB-wasm
55
+ assertNonNullable(bundle.mainWorker, `Can't initialize duck db`);
56
+ const worker = new Worker(bundle.mainWorker);
57
+ const logger = new duckdb.ConsoleLogger();
58
+ this._database = new duckdb.AsyncDuckDB(logger, worker);
59
+ await this._database.instantiate(bundle.mainModule, bundle.pthreadWorker);
60
+ this._connection = await this._database.connect();
61
+
62
+ const columns: string[] = [];
63
+ result.builder.columns.forEach((col) =>
64
+ columns.push(`"${col.name}" ${this.getDuckDbType(col.type)}`),
65
+ );
66
+
67
+ const CREATE_TABLE_SQL = `CREATE TABLE cached_tbl (${columns.join(',')})`;
68
+ await this._connection.query(CREATE_TABLE_SQL);
69
+
70
+ const rowString: string[] = [];
71
+
72
+ result.result.rows.forEach((row) => {
73
+ const updatedRows = row.values.map((val) => {
74
+ if (val !== null && typeof val === 'string') {
75
+ return `'${val.replaceAll(`'`, `''`)}'`;
76
+ } else if (val === null) {
77
+ return `NULL`;
78
+ }
79
+ return val;
80
+ });
81
+ rowString.push(`(${updatedRows.join(',')})`);
82
+ });
83
+
84
+ const INSERT_TABLE_SQL = `INSERT INTO cached_tbl VALUES ${rowString.join(',')}`;
85
+
86
+ await this._connection.query(INSERT_TABLE_SQL);
87
+ }
88
+
89
+ async runQuery(sql: string) {
90
+ const result = await this._connection?.query(sql);
91
+ const columnNames = Object.keys(result?.toArray().at(0));
92
+ const rows = result?.toArray().map((row) => {
93
+ const values = new TDSRow();
94
+ values.values = columnNames.map(
95
+ (column) => row[column] as string | number | boolean | null,
96
+ );
97
+ return values;
98
+ });
99
+ const tdsExecutionResult = new TDSExecutionResult();
100
+ const tds = new TabularDataSet();
101
+ tds.columns = columnNames;
102
+ tds.rows = rows !== undefined ? rows : [new TDSRow()];
103
+ tdsExecutionResult.result = tds;
104
+ return tdsExecutionResult;
105
+ }
106
+
107
+ async clearDuckDb() {
108
+ await this._connection?.close();
109
+ await this._database?.flushFiles();
110
+ await this._database?.terminate();
111
+ }
112
+
113
+ private getDuckDbType(type: string | undefined): string {
114
+ switch (type?.toLowerCase()) {
115
+ //TODO: mapping from tds build to duckdb data types
116
+ case 'string':
117
+ return 'VARCHAR';
118
+ case 'boolean':
119
+ return 'BOOLEAN';
120
+ case 'bigint':
121
+ return 'BIGINT';
122
+ case 'number':
123
+ return 'DOUBLE';
124
+ case 'integer':
125
+ return 'INTEGER';
126
+ case 'date':
127
+ return 'TIMESTAMP';
128
+ default:
129
+ return 'VARCHAR';
130
+ }
131
+ }
132
+ }
@@ -15,8 +15,8 @@
15
15
  */
16
16
 
17
17
  import {
18
- type V1_Lambda,
19
- type V1_ValueSpecification,
18
+ V1_Lambda,
19
+ V1_ValueSpecification,
20
20
  type V1_EngineServerClient,
21
21
  V1_PureGraphManager,
22
22
  type V1_PureModelContext,
@@ -44,6 +44,33 @@ import {
44
44
  V1_PackageableType,
45
45
  V1_deserializeRawValueSpecificationType,
46
46
  V1_Protocol,
47
+ type V1_ExecutionPlan,
48
+ V1_deserializeExecutionPlan,
49
+ V1_SQLExecutionNode,
50
+ V1_SimpleExecutionPlan,
51
+ V1_Binary,
52
+ V1_ClassInstance,
53
+ V1_ClassInstanceType,
54
+ V1_Column,
55
+ V1_Database,
56
+ V1_Date,
57
+ V1_Double,
58
+ V1_DuckDBDatasourceSpecification,
59
+ V1_EngineRuntime,
60
+ V1_IdentifiedConnection,
61
+ V1_Integer,
62
+ V1_PackageableElementPointer,
63
+ V1_PackageableRuntime,
64
+ V1_PureModelContextData,
65
+ V1_RelationStoreAccessor,
66
+ type V1_RelationalDataType,
67
+ V1_RelationalDatabaseConnection,
68
+ V1_Schema,
69
+ V1_StoreConnections,
70
+ V1_Table,
71
+ V1_TestAuthenticationStrategy,
72
+ V1_VarChar,
73
+ type TDSBuilder,
47
74
  } from '@finos/legend-graph';
48
75
  import {
49
76
  _elementPtr,
@@ -61,6 +88,8 @@ import {
61
88
  _deserializeValueSpecification,
62
89
  _defaultPrimitiveTypeValue,
63
90
  type DataCubeExecutionOptions,
91
+ CachedDataCubeSource,
92
+ type DataCubeQuerySnapshot,
64
93
  } from '@finos/legend-data-cube';
65
94
  import {
66
95
  isNonNullable,
@@ -72,8 +101,11 @@ import {
72
101
  HttpStatus,
73
102
  at,
74
103
  assertType,
104
+ guaranteeType,
105
+ assertNonNullable,
75
106
  } from '@finos/legend-shared';
76
107
  import type { LegendDataCubeApplicationStore } from './LegendDataCubeBaseStore.js';
108
+ import { LegendDataCubeDataCubeCacheEngine } from './LegendDataCubeCacheManager.js';
77
109
  import { APPLICATION_EVENT } from '@finos/legend-application';
78
110
  import {
79
111
  LEGEND_QUERY_DATA_CUBE_SOURCE_TYPE,
@@ -91,6 +123,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
91
123
  private readonly _depotServerClient: DepotServerClient;
92
124
  private readonly _engineServerClient: V1_EngineServerClient;
93
125
  private readonly _graphManager: V1_PureGraphManager;
126
+ private readonly _cacheManager: LegendDataCubeDataCubeCacheEngine;
94
127
 
95
128
  constructor(
96
129
  application: LegendDataCubeApplicationStore,
@@ -104,6 +137,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
104
137
  this._depotServerClient = depotServerClient;
105
138
  this._engineServerClient = engineServerClient;
106
139
  this._graphManager = graphManager;
140
+ this._cacheManager = new LegendDataCubeDataCubeCacheEngine();
107
141
  }
108
142
 
109
143
  // ---------------------------------- IMPLEMENTATION ----------------------------------
@@ -369,6 +403,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
369
403
  ) {
370
404
  const queryCodePromise = this.getValueSpecificationCode(query);
371
405
  let result: ExecutionResult;
406
+ const startTime = performance.now();
372
407
  if (source instanceof AdhocQueryDataCubeSource) {
373
408
  result = await this._runQuery(query, source.model, undefined, options);
374
409
  } else if (source instanceof LegendQueryDataCubeSource) {
@@ -379,6 +414,35 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
379
414
  source.parameterValues,
380
415
  options,
381
416
  );
417
+ } else if (source instanceof CachedDataCubeSource) {
418
+ //execute plan
419
+ const executionPlan = await this.generateExecutionPlan(
420
+ query,
421
+ source.model,
422
+ [],
423
+ options,
424
+ );
425
+ let sql;
426
+ if (executionPlan instanceof V1_SimpleExecutionPlan) {
427
+ sql = executionPlan.rootExecutionNode.executionNodes
428
+ .filter((node) => node instanceof V1_SQLExecutionNode)
429
+ .map((x) =>
430
+ guaranteeType(
431
+ x,
432
+ V1_SQLExecutionNode,
433
+ `Can't generate sql for the query`,
434
+ ),
435
+ )
436
+ .at(-1)?.sqlQuery;
437
+ }
438
+ assertNonNullable(sql, `Can't generate sql for the query`);
439
+ const endTime = performance.now();
440
+ return {
441
+ executedQuery: await queryCodePromise,
442
+ executedSQL: sql,
443
+ result: await this._cacheManager.runQuery(sql),
444
+ executionTime: endTime - startTime,
445
+ };
382
446
  } else {
383
447
  throw new UnsupportedOperationError(
384
448
  `Can't execute query with unsupported source`,
@@ -389,6 +453,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
389
453
  TDSExecutionResult,
390
454
  `Can't extract execution result: expected tabular data set format`,
391
455
  );
456
+ const endTime = performance.now();
392
457
  const queryCode = await queryCodePromise;
393
458
  const sql =
394
459
  result.activities?.[0] instanceof RelationalExecutionActivities
@@ -401,6 +466,7 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
401
466
  result: result,
402
467
  executedQuery: queryCode,
403
468
  executedSQL: sql,
469
+ executionTime: endTime - startTime,
404
470
  };
405
471
  }
406
472
 
@@ -418,10 +484,25 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
418
484
  _elementPtr(source.runtime),
419
485
  ].filter(isNonNullable),
420
486
  );
487
+ } else if (source instanceof CachedDataCubeSource) {
488
+ return _function(
489
+ DataCubeFunction.FROM,
490
+ [_elementPtr(source.runtime)].filter(isNonNullable),
491
+ );
421
492
  }
422
493
  return undefined;
423
494
  }
424
495
 
496
+ override processInitialSnapshot(
497
+ source: DataCubeSource,
498
+ snapshot: DataCubeQuerySnapshot,
499
+ ): DataCubeQuerySnapshot {
500
+ if (source instanceof LegendQueryDataCubeSource) {
501
+ snapshot.data.configuration.name = source.info.name;
502
+ }
503
+ return snapshot;
504
+ }
505
+
425
506
  // ---------------------------------- UTILITIES ----------------------------------
426
507
 
427
508
  private async _getQueryRelationType(
@@ -432,6 +513,8 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
432
513
  return this._getLambdaRelationType(query, source.model);
433
514
  } else if (source instanceof LegendQueryDataCubeSource) {
434
515
  return this._getLambdaRelationType(query, source.model);
516
+ } else if (source instanceof CachedDataCubeSource) {
517
+ return this._getLambdaRelationType(query, serialize(source.model));
435
518
  }
436
519
  throw new UnsupportedOperationError(
437
520
  `Can't get relation type for lambda with unsupported source`,
@@ -486,6 +569,165 @@ export class LegendDataCubeDataCubeEngine extends DataCubeEngine {
486
569
  );
487
570
  }
488
571
 
572
+ private async generateExecutionPlan(
573
+ query: V1_Lambda,
574
+ model: V1_PureModelContext,
575
+ parameterValues?: V1_ParameterValue[] | undefined,
576
+ options?: DataCubeExecutionOptions | undefined,
577
+ ): Promise<V1_ExecutionPlan> {
578
+ return V1_deserializeExecutionPlan(
579
+ await this._engineServerClient.generatePlan({
580
+ clientVersion:
581
+ options?.clientVersion ??
582
+ // eslint-disable-next-line no-process-env
583
+ (process.env.NODE_ENV === 'development'
584
+ ? PureClientVersion.VX_X_X
585
+ : undefined),
586
+ function: _serializeValueSpecification(query),
587
+ model: serialize(model),
588
+ context: serialize(
589
+ V1_rawBaseExecutionContextModelSchema,
590
+ new V1_RawBaseExecutionContext(),
591
+ ),
592
+ parameterValues: (parameterValues ?? []).map((parameterValue) =>
593
+ serialize(V1_parameterValueModelSchema, parameterValue),
594
+ ),
595
+ }),
596
+ );
597
+ }
598
+
599
+ // ---------------------------------- CACHING --------------------------------------
600
+
601
+ override async initializeCache(
602
+ source: DataCubeSource,
603
+ ): Promise<CachedDataCubeSource | undefined> {
604
+ if (source instanceof LegendQueryDataCubeSource) {
605
+ const fromQuery = this.buildExecutionContext(source);
606
+ fromQuery?.parameters.unshift(source.query);
607
+ const valSpec = guaranteeType(
608
+ fromQuery,
609
+ V1_ValueSpecification,
610
+ 'Query is not a valueSpecification',
611
+ );
612
+ const queryLambda = new V1_Lambda();
613
+ queryLambda.body = [valSpec];
614
+ const resultQuery = await this.executeQuery(
615
+ queryLambda,
616
+ source,
617
+ undefined,
618
+ );
619
+ const result = resultQuery.result;
620
+ await this._cacheManager.initializeDuckDb(result);
621
+ return this._synthesizeCachedSource(source, result.builder);
622
+ }
623
+ return undefined;
624
+ }
625
+
626
+ override async clearCache() {
627
+ await this._cacheManager.clearDuckDb();
628
+ }
629
+
630
+ // --------------------------------- CACHING UTILITY -------------------------------
631
+
632
+ private _synthesizeCachedSource(
633
+ source: LegendQueryDataCubeSource,
634
+ builder: TDSBuilder,
635
+ ) {
636
+ const cachedSource = new CachedDataCubeSource();
637
+ cachedSource.columns = source.columns;
638
+ cachedSource.query = this._synthesizeQuery([
639
+ 'local::duckdb::cachedStore',
640
+ 'main',
641
+ 'cached_tbl',
642
+ ]);
643
+ cachedSource.model = this._synthesizeModel(builder);
644
+ cachedSource.runtime = 'local::duckdb::runtime';
645
+ return cachedSource;
646
+ }
647
+
648
+ private _synthesizeQuery(databaseAccessor: string[]) {
649
+ const classInstance = new V1_ClassInstance();
650
+ classInstance.type = V1_ClassInstanceType.RELATION_STORE_ACCESSOR;
651
+ const storeAccessor = new V1_RelationStoreAccessor();
652
+ storeAccessor.path = databaseAccessor;
653
+ classInstance.value = storeAccessor;
654
+ return classInstance;
655
+ }
656
+
657
+ private _synthesizeModel(builder: TDSBuilder) {
658
+ // synthesize table
659
+ const table = new V1_Table();
660
+ table.name = 'cached_tbl';
661
+ table.columns = builder.columns.map((col) => {
662
+ const column = new V1_Column();
663
+ column.name = col.name;
664
+ column.type = this._getColumnType(col.type);
665
+ return column;
666
+ });
667
+ // synthesize schema
668
+ const schema = new V1_Schema();
669
+ schema.name = 'main';
670
+ schema.tables = [table];
671
+ // synthesize database
672
+ const database = new V1_Database();
673
+ database.name = 'cachedStore';
674
+ database.package = 'local::duckdb';
675
+ database.schemas = [schema];
676
+
677
+ // build connection
678
+ const connection = new V1_RelationalDatabaseConnection();
679
+ connection.databaseType = 'DuckDB';
680
+ connection.type = 'DuckDB';
681
+ const dataSourceSpec = new V1_DuckDBDatasourceSpecification();
682
+ dataSourceSpec.path = '/temp/path';
683
+ connection.store = 'local::duckdb::cachedStore';
684
+ connection.datasourceSpecification = dataSourceSpec;
685
+ connection.authenticationStrategy = new V1_TestAuthenticationStrategy();
686
+
687
+ // build runtime
688
+ const runtime = new V1_EngineRuntime();
689
+ const storeConnections = new V1_StoreConnections();
690
+ storeConnections.store = new V1_PackageableElementPointer(
691
+ 'STORE',
692
+ `${database.package}::${database.name}`,
693
+ );
694
+ const idConnection = new V1_IdentifiedConnection();
695
+ idConnection.connection = connection;
696
+ idConnection.id = 'local_duckdb_connection';
697
+ storeConnections.storeConnections = [idConnection];
698
+ runtime.connections = [storeConnections];
699
+
700
+ const packageableRuntime = new V1_PackageableRuntime();
701
+ packageableRuntime.runtimeValue = runtime;
702
+ packageableRuntime.package = 'local::duckdb';
703
+ packageableRuntime.name = 'runtime';
704
+
705
+ const pmcd = new V1_PureModelContextData();
706
+ pmcd.elements = [database, packageableRuntime];
707
+ return pmcd;
708
+ }
709
+
710
+ // TODO: need a better way to infer datatype from tds builder
711
+ private _getColumnType(type: string | undefined): V1_RelationalDataType {
712
+ if (type === undefined) {
713
+ throw Error('Unsupported data type');
714
+ }
715
+ switch (type) {
716
+ case 'string':
717
+ return new V1_VarChar();
718
+ case 'integer':
719
+ return new V1_Integer();
720
+ case 'date':
721
+ return new V1_Date();
722
+ case 'boolean':
723
+ return new V1_Binary();
724
+ case 'number':
725
+ return new V1_Double();
726
+ default:
727
+ return new V1_VarChar();
728
+ }
729
+ }
730
+
489
731
  // ---------------------------------- APPLICATION ----------------------------------
490
732
 
491
733
  override logDebug(message: string, ...data: unknown[]) {